diff options
65 files changed, 2921 insertions, 1187 deletions
diff --git a/release/scripts/modules/rna_prop_ui.py b/release/scripts/modules/rna_prop_ui.py index bafa2b28bbf..26a2f9ad89b 100644 --- a/release/scripts/modules/rna_prop_ui.py +++ b/release/scripts/modules/rna_prop_ui.py @@ -30,24 +30,6 @@ ARRAY_TYPES = (list, tuple, IDPropertyArray, Vector) MAX_DISPLAY_ROWS = 4 -def rna_idprop_ui_get(item, *, create=True): - try: - return item['_RNA_UI'] - except: - if create: - item['_RNA_UI'] = {} - return item['_RNA_UI'] - else: - return None - - -def rna_idprop_ui_del(item): - try: - del item['_RNA_UI'] - except KeyError: - pass - - def rna_idprop_quote_path(prop): return "[\"%s\"]" % bpy.utils.escape_identifier(prop) @@ -59,32 +41,9 @@ def rna_idprop_ui_prop_update(item, prop): prop_rna.update() -def rna_idprop_ui_prop_get(item, prop, *, create=True): - - rna_ui = rna_idprop_ui_get(item, create=create) - - if rna_ui is None: - return None - - try: - return rna_ui[prop] - except: - rna_ui[prop] = {} - return rna_ui[prop] - - -def rna_idprop_ui_prop_clear(item, prop, *, remove=True): - rna_ui = rna_idprop_ui_get(item, create=False) - - if rna_ui is None: - return - - try: - del rna_ui[prop] - except KeyError: - pass - if remove and len(item.keys()) == 1: - rna_idprop_ui_del(item) +def rna_idprop_ui_prop_clear(item, prop): + ui_data = item.id_properties_ui(prop) + ui_data.clear() def rna_idprop_context_value(context, context_member, property_type): @@ -106,8 +65,7 @@ def rna_idprop_context_value(context, context_member, property_type): def rna_idprop_has_properties(rna_item): keys = rna_item.keys() - nbr_props = len(keys) - return (nbr_props > 1) or (nbr_props and '_RNA_UI' not in keys) + return bool(keys) def rna_idprop_value_to_python(value): @@ -126,31 +84,8 @@ def rna_idprop_value_item_type(value): def rna_idprop_ui_prop_default_set(item, prop, value): - defvalue = None - try: - prop_type, is_array = rna_idprop_value_item_type(item[prop]) - - if prop_type in {int, float, str}: - if is_array and isinstance(value, ARRAY_TYPES): - value = [prop_type(item) for item in value] - if any(value): - defvalue = value - else: - defvalue = prop_type(value) - except KeyError: - pass - except ValueError: - pass - - if defvalue: - rna_ui = rna_idprop_ui_prop_get(item, prop, create=True) - rna_ui["default"] = defvalue - else: - rna_ui = rna_idprop_ui_prop_get(item, prop) - if rna_ui: - rna_ui.pop("default", None) - - return defvalue + ui_data = item.id_properties_ui(prop) + ui_data.update(default=value) def rna_idprop_ui_create( @@ -163,7 +98,7 @@ def rna_idprop_ui_create( ): """Create and initialize a custom property with limits, defaults and other settings.""" - proptype, is_array = rna_idprop_value_item_type(default) + proptype, _ = rna_idprop_value_item_type(default) # Sanitize limits if proptype is bool: @@ -180,35 +115,22 @@ def rna_idprop_ui_create( rna_idprop_ui_prop_update(item, prop) - # Clear the UI settings - rna_ui_group = rna_idprop_ui_get(item, create=True) - rna_ui_group[prop] = {} - rna_ui = rna_ui_group[prop] - - # Assign limits and default - if proptype in {int, float, bool}: - # The type must be exactly the same - rna_ui["min"] = proptype(min) - rna_ui["soft_min"] = proptype(soft_min) - rna_ui["max"] = proptype(max) - rna_ui["soft_max"] = proptype(soft_max) - - if default and (not is_array or any(default)): - rna_ui["default"] = default - - if is_array and subtype and subtype != 'NONE': - rna_ui["subtype"] = subtype - - # Assign other settings - if description is not None: - rna_ui["description"] = description + # Update the UI settings. + ui_data = item.id_properties_ui(prop) + ui_data.update( + subtype=subtype, + min=min, + max=max, + soft_min=soft_min, + soft_max=soft_max, + description=description, + default=default, + ) prop_path = rna_idprop_quote_path(prop) item.property_overridable_library_set(prop_path, overridable) - return rna_ui - def draw(layout, context, context_member, property_type, *, use_edit=True): @@ -254,10 +176,6 @@ def draw(layout, context, context_member, property_type, *, use_edit=True): flow = layout.grid_flow(row_major=False, columns=0, even_columns=True, even_rows=False, align=True) for key, val in items: - - if key == '_RNA_UI': - continue - is_rna = (key in rna_properties) # only show API defined for developers diff --git a/release/scripts/startup/bl_operators/object.py b/release/scripts/startup/bl_operators/object.py index d61bed71cab..df37db40102 100644 --- a/release/scripts/startup/bl_operators/object.py +++ b/release/scripts/startup/bl_operators/object.py @@ -970,7 +970,7 @@ class OBJECT_OT_assign_property_defaults(Operator): def assign_defaults(obj): from rna_prop_ui import rna_idprop_ui_prop_default_set - rna_properties = {'_RNA_UI'} | {prop.identifier for prop in obj.bl_rna.properties if prop.is_runtime} + rna_properties = {prop.identifier for prop in obj.bl_rna.properties if prop.is_runtime} for prop, value in obj.items(): if prop not in rna_properties: diff --git a/release/scripts/startup/bl_operators/wm.py b/release/scripts/startup/bl_operators/wm.py index 6a3830ad1e4..989c067efec 100644 --- a/release/scripts/startup/bl_operators/wm.py +++ b/release/scripts/startup/bl_operators/wm.py @@ -1388,10 +1388,8 @@ class WM_OT_properties_edit(Operator): def execute(self, context): from rna_prop_ui import ( - rna_idprop_ui_prop_get, rna_idprop_ui_prop_clear, rna_idprop_ui_prop_update, - rna_idprop_ui_prop_default_set, rna_idprop_value_item_type, ) @@ -1431,27 +1429,35 @@ class WM_OT_properties_edit(Operator): prop_type_new = type(prop_value) prop_type, is_array = rna_idprop_value_item_type(prop_value) - prop_ui = rna_idprop_ui_prop_get(item, prop) - - if prop_type in {float, int}: - prop_ui["min"] = prop_type(self.min) - prop_ui["max"] = prop_type(self.max) - - if self.use_soft_limits: - prop_ui["soft_min"] = prop_type(self.soft_min) - prop_ui["soft_max"] = prop_type(self.soft_max) - else: - prop_ui["soft_min"] = prop_type(self.min) - prop_ui["soft_max"] = prop_type(self.max) - - if prop_type == float and is_array and self.subtype != 'NONE': - prop_ui["subtype"] = self.subtype - else: - prop_ui.pop("subtype", None) - - prop_ui["description"] = self.description - - rna_idprop_ui_prop_default_set(item, prop, default_eval) + ui_data = item.id_properties_ui(prop) + ui_data.update(subtype=self.subtype, description=self.description) + + if prop_type == int: + if type(default_eval) == str: + self.report({'WARNING'}, "Could not evaluate number from default value") + default_eval = None + elif hasattr(default_eval, "__len__"): + default_eval = [int(round(value)) for value in default_eval] + ui_data.update( + min=int(round(self.min)), + max=int(round(self.max)), + soft_min=int(round(self.soft_min)), + soft_max=int(round(self.soft_max)), + default=default_eval, + ) + elif prop_type == float: + if type(default_eval) == str: + self.report({'WARNING'}, "Could not evaluate number from default value") + default_eval = None + ui_data.update( + min=self.min, + max=self.max, + soft_min=self.soft_min, + soft_max=self.soft_max, + default=default_eval, + ) + elif prop_type == str: + ui_data.update(default=self.default) # If we have changed the type of the property, update its potential anim curves! if prop_type_old != prop_type_new: @@ -1492,7 +1498,6 @@ class WM_OT_properties_edit(Operator): def invoke(self, context, _event): from rna_prop_ui import ( - rna_idprop_ui_prop_get, rna_idprop_value_to_python, rna_idprop_value_item_type ) @@ -1526,28 +1531,22 @@ class WM_OT_properties_edit(Operator): self.default = "" # setup defaults - prop_ui = rna_idprop_ui_prop_get(item, prop, create=False) - if prop_ui: - self.min = prop_ui.get("min", -1000000000) - self.max = prop_ui.get("max", 1000000000) - self.description = prop_ui.get("description", "") - - defval = prop_ui.get("default", None) - if defval is not None: - self.default = str(rna_idprop_value_to_python(defval)) - - self.soft_min = prop_ui.get("soft_min", self.min) - self.soft_max = prop_ui.get("soft_max", self.max) + ui_data = item.id_properties_ui(prop) + rna_data = ui_data.as_dict() + self.subtype = rna_data["subtype"] + if prop_type in {int, float}: + self.min = rna_data["min"] + self.max = rna_data["max"] + self.soft_min = rna_data["soft_min"] + self.soft_max = rna_data["soft_max"] self.use_soft_limits = ( self.min != self.soft_min or self.max != self.soft_max ) + if prop_type in {int, float, str}: + self.default = str(rna_data["default"]) - subtype = prop_ui.get("subtype", None) - else: - subtype = None - - self._init_subtype(prop_type, is_array, subtype) + self._init_subtype(prop_type, is_array, self.subtype) # store for comparison self._cmp_props = self._cmp_props_get() @@ -1688,7 +1687,6 @@ class WM_OT_properties_remove(Operator): def execute(self, context): from rna_prop_ui import ( - rna_idprop_ui_prop_clear, rna_idprop_ui_prop_update, ) data_path = self.data_path @@ -1701,7 +1699,6 @@ class WM_OT_properties_remove(Operator): prop = self.property rna_idprop_ui_prop_update(item, prop) del item[prop] - rna_idprop_ui_prop_clear(item, prop) return {'FINISHED'} diff --git a/release/scripts/startup/bl_ui/space_sequencer.py b/release/scripts/startup/bl_ui/space_sequencer.py index 20fb39e8c1f..79c1cb973ca 100644 --- a/release/scripts/startup/bl_ui/space_sequencer.py +++ b/release/scripts/startup/bl_ui/space_sequencer.py @@ -150,6 +150,9 @@ class SEQUENCER_HT_header(Header): if st.view_type in {'SEQUENCER', 'SEQUENCER_PREVIEW'}: tool_settings = context.tool_settings + sequencer_tool_settings = tool_settings.sequencer_tool_settings + row = layout.row(align=True) + row.prop(sequencer_tool_settings, "overlap_mode", text="") row = layout.row(align=True) row.prop(tool_settings, "use_snap_sequencer", text="") sub = row.row(align=True) diff --git a/release/scripts/startup/keyingsets_builtins.py b/release/scripts/startup/keyingsets_builtins.py index ceffeaaff6c..d9811b6c0ed 100644 --- a/release/scripts/startup/keyingsets_builtins.py +++ b/release/scripts/startup/keyingsets_builtins.py @@ -523,10 +523,6 @@ class WholeCharacterMixin: # go over all custom properties for bone for prop in bone.keys(): - # ignore special "_RNA_UI" used for UI editing - if prop == "_RNA_UI": - continue - # for now, just add all of 'em prop_rna = type(bone).bl_rna.properties.get(prop, None) if prop_rna is None: diff --git a/source/blender/blenfont/intern/blf_dir.c b/source/blender/blenfont/intern/blf_dir.c index 55c02b34f3d..f4a20faf109 100644 --- a/source/blender/blenfont/intern/blf_dir.c +++ b/source/blender/blenfont/intern/blf_dir.c @@ -47,9 +47,6 @@ #include "blf_internal.h" #include "blf_internal_types.h" -#include "BKE_global.h" -#include "BKE_main.h" - static ListBase global_font_dir = {NULL, NULL}; static DirBLF *blf_dir_find(const char *path) @@ -127,6 +124,8 @@ void BLF_dir_free(char **dirs, int count) char *blf_dir_search(const char *file) { + BLI_assert_msg(!BLI_path_is_rel(file), "Relative paths must always be expanded!"); + DirBLF *dir; char full_path[FILE_MAX]; char *s = NULL; @@ -140,11 +139,9 @@ char *blf_dir_search(const char *file) } if (!s) { - /* Assume file is either an absolute path, or a relative path to current directory. */ - BLI_strncpy(full_path, file, sizeof(full_path)); - BLI_path_abs(full_path, BKE_main_blendfile_path(G_MAIN)); - if (BLI_exists(full_path)) { - s = BLI_strdup(full_path); + /* This may be an absolute path which exists. */ + if (BLI_exists(file)) { + s = BLI_strdup(file); } } diff --git a/source/blender/blenfont/intern/blf_font.c b/source/blender/blenfont/intern/blf_font.c index dbcd1d6016d..27478bd7f8e 100644 --- a/source/blender/blenfont/intern/blf_font.c +++ b/source/blender/blenfont/intern/blf_font.c @@ -673,23 +673,23 @@ size_t blf_font_width_to_rstrlen( GlyphBLF *g, *g_prev; int pen_x, width_new; size_t i, i_prev, i_tmp; - char *s, *s_prev; + const char *s, *s_prev; GlyphCacheBLF *gc = blf_glyph_cache_acquire(font); const int width_i = (int)width; i = BLI_strnlen(str, str_len); - s = BLI_str_find_prev_char_utf8(str, &str[i]); - i = (size_t)((s != NULL) ? s - str : 0); - s_prev = BLI_str_find_prev_char_utf8(str, s); - i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0); + s = BLI_str_find_prev_char_utf8(&str[i], str); + i = (size_t)(s - str); + s_prev = BLI_str_find_prev_char_utf8(s, str); + i_prev = (size_t)(s_prev - str); i_tmp = i; g = blf_utf8_next_fast(font, gc, str, str_len, &i_tmp, &c); for (width_new = pen_x = 0; (s != NULL); i = i_prev, s = s_prev, c = c_prev, g = g_prev, g_prev = NULL, width_new = pen_x) { - s_prev = BLI_str_find_prev_char_utf8(str, s); - i_prev = (size_t)((s_prev != NULL) ? s_prev - str : 0); + s_prev = BLI_str_find_prev_char_utf8(s, str); + i_prev = (size_t)(s_prev - str); if (s_prev != NULL) { i_tmp = i_prev; diff --git a/source/blender/blenkernel/BKE_blender_version.h b/source/blender/blenkernel/BKE_blender_version.h index 4ed4225c836..315a77499e5 100644 --- a/source/blender/blenkernel/BKE_blender_version.h +++ b/source/blender/blenkernel/BKE_blender_version.h @@ -39,7 +39,7 @@ extern "C" { /* Blender file format version. */ #define BLENDER_FILE_VERSION BLENDER_VERSION -#define BLENDER_FILE_SUBVERSION 18 +#define BLENDER_FILE_SUBVERSION 19 /* Minimum Blender version that supports reading file written with the current * version. Older Blender versions will test this and show a warning if the file diff --git a/source/blender/blenkernel/BKE_idprop.h b/source/blender/blenkernel/BKE_idprop.h index a5cb6489194..c28ac63388b 100644 --- a/source/blender/blenkernel/BKE_idprop.h +++ b/source/blender/blenkernel/BKE_idprop.h @@ -32,6 +32,7 @@ struct BlendLibReader; struct BlendWriter; struct ID; struct IDProperty; +struct IDPropertyUIData; typedef union IDPropertyTemplate { int i; @@ -183,6 +184,10 @@ void IDP_Reset(struct IDProperty *prop, const struct IDProperty *reference); # define IDP_Id(prop) ((ID *)(prop)->data.pointer) #endif +int IDP_coerce_to_int_or_zero(const struct IDProperty *prop); +float IDP_coerce_to_float_or_zero(const struct IDProperty *prop); +double IDP_coerce_to_double_or_zero(const struct IDProperty *prop); + /** * Call a callback for each idproperty in the hierarchy under given root one (included). * @@ -209,6 +214,28 @@ void IDP_BlendReadData_impl(struct BlendDataReader *reader, void IDP_BlendReadLib(struct BlendLibReader *reader, struct IDProperty *prop); void IDP_BlendReadExpand(struct BlendExpander *expander, struct IDProperty *prop); +typedef enum eIDPropertyUIDataType { + /** Other properties types that don't support RNA UI data. */ + IDP_UI_DATA_TYPE_UNSUPPORTED = -1, + /** IDP_INT or IDP_ARRAY with subtype IDP_INT. */ + IDP_UI_DATA_TYPE_INT = 0, + /** IDP_FLOAT and IDP_DOUBLE or IDP_ARRAY properties with a float or double subtypes. */ + IDP_UI_DATA_TYPE_FLOAT = 1, + /** IDP_STRING properties. */ + IDP_UI_DATA_TYPE_STRING = 2, + /** IDP_ID. */ + IDP_UI_DATA_TYPE_ID = 3, +} eIDPropertyUIDataType; + +bool IDP_ui_data_supported(const struct IDProperty *prop); +eIDPropertyUIDataType IDP_ui_data_type(const struct IDProperty *prop); +void IDP_ui_data_free(struct IDProperty *prop); +void IDP_ui_data_free_unique_contents(struct IDPropertyUIData *ui_data, + eIDPropertyUIDataType type, + const struct IDPropertyUIData *other); +struct IDPropertyUIData *IDP_ui_data_ensure(struct IDProperty *prop); +struct IDPropertyUIData *IDP_ui_data_copy(const struct IDProperty *prop); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/intern/idprop.c b/source/blender/blenkernel/intern/idprop.c index 7a88a00c44f..f7411f541b7 100644 --- a/source/blender/blenkernel/intern/idprop.c +++ b/source/blender/blenkernel/intern/idprop.c @@ -21,6 +21,7 @@ * \ingroup bke */ +#include <limits.h> #include <stddef.h> #include <stdio.h> #include <stdlib.h> @@ -274,6 +275,43 @@ void IDP_FreeArray(IDProperty *prop) } } +IDPropertyUIData *IDP_ui_data_copy(const IDProperty *prop) +{ + IDPropertyUIData *dst_ui_data = MEM_dupallocN(prop->ui_data); + + /* Copy extra type specific data. */ + switch (IDP_ui_data_type(prop)) { + case IDP_UI_DATA_TYPE_STRING: { + const IDPropertyUIDataString *src = (const IDPropertyUIDataString *)prop->ui_data; + IDPropertyUIDataString *dst = (IDPropertyUIDataString *)dst_ui_data; + dst->default_value = MEM_dupallocN(src->default_value); + break; + } + case IDP_UI_DATA_TYPE_ID: { + break; + } + case IDP_UI_DATA_TYPE_INT: { + const IDPropertyUIDataInt *src = (const IDPropertyUIDataInt *)prop->ui_data; + IDPropertyUIDataInt *dst = (IDPropertyUIDataInt *)dst_ui_data; + dst->default_array = MEM_dupallocN(src->default_array); + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + const IDPropertyUIDataFloat *src = (const IDPropertyUIDataFloat *)prop->ui_data; + IDPropertyUIDataFloat *dst = (IDPropertyUIDataFloat *)dst_ui_data; + dst->default_array = MEM_dupallocN(src->default_array); + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: { + break; + } + } + + dst_ui_data->description = MEM_dupallocN(prop->ui_data->description); + + return dst_ui_data; +} + static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(flag)) { IDProperty *newp = MEM_callocN(sizeof(IDProperty), __func__); @@ -284,6 +322,10 @@ static IDProperty *idp_generic_copy(const IDProperty *prop, const int UNUSED(fla newp->data.val = prop->data.val; newp->data.val2 = prop->data.val2; + if (prop->ui_data != NULL) { + newp->ui_data = IDP_ui_data_copy(prop); + } + return newp; } @@ -679,6 +721,7 @@ bool IDP_InsertToGroup(IDProperty *group, IDProperty *previous, IDProperty *pnew void IDP_RemoveFromGroup(IDProperty *group, IDProperty *prop) { BLI_assert(group->type == IDP_GROUP); + BLI_assert(BLI_findindex(&group->data.group, prop) != -1); group->len--; BLI_remlink(&group->data.group, prop); @@ -725,6 +768,60 @@ static void IDP_FreeGroup(IDProperty *prop, const bool do_id_user) /** \name Main Functions (IDProperty Main API) * \{ */ +/** + * Return an int from an IDProperty with a compatible type. This should be avoided, but + * it's sometimes necessary, for example when legacy files have incorrect property types. + */ +int IDP_coerce_to_int_or_zero(const IDProperty *prop) +{ + switch (prop->type) { + case IDP_INT: + return IDP_Int(prop); + case IDP_DOUBLE: + return (int)IDP_Double(prop); + case IDP_FLOAT: + return (int)IDP_Float(prop); + default: + return 0; + } +} + +/** + * Return a double from an IDProperty with a compatible type. This should be avoided, but + * it's sometimes necessary, for example when legacy files have incorrect property types. + */ +double IDP_coerce_to_double_or_zero(const IDProperty *prop) +{ + switch (prop->type) { + case IDP_DOUBLE: + return IDP_Double(prop); + case IDP_FLOAT: + return (double)IDP_Float(prop); + case IDP_INT: + return (double)IDP_Int(prop); + default: + return 0.0; + } +} + +/** + * Return a float from an IDProperty with a compatible type. This should be avoided, but + * it's sometimes necessary, for example when legacy files have incorrect property types. + */ +float IDP_coerce_to_float_or_zero(const IDProperty *prop) +{ + switch (prop->type) { + case IDP_FLOAT: + return IDP_Float(prop); + case IDP_DOUBLE: + return (float)IDP_Double(prop); + case IDP_INT: + return (float)IDP_Int(prop); + default: + return 0.0f; + } +} + IDProperty *IDP_CopyProperty_ex(const IDProperty *prop, const int flag) { switch (prop->type) { @@ -1000,6 +1097,85 @@ IDProperty *IDP_New(const char type, const IDPropertyTemplate *val, const char * } /** + * Free allocated pointers in the UI data that isn't shared with the UI data in the #other + * argument. Useful for returning early on failure when updating UI data in place, or when + * replacing a subset of the UI data's allocated pointers. + */ +void IDP_ui_data_free_unique_contents(IDPropertyUIData *ui_data, + const eIDPropertyUIDataType type, + const IDPropertyUIData *other) +{ + if (ui_data->description != other->description) { + MEM_SAFE_FREE(ui_data->description); + } + + switch (type) { + case IDP_UI_DATA_TYPE_STRING: { + const IDPropertyUIDataString *other_string = (const IDPropertyUIDataString *)other; + IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data; + if (ui_data_string->default_value != other_string->default_value) { + MEM_SAFE_FREE(ui_data_string->default_value); + } + break; + } + case IDP_UI_DATA_TYPE_ID: { + break; + } + case IDP_UI_DATA_TYPE_INT: { + const IDPropertyUIDataInt *other_int = (const IDPropertyUIDataInt *)other; + IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data; + if (ui_data_int->default_array != other_int->default_array) { + MEM_SAFE_FREE(ui_data_int->default_array); + } + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + const IDPropertyUIDataFloat *other_float = (const IDPropertyUIDataFloat *)other; + IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data; + if (ui_data_float->default_array != other_float->default_array) { + MEM_SAFE_FREE(ui_data_float->default_array); + } + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: { + break; + } + } +} + +void IDP_ui_data_free(IDProperty *prop) +{ + switch (IDP_ui_data_type(prop)) { + case IDP_UI_DATA_TYPE_STRING: { + IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data; + MEM_SAFE_FREE(ui_data_string->default_value); + break; + } + case IDP_UI_DATA_TYPE_ID: { + break; + } + case IDP_UI_DATA_TYPE_INT: { + IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data; + MEM_SAFE_FREE(ui_data_int->default_array); + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data; + MEM_SAFE_FREE(ui_data_float->default_array); + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: { + break; + } + } + + MEM_SAFE_FREE(prop->ui_data->description); + + MEM_freeN(prop->ui_data); + prop->ui_data = NULL; +} + +/** * \note This will free allocated data, all child properties of arrays and groups, and unlink IDs! * But it does not free the actual IDProperty struct itself. */ @@ -1024,6 +1200,10 @@ void IDP_FreePropertyContent_ex(IDProperty *prop, const bool do_id_user) } break; } + + if (prop->ui_data != NULL) { + IDP_ui_data_free(prop); + } } void IDP_FreePropertyContent(IDProperty *prop) @@ -1104,6 +1284,48 @@ void IDP_foreach_property(IDProperty *id_property_root, void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer); +static void write_ui_data(const IDProperty *prop, BlendWriter *writer) +{ + IDPropertyUIData *ui_data = prop->ui_data; + + BLO_write_string(writer, ui_data->description); + + switch (IDP_ui_data_type(prop)) { + case IDP_UI_DATA_TYPE_STRING: { + IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)ui_data; + BLO_write_string(writer, ui_data_string->default_value); + BLO_write_struct(writer, IDPropertyUIDataString, ui_data); + break; + } + case IDP_UI_DATA_TYPE_ID: { + BLO_write_struct(writer, IDPropertyUIDataID, ui_data); + break; + } + case IDP_UI_DATA_TYPE_INT: { + IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)ui_data; + if (prop->type == IDP_ARRAY) { + BLO_write_int32_array( + writer, (uint)ui_data_int->default_array_len, (int32_t *)ui_data_int->default_array); + } + BLO_write_struct(writer, IDPropertyUIDataInt, ui_data); + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)ui_data; + if (prop->type == IDP_ARRAY) { + BLO_write_double_array( + writer, (uint)ui_data_float->default_array_len, ui_data_float->default_array); + } + BLO_write_struct(writer, IDPropertyUIDataFloat, ui_data); + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: { + BLI_assert_unreachable(); + break; + } + } +} + static void IDP_WriteArray(const IDProperty *prop, BlendWriter *writer) { /* Remember to set #IDProperty.totallen to len in the linking code! */ @@ -1165,6 +1387,9 @@ void IDP_WriteProperty_OnlyData(const IDProperty *prop, BlendWriter *writer) IDP_WriteIDPArray(prop, writer); break; } + if (prop->ui_data != NULL) { + write_ui_data(prop, writer); + } } void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop) @@ -1175,6 +1400,43 @@ void IDP_BlendWrite(BlendWriter *writer, const IDProperty *prop) static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader); +static void read_ui_data(IDProperty *prop, BlendDataReader *reader) +{ + BLO_read_data_address(reader, &prop->ui_data); + BLO_read_data_address(reader, &prop->ui_data->description); + + switch (IDP_ui_data_type(prop)) { + case IDP_UI_DATA_TYPE_STRING: { + IDPropertyUIDataString *ui_data_string = (IDPropertyUIDataString *)prop->ui_data; + BLO_read_data_address(reader, &ui_data_string->default_value); + break; + } + case IDP_UI_DATA_TYPE_ID: { + break; + } + case IDP_UI_DATA_TYPE_INT: { + IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)prop->ui_data; + if (prop->type == IDP_ARRAY) { + BLO_read_int32_array( + reader, ui_data_int->default_array_len, (int **)&ui_data_int->default_array); + } + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *ui_data_float = (IDPropertyUIDataFloat *)prop->ui_data; + if (prop->type == IDP_ARRAY) { + BLO_read_double_array( + reader, ui_data_float->default_array_len, (double **)&ui_data_float->default_array); + } + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: { + BLI_assert_unreachable(); + break; + } + } +} + static void IDP_DirectLinkIDPArray(IDProperty *prop, BlendDataReader *reader) { /* since we didn't save the extra buffer, set totallen to len */ @@ -1279,6 +1541,10 @@ static void IDP_DirectLinkProperty(IDProperty *prop, BlendDataReader *reader) prop->subtype = 0; IDP_Int(prop) = 0; } + + if (prop->ui_data != NULL) { + read_ui_data(prop, reader); + } } void IDP_BlendReadData_impl(BlendDataReader *reader, IDProperty **prop, const char *caller_func_id) @@ -1358,4 +1624,74 @@ void IDP_BlendReadExpand(struct BlendExpander *expander, IDProperty *prop) } } +eIDPropertyUIDataType IDP_ui_data_type(const IDProperty *prop) +{ + if (prop->type == IDP_STRING) { + return IDP_UI_DATA_TYPE_STRING; + } + if (prop->type == IDP_ID) { + return IDP_UI_DATA_TYPE_ID; + } + if (prop->type == IDP_INT || (prop->type == IDP_ARRAY && prop->subtype == IDP_INT)) { + return IDP_UI_DATA_TYPE_INT; + } + if (ELEM(prop->type, IDP_FLOAT, IDP_DOUBLE) || + (prop->type == IDP_ARRAY && ELEM(prop->subtype, IDP_FLOAT, IDP_DOUBLE))) { + return IDP_UI_DATA_TYPE_FLOAT; + } + return IDP_UI_DATA_TYPE_UNSUPPORTED; +} + +bool IDP_ui_data_supported(const IDProperty *prop) +{ + return IDP_ui_data_type(prop) != IDP_UI_DATA_TYPE_UNSUPPORTED; +} + +IDPropertyUIData *IDP_ui_data_ensure(IDProperty *prop) +{ + if (prop->ui_data != NULL) { + return prop->ui_data; + } + + switch (IDP_ui_data_type(prop)) { + case IDP_UI_DATA_TYPE_STRING: { + prop->ui_data = MEM_callocN(sizeof(IDPropertyUIDataString), __func__); + break; + } + case IDP_UI_DATA_TYPE_ID: { + IDPropertyUIDataID *ui_data = MEM_callocN(sizeof(IDPropertyUIDataID), __func__); + prop->ui_data = (IDPropertyUIData *)ui_data; + break; + } + case IDP_UI_DATA_TYPE_INT: { + IDPropertyUIDataInt *ui_data = MEM_callocN(sizeof(IDPropertyUIDataInt), __func__); + ui_data->min = INT_MIN; + ui_data->max = INT_MAX; + ui_data->soft_min = INT_MIN; + ui_data->soft_max = INT_MAX; + ui_data->step = 1; + prop->ui_data = (IDPropertyUIData *)ui_data; + break; + } + case IDP_UI_DATA_TYPE_FLOAT: { + IDPropertyUIDataFloat *ui_data = MEM_callocN(sizeof(IDPropertyUIDataFloat), __func__); + ui_data->min = -FLT_MAX; + ui_data->max = FLT_MAX; + ui_data->soft_min = -FLT_MAX; + ui_data->soft_max = FLT_MAX; + ui_data->step = 1.0f; + ui_data->precision = 3; + prop->ui_data = (IDPropertyUIData *)ui_data; + break; + } + case IDP_UI_DATA_TYPE_UNSUPPORTED: { + /* UI data not supported for remaining types, this shouldn't be called in those cases. */ + BLI_assert_unreachable(); + break; + } + } + + return prop->ui_data; +} + /** \} */ diff --git a/source/blender/blenkernel/intern/object_dupli.cc b/source/blender/blenkernel/intern/object_dupli.cc index 141a9a25eca..a46ac4b1175 100644 --- a/source/blender/blenkernel/intern/object_dupli.cc +++ b/source/blender/blenkernel/intern/object_dupli.cc @@ -653,10 +653,10 @@ static Object *find_family_object( return *ob_pt; } - char ch_utf8[7]; + char ch_utf8[BLI_UTF8_MAX + 1]; size_t ch_utf8_len; - ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8); + ch_utf8_len = BLI_str_utf8_from_unicode(ch, ch_utf8, sizeof(ch_utf8) - 1); ch_utf8[ch_utf8_len] = '\0'; ch_utf8_len += 1; /* Compare with null terminator. */ diff --git a/source/blender/blenkernel/intern/text.c b/source/blender/blenkernel/intern/text.c index 7f1f6590e48..6b7b3213a83 100644 --- a/source/blender/blenkernel/intern/text.c +++ b/source/blender/blenkernel/intern/text.c @@ -329,7 +329,8 @@ int txt_extended_ascii_as_utf8(char **str) memcpy(newstr + mi, (*str) + i, bad_char); - BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mi + bad_char); + const int mofs = mi + bad_char; + BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mofs, (length + added) - mofs); i += bad_char + 1; mi += bad_char + 2; } @@ -933,7 +934,7 @@ void txt_move_left(Text *text, const bool sel) (*charp) -= tabsize; } else { - const char *prev = BLI_str_prev_char_utf8((*linep)->line + *charp); + const char *prev = BLI_str_find_prev_char_utf8((*linep)->line + *charp, (*linep)->line); *charp = prev - (*linep)->line; } } @@ -1938,7 +1939,8 @@ void txt_backspace_char(Text *text) txt_pop_sel(text); } else { /* Just backspacing a char */ - const char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc); + const char *prev = BLI_str_find_prev_char_utf8(text->curl->line + text->curc, + text->curl->line); size_t c_len = prev - text->curl->line; c = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &c_len); c_len -= prev - text->curl->line; @@ -2004,7 +2006,7 @@ static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs) txt_delete_sel(text); - add_len = BLI_str_utf8_from_unicode(add, ch); + add_len = BLI_str_utf8_from_unicode(add, ch, sizeof(ch)); tmp = MEM_mallocN(text->curl->len + add_len + 1, "textline_string"); @@ -2060,7 +2062,7 @@ bool txt_replace_char(Text *text, unsigned int add) del = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &del_size); del_size -= text->curc; UNUSED_VARS(del); - add_size = BLI_str_utf8_from_unicode(add, ch); + add_size = BLI_str_utf8_from_unicode(add, ch, sizeof(ch)); if (add_size > del_size) { char *tmp = MEM_mallocN(text->curl->len + add_size - del_size + 1, "textline_string"); diff --git a/source/blender/blenkernel/intern/unit.c b/source/blender/blenkernel/intern/unit.c index 4581d410444..4e9a3c9fb2e 100644 --- a/source/blender/blenkernel/intern/unit.c +++ b/source/blender/blenkernel/intern/unit.c @@ -717,7 +717,7 @@ static const char *unit_find_str(const char *str, const char *substr, bool case_ if (str_found == str || /* Weak unicode support!, so "µm" won't match up be replaced by "m" * since non ascii utf8 values will NEVER return true */ - isalpha_or_utf8(*BLI_str_prev_char_utf8(str_found)) == 0) { + isalpha_or_utf8(*BLI_str_find_prev_char_utf8(str_found, str)) == 0) { /* Next char cannot be alphanum. */ int len_name = strlen(substr); diff --git a/source/blender/blenlib/BLI_string_utf8.h b/source/blender/blenlib/BLI_string_utf8.h index a9cb13a3277..3b7463affc0 100644 --- a/source/blender/blenlib/BLI_string_utf8.h +++ b/source/blender/blenlib/BLI_string_utf8.h @@ -48,7 +48,9 @@ unsigned int BLI_str_utf8_as_unicode_step_or_error( const char *__restrict p, size_t p_len, size_t *__restrict index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1, 3); -size_t BLI_str_utf8_from_unicode(unsigned int c, char *outbuf); +size_t BLI_str_utf8_from_unicode_len(unsigned int c) ATTR_WARN_UNUSED_RESULT; +size_t BLI_str_utf8_from_unicode(unsigned int c, char *outbuf, const size_t outbuf_len) + ATTR_NONNULL(2); size_t BLI_str_utf8_as_utf32(char32_t *__restrict dst_w, const char *__restrict src_c, const size_t maxncpy) ATTR_NONNULL(1, 2); @@ -57,11 +59,10 @@ size_t BLI_str_utf32_as_utf8(char *__restrict dst, const size_t maxncpy) ATTR_NONNULL(1, 2); size_t BLI_str_utf32_as_utf8_len(const char32_t *src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); -char *BLI_str_find_prev_char_utf8(const char *str, const char *p) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(1, 2); -char *BLI_str_find_next_char_utf8(const char *p, const char *end) ATTR_WARN_UNUSED_RESULT - ATTR_NONNULL(1); -char *BLI_str_prev_char_utf8(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1); +const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start) + ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1, 2); +const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end) + ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1, 2); /* wchar_t functions, copied from blenders own font.c originally */ size_t BLI_wstrlen_utf8(const wchar_t *src) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT; diff --git a/source/blender/blenlib/intern/string_cursor_utf8.c b/source/blender/blenlib/intern/string_cursor_utf8.c index f76a3114e09..eb49572f06c 100644 --- a/source/blender/blenlib/intern/string_cursor_utf8.c +++ b/source/blender/blenlib/intern/string_cursor_utf8.c @@ -117,7 +117,7 @@ bool BLI_str_cursor_step_next_utf8(const char *str, size_t maxlen, int *pos) const char *str_end = str + (maxlen + 1); const char *str_pos = str + (*pos); const char *str_next = BLI_str_find_next_char_utf8(str_pos, str_end); - if (str_next) { + if (str_next != str_end) { (*pos) += (str_next - str_pos); if ((*pos) > (int)maxlen) { (*pos) = (int)maxlen; @@ -132,11 +132,9 @@ bool BLI_str_cursor_step_prev_utf8(const char *str, size_t UNUSED(maxlen), int * { if ((*pos) > 0) { const char *str_pos = str + (*pos); - const char *str_prev = BLI_str_find_prev_char_utf8(str, str_pos); - if (str_prev) { - (*pos) -= (str_pos - str_prev); - return true; - } + const char *str_prev = BLI_str_find_prev_char_utf8(str_pos, str); + (*pos) -= (str_pos - str_prev); + return true; } return false; diff --git a/source/blender/blenlib/intern/string_utf8.c b/source/blender/blenlib/intern/string_utf8.c index e35e2bcca3c..3a5e2713b76 100644 --- a/source/blender/blenlib/intern/string_utf8.c +++ b/source/blender/blenlib/intern/string_utf8.c @@ -296,36 +296,19 @@ size_t BLI_strncpy_wchar_as_utf8(char *__restrict dst, const wchar_t *__restrict src, const size_t maxncpy) { - const size_t maxlen = maxncpy - 1; - /* #BLI_UTF8_MAX is max utf8 length of an unicode char. */ - const int64_t maxlen_secured = (int64_t)maxlen - BLI_UTF8_MAX; - size_t len = 0; - BLI_assert(maxncpy != 0); - + size_t len = 0; #ifdef DEBUG_STRSIZE memset(dst, 0xff, sizeof(*dst) * maxncpy); #endif - - while (*src && len <= maxlen_secured) { - len += BLI_str_utf8_from_unicode((uint)*src++, dst + len); - } - - /* We have to be more careful for the last six bytes, - * to avoid buffer overflow in case utf8-encoded char would be too long for our dst buffer. */ - while (*src) { - char t[BLI_UTF8_MAX]; - size_t l = BLI_str_utf8_from_unicode((uint)*src++, t); - BLI_assert(l <= BLI_UTF8_MAX); - if (len + l > maxlen) { - break; - } - memcpy(dst + len, t, l); - len += l; + while (*src && len < maxncpy) { + len += BLI_str_utf8_from_unicode((uint)*src++, dst + len, maxncpy - len); } - dst[len] = '\0'; - + /* Return the correct length when part of the final byte did not fit into the string. */ + while ((len > 0) && UNLIKELY(dst[len - 1] == '\0')) { + len--; + } return len; } @@ -335,7 +318,7 @@ size_t BLI_wstrlen_utf8(const wchar_t *src) size_t len = 0; while (*src) { - len += BLI_str_utf8_from_unicode((uint)*src++, NULL); + len += BLI_str_utf8_from_unicode_len((uint)*src++); } return len; @@ -608,56 +591,78 @@ uint BLI_str_utf8_as_unicode_step(const char *__restrict p, } /* was g_unichar_to_utf8 */ + +#define UTF8_VARS_FROM_CHAR32(Char, First, Len) \ + if (Char < 0x80) { \ + First = 0; \ + Len = 1; \ + } \ + else if (Char < 0x800) { \ + First = 0xc0; \ + Len = 2; \ + } \ + else if (Char < 0x10000) { \ + First = 0xe0; \ + Len = 3; \ + } \ + else if (Char < 0x200000) { \ + First = 0xf0; \ + Len = 4; \ + } \ + else if (Char < 0x4000000) { \ + First = 0xf8; \ + Len = 5; \ + } \ + else { \ + First = 0xfc; \ + Len = 6; \ + } \ + (void)0 + +size_t BLI_str_utf8_from_unicode_len(const uint c) +{ + /* If this gets modified, also update the copy in g_string_insert_unichar() */ + uint len = 0; + uint first; + + UTF8_VARS_FROM_CHAR32(c, first, len); + (void)first; + + return len; +} + /** * BLI_str_utf8_from_unicode: + * * \param c: a Unicode character code - * \param outbuf: output buffer, must have at least 6 bytes of space. - * If %NULL, the length will be computed and returned - * and nothing will be written to outbuf. + * \param outbuf: output buffer, must have at least `outbuf_len` bytes of space. + * If the length required by `c` exceeds `outbuf_len`, + * the bytes available bytes will be zeroed and `outbuf_len` returned. * * Converts a single character to UTF-8. * - * \return number of bytes written + * \return number of bytes written. */ -size_t BLI_str_utf8_from_unicode(uint c, char *outbuf) +size_t BLI_str_utf8_from_unicode(uint c, char *outbuf, const size_t outbuf_len) + { /* If this gets modified, also update the copy in g_string_insert_unichar() */ uint len = 0; uint first; - uint i; - if (c < 0x80) { - first = 0; - len = 1; - } - else if (c < 0x800) { - first = 0xc0; - len = 2; - } - else if (c < 0x10000) { - first = 0xe0; - len = 3; - } - else if (c < 0x200000) { - first = 0xf0; - len = 4; - } - else if (c < 0x4000000) { - first = 0xf8; - len = 5; - } - else { - first = 0xfc; - len = 6; + UTF8_VARS_FROM_CHAR32(c, first, len); + + if (UNLIKELY(outbuf_len < len)) { + /* NULL terminate instead of writing a partial byte. */ + memset(outbuf, 0x0, outbuf_len); + return outbuf_len; } - if (outbuf) { - for (i = len - 1; i > 0; i--) { - outbuf[i] = (c & 0x3f) | 0x80; - c >>= 6; - } - outbuf[0] = c | first; + for (uint i = len - 1; i > 0; i--) { + outbuf[i] = (c & 0x3f) | 0x80; + c >>= 6; } + outbuf[0] = c | first; return len; } @@ -686,12 +691,7 @@ size_t BLI_str_utf8_as_utf32(char32_t *__restrict dst_w, else { *dst_w = '?'; const char *src_c_next = BLI_str_find_next_char_utf8(src_c + index, src_c_end); - if (src_c_next != NULL) { - index = (size_t)(src_c_next - src_c); - } - else { - index += 1; - } + index = (size_t)(src_c_next - src_c); } dst_w++; len++; @@ -706,36 +706,19 @@ size_t BLI_str_utf32_as_utf8(char *__restrict dst, const char32_t *__restrict src, const size_t maxncpy) { - const size_t maxlen = maxncpy - 1; - /* #BLI_UTF8_MAX is max utf8 length of an unicode char. */ - const int64_t maxlen_secured = (int64_t)maxlen - BLI_UTF8_MAX; - size_t len = 0; - BLI_assert(maxncpy != 0); - + size_t len = 0; #ifdef DEBUG_STRSIZE memset(dst, 0xff, sizeof(*dst) * maxncpy); #endif - - while (*src && len <= maxlen_secured) { - len += BLI_str_utf8_from_unicode((uint)*src++, dst + len); - } - - /* We have to be more careful for the last six bytes, - * to avoid buffer overflow in case utf8-encoded char would be too long for our dst buffer. */ - while (*src) { - char t[BLI_UTF8_MAX]; - size_t l = BLI_str_utf8_from_unicode((uint)*src++, t); - BLI_assert(l <= BLI_UTF8_MAX); - if (len + l > maxlen) { - break; - } - memcpy(dst + len, t, l); - len += l; + while (*src && len < maxncpy) { + len += BLI_str_utf8_from_unicode((uint)*src++, dst + len, maxncpy - len); } - dst[len] = '\0'; - + /* Return the correct length when part of the final byte did not fit into the string. */ + while ((len > 0) && UNLIKELY(dst[len - 1] == '\0')) { + len--; + } return len; } @@ -745,7 +728,7 @@ size_t BLI_str_utf32_as_utf8_len(const char32_t *src) size_t len = 0; while (*src) { - len += BLI_str_utf8_from_unicode((uint)*src++, NULL); + len += BLI_str_utf8_from_unicode_len((uint)*src++); } return len; @@ -758,31 +741,33 @@ size_t BLI_str_utf32_as_utf8_len(const char32_t *src) * \param p: pointer to some position within \a str * * Given a position \a p with a UTF-8 encoded string \a str, find the start - * of the previous UTF-8 character starting before. \a p Returns %NULL if no - * UTF-8 characters are present in \a str before \a p + * of the previous UTF-8 character starting before. \a p Returns \a str_start if no + * UTF-8 characters are present in \a str_start before \a p. * * \a p does not have to be at the beginning of a UTF-8 character. No check * is made to see if the character found is actually valid other than * it starts with an appropriate byte. * - * Return value: a pointer to the found character or %NULL. + * \return A pointer to the found character. */ -char *BLI_str_find_prev_char_utf8(const char *str, const char *p) +const char *BLI_str_find_prev_char_utf8(const char *p, const char *str_start) { - for (--p; p >= str; p--) { - if ((*p & 0xc0) != 0x80) { - return (char *)p; + BLI_assert(p >= str_start); + if (str_start < p) { + for (--p; p >= str_start; p--) { + if ((*p & 0xc0) != 0x80) { + return (char *)p; + } } } - return NULL; + return p; } /* was g_utf8_find_next_char */ /** * BLI_str_find_next_char_utf8: * \param p: a pointer to a position within a UTF-8 encoded string - * \param end: a pointer to the byte following the end of the string, - * or %NULL to indicate that the string is nul-terminated. + * \param end: a pointer to the byte following the end of the string. * * Finds the start of the next UTF-8 character in the string after \a p * @@ -790,50 +775,18 @@ char *BLI_str_find_prev_char_utf8(const char *str, const char *p) * is made to see if the character found is actually valid other than * it starts with an appropriate byte. * - * Return value: a pointer to the found character or %NULL - */ -char *BLI_str_find_next_char_utf8(const char *p, const char *end) -{ - if (*p) { - if (end) { - BLI_assert(end >= p); - for (++p; p < end && (*p & 0xc0) == 0x80; p++) { - /* do nothing */ - } - } - else { - for (++p; (*p & 0xc0) == 0x80; p++) { - /* do nothing */ - } - } - } - return (p == end) ? NULL : (char *)p; -} - -/* was g_utf8_prev_char */ -/** - * BLI_str_prev_char_utf8: - * \param p: a pointer to a position within a UTF-8 encoded string - * - * Finds the previous UTF-8 character in the string before \a p - * - * \a p does not have to be at the beginning of a UTF-8 character. No check - * is made to see if the character found is actually valid other than - * it starts with an appropriate byte. If \a p might be the first - * character of the string, you must use g_utf8_find_prev_char() instead. - * - * Return value: a pointer to the found character. + * \return a pointer to the found character or a pointer to the null terminating character '\0'. */ -char *BLI_str_prev_char_utf8(const char *p) +const char *BLI_str_find_next_char_utf8(const char *p, const char *str_end) { - while (1) { - p--; - if ((*p & 0xc0) != 0x80) { - return (char *)p; + BLI_assert(p <= str_end); + if ((p < str_end) && (*p != '\0')) { + for (++p; p < str_end && (*p & 0xc0) == 0x80; p++) { + /* do nothing */ } } + return p; } -/* end glib copy */ size_t BLI_str_partition_utf8(const char *str, const uint delim[], @@ -858,19 +811,21 @@ size_t BLI_str_partition_ex_utf8(const char *str, const char **suf, const bool from_right) { - const uint *d; const size_t str_len = end ? (size_t)(end - str) : strlen(str); - size_t index; + if (end == NULL) { + end = str + str_len; + } /* Note that here, we assume end points to a valid utf8 char! */ - BLI_assert(end == NULL || (end >= str && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR))); + BLI_assert((end >= str) && (BLI_str_utf8_as_unicode(end) != BLI_UTF8_ERR)); *suf = (char *)(str + str_len); - for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(str, str + str_len) : str), - index = 0; - *sep >= str && (!end || *sep < end) && **sep != '\0'; - *sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(str, *sep) : str + index)) { + size_t index; + for (*sep = (char *)(from_right ? BLI_str_find_prev_char_utf8(end, str) : str), index = 0; + from_right ? (*sep > str) : ((*sep < end) && (**sep != '\0')); + *sep = (char *)(from_right ? (str != *sep ? BLI_str_find_prev_char_utf8(*sep, str) : NULL) : + str + index)) { size_t index_ofs = 0; const uint c = BLI_str_utf8_as_unicode_step_or_error(*sep, (size_t)(end - *sep), &index_ofs); index += index_ofs; @@ -880,7 +835,7 @@ size_t BLI_str_partition_ex_utf8(const char *str, break; } - for (d = delim; *d != '\0'; d++) { + for (const uint *d = delim; *d != '\0'; d++) { if (*d == c) { /* *suf is already correct in case from_right is true. */ if (!from_right) { diff --git a/source/blender/blenlib/tests/BLI_span_test.cc b/source/blender/blenlib/tests/BLI_span_test.cc index 5e3c6a1b680..4d23a53c08a 100644 --- a/source/blender/blenlib/tests/BLI_span_test.cc +++ b/source/blender/blenlib/tests/BLI_span_test.cc @@ -235,7 +235,7 @@ TEST(span, FillIndices) TEST(span, SizeInBytes) { - std::array<int, 10> a; + std::array<int, 10> a{}; Span<int> a_span(a); EXPECT_EQ(a_span.size_in_bytes(), static_cast<int64_t>(sizeof(a))); EXPECT_EQ(a_span.size_in_bytes(), 40); diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 72572b05ef6..2456b22f046 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -20,6 +20,10 @@ /* allow readfile to use deprecated functionality */ #define DNA_DEPRECATED_ALLOW +#include <string.h> + +#include "MEM_guardedalloc.h" + #include "BLI_listbase.h" #include "BLI_math_vector.h" #include "BLI_path_util.h" @@ -46,10 +50,14 @@ #include "BKE_deform.h" #include "BKE_fcurve.h" #include "BKE_fcurve_driver.h" +#include "BKE_idprop.h" #include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_node.h" +#include "RNA_access.h" +#include "RNA_enum_types.h" + #include "BLO_readfile.h" #include "MEM_guardedalloc.h" #include "readfile.h" @@ -60,6 +68,235 @@ #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); + 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 +489,7 @@ void do_versions_after_linking_300(Main *bmain, ReportList *UNUSED(reports)) */ { /* Keep this block, even when empty. */ + do_versions_idproperty_ui_data(bmain); } } @@ -776,18 +1014,7 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - /** - * Versioning code until next subversion bump goes here. - * - * \note Be sure to check when bumping the version: - * - "versioning_userdef.c", #blo_do_versions_userdef - * - "versioning_userdef.c", #do_versions_theme - * - * \note Keep this message at the bottom of the function. - */ - { - /* Keep this block, even when empty. */ - + if (!MAIN_VERSION_ATLEAST(bmain, 300, 19)) { /* Add node storage for subdivision surface node. */ FOREACH_NODETREE_BEGIN (bmain, ntree, id) { if (ntree->type == NTREE_GEOMETRY) { @@ -818,5 +1045,23 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) } } } + + LISTBASE_FOREACH (Scene *, scene, &bmain->scenes) { + SequencerToolSettings *sequencer_tool_settings = SEQ_tool_settings_ensure(scene); + sequencer_tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE; + } + } + + /** + * Versioning code until next subversion bump goes here. + * + * \note Be sure to check when bumping the version: + * - "versioning_userdef.c", #blo_do_versions_userdef + * - "versioning_userdef.c", #do_versions_theme + * + * \note Keep this message at the bottom of the function. + */ + { + /* Keep this block, even when empty. */ } } diff --git a/source/blender/editors/interface/interface_eyedropper.c b/source/blender/editors/interface/interface_eyedropper.c index 13566a59910..2e7b0ce532c 100644 --- a/source/blender/editors/interface/interface_eyedropper.c +++ b/source/blender/editors/interface/interface_eyedropper.c @@ -125,10 +125,7 @@ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const cha eyedropper_draw_cursor_text_ex(x, y, name); } -void eyedropper_draw_cursor_text_region(const struct bContext *C, - const int x, - const int y, - const char *name) +void eyedropper_draw_cursor_text_region(const int x, const int y, const char *name) { if (name[0] == '\0') { return; diff --git a/source/blender/editors/interface/interface_eyedropper_datablock.c b/source/blender/editors/interface/interface_eyedropper_datablock.c index 0eeeb0a39e4..4996c803dfe 100644 --- a/source/blender/editors/interface/interface_eyedropper_datablock.c +++ b/source/blender/editors/interface/interface_eyedropper_datablock.c @@ -75,10 +75,12 @@ typedef struct DataDropper { char name[200]; } DataDropper; -static void datadropper_draw_cb(const struct bContext *C, ARegion *region, void *arg) +static void datadropper_draw_cb(const struct bContext *UNUSED(C), + ARegion *UNUSED(region), + void *arg) { DataDropper *ddr = arg; - eyedropper_draw_cursor_text_region(C, UNPACK2(ddr->name_pos), ddr->name); + eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name); } static int datadropper_init(bContext *C, wmOperator *op) @@ -254,7 +256,7 @@ static void datadropper_cancel(bContext *C, wmOperator *op) } /* To switch the draw callback when region under mouse event changes */ -static void datadropper_set_draw_callback_region(bContext *C, ScrArea *area, DataDropper *ddr) +static void datadropper_set_draw_callback_region(ScrArea *area, DataDropper *ddr) { if (area) { /* If spacetype changed */ @@ -311,7 +313,7 @@ static int datadropper_modal(bContext *C, wmOperator *op, const wmEvent *event) datadropper_win_area_find(C, mval, mval, &win, &area); /* Set the region for eyedropper cursor text drawing */ - datadropper_set_draw_callback_region(C, area, ddr); + datadropper_set_draw_callback_region(area, ddr); datadropper_id_sample_pt(C, win, area, ddr, mval[0], mval[1], &id); } diff --git a/source/blender/editors/interface/interface_eyedropper_depth.c b/source/blender/editors/interface/interface_eyedropper_depth.c index 6f272201085..311f718d950 100644 --- a/source/blender/editors/interface/interface_eyedropper_depth.c +++ b/source/blender/editors/interface/interface_eyedropper_depth.c @@ -76,10 +76,12 @@ typedef struct DepthDropper { char name[200]; } DepthDropper; -static void depthdropper_draw_cb(const struct bContext *C, ARegion *region, void *arg) +static void depthdropper_draw_cb(const struct bContext *UNUSED(C), + ARegion *UNUSED(region), + void *arg) { DepthDropper *ddr = arg; - eyedropper_draw_cursor_text_region(C, UNPACK2(ddr->name_pos), ddr->name); + eyedropper_draw_cursor_text_region(UNPACK2(ddr->name_pos), ddr->name); } static int depthdropper_init(bContext *C, wmOperator *op) diff --git a/source/blender/editors/interface/interface_eyedropper_intern.h b/source/blender/editors/interface/interface_eyedropper_intern.h index 4295a781af8..f9f3fcfb5d1 100644 --- a/source/blender/editors/interface/interface_eyedropper_intern.h +++ b/source/blender/editors/interface/interface_eyedropper_intern.h @@ -24,10 +24,7 @@ /* interface_eyedropper.c */ void eyedropper_draw_cursor_text_window(const struct wmWindow *window, const char *name); -void eyedropper_draw_cursor_text_region(const struct bContext *C, - const int x, - const int y, - const char *name); +void eyedropper_draw_cursor_text_region(const int x, const int y, const char *name); uiBut *eyedropper_get_property_button_under_mouse(bContext *C, const wmEvent *event); void datadropper_win_area_find(const struct bContext *C, const int mval[2], diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index a618daec4bc..977e9661dd9 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -2933,8 +2933,9 @@ static int ui_text_position_from_hidden(uiBut *but, int pos) { const char *butstr = (but->editstr) ? but->editstr : but->drawstr; const char *strpos = butstr; + const char *str_end = butstr + strlen(butstr); for (int i = 0; i < pos; i++) { - strpos = BLI_str_find_next_char_utf8(strpos, NULL); + strpos = BLI_str_find_next_char_utf8(strpos, str_end); } return (strpos - butstr); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 48f638dac33..a2b86ccd947 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1498,15 +1498,15 @@ static void widget_draw_submenu_tria(const uiBut *but, static void ui_text_clip_give_prev_off(uiBut *but, const char *str) { - const char *prev_utf8 = BLI_str_find_prev_char_utf8(str, str + but->ofs); + const char *prev_utf8 = BLI_str_find_prev_char_utf8(str + but->ofs, str); const int bytes = str + but->ofs - prev_utf8; but->ofs -= bytes; } -static void ui_text_clip_give_next_off(uiBut *but, const char *str) +static void ui_text_clip_give_next_off(uiBut *but, const char *str, const char *str_end) { - const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, NULL); + const char *next_utf8 = BLI_str_find_next_char_utf8(str + but->ofs, str_end); const int bytes = next_utf8 - (str + but->ofs); but->ofs += bytes; @@ -1739,7 +1739,8 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct but->strwidth = BLF_width(fstyle->uifont_id, but->editstr + but->ofs, INT_MAX); if (but->strwidth > okwidth) { - int len = strlen(but->editstr); + const int editstr_len = strlen(but->editstr); + int len = editstr_len; while (but->strwidth > okwidth) { float width; @@ -1749,7 +1750,7 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct /* if cursor is at 20 pixels of right side button we clip left */ if (width > okwidth - 20) { - ui_text_clip_give_next_off(but, but->editstr); + ui_text_clip_give_next_off(but, but->editstr, but->editstr + editstr_len); } else { int bytes; @@ -1757,7 +1758,7 @@ static void ui_text_clip_cursor(const uiFontStyle *fstyle, uiBut *but, const rct if (width < 20 && but->ofs > 0) { ui_text_clip_give_prev_off(but, but->editstr); } - bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr, but->editstr + len)); + bytes = BLI_str_utf8_size(BLI_str_find_prev_char_utf8(but->editstr + len, but->editstr)); if (bytes == -1) { bytes = 1; } @@ -1805,7 +1806,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons /* chop off the leading text, starting from the right */ while (but->strwidth > okwidth && cp2 > but->drawstr) { - const char *prev_utf8 = BLI_str_find_prev_char_utf8(but->drawstr, cp2); + const char *prev_utf8 = BLI_str_find_prev_char_utf8(cp2, but->drawstr); const int bytes = cp2 - prev_utf8; /* shift the text after and including cp2 back by 1 char, @@ -1825,7 +1826,7 @@ static void ui_text_clip_right_label(const uiFontStyle *fstyle, uiBut *but, cons /* after the leading text is gone, chop off the : and following space, with ofs */ while ((but->strwidth > okwidth) && (but->ofs < 2)) { - ui_text_clip_give_next_off(but, but->drawstr); + ui_text_clip_give_next_off(but, but->drawstr, but->drawstr + drawstr_len); but->strwidth = BLF_width( fstyle->uifont_id, but->drawstr + but->ofs, sizeof(but->drawstr) - but->ofs); if (but->strwidth < 10) { diff --git a/source/blender/editors/space_action/action_draw.c b/source/blender/editors/space_action/action_draw.c index a3bdcd2adf5..6f1a90e56a5 100644 --- a/source/blender/editors/space_action/action_draw.c +++ b/source/blender/editors/space_action/action_draw.c @@ -259,17 +259,18 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region else { color = sel ? col1 : col2; } - /* frames less than one get less saturated background */ + + /* Color overlay on frames between the start/end frames. */ immUniformColor4ubv(color); - immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax); + immRectf(pos, ac->scene->r.sfra, ymin, ac->scene->r.efra, ymax); - /* frames one and higher get a saturated background */ - immUniformColor3ubvAlpha(color, MIN2(255, color[3] * 2)); - immRectf(pos, v2d->cur.xmin, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax); + /* Color overlay outside the start/end frame range get a more transparent overlay. */ + immUniformColor3ubvAlpha(color, MIN2(255, color[3] / 2)); + immRectf(pos, v2d->cur.xmin, ymin, ac->scene->r.sfra, ymax); + immRectf(pos, ac->scene->r.efra, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax); } else if (ac->datatype == ANIMCONT_MASK) { /* TODO: this is a copy of gpencil. */ - /* frames less than one get less saturated background */ uchar *color; if (ale->type == ANIMTYPE_SUMMARY) { color = col_summary; @@ -277,12 +278,15 @@ void draw_channel_strips(bAnimContext *ac, SpaceAction *saction, ARegion *region else { color = sel ? col1 : col2; } + + /* Color overlay on frames between the start/end frames. */ immUniformColor4ubv(color); - immRectf(pos, 0.0f, ymin, v2d->cur.xmin, ymax); + immRectf(pos, ac->scene->r.sfra, ymin, ac->scene->r.efra, ymax); - /* frames one and higher get a saturated background */ - immUniformColor3ubvAlpha(color, MIN2(255, color[3] * 2)); - immRectf(pos, v2d->cur.xmin, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax); + /* Color overlay outside the start/end frame range get a more transparent overlay. */ + immUniformColor3ubvAlpha(color, MIN2(255, color[3] / 2)); + immRectf(pos, v2d->cur.xmin, ymin, ac->scene->r.sfra, ymax); + immRectf(pos, ac->scene->r.efra, ymin, v2d->cur.xmax + EXTRA_SCROLL_PAD, ymax); } } } diff --git a/source/blender/editors/space_buttons/buttons_context.c b/source/blender/editors/space_buttons/buttons_context.c index 70b715e0119..bf855db07e9 100644 --- a/source/blender/editors/space_buttons/buttons_context.c +++ b/source/blender/editors/space_buttons/buttons_context.c @@ -987,7 +987,8 @@ int /*eContextResult*/ buttons_context(const bContext *C, matnr = 0; } /* Keep aligned with rna_Object_material_slots_get. */ - CTX_data_pointer_set(result, &ob->id, &RNA_MaterialSlot, POINTER_FROM_INT(matnr + 1)); + CTX_data_pointer_set( + result, &ob->id, &RNA_MaterialSlot, (void *)(matnr + (uintptr_t)&ob->id)); } } diff --git a/source/blender/editors/space_console/console_ops.c b/source/blender/editors/space_console/console_ops.c index 763beb8671b..c6fb2560dc0 100644 --- a/source/blender/editors/space_console/console_ops.c +++ b/source/blender/editors/space_console/console_ops.c @@ -435,7 +435,7 @@ static int console_insert_invoke(bContext *C, wmOperator *op, const wmEvent *eve } else { /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str); + len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); } str[len] = '\0'; RNA_string_set(op->ptr, "text", str); diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 4f66506d28b..d2af26aa1d7 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -389,10 +389,12 @@ void ED_image_draw_info(Scene *scene, immRecti(pos, color_quater_x, color_quater_y, color_rect_half.xmax, color_rect_half.ymax); immRecti(pos, color_rect_half.xmin, color_rect_half.ymin, color_quater_x, color_quater_y); - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor3fvAlpha(finalcol, fp ? fp[3] : (cp[3] / 255.0f)); - immRecti(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax); - GPU_blend(GPU_BLEND_NONE); + if (fp != NULL || cp != NULL) { + GPU_blend(GPU_BLEND_ALPHA); + immUniformColor3fvAlpha(finalcol, fp ? fp[3] : (cp[3] / 255.0f)); + immRecti(pos, color_rect.xmin, color_rect.ymin, color_rect.xmax, color_rect.ymax); + GPU_blend(GPU_BLEND_NONE); + } } else { immUniformColor3fv(finalcol); diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index b67117f1ad0..5b4e3b3b6f5 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -360,7 +360,11 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) /* Get "global" coordinates. */ float locx, locy; node_to_view(node, 0.0f, 0.0f, &locx, &locy); - float dy = locy; + /* Round the node origin because text contents are always pixel-aligned. */ + locx = round(locx); + locy = round(locy); + + int dy = locy; /* Header. */ dy -= NODE_DY; @@ -412,9 +416,9 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) /* Ensure minimum socket height in case layout is empty. */ buty = min_ii(buty, dy - NODE_DY); - nsock->locx = locx + NODE_WIDTH(node); - /* Place the socket circle in the middle of the layout. */ - nsock->locy = 0.5f * (dy + buty); + /* Round the socket location to stop it from jiggling. */ + nsock->locx = round(locx + NODE_WIDTH(node)); + nsock->locy = round(0.5f * (dy + buty)); dy = buty; if (nsock->next) { @@ -549,8 +553,8 @@ static void node_update_basis(const bContext *C, bNodeTree *ntree, bNode *node) buty = min_ii(buty, dy - NODE_DY); nsock->locx = locx; - /* Place the socket circle in the middle of the layout. */ - nsock->locy = 0.5f * (dy + buty); + /* Round the socket vertical position to stop it from jiggling. */ + nsock->locy = round(0.5f * (dy + buty)); dy = buty - multi_input_socket_offset * 0.5; if (nsock->next) { @@ -587,6 +591,9 @@ static void node_update_hidden(bNode *node) /* Get "global" coords. */ float locx, locy; node_to_view(node, 0.0f, 0.0f, &locx, &locy); + /* Round the node origin because text contents are always pixel-aligned. */ + locx = round(locx); + locy = round(locy); /* Calculate minimal radius. */ LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) { @@ -617,8 +624,9 @@ static void node_update_hidden(bNode *node) LISTBASE_FOREACH (bNodeSocket *, nsock, &node->outputs) { if (!nodeSocketIsHidden(nsock)) { - nsock->locx = node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad; - nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad; + /* Round the socket location to stop it from jiggling. */ + nsock->locx = round(node->totr.xmax - hiddenrad + sinf(rad) * hiddenrad); + nsock->locy = round(node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad); rad += drad; } } @@ -628,8 +636,9 @@ static void node_update_hidden(bNode *node) LISTBASE_FOREACH (bNodeSocket *, nsock, &node->inputs) { if (!nodeSocketIsHidden(nsock)) { - nsock->locx = node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad; - nsock->locy = node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad; + /* Round the socket location to stop it from jiggling. */ + nsock->locx = round(node->totr.xmin + hiddenrad + sinf(rad) * hiddenrad); + nsock->locy = round(node->totr.ymin + hiddenrad + cosf(rad) * hiddenrad); rad += drad; } } diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index df20420e472..d35fd729131 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -328,7 +328,7 @@ extern const char *node_context_dir[]; #define BASIS_RAD (0.2f * U.widget_unit) #define NODE_DYS (U.widget_unit / 2) #define NODE_DY U.widget_unit -#define NODE_SOCKDY (0.08f * U.widget_unit) +#define NODE_SOCKDY (0.1f * U.widget_unit) #define NODE_WIDTH(node) (node->width * UI_DPI_FAC) #define NODE_HEIGHT(node) (node->height * UI_DPI_FAC) #define NODE_MARGIN_X (1.10f * U.widget_unit) diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b3c39e2fa6f..fe3ff469e50 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -737,7 +737,8 @@ static void draw_seq_handle(View2D *v2d, } } -static void draw_seq_outline(Sequence *seq, +static void draw_seq_outline(Scene *scene, + Sequence *seq, uint pos, float x1, float x2, @@ -765,7 +766,9 @@ static void draw_seq_outline(Sequence *seq, * - Slightly lighter. * - Red when overlapping with other strips. */ - if ((G.moving & G_TRANSFORM_SEQ) && (seq->flag & SELECT)) { + const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(scene); + if ((G.moving & G_TRANSFORM_SEQ) && (seq->flag & SELECT) && + overlap_mode != SEQ_OVERLAP_OVERWRITE) { if (seq->flag & SEQ_OVERLAP) { col[0] = 255; col[1] = col[2] = 33; @@ -1383,7 +1386,7 @@ static void draw_seq_strip(const bContext *C, v2d, seq, handsize_clamped, SEQ_RIGHTHANDLE, pos, seq_active, pixelx, y_threshold); } - draw_seq_outline(seq, pos, x1, x2, y1, y2, pixelx, pixely, seq_active); + draw_seq_outline(scene, seq, pos, x1, x2, y1, y2, pixelx, pixely, seq_active); immUnbindProgram(); diff --git a/source/blender/editors/space_text/text_draw.c b/source/blender/editors/space_text/text_draw.c index b6ba95885e4..99fcb2092c3 100644 --- a/source/blender/editors/space_text/text_draw.c +++ b/source/blender/editors/space_text/text_draw.c @@ -1472,7 +1472,7 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi /* closing bracket, search backward for open */ fc--; if (c > 0) { - c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c); + c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line); } while (linep) { while (fc >= 0) { @@ -1493,7 +1493,7 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi } fc--; if (c > 0) { - c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c); + c -= linep->line + c - BLI_str_find_prev_char_utf8(linep->line + c, linep->line); } } if (endl) { @@ -1508,7 +1508,7 @@ static void draw_brackets(const SpaceText *st, const TextDrawContext *tdc, ARegi fc = -1; } if (linep->len) { - c = BLI_str_prev_char_utf8(linep->line + linep->len) - linep->line; + c = BLI_str_find_prev_char_utf8(linep->line + linep->len, linep->line) - linep->line; } else { fc = -1; diff --git a/source/blender/editors/space_text/text_ops.c b/source/blender/editors/space_text/text_ops.c index f480f60a2b9..c3bc474b98a 100644 --- a/source/blender/editors/space_text/text_ops.c +++ b/source/blender/editors/space_text/text_ops.c @@ -1955,7 +1955,7 @@ static void txt_wrap_move_eol(SpaceText *st, ARegion *region, const bool sel) end = MIN2(end, i); if (chop) { - endj = BLI_str_prev_char_utf8((*linep)->line + j) - (*linep)->line; + endj = BLI_str_find_prev_char_utf8((*linep)->line + j, (*linep)->line) - (*linep)->line; } if (endj >= oldc) { @@ -3486,7 +3486,7 @@ static int text_insert_invoke(bContext *C, wmOperator *op, const wmEvent *event) } else { /* in theory, ghost can set value to extended ascii here */ - len = BLI_str_utf8_from_unicode(event->ascii, str); + len = BLI_str_utf8_from_unicode(event->ascii, str, sizeof(str) - 1); } str[len] = '\0'; RNA_string_set(op->ptr, "text", str); diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c index b055a0fe947..466820353b9 100644 --- a/source/blender/editors/space_view3d/view3d_edit.c +++ b/source/blender/editors/space_view3d/view3d_edit.c @@ -5292,7 +5292,7 @@ static int toggle_shading_exec(bContext *C, wmOperator *op) } ED_view3d_shade_update(bmain, v3d, area); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, v3d); return OPERATOR_FINISHED; } @@ -5347,6 +5347,7 @@ static int toggle_xray_exec(bContext *C, wmOperator *op) } ED_area_tag_redraw(area); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D | NS_VIEW3D_SHADING, v3d); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 45ed0f3b664..9548498f0b8 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -29,10 +29,13 @@ #include "BLI_math.h" #include "BKE_context.h" +#include "BKE_main.h" #include "BKE_report.h" #include "ED_markers.h" +#include "SEQ_edit.h" +#include "SEQ_effects.h" #include "SEQ_iterator.h" #include "SEQ_relations.h" #include "SEQ_sequencer.h" @@ -341,12 +344,17 @@ static bool seq_transform_check_strip_effects(SeqCollection *transformed_strips) return false; } +static ListBase *seqbase_active_get(const TransInfo *t) +{ + Editing *ed = SEQ_editing_get(t->scene, false); + return SEQ_active_seqbase_get(ed); +} + /* Offset all strips positioned after left edge of transformed strips boundbox by amount equal * to overlap of transformed strips. */ static void seq_transform_handle_expand_to_fit(TransInfo *t, SeqCollection *transformed_strips) { - Editing *ed = SEQ_editing_get(t->scene, false); - ListBase *seqbasep = SEQ_active_seqbase_get(ed); + ListBase *seqbasep = seqbase_active_get(t); ListBase *markers = &t->scene->markers; const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & SEQ_MARKER_TRANS) != 0; @@ -378,23 +386,178 @@ static void seq_transform_handle_expand_to_fit(TransInfo *t, SeqCollection *tran SEQ_collection_free(right_side_strips); } -static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transformed_strips) +static SeqCollection *query_overwrite_targets(const TransInfo *t, + SeqCollection *transformed_strips) { - Editing *ed = SEQ_editing_get(t->scene, false); - ListBase *seqbasep = SEQ_active_seqbase_get(ed); + SeqCollection *collection = SEQ_query_unselected_strips(seqbase_active_get(t)); + + Sequence *seq, *seq_transformed; + SEQ_ITERATOR_FOREACH (seq, collection) { + bool does_overlap = false; - if (t->flag & T_ALT_TRANSFORM) { - seq_transform_handle_expand_to_fit(t, transformed_strips); + SEQ_ITERATOR_FOREACH (seq_transformed, transformed_strips) { + /* Effects of transformed strips can be unselected. These must not be included. */ + if (seq == seq_transformed) { + SEQ_collection_remove_strip(seq, collection); + } + if (SEQ_transform_test_overlap_seq_seq(seq, seq_transformed)) { + does_overlap = true; + } + } + + if (!does_overlap) { + SEQ_collection_remove_strip(seq, collection); + } } - else { - ListBase *markers = &t->scene->markers; - const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & - SEQ_MARKER_TRANS) != 0; - /* Shuffle non strips with no effects attached. */ - SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); - SEQ_transform_seqbase_shuffle_time( - standalone_strips, seqbasep, t->scene, markers, use_sync_markers); - SEQ_collection_free(standalone_strips); + + return collection; +} + +typedef enum eOvelapDescrition { + /* No overlap. */ + STRIP_OVERLAP_NONE, + /* Overlapping strip covers overlapped completely. */ + STRIP_OVERLAP_IS_FULL, + /* Overlapping strip is inside overlapped. */ + STRIP_OVERLAP_IS_INSIDE, + /* Partial overlap between 2 strips. */ + STRIP_OVERLAP_LEFT_SIDE, + STRIP_OVERLAP_RIGHT_SIDE, +} eOvelapDescrition; + +static eOvelapDescrition overlap_description_get(const Sequence *transformed, + const Sequence *target) +{ + if (transformed->startdisp <= target->startdisp && transformed->enddisp >= target->enddisp) { + return STRIP_OVERLAP_IS_FULL; + } + if (transformed->startdisp > target->startdisp && transformed->enddisp < target->enddisp) { + return STRIP_OVERLAP_IS_INSIDE; + } + if (transformed->startdisp <= target->startdisp && target->startdisp <= transformed->enddisp) { + return STRIP_OVERLAP_LEFT_SIDE; + } + if (transformed->startdisp <= target->enddisp && target->enddisp <= transformed->enddisp) { + return STRIP_OVERLAP_RIGHT_SIDE; + } + return STRIP_OVERLAP_NONE; +} + +/* Split strip in 3 parts, remove middle part and fit transformed inside. */ +static void seq_transform_handle_overwrite_split(const TransInfo *t, + const Sequence *transformed, + Sequence *target) +{ + Main *bmain = CTX_data_main(t->context); + Scene *scene = t->scene; + ListBase *seqbase = seqbase_active_get(t); + + Sequence *split_strip = SEQ_edit_strip_split( + bmain, scene, seqbase, target, transformed->startdisp, SEQ_SPLIT_SOFT, NULL); + SEQ_edit_strip_split( + bmain, scene, seqbase, split_strip, transformed->enddisp, SEQ_SPLIT_SOFT, NULL); + SEQ_edit_flag_for_removal(scene, seqbase_active_get(t), split_strip); +} + +/* Trim strips by adjusting handle position. + * This is bit more complicated in case overlap happens on effect. */ +static void seq_transform_handle_overwrite_trim(const TransInfo *t, + const Sequence *transformed, + Sequence *target, + const eOvelapDescrition overlap) +{ + SeqCollection *targets = SEQ_query_by_reference( + target, seqbase_active_get(t), SEQ_query_strip_effect_chain); + + /* Expand collection by adding all target's children, effects and their children. */ + if ((target->type & SEQ_TYPE_EFFECT) != 0) { + SEQ_collection_expand(seqbase_active_get(t), targets, SEQ_query_strip_effect_chain); + } + + /* Trim all non effects, that have influence on effect length which is overlapping. */ + Sequence *seq; + SEQ_ITERATOR_FOREACH (seq, targets) { + if ((seq->type & SEQ_TYPE_EFFECT) != 0 && SEQ_effect_get_num_inputs(seq->type) > 0) { + continue; + } + if (overlap == STRIP_OVERLAP_LEFT_SIDE) { + SEQ_transform_set_left_handle_frame(seq, transformed->enddisp); + } + else { + BLI_assert(overlap == STRIP_OVERLAP_RIGHT_SIDE); + SEQ_transform_set_right_handle_frame(seq, transformed->startdisp); + } + SEQ_time_update_sequence(t->scene, seq); + } + SEQ_collection_free(targets); +} + +static void seq_transform_handle_overwrite(const TransInfo *t, SeqCollection *transformed_strips) +{ + SeqCollection *targets = query_overwrite_targets(t, transformed_strips); + + bool strips_delete = false; + Sequence *target; + Sequence *transformed; + SEQ_ITERATOR_FOREACH (target, targets) { + SEQ_ITERATOR_FOREACH (transformed, transformed_strips) { + if (transformed->machine != target->machine) { + continue; + } + + const eOvelapDescrition overlap = overlap_description_get(transformed, target); + + if (overlap == STRIP_OVERLAP_IS_FULL) { + /* Remove covered strip. */ + SEQ_edit_flag_for_removal(t->scene, seqbase_active_get(t), target); + strips_delete = true; + } + else if (overlap == STRIP_OVERLAP_IS_INSIDE) { + seq_transform_handle_overwrite_split(t, transformed, target); + strips_delete = true; + } + else if (ELEM(overlap, STRIP_OVERLAP_LEFT_SIDE, STRIP_OVERLAP_RIGHT_SIDE)) { + seq_transform_handle_overwrite_trim(t, transformed, target, overlap); + } + } + } + + SEQ_collection_free(targets); + + if (strips_delete) { + SEQ_edit_remove_flagged_sequences(t->scene, seqbase_active_get(t)); + } +} + +static void seq_transform_handle_overlap_shuffle(const TransInfo *t, + SeqCollection *transformed_strips) +{ + ListBase *seqbase = seqbase_active_get(t); + ListBase *markers = &t->scene->markers; + const bool use_sync_markers = (((SpaceSeq *)t->area->spacedata.first)->flag & + SEQ_MARKER_TRANS) != 0; + /* Shuffle non strips with no effects attached. */ + SeqCollection *standalone_strips = extract_standalone_strips(transformed_strips); + SEQ_transform_seqbase_shuffle_time( + standalone_strips, seqbase, t->scene, markers, use_sync_markers); + SEQ_collection_free(standalone_strips); +} + +static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transformed_strips) +{ + ListBase *seqbasep = seqbase_active_get(t); + const eSeqOverlapMode overlap_mode = SEQ_tool_settings_overlap_mode_get(t->scene); + + switch (overlap_mode) { + case SEQ_OVERLAP_EXPAND: + seq_transform_handle_expand_to_fit(t, transformed_strips); + break; + case SEQ_OVERLAP_OVERWRITE: + seq_transform_handle_overwrite(t, transformed_strips); + break; + case SEQ_OVERLAP_SHUFFLE: + seq_transform_handle_overlap_shuffle(t, transformed_strips); + break; } if (seq_transform_check_strip_effects(transformed_strips)) { @@ -409,6 +572,7 @@ static void seq_transform_handle_overlap(TransInfo *t, SeqCollection *transforme if (SEQ_transform_test_overlap(seqbasep, seq)) { SEQ_transform_seqbase_shuffle(seqbasep, seq, t->scene); } + seq->flag &= ~SEQ_OVERLAP; } } @@ -432,6 +596,7 @@ static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *c } SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc); + SEQ_collection_expand(seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain); if (t->state == TRANS_CANCEL) { seq_transform_cancel(t, transformed_strips); @@ -549,7 +714,7 @@ BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int s static void flushTransSeq(TransInfo *t) { /* Editing null check already done */ - ListBase *seqbasep = SEQ_editing_get(t->scene, false)->seqbasep; + ListBase *seqbasep = seqbase_active_get(t); int a, new_frame; TransData *td = NULL; @@ -601,22 +766,17 @@ static void flushTransSeq(TransInfo *t) /* need to do the overlap check in a new loop otherwise adjacent strips * will not be updated and we'll get false positives */ - Sequence *seq_prev = NULL; - seq_prev = NULL; - for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { - - tdsq = (TransDataSeq *)td->extra; - seq = tdsq->seq; + SeqCollection *transformed_strips = seq_transform_collection_from_transdata(tc); + SEQ_collection_expand(seqbase_active_get(t), transformed_strips, SEQ_query_strip_effect_chain); - if (seq != seq_prev) { - /* test overlap, displays red outline */ - seq->flag &= ~SEQ_OVERLAP; - if (SEQ_transform_test_overlap(seqbasep, seq)) { - seq->flag |= SEQ_OVERLAP; - } + SEQ_ITERATOR_FOREACH (seq, transformed_strips) { + /* test overlap, displays red outline */ + seq->flag &= ~SEQ_OVERLAP; + if (SEQ_transform_test_overlap(seqbasep, seq)) { + seq->flag |= SEQ_OVERLAP; } - seq_prev = seq; } + SEQ_collection_free(transformed_strips); } /* helper for recalcData() - for sequencer transforms */ diff --git a/source/blender/editors/transform/transform_mode_edge_seq_slide.c b/source/blender/editors/transform/transform_mode_edge_seq_slide.c index a8f7fc43b5e..fb70d04a530 100644 --- a/source/blender/editors/transform/transform_mode_edge_seq_slide.c +++ b/source/blender/editors/transform/transform_mode_edge_seq_slide.c @@ -54,18 +54,6 @@ /** \name Transform (Sequencer Slide) * \{ */ -static eRedrawFlag seq_slide_handleEvent(struct TransInfo *t, const wmEvent *event) -{ - BLI_assert(t->mode == TFM_SEQ_SLIDE); - const wmKeyMapItem *kmi = t->custom.mode.data; - if (kmi && event->type == kmi->type && event->val == kmi->val) { - /* Allows the "Expand to Fit" effect to be enabled as a toggle. */ - t->flag ^= T_ALT_TRANSFORM; - return TREDRAW_HARD; - } - return TREDRAW_NOTHING; -} - static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRAW_STR]) { char tvec[NUM_STR_REP_LEN * 3]; @@ -79,17 +67,7 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA } ofs += BLI_snprintf_rlen( - str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s, ("), &tvec[0], t->con.text); - - const wmKeyMapItem *kmi = t->custom.mode.data; - if (kmi) { - ofs += WM_keymap_item_to_string(kmi, false, str + ofs, UI_MAX_DRAW_STR - ofs); - } - - ofs += BLI_snprintf_rlen(str + ofs, - UI_MAX_DRAW_STR - ofs, - TIP_(" or Alt) Expand to fit %s"), - WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); + str + ofs, UI_MAX_DRAW_STR - ofs, TIP_("Sequence Slide: %s%s"), &tvec[0], t->con.text); } static void applySeqSlideValue(TransInfo *t, const float val[2]) @@ -148,7 +126,6 @@ static void applySeqSlide(TransInfo *t, const int UNUSED(mval[2])) void initSeqSlide(TransInfo *t) { t->transform = applySeqSlide; - t->handleEvent = seq_slide_handleEvent; t->tsnap.applySnap = transform_snap_sequencer_apply_translate; initMouseInputMode(t, &t->mouse, INPUT_VECTOR); diff --git a/source/blender/imbuf/intern/thumbs.c b/source/blender/imbuf/intern/thumbs.c index 19f34a6aacd..a09f06726a6 100644 --- a/source/blender/imbuf/intern/thumbs.c +++ b/source/blender/imbuf/intern/thumbs.c @@ -435,8 +435,9 @@ static ImBuf *thumb_create_ex(const char *file_path, scaledy = (float)tsize; scaledx = ((float)img->x / (float)img->y) * tsize; } - ex = (short)scaledx; - ey = (short)scaledy; + /* Scaling down must never assign zero width/height, see: T89868. */ + ex = MAX2(1, (short)scaledx); + ey = MAX2(1, (short)scaledy); /* save some time by only scaling byte buf */ if (img->rect_float) { diff --git a/source/blender/io/alembic/exporter/abc_custom_props.cc b/source/blender/io/alembic/exporter/abc_custom_props.cc index 4ea2fd03fff..e27a9012dab 100644 --- a/source/blender/io/alembic/exporter/abc_custom_props.cc +++ b/source/blender/io/alembic/exporter/abc_custom_props.cc @@ -60,9 +60,6 @@ void CustomPropertiesExporter::write_all(const IDProperty *group) /* Loop over the properties, just like IDP_foreach_property() does, but without the recursion. */ LISTBASE_FOREACH (IDProperty *, id_property, &group->data.group) { - if (STREQ(id_property->name, "_RNA_UI")) { - continue; - } write(id_property); } } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 10a5a0f1c47..5b7c99f2545 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -59,6 +59,58 @@ typedef struct DrawDataList { struct DrawData *first, *last; } DrawDataList; +typedef struct IDPropertyUIData { + /** Tooltip / property description pointer. Owned by the IDProperty. */ + char *description; + /** RNA subtype, used for every type except string properties (PropertySubType). */ + int rna_subtype; + + char _pad[4]; +} IDPropertyUIData; + +/* IDP_UI_DATA_TYPE_INT */ +typedef struct IDPropertyUIDataInt { + IDPropertyUIData base; + int *default_array; /* Only for array properties. */ + int default_array_len; + char _pad[4]; + + int min; + int max; + int soft_min; + int soft_max; + int step; + int default_value; +} IDPropertyUIDataInt; + +/* IDP_UI_DATA_TYPE_FLOAT */ +typedef struct IDPropertyUIDataFloat { + IDPropertyUIData base; + double *default_array; /* Only for array properties. */ + int default_array_len; + char _pad[4]; + + float step; + int precision; + + double min; + double max; + double soft_min; + double soft_max; + double default_value; +} IDPropertyUIDataFloat; + +/* IDP_UI_DATA_TYPE_STRING */ +typedef struct IDPropertyUIDataString { + IDPropertyUIData base; + char *default_value; +} IDPropertyUIDataString; + +/* IDP_UI_DATA_TYPE_ID */ +typedef struct IDPropertyUIDataID { + IDPropertyUIData base; +} IDPropertyUIDataID; + typedef struct IDPropertyData { void *pointer; ListBase group; @@ -87,6 +139,8 @@ typedef struct IDProperty { /* totallen is total length of allocated array/string, including a buffer. * Note that the buffering is mild; the code comes from python's list implementation. */ int totallen; + + IDPropertyUIData *ui_data; } IDProperty; #define MAX_IDPROP_NAME 64 diff --git a/source/blender/makesdna/DNA_modifier_types.h b/source/blender/makesdna/DNA_modifier_types.h index 1bebbc35747..13213f70fed 100644 --- a/source/blender/makesdna/DNA_modifier_types.h +++ b/source/blender/makesdna/DNA_modifier_types.h @@ -298,6 +298,7 @@ enum { /* Mask Modifier -> flag */ enum { MOD_MASK_INV = (1 << 0), + MOD_MASK_SMOOTH = (1 << 1), }; typedef struct ArrayModifierData { diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index f6bf5446a60..2840c795884 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -1347,12 +1347,19 @@ typedef struct SequencerToolSettings { int fit_method; short snap_mode; short snap_flag; - int _pad0; + /* eSeqOverlapMode */ + int overlap_mode; /** When there are many snap points, 0-1 range corresponds to resolution from boundbox to all * possible snap points. */ int snap_distance; } SequencerToolSettings; +typedef enum eSeqOverlapMode { + SEQ_OVERLAP_EXPAND, + SEQ_OVERLAP_OVERWRITE, + SEQ_OVERLAP_SHUFFLE, +} eSeqOverlapMode; + typedef enum eSeqImageFitMethod { SEQ_SCALE_TO_FIT, SEQ_SCALE_TO_FILL, diff --git a/source/blender/makesrna/RNA_access.h b/source/blender/makesrna/RNA_access.h index b943a8fad5a..abbe609d0ef 100644 --- a/source/blender/makesrna/RNA_access.h +++ b/source/blender/makesrna/RNA_access.h @@ -1006,7 +1006,7 @@ int RNA_property_int_get_index(PointerRNA *ptr, PropertyRNA *prop, int index); void RNA_property_int_set_array(PointerRNA *ptr, PropertyRNA *prop, const int *values); void RNA_property_int_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, int value); int RNA_property_int_get_default(PointerRNA *ptr, PropertyRNA *prop); -bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value); +bool RNA_property_int_set_default(PropertyRNA *prop, int value); void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values); int RNA_property_int_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index); @@ -1018,7 +1018,7 @@ float RNA_property_float_get_index(PointerRNA *ptr, PropertyRNA *prop, int index void RNA_property_float_set_array(PointerRNA *ptr, PropertyRNA *prop, const float *values); void RNA_property_float_set_index(PointerRNA *ptr, PropertyRNA *prop, int index, float value); float RNA_property_float_get_default(PointerRNA *ptr, PropertyRNA *prop); -bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value); +bool RNA_property_float_set_default(PropertyRNA *prop, float value); void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values); float RNA_property_float_get_default_index(PointerRNA *ptr, PropertyRNA *prop, int index); @@ -1028,7 +1028,7 @@ char *RNA_property_string_get_alloc( void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value); void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const char *value, int len); int RNA_property_string_length(PointerRNA *ptr, PropertyRNA *prop); -void RNA_property_string_get_default(PointerRNA *ptr, PropertyRNA *prop, char *value); +void RNA_property_string_get_default(PropertyRNA *prop, char *value, int max_len); char *RNA_property_string_get_default_alloc( PointerRNA *ptr, PropertyRNA *prop, char *fixedbuf, int fixedlen, int *r_len); int RNA_property_string_default_length(PointerRNA *ptr, PropertyRNA *prop); diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index 8fa2d0ed849..a2905018cc7 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -246,130 +246,6 @@ void rna_idproperty_touch(IDProperty *idprop) idprop->flag &= ~IDP_FLAG_GHOST; } -static IDProperty *rna_idproperty_ui_container(PropertyRNA *prop) -{ - IDProperty *idprop; - - for (idprop = ((IDProperty *)prop)->prev; idprop; idprop = idprop->prev) { - if (STREQ(RNA_IDP_UI, idprop->name)) { - break; - } - } - - if (idprop == NULL) { - for (idprop = ((IDProperty *)prop)->next; idprop; idprop = idprop->next) { - if (STREQ(RNA_IDP_UI, idprop->name)) { - break; - } - } - } - - return idprop; -} - -/* return a UI local ID prop definition for this prop */ -static const IDProperty *rna_idproperty_ui(const PropertyRNA *prop) -{ - IDProperty *idprop = rna_idproperty_ui_container((PropertyRNA *)prop); - - if (idprop) { - return IDP_GetPropertyTypeFromGroup(idprop, ((IDProperty *)prop)->name, IDP_GROUP); - } - - return NULL; -} - -/* return or create a UI local ID prop definition for this prop */ -static IDProperty *rna_idproperty_ui_ensure(PointerRNA *ptr, PropertyRNA *prop, bool create) -{ - IDProperty *idprop = rna_idproperty_ui_container(prop); - IDPropertyTemplate dummy = {0}; - - if (idprop == NULL && create) { - IDProperty *props = RNA_struct_idprops(ptr, false); - - /* Sanity check: props is the actual container of this property. */ - if (props != NULL && BLI_findindex(&props->data.group, prop) >= 0) { - idprop = IDP_New(IDP_GROUP, &dummy, RNA_IDP_UI); - - if (!IDP_AddToGroup(props, idprop)) { - IDP_FreePropertyContent(idprop); - return NULL; - } - } - } - - if (idprop) { - const char *name = ((IDProperty *)prop)->name; - IDProperty *rv = IDP_GetPropertyTypeFromGroup(idprop, name, IDP_GROUP); - - if (rv == NULL && create) { - rv = IDP_New(IDP_GROUP, &dummy, name); - - if (!IDP_AddToGroup(idprop, rv)) { - IDP_FreePropertyContent(rv); - return NULL; - } - } - - return rv; - } - - return NULL; -} - -static bool rna_idproperty_ui_set_default(PointerRNA *ptr, - PropertyRNA *prop, - const char type, - IDPropertyTemplate *value) -{ - BLI_assert(ELEM(type, IDP_INT, IDP_DOUBLE)); - - if (prop->magic == RNA_MAGIC) { - return false; - } - - /* attempt to get the local ID values */ - IDProperty *idp_ui = rna_idproperty_ui_ensure(ptr, prop, value != NULL); - - if (idp_ui == NULL) { - return (value == NULL); - } - - IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", type); - - if (value == NULL) { - if (item != NULL) { - IDP_RemoveFromGroup(idp_ui, item); - } - } - else { - if (item != NULL) { - switch (type) { - case IDP_INT: - IDP_Int(item) = value->i; - break; - case IDP_DOUBLE: - IDP_Double(item) = value->d; - break; - default: - BLI_assert(false); - return false; - } - } - else { - item = IDP_New(type, value, "default"); - - if (!IDP_AddToGroup(idp_ui, item)) { - IDP_FreePropertyContent(item); - return false; - } - } - } - - return true; -} - IDProperty **RNA_struct_idprops_p(PointerRNA *ptr) { StructRNA *type = ptr->type; @@ -693,28 +569,17 @@ static const char *rna_ensure_property_identifier(const PropertyRNA *prop) static const char *rna_ensure_property_description(const PropertyRNA *prop) { - const char *description = NULL; - if (prop->magic == RNA_MAGIC) { - description = prop->description; + return prop->description; } - else { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "description", IDP_STRING); - if (item) { - description = IDP_String(item); - } - } - if (description == NULL) { - description = ((IDProperty *)prop)->name; /* XXX: not correct. */ - } + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + const IDPropertyUIData *ui_data = idprop->ui_data; + return ui_data->description; } - return description; + return ""; } static const char *rna_ensure_property_name(const PropertyRNA *prop) @@ -1196,19 +1061,9 @@ PropertySubType RNA_property_subtype(PropertyRNA *prop) if (prop->magic != RNA_MAGIC) { IDProperty *idprop = (IDProperty *)prop; - if (ELEM(idprop->type, IDP_INT, IDP_FLOAT, IDP_DOUBLE) || - ((idprop->type == IDP_ARRAY) && ELEM(idprop->subtype, IDP_INT, IDP_FLOAT, IDP_DOUBLE))) { - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item = IDP_GetPropertyTypeFromGroup(idp_ui, "subtype", IDP_STRING); - - if (item) { - int result = PROP_NONE; - RNA_enum_value_from_id(rna_enum_property_subtype_items, IDP_String(item), &result); - return (PropertySubType)result; - } - } + if (idprop->ui_data) { + IDPropertyUIData *ui_data = idprop->ui_data; + return (PropertySubType)ui_data->rna_subtype; } } @@ -1387,20 +1242,17 @@ void RNA_property_int_range(PointerRNA *ptr, PropertyRNA *prop, int *hardmin, in int softmin, softmax; if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_INT); - *hardmin = item ? IDP_Int(item) : INT_MIN; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_INT); - *hardmax = item ? IDP_Int(item) : INT_MAX; - - return; + const IDProperty *idprop = (IDProperty *)prop; + if (idprop->ui_data) { + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)idprop->ui_data; + *hardmin = ui_data->min; + *hardmax = ui_data->max; + } + else { + *hardmin = INT_MIN; + *hardmax = INT_MAX; } + return; } if (iprop->range) { @@ -1428,23 +1280,19 @@ void RNA_property_int_ui_range( int hardmin, hardmax; if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_min", IDP_INT); - *softmin = item ? IDP_Int(item) : INT_MIN; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "soft_max", IDP_INT); - *softmax = item ? IDP_Int(item) : INT_MAX; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "step", IDP_INT); - *step = item ? IDP_Int(item) : 1; - - return; + const IDProperty *idprop = (IDProperty *)prop; + if (idprop->ui_data) { + IDPropertyUIDataInt *ui_data_int = (IDPropertyUIDataInt *)idprop->ui_data; + *softmin = ui_data_int->soft_min; + *softmax = ui_data_int->soft_max; + *step = ui_data_int->step; } + else { + *softmin = INT_MIN; + *softmax = INT_MAX; + *step = 1; + } + return; } *softmin = iprop->softmin; @@ -1478,20 +1326,17 @@ void RNA_property_float_range(PointerRNA *ptr, PropertyRNA *prop, float *hardmin float softmin, softmax; if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "min", IDP_DOUBLE); - *hardmin = item ? (float)IDP_Double(item) : -FLT_MAX; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "max", IDP_DOUBLE); - *hardmax = item ? (float)IDP_Double(item) : FLT_MAX; - - return; + const IDProperty *idprop = (IDProperty *)prop; + if (idprop->ui_data) { + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)idprop->ui_data; + *hardmin = (float)ui_data->min; + *hardmax = (float)ui_data->max; + } + else { + *hardmin = -FLT_MAX; + *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_MAX; + *softmax = FLT_MAX; + *step = 1.0f; + *precision = 3.0f; } + return; } *softmin = fprop->softmin; @@ -2905,29 +2745,28 @@ int RNA_property_int_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop) IntPropertyRNA *iprop = (IntPropertyRNA *)rna_ensure_property(prop); if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_INT); - return item ? IDP_Int(item) : iprop->defaultvalue; + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + const IDPropertyUIDataInt *ui_data = (const IDPropertyUIDataInt *)idprop->ui_data; + return ui_data->default_value; } } return iprop->defaultvalue; } -bool RNA_property_int_set_default(PointerRNA *ptr, PropertyRNA *prop, int value) +bool RNA_property_int_set_default(PropertyRNA *prop, int value) { - if (value != 0) { - IDPropertyTemplate val = { - .i = value, - }; - return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, &val); + if (prop->magic == RNA_MAGIC) { + return false; } - return rna_idproperty_ui_set_default(ptr, prop, IDP_INT, NULL); + + IDProperty *idprop = (IDProperty *)prop; + BLI_assert(idprop->type == IDP_INT); + + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(idprop); + ui_data->default_value = value; + return true; } void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int *values) @@ -2940,17 +2779,22 @@ void RNA_property_int_get_default_array(PointerRNA *ptr, PropertyRNA *prop, int if (prop->magic != RNA_MAGIC) { int length = rna_ensure_property_array_length(ptr, prop); - const IDProperty *idp_ui = rna_idproperty_ui(prop); - IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL; - - int defval = (item && item->type == IDP_INT) ? IDP_Int(item) : iprop->defaultvalue; - - if (item && item->type == IDP_ARRAY && item->subtype == IDP_INT) { - rna_property_int_fill_default_array_values( - IDP_Array(item), item->len, defval, length, values); - } - else { - rna_property_int_fill_default_array_values(NULL, 0, defval, length, values); + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + BLI_assert(idprop->type == IDP_ARRAY); + BLI_assert(idprop->subtype == IDP_INT); + const IDPropertyUIDataInt *ui_data = (const IDPropertyUIDataInt *)idprop->ui_data; + if (ui_data->default_array) { + rna_property_int_fill_default_array_values(ui_data->default_array, + ui_data->default_array_len, + ui_data->default_value, + length, + values); + } + else { + rna_property_int_fill_default_array_values( + NULL, 0, ui_data->default_value, length, values); + } } } else if (prop->arraydimension == 0) { @@ -3066,6 +2910,26 @@ static void rna_property_float_fill_default_array_values( } } +/** + * The same logic as #rna_property_float_fill_default_array_values for a double array. + */ +static void rna_property_float_fill_default_array_values_double(const double *default_array, + const int default_array_len, + const double default_value, + const int out_length, + float *r_values) +{ + const int array_copy_len = MIN2(out_length, default_array_len); + + for (int i = 0; i < array_copy_len; i++) { + r_values[i] = (float)default_array[i]; + } + + for (int i = array_copy_len; i < out_length; i++) { + r_values[i] = (float)default_value; + } +} + static void rna_property_float_get_default_array_values(PointerRNA *ptr, FloatPropertyRNA *fprop, float *r_values) @@ -3268,29 +3132,29 @@ float RNA_property_float_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop) BLI_assert(RNA_property_array_check(prop) == false); if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_DOUBLE); - return item ? IDP_Double(item) : fprop->defaultvalue; + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + BLI_assert(ELEM(idprop->type, IDP_FLOAT, IDP_DOUBLE)); + const IDPropertyUIDataFloat *ui_data = (const IDPropertyUIDataFloat *)idprop->ui_data; + return (float)ui_data->default_value; } } return fprop->defaultvalue; } -bool RNA_property_float_set_default(PointerRNA *ptr, PropertyRNA *prop, float value) +bool RNA_property_float_set_default(PropertyRNA *prop, float value) { - if (value != 0) { - IDPropertyTemplate val = { - .d = value, - }; - return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, &val); + if (prop->magic == RNA_MAGIC) { + return false; } - return rna_idproperty_ui_set_default(ptr, prop, IDP_DOUBLE, NULL); + + IDProperty *idprop = (IDProperty *)prop; + BLI_assert(idprop->type == IDP_FLOAT); + + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(idprop); + ui_data->default_value = (double)value; + return true; } void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, float *values) @@ -3303,23 +3167,16 @@ void RNA_property_float_get_default_array(PointerRNA *ptr, PropertyRNA *prop, fl if (prop->magic != RNA_MAGIC) { int length = rna_ensure_property_array_length(ptr, prop); - const IDProperty *idp_ui = rna_idproperty_ui(prop); - IDProperty *item = idp_ui ? IDP_GetPropertyFromGroup(idp_ui, "default") : NULL; - - float defval = (item && item->type == IDP_DOUBLE) ? IDP_Double(item) : fprop->defaultvalue; - - if (item && item->type == IDP_ARRAY && item->subtype == IDP_DOUBLE) { - double *defarr = IDP_Array(item); - for (int i = 0; i < length; i++) { - values[i] = (i < item->len) ? (float)defarr[i] : defval; - } - } - else if (item && item->type == IDP_ARRAY && item->subtype == IDP_FLOAT) { - rna_property_float_fill_default_array_values( - IDP_Array(item), item->len, defval, length, values); - } - else { - rna_property_float_fill_default_array_values(NULL, 0, defval, length, values); + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + BLI_assert(idprop->type == IDP_ARRAY); + BLI_assert(ELEM(idprop->subtype, IDP_FLOAT, IDP_DOUBLE)); + const IDPropertyUIDataFloat *ui_data = (const IDPropertyUIDataFloat *)idprop->ui_data; + rna_property_float_fill_default_array_values_double(ui_data->default_array, + ui_data->default_array_len, + ui_data->default_value, + length, + values); } } else if (prop->arraydimension == 0) { @@ -3510,22 +3367,17 @@ void RNA_property_string_set_bytes(PointerRNA *ptr, PropertyRNA *prop, const cha } } -void RNA_property_string_get_default(PointerRNA *UNUSED(ptr), PropertyRNA *prop, char *value) +void RNA_property_string_get_default(PropertyRNA *prop, char *value, const int max_len) { StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING); - if (item) { - strcpy(value, IDP_String(item)); - return; - } + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + BLI_assert(idprop->type == IDP_STRING); + const IDPropertyUIDataString *ui_data = (const IDPropertyUIDataString *)idprop->ui_data; + BLI_strncpy(value, ui_data->default_value, max_len); + return; } strcpy(value, ""); @@ -3554,7 +3406,7 @@ char *RNA_property_string_get_default_alloc( buf = MEM_callocN(sizeof(char) * (length + 1), __func__); } - RNA_property_string_get_default(ptr, prop, buf); + RNA_property_string_get_default(prop, buf, length + 1); if (r_len) { *r_len = length; @@ -3569,15 +3421,12 @@ int RNA_property_string_default_length(PointerRNA *UNUSED(ptr), PropertyRNA *pro StringPropertyRNA *sprop = (StringPropertyRNA *)rna_ensure_property(prop); if (prop->magic != RNA_MAGIC) { - /* attempt to get the local ID values */ - const IDProperty *idp_ui = rna_idproperty_ui(prop); - - if (idp_ui) { - IDProperty *item; - - item = IDP_GetPropertyTypeFromGroup(idp_ui, "default", IDP_STRING); - if (item) { - return strlen(IDP_String(item)); + const IDProperty *idprop = (const IDProperty *)prop; + if (idprop->ui_data) { + BLI_assert(idprop->type == IDP_STRING); + const IDPropertyUIDataString *ui_data = (const IDPropertyUIDataString *)idprop->ui_data; + if (ui_data->default_value != NULL) { + return strlen(ui_data->default_value); } } @@ -8200,12 +8049,12 @@ bool RNA_property_assign_default(PointerRNA *ptr, PropertyRNA *prop) switch (RNA_property_type(prop)) { case PROP_INT: { int value = RNA_property_int_get(ptr, prop); - return RNA_property_int_set_default(ptr, prop, value); + return RNA_property_int_set_default(prop, value); } case PROP_FLOAT: { float value = RNA_property_float_get(ptr, prop); - return RNA_property_float_set_default(ptr, prop, value); + return RNA_property_float_set_default(prop, value); } default: diff --git a/source/blender/makesrna/intern/rna_internal_types.h b/source/blender/makesrna/intern/rna_internal_types.h index 479306e8c06..22a9b9d930a 100644 --- a/source/blender/makesrna/intern/rna_internal_types.h +++ b/source/blender/makesrna/intern/rna_internal_types.h @@ -42,9 +42,6 @@ struct bContext; typedef struct IDProperty IDProperty; -/* store local properties here */ -#define RNA_IDP_UI "_RNA_UI" - /* Function Callbacks */ typedef void (*UpdateFunc)(struct Main *main, struct Scene *scene, struct PointerRNA *ptr); diff --git a/source/blender/makesrna/intern/rna_modifier.c b/source/blender/makesrna/intern/rna_modifier.c index 486d8d13564..f11d845c582 100644 --- a/source/blender/makesrna/intern/rna_modifier.c +++ b/source/blender/makesrna/intern/rna_modifier.c @@ -4431,6 +4431,12 @@ static void rna_def_modifier_mask(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Invert", "Use vertices that are not part of region defined"); RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "use_smooth", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, NULL, "flag", MOD_MASK_SMOOTH); + RNA_def_property_ui_text( + prop, "Smooth", "Use vertex group weights to cut faces at the weight contour"); + RNA_def_property_update(prop, 0, "rna_Modifier_update"); + prop = RNA_def_property(srna, "threshold", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "threshold"); RNA_def_property_range(prop, 0.0, 1.0); diff --git a/source/blender/makesrna/intern/rna_object.c b/source/blender/makesrna/intern/rna_object.c index 0f6b89722a4..d3cd3158db1 100644 --- a/source/blender/makesrna/intern/rna_object.c +++ b/source/blender/makesrna/intern/rna_object.c @@ -1326,8 +1326,8 @@ static int rna_Object_rotation_4d_editable(PointerRNA *ptr, int index) static int rna_MaterialSlot_index(PointerRNA *ptr) { - /* There is an offset of one, so that `ptr->data` is not null. */ - return POINTER_AS_INT(ptr->data) - 1; + /* There is an offset, so that `ptr->data` is not null and unique across IDs. */ + return (uintptr_t)ptr->data - (uintptr_t)ptr->owner_id; } static int rna_MaterialSlot_material_editable(PointerRNA *ptr, const char **UNUSED(r_info)) @@ -1490,10 +1490,11 @@ static void rna_Object_material_slots_next(CollectionPropertyIterator *iter) static PointerRNA rna_Object_material_slots_get(CollectionPropertyIterator *iter) { PointerRNA ptr; - RNA_pointer_create((ID *)iter->internal.count.ptr, + ID *id = (ID *)iter->internal.count.ptr; + RNA_pointer_create(id, &RNA_MaterialSlot, - /* Add one, so that `ptr->data` is not null. */ - POINTER_FROM_INT(iter->internal.count.item + 1), + /* Add offset, so that `ptr->data` is not null and unique across IDs. */ + (void *)(iter->internal.count.item + (uintptr_t)id), &ptr); return ptr; } diff --git a/source/blender/makesrna/intern/rna_scene.c b/source/blender/makesrna/intern/rna_scene.c index 9d158761a21..0583cdeb1bb 100644 --- a/source/blender/makesrna/intern/rna_scene.c +++ b/source/blender/makesrna/intern/rna_scene.c @@ -3510,6 +3510,21 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem scale_overlap_modes[] = { + {SEQ_OVERLAP_EXPAND, "EXPAND", 0, "Expand", "Move strips so transformed strips fits"}, + {SEQ_OVERLAP_OVERWRITE, + "OVERWRITE", + 0, + "Overwrite", + "Trim or split strips to resolve overlap"}, + {SEQ_OVERLAP_SHUFFLE, + "SHUFFLE", + 0, + "Shuffle", + "Move transformed strips to nearest free space to resolve overlap"}, + {0, NULL, 0, NULL, NULL}, + }; + srna = RNA_def_struct(brna, "SequencerToolSettings", NULL); RNA_def_struct_path_func(srna, "rna_SequencerToolSettings_path"); RNA_def_struct_ui_text(srna, "Sequencer Tool Settings", ""); @@ -3548,6 +3563,11 @@ static void rna_def_sequencer_tool_settings(BlenderRNA *brna) RNA_def_property_int_default(prop, 15); RNA_def_property_ui_range(prop, 0, 50, 1, 1); RNA_def_property_ui_text(prop, "Snapping Distance", "Maximum distance for snapping in pixels"); + + /* Transform overlap handling. */ + prop = RNA_def_property(srna, "overlap_mode", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, scale_overlap_modes); + RNA_def_property_ui_text(prop, "Overlap Mode", "How to resolve overlap after transformation"); } static void rna_def_unified_paint_settings(BlenderRNA *brna) diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 9aa8e3dd7c8..306e79aa647 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -27,6 +27,7 @@ #include "BLI_ghash.h" #include "BLI_listbase.h" +#include "BLI_math.h" #include "BLT_translation.h" @@ -214,6 +215,40 @@ static void computed_masked_edges(const Mesh *mesh, *r_num_masked_edges = num_masked_edges; } +static void computed_masked_edges_smooth(const Mesh *mesh, + Span<bool> vertex_mask, + MutableSpan<int> r_edge_map, + uint *r_num_masked_edges, + uint *r_num_add_vertices) +{ + BLI_assert(mesh->totedge == r_edge_map.size()); + + uint num_masked_edges = 0; + uint num_add_vertices = 0; + for (int i : IndexRange(mesh->totedge)) { + const MEdge &edge = mesh->medge[i]; + + /* only add if both verts will be in new mesh */ + bool v1 = vertex_mask[edge.v1]; + bool v2 = vertex_mask[edge.v2]; + if (v1 && v2) { + r_edge_map[i] = num_masked_edges; + num_masked_edges++; + } + else if (v1 != v2) { + r_edge_map[i] = -2; + num_add_vertices++; + } + else { + r_edge_map[i] = -1; + } + } + + num_masked_edges += num_add_vertices; + *r_num_masked_edges = num_masked_edges; + *r_num_add_vertices = num_add_vertices; +} + static void computed_masked_polygons(const Mesh *mesh, Span<bool> vertex_mask, Vector<int> &r_masked_poly_indices, @@ -224,7 +259,7 @@ static void computed_masked_polygons(const Mesh *mesh, BLI_assert(mesh->totvert == vertex_mask.size()); r_masked_poly_indices.reserve(mesh->totpoly); - r_loop_starts.reserve(mesh->totloop); + r_loop_starts.reserve(mesh->totpoly); uint num_masked_loops = 0; for (int i : IndexRange(mesh->totpoly)) { @@ -250,6 +285,76 @@ static void computed_masked_polygons(const Mesh *mesh, *r_num_masked_loops = num_masked_loops; } +static void compute_interpolated_polygons(const Mesh *mesh, + Span<bool> vertex_mask, + uint num_add_vertices, + uint num_masked_loops, + Vector<int> &r_masked_poly_indices, + Vector<int> &r_loop_starts, + uint *r_num_add_edges, + uint *r_num_add_polys, + uint *r_num_add_loops) +{ + BLI_assert(mesh->totvert == vertex_mask.size()); + + /* Can't really know ahead of time how much space to use exactly. Estimate limit instead. */ + /* NOTE: this reserve can only lift the capacity if there are ngons, which get split. */ + r_masked_poly_indices.reserve(r_masked_poly_indices.size() + num_add_vertices); + r_loop_starts.reserve(r_loop_starts.size() + num_add_vertices); + + uint num_add_edges = 0; + uint num_add_polys = 0; + uint num_add_loops = 0; + for (int i : IndexRange(mesh->totpoly)) { + const MPoly &poly_src = mesh->mpoly[i]; + + int in_count = 0; + int start = -1; + int dst_totloop = -1; + Span<MLoop> loops_src(&mesh->mloop[poly_src.loopstart], poly_src.totloop); + for (const int j : loops_src.index_range()) { + const MLoop &loop = loops_src[j]; + if (vertex_mask[loop.v]) { + in_count++; + } + else if (start == -1) { + start = j; + } + } + if (0 < in_count && in_count < poly_src.totloop) { + /* Ring search starting at a vertex which is not included in the mask. */ + const MLoop *last_loop = &loops_src[start]; + bool v_loop_in_mask_last = vertex_mask[last_loop->v]; + for (const int j : loops_src.index_range()) { + const MLoop &loop = loops_src[(start + 1 + j) % poly_src.totloop]; + const bool v_loop_in_mask = vertex_mask[loop.v]; + if (v_loop_in_mask && !v_loop_in_mask_last) { + dst_totloop = 3; + } + else if (!v_loop_in_mask && v_loop_in_mask_last) { + BLI_assert(dst_totloop > 2); + r_masked_poly_indices.append(i); + r_loop_starts.append(num_masked_loops + num_add_loops); + num_add_loops += dst_totloop; + num_add_polys++; + num_add_edges++; + dst_totloop = -1; + } + else if (v_loop_in_mask && v_loop_in_mask_last) { + BLI_assert(dst_totloop > 2); + dst_totloop++; + } + last_loop = &loop; + v_loop_in_mask_last = v_loop_in_mask; + } + } + } + + *r_num_add_edges = num_add_edges; + *r_num_add_polys = num_add_polys; + *r_num_add_loops = num_add_loops; +} + void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map) { BLI_assert(src_mesh.totvert == vertex_map.size()); @@ -267,6 +372,89 @@ void copy_masked_vertices_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span } } +static float get_interp_factor_from_vgroup( + MDeformVert *dvert, int defgrp_index, float threshold, uint v1, uint v2) +{ + /* NOTE: this calculation is done twice for every vertex, + * instead of storing it the first time and then reusing it. */ + float value1 = BKE_defvert_find_weight(&dvert[v1], defgrp_index); + float value2 = BKE_defvert_find_weight(&dvert[v2], defgrp_index); + return (threshold - value1) / (value2 - value1); +} + +static void add_interp_verts_copy_edges_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<bool> vertex_mask, + Span<int> vertex_map, + MDeformVert *dvert, + int defgrp_index, + float threshold, + uint num_masked_edges, + uint num_add_verts, + MutableSpan<int> r_edge_map) +{ + BLI_assert(src_mesh.totvert == vertex_mask.size()); + BLI_assert(src_mesh.totedge == r_edge_map.size()); + + uint vert_index = dst_mesh.totvert - num_add_verts; + uint edge_index = num_masked_edges - num_add_verts; + for (int i_src : IndexRange(src_mesh.totedge)) { + if (r_edge_map[i_src] != -1) { + int i_dst = r_edge_map[i_src]; + if (i_dst == -2) { + i_dst = edge_index; + } + const MEdge &e_src = src_mesh.medge[i_src]; + MEdge &e_dst = dst_mesh.medge[i_dst]; + + CustomData_copy_data(&src_mesh.edata, &dst_mesh.edata, i_src, i_dst, 1); + e_dst = e_src; + e_dst.v1 = vertex_map[e_src.v1]; + e_dst.v2 = vertex_map[e_src.v2]; + } + if (r_edge_map[i_src] == -2) { + const int i_dst = edge_index++; + r_edge_map[i_src] = i_dst; + const MEdge &e_src = src_mesh.medge[i_src]; + /* Cut destination edge and make v1 the new vertex. */ + MEdge &e_dst = dst_mesh.medge[i_dst]; + if (!vertex_mask[e_src.v1]) { + e_dst.v1 = vert_index; + } + else { + BLI_assert(!vertex_mask[e_src.v2]); + e_dst.v2 = e_dst.v1; + e_dst.v1 = vert_index; + } + /* Create the new vertex. */ + float fac = get_interp_factor_from_vgroup( + dvert, defgrp_index, threshold, e_src.v1, e_src.v2); + + float weights[2] = {1.0f - fac, fac}; + CustomData_interp( + &src_mesh.vdata, &dst_mesh.vdata, (int *)&e_src.v1, weights, nullptr, 2, vert_index); + MVert &v = dst_mesh.mvert[vert_index]; + MVert &v1 = src_mesh.mvert[e_src.v1]; + MVert &v2 = src_mesh.mvert[e_src.v2]; + + interp_v3_v3v3(v.co, v1.co, v2.co, fac); + + float no1[3]; + float no2[3]; + normal_short_to_float_v3(no1, v1.no); + normal_short_to_float_v3(no2, v2.no); + mul_v3_fl(no1, weights[0]); + madd_v3_v3fl(no1, no2, weights[1]); + normalize_v3(no1); + normal_float_to_short_v3(v.no, no1); + + vert_index++; + } + } + BLI_assert(vert_index == dst_mesh.totvert); + BLI_assert(edge_index == num_masked_edges); +} + void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map, @@ -276,7 +464,7 @@ void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, BLI_assert(src_mesh.totedge == edge_map.size()); for (const int i_src : IndexRange(src_mesh.totedge)) { const int i_dst = edge_map[i_src]; - if (i_dst == -1) { + if (i_dst == -1 || i_dst == -2) { continue; } @@ -290,6 +478,36 @@ void copy_masked_edges_to_new_mesh(const Mesh &src_mesh, } } +static void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<int> vertex_map, + Span<int> edge_map, + Span<int> masked_poly_indices, + Span<int> new_loop_starts, + int num_masked_polys) +{ + for (const int i_dst : IndexRange(num_masked_polys)) { + const int i_src = masked_poly_indices[i_dst]; + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + const int i_ml_dst = new_loop_starts[i_dst]; + + CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1); + CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src, i_ml_dst, mp_src.totloop); + + const MLoop *ml_src = src_mesh.mloop + i_ml_src; + MLoop *ml_dst = dst_mesh.mloop + i_ml_dst; + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + for (int i : IndexRange(mp_src.totloop)) { + ml_dst[i].v = vertex_map[ml_src[i].v]; + ml_dst[i].e = edge_map[ml_src[i].e]; + } + } +} void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, Mesh &dst_mesh, Span<int> vertex_map, @@ -320,6 +538,139 @@ void copy_masked_polys_to_new_mesh(const Mesh &src_mesh, } } +static void add_interpolated_polys_to_new_mesh(const Mesh &src_mesh, + Mesh &dst_mesh, + Span<bool> vertex_mask, + Span<int> vertex_map, + Span<int> edge_map, + MDeformVert *dvert, + int defgrp_index, + float threshold, + Span<int> masked_poly_indices, + Span<int> new_loop_starts, + int num_masked_polys, + int num_add_edges) +{ + int edge_index = dst_mesh.totedge - num_add_edges; + int sub_poly_index = 0; + int last_i_src = -1; + for (const int i_dst : + IndexRange(num_masked_polys, masked_poly_indices.size() - num_masked_polys)) { + const int i_src = masked_poly_indices[i_dst]; + if (i_src == last_i_src) { + sub_poly_index++; + } + else { + sub_poly_index = 0; + last_i_src = i_src; + } + + const MPoly &mp_src = src_mesh.mpoly[i_src]; + MPoly &mp_dst = dst_mesh.mpoly[i_dst]; + const int i_ml_src = mp_src.loopstart; + int i_ml_dst = new_loop_starts[i_dst]; + const int mp_totloop = (i_dst + 1 < new_loop_starts.size() ? new_loop_starts[i_dst + 1] : + dst_mesh.totloop) - + i_ml_dst; + + CustomData_copy_data(&src_mesh.pdata, &dst_mesh.pdata, i_src, i_dst, 1); + + mp_dst = mp_src; + mp_dst.loopstart = i_ml_dst; + mp_dst.totloop = mp_totloop; + + /* Ring search starting at a vertex which is not included in the mask. */ + int start = -sub_poly_index - 1; + bool skip = false; + Span<MLoop> loops_src(&src_mesh.mloop[i_ml_src], mp_src.totloop); + for (const int j : loops_src.index_range()) { + if (!vertex_mask[loops_src[j].v]) { + if (start == -1) { + start = j; + break; + } + if (!skip) { + skip = true; + } + } + else if (skip) { + skip = false; + start++; + } + } + + BLI_assert(start >= 0); + BLI_assert(edge_index < dst_mesh.totedge); + + const MLoop *last_loop = &loops_src[start]; + bool v_loop_in_mask_last = vertex_mask[last_loop->v]; + int last_index = start; + for (const int j : loops_src.index_range()) { + const int index = (start + 1 + j) % mp_src.totloop; + const MLoop &loop = loops_src[index]; + const bool v_loop_in_mask = vertex_mask[loop.v]; + if (v_loop_in_mask && !v_loop_in_mask_last) { + /* Start new cut. */ + float fac = get_interp_factor_from_vgroup( + dvert, defgrp_index, threshold, last_loop->v, loop.v); + float weights[2] = {1.0f - fac, fac}; + int indices[2] = {i_ml_src + last_index, i_ml_src + index}; + CustomData_interp( + &src_mesh.ldata, &dst_mesh.ldata, indices, weights, nullptr, 2, i_ml_dst); + MLoop &cut_dst_loop = dst_mesh.mloop[i_ml_dst]; + cut_dst_loop.e = edge_map[last_loop->e]; + cut_dst_loop.v = dst_mesh.medge[cut_dst_loop.e].v1; + i_ml_dst++; + + CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src + index, i_ml_dst, 1); + MLoop &next_dst_loop = dst_mesh.mloop[i_ml_dst]; + next_dst_loop.v = vertex_map[loop.v]; + next_dst_loop.e = edge_map[loop.e]; + i_ml_dst++; + } + else if (!v_loop_in_mask && v_loop_in_mask_last) { + BLI_assert(i_ml_dst != mp_dst.loopstart); + /* End active cut. */ + float fac = get_interp_factor_from_vgroup( + dvert, defgrp_index, threshold, last_loop->v, loop.v); + float weights[2] = {1.0f - fac, fac}; + int indices[2] = {i_ml_src + last_index, i_ml_src + index}; + CustomData_interp( + &src_mesh.ldata, &dst_mesh.ldata, indices, weights, nullptr, 2, i_ml_dst); + MLoop &cut_dst_loop = dst_mesh.mloop[i_ml_dst]; + cut_dst_loop.e = edge_index; + cut_dst_loop.v = dst_mesh.medge[edge_map[last_loop->e]].v1; + i_ml_dst++; + + /* Create closing edge. */ + MEdge &cut_edge = dst_mesh.medge[edge_index]; + cut_edge.v1 = dst_mesh.mloop[mp_dst.loopstart].v; + cut_edge.v2 = cut_dst_loop.v; + BLI_assert(cut_edge.v1 != cut_edge.v2); + cut_edge.flag = ME_EDGEDRAW | ME_EDGERENDER; + edge_index++; + + /* Only handle one of the cuts per iteration. */ + break; + } + else if (v_loop_in_mask && v_loop_in_mask_last) { + BLI_assert(i_ml_dst != mp_dst.loopstart); + /* Extend active poly. */ + CustomData_copy_data(&src_mesh.ldata, &dst_mesh.ldata, i_ml_src + index, i_ml_dst, 1); + MLoop &dst_loop = dst_mesh.mloop[i_ml_dst]; + dst_loop.v = vertex_map[loop.v]; + dst_loop.e = edge_map[loop.e]; + i_ml_dst++; + } + last_loop = &loop; + last_index = index; + v_loop_in_mask_last = v_loop_in_mask; + } + BLI_assert(mp_dst.loopstart + mp_dst.totloop == i_ml_dst); + } + BLI_assert(edge_index == dst_mesh.totedge); +} + /* Components of the algorithm: * 1. Figure out which vertices should be present in the output mesh. * 2. Find edges and polygons only using those vertices. @@ -329,6 +680,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) { MaskModifierData *mmd = reinterpret_cast<MaskModifierData *>(md); const bool invert_mask = mmd->flag & MOD_MASK_INV; + const bool use_interpolation = mmd->mode == MOD_MASK_MODE_VGROUP && + (mmd->flag & MOD_MASK_SMOOTH); /* Return empty or input mesh when there are no vertex groups. */ MDeformVert *dvert = (MDeformVert *)CustomData_get_layer(&mesh->vdata, CD_MDEFORMVERT); @@ -342,6 +695,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) return mesh; } + int defgrp_index = -1; + Array<bool> vertex_mask; if (mmd->mode == MOD_MASK_MODE_ARM) { Object *armature_ob = mmd->ob_arm; @@ -355,7 +710,8 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) compute_vertex_mask__armature_mode(dvert, mesh, armature_ob, mmd->threshold, vertex_mask); } else { - int defgrp_index = BKE_id_defgroup_name_index(&mesh->id, mmd->vgroup); + BLI_assert(mmd->mode == MOD_MASK_MODE_VGROUP); + defgrp_index = BKE_id_defgroup_name_index(&mesh->id, mmd->vgroup); /* Return input mesh if the vertex group does not exist. */ if (defgrp_index == -1) { @@ -376,7 +732,15 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) Array<int> edge_map(mesh->totedge); uint num_masked_edges; - computed_masked_edges(mesh, vertex_mask, edge_map, &num_masked_edges); + uint num_add_vertices; + if (use_interpolation) { + computed_masked_edges_smooth( + mesh, vertex_mask, edge_map, &num_masked_edges, &num_add_vertices); + } + else { + computed_masked_edges(mesh, vertex_mask, edge_map, &num_masked_edges); + num_add_vertices = 0; + } Vector<int> masked_poly_indices; Vector<int> new_loop_starts; @@ -389,13 +753,65 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *UNUSED(ctx) &num_masked_polys, &num_masked_loops); - Mesh *result = BKE_mesh_new_nomain_from_template( - mesh, num_masked_vertices, num_masked_edges, 0, num_masked_loops, num_masked_polys); + uint num_add_edges = 0; + uint num_add_polys = 0; + uint num_add_loops = 0; + if (use_interpolation) { + compute_interpolated_polygons(mesh, + vertex_mask, + num_add_vertices, + num_masked_loops, + masked_poly_indices, + new_loop_starts, + &num_add_edges, + &num_add_polys, + &num_add_loops); + } + + Mesh *result = BKE_mesh_new_nomain_from_template(mesh, + num_masked_vertices + num_add_vertices, + num_masked_edges + num_add_edges, + 0, + num_masked_loops + num_add_loops, + num_masked_polys + num_add_polys); copy_masked_vertices_to_new_mesh(*mesh, *result, vertex_map); - copy_masked_edges_to_new_mesh(*mesh, *result, vertex_map, edge_map); - copy_masked_polys_to_new_mesh( - *mesh, *result, vertex_map, edge_map, masked_poly_indices, new_loop_starts); + if (use_interpolation) { + add_interp_verts_copy_edges_to_new_mesh(*mesh, + *result, + vertex_mask, + vertex_map, + dvert, + defgrp_index, + mmd->threshold, + num_masked_edges, + num_add_vertices, + edge_map); + } + else { + copy_masked_edges_to_new_mesh(*mesh, *result, vertex_map, edge_map); + } + copy_masked_polys_to_new_mesh(*mesh, + *result, + vertex_map, + edge_map, + masked_poly_indices, + new_loop_starts, + num_masked_polys); + if (use_interpolation) { + add_interpolated_polys_to_new_mesh(*mesh, + *result, + vertex_mask, + vertex_map, + edge_map, + dvert, + defgrp_index, + mmd->threshold, + masked_poly_indices, + new_loop_starts, + num_masked_polys, + num_add_edges); + } BKE_mesh_calc_edges_loose(result); /* Tag to recalculate normals later. */ @@ -441,6 +857,7 @@ static void panel_draw(const bContext *UNUSED(C), Panel *panel) } else if (mode == MOD_MASK_MODE_VGROUP) { modifier_vgroup_ui(layout, ptr, &ob_ptr, "vertex_group", "invert_vertex_group", nullptr); + uiItemR(layout, ptr, "use_smooth", 0, nullptr, ICON_NONE); } uiItemR(layout, ptr, "threshold", 0, nullptr, ICON_NONE); diff --git a/source/blender/modifiers/intern/MOD_nodes.cc b/source/blender/modifiers/intern/MOD_nodes.cc index 620c7ef438a..9d8630b21e7 100644 --- a/source/blender/modifiers/intern/MOD_nodes.cc +++ b/source/blender/modifiers/intern/MOD_nodes.cc @@ -289,348 +289,178 @@ static bool logging_enabled(const ModifierEvalContext *ctx) return true; } -/** - * This code is responsible for creating the new property and also creating the group of - * properties in the prop_ui_container group for the UI info, the mapping for which is - * scattered about in RNA_access.c. - * - * TODO(Hans): Codify this with some sort of table or refactor IDProperty use in RNA_access.c. - */ -struct SocketPropertyType { - /* Create the actual property used to store the data for the modifier. */ - IDProperty *(*create_prop)(const bNodeSocket &socket, const char *name); - /* Reused to build the "soft_min" property too. */ - IDProperty *(*create_min_ui_prop)(const bNodeSocket &socket, const char *name); - /* Reused to build the "soft_max" property too. */ - IDProperty *(*create_max_ui_prop)(const bNodeSocket &socket, const char *name); - /* This uses the same values as #create_prop, but sometimes the type is different, so it can't - * be the same function. */ - IDProperty *(*create_default_ui_prop)(const bNodeSocket &socket, const char *name); - PropertyType (*rna_subtype_get)(const bNodeSocket &socket); - bool (*is_correct_type)(const IDProperty &property); - void (*init_cpp_value)(const IDProperty &property, void *r_value); -}; - -static IDProperty *socket_add_property(IDProperty *settings_prop_group, - IDProperty *ui_container, - const SocketPropertyType &property_type, - const bNodeSocket &socket) +static IDProperty *id_property_create_from_socket(const bNodeSocket &socket) { - const char *new_prop_name = socket.identifier; - /* Add the property actually storing the data to the modifier's group. */ - IDProperty *prop = property_type.create_prop(socket, new_prop_name); - IDP_AddToGroup(settings_prop_group, prop); - - prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY; - - /* Make the group in the UI container group to hold the property's UI settings. */ - IDProperty *prop_ui_group; - { - IDPropertyTemplate idprop = {0}; - prop_ui_group = IDP_New(IDP_GROUP, &idprop, new_prop_name); - IDP_AddToGroup(ui_container, prop_ui_group); - } - - /* Set property description (tooltip). */ - IDPropertyTemplate property_description_template; - property_description_template.string.str = socket.description; - property_description_template.string.len = BLI_strnlen(socket.description, MAX_NAME) + 1; - property_description_template.string.subtype = IDP_STRING_SUB_UTF8; - IDProperty *description = IDP_New(IDP_STRING, &property_description_template, "description"); - IDP_AddToGroup(prop_ui_group, description); - - /* Create the properties for the socket's UI settings. */ - if (property_type.create_min_ui_prop != nullptr) { - IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "min")); - IDP_AddToGroup(prop_ui_group, property_type.create_min_ui_prop(socket, "soft_min")); - } - if (property_type.create_max_ui_prop != nullptr) { - IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "max")); - IDP_AddToGroup(prop_ui_group, property_type.create_max_ui_prop(socket, "soft_max")); - } - if (property_type.create_default_ui_prop != nullptr) { - IDP_AddToGroup(prop_ui_group, property_type.create_default_ui_prop(socket, "default")); - } - if (property_type.rna_subtype_get != nullptr) { - const char *subtype_identifier = nullptr; - RNA_enum_identifier(rna_enum_property_subtype_items, - property_type.rna_subtype_get(socket), - &subtype_identifier); - - if (subtype_identifier != nullptr) { + switch (socket.type) { + case SOCK_FLOAT: { + bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.f = value->value; + IDProperty *property = IDP_New(IDP_FLOAT, &idprop, socket.identifier); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + ui_data->base.rna_subtype = value->subtype; + ui_data->min = ui_data->soft_min = (double)value->min; + ui_data->max = ui_data->soft_max = (double)value->max; + ui_data->default_value = value->value; + return property; + } + case SOCK_INT: { + bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.i = value->value; + IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier); + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property); + ui_data->base.rna_subtype = value->subtype; + ui_data->min = ui_data->soft_min = value->min; + ui_data->max = ui_data->soft_max = value->max; + ui_data->default_value = value->value; + return property; + } + case SOCK_VECTOR: { + bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.array.len = 3; + idprop.array.type = IDP_FLOAT; + IDProperty *property = IDP_New(IDP_ARRAY, &idprop, socket.identifier); + copy_v3_v3((float *)IDP_Array(property), value->value); + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)IDP_ui_data_ensure(property); + ui_data->base.rna_subtype = value->subtype; + ui_data->min = ui_data->soft_min = (double)value->min; + ui_data->max = ui_data->soft_max = (double)value->max; + ui_data->default_array = (double *)MEM_mallocN(sizeof(double[3]), "mod_prop_default"); + ui_data->default_array_len = 3; + for (int i = 3; i < 3; i++) { + ui_data->default_array[i] = (double)value->value[i]; + } + return property; + } + case SOCK_BOOLEAN: { + bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value; IDPropertyTemplate idprop = {0}; - idprop.string.str = subtype_identifier; - idprop.string.len = BLI_strnlen(subtype_identifier, MAX_NAME) + 1; - IDP_AddToGroup(prop_ui_group, IDP_New(IDP_STRING, &idprop, "subtype")); + idprop.i = value->value != 0; + IDProperty *property = IDP_New(IDP_INT, &idprop, socket.identifier); + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)IDP_ui_data_ensure(property); + ui_data->min = ui_data->soft_min = 0; + ui_data->max = ui_data->soft_max = 1; + ui_data->default_value = value->value != 0; + return property; + } + case SOCK_STRING: { + bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value; + IDProperty *property = IDP_NewString( + value->value, socket.identifier, BLI_strnlen(value->value, sizeof(value->value)) + 1); + IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)IDP_ui_data_ensure(property); + ui_data->default_value = BLI_strdup(value->value); + return property; + } + case SOCK_OBJECT: { + bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, socket.identifier); + } + case SOCK_COLLECTION: { + bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, socket.identifier); + } + case SOCK_TEXTURE: { + bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, socket.identifier); + } + case SOCK_MATERIAL: { + bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; + IDPropertyTemplate idprop = {0}; + idprop.id = (ID *)value->value; + return IDP_New(IDP_ID, &idprop, socket.identifier); } } + return nullptr; +} - return prop; +static bool id_property_type_matches_socket(const bNodeSocket &socket, const IDProperty &property) +{ + switch (socket.type) { + case SOCK_FLOAT: + return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); + case SOCK_INT: + return property.type == IDP_INT; + case SOCK_VECTOR: + return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && property.len == 3; + case SOCK_BOOLEAN: + return property.type == IDP_INT; + case SOCK_STRING: + return property.type == IDP_STRING; + case SOCK_OBJECT: + case SOCK_COLLECTION: + case SOCK_TEXTURE: + case SOCK_MATERIAL: + return property.type == IDP_ID; + } + BLI_assert_unreachable(); + return false; } -static const SocketPropertyType *get_socket_property_type(const bNodeSocket &bsocket) +static void init_socket_cpp_value_from_property(const IDProperty &property, + const eNodeSocketDatatype socket_value_type, + void *r_value) { - switch (bsocket.type) { + switch (socket_value_type) { case SOCK_FLOAT: { - static const SocketPropertyType float_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.f = value->value; - return IDP_New(IDP_FLOAT, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.d = value->min; - return IDP_New(IDP_DOUBLE, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.d = value->max; - return IDP_New(IDP_DOUBLE, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueFloat *value = (bNodeSocketValueFloat *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.d = value->value; - return IDP_New(IDP_DOUBLE, &idprop, name); - }, - [](const bNodeSocket &socket) { - return (PropertyType)((bNodeSocketValueFloat *)socket.default_value)->subtype; - }, - [](const IDProperty &property) { return ELEM(property.type, IDP_FLOAT, IDP_DOUBLE); }, - [](const IDProperty &property, void *r_value) { - if (property.type == IDP_FLOAT) { - *(float *)r_value = IDP_Float(&property); - } - else if (property.type == IDP_DOUBLE) { - *(float *)r_value = (float)IDP_Double(&property); - } - }, - }; - return &float_type; + if (property.type == IDP_FLOAT) { + *(float *)r_value = IDP_Float(&property); + } + else if (property.type == IDP_DOUBLE) { + *(float *)r_value = (float)IDP_Double(&property); + } + break; } case SOCK_INT: { - static const SocketPropertyType int_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->min; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->max; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueInt *value = (bNodeSocketValueInt *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &socket) { - return (PropertyType)((bNodeSocketValueInt *)socket.default_value)->subtype; - }, - [](const IDProperty &property) { return property.type == IDP_INT; }, - [](const IDProperty &property, void *r_value) { *(int *)r_value = IDP_Int(&property); }, - }; - return &int_type; + *(int *)r_value = IDP_Int(&property); + break; } case SOCK_VECTOR: { - static const SocketPropertyType vector_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.array.len = 3; - idprop.array.type = IDP_FLOAT; - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name); - copy_v3_v3((float *)IDP_Array(property), value->value); - return property; - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.d = value->min; - return IDP_New(IDP_DOUBLE, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.d = value->max; - return IDP_New(IDP_DOUBLE, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueVector *value = (bNodeSocketValueVector *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.array.len = 3; - idprop.array.type = IDP_FLOAT; - IDProperty *property = IDP_New(IDP_ARRAY, &idprop, name); - copy_v3_v3((float *)IDP_Array(property), value->value); - return property; - }, - [](const bNodeSocket &socket) { - return (PropertyType)((bNodeSocketValueVector *)socket.default_value)->subtype; - }, - [](const IDProperty &property) { - return property.type == IDP_ARRAY && property.subtype == IDP_FLOAT && - property.len == 3; - }, - [](const IDProperty &property, void *r_value) { - copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); - }, - }; - return &vector_type; + copy_v3_v3((float *)r_value, (const float *)IDP_Array(&property)); + break; } case SOCK_BOOLEAN: { - static const SocketPropertyType boolean_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value != 0; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &UNUSED(socket), const char *name) { - IDPropertyTemplate idprop = {0}; - idprop.i = 0; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &UNUSED(socket), const char *name) { - IDPropertyTemplate idprop = {0}; - idprop.i = 1; - return IDP_New(IDP_INT, &idprop, name); - }, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueBoolean *value = (bNodeSocketValueBoolean *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.i = value->value != 0; - return IDP_New(IDP_INT, &idprop, name); - }, - nullptr, - [](const IDProperty &property) { return property.type == IDP_INT; }, - [](const IDProperty &property, void *r_value) { - *(bool *)r_value = IDP_Int(&property) != 0; - }, - }; - return &boolean_type; + *(bool *)r_value = IDP_Int(&property) != 0; + break; } case SOCK_STRING: { - static const SocketPropertyType string_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value; - return IDP_NewString( - value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1); - }, - nullptr, - nullptr, - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueString *value = (bNodeSocketValueString *)socket.default_value; - return IDP_NewString( - value->value, name, BLI_strnlen(value->value, sizeof(value->value)) + 1); - }, - nullptr, - [](const IDProperty &property) { return property.type == IDP_STRING; }, - [](const IDProperty &property, void *r_value) { - new (r_value) std::string(IDP_String(&property)); - }, - }; - return &string_type; + new (r_value) std::string(IDP_String(&property)); + break; } case SOCK_OBJECT: { - static const SocketPropertyType object_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueObject *value = (bNodeSocketValueObject *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, name); - }, - nullptr, - nullptr, - nullptr, - nullptr, - [](const IDProperty &property) { return property.type == IDP_ID; }, - [](const IDProperty &property, void *r_value) { - ID *id = IDP_Id(&property); - Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr; - *(Object **)r_value = object; - }, - }; - return &object_type; + ID *id = IDP_Id(&property); + Object *object = (id && GS(id->name) == ID_OB) ? (Object *)id : nullptr; + *(Object **)r_value = object; + break; } case SOCK_COLLECTION: { - static const SocketPropertyType collection_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueCollection *value = (bNodeSocketValueCollection *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, name); - }, - nullptr, - nullptr, - nullptr, - nullptr, - [](const IDProperty &property) { return property.type == IDP_ID; }, - [](const IDProperty &property, void *r_value) { - ID *id = IDP_Id(&property); - Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr; - *(Collection **)r_value = collection; - }, - }; - return &collection_type; + ID *id = IDP_Id(&property); + Collection *collection = (id && GS(id->name) == ID_GR) ? (Collection *)id : nullptr; + *(Collection **)r_value = collection; + break; } case SOCK_TEXTURE: { - static const SocketPropertyType texture_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueTexture *value = (bNodeSocketValueTexture *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, name); - }, - nullptr, - nullptr, - nullptr, - nullptr, - [](const IDProperty &property) { return property.type == IDP_ID; }, - [](const IDProperty &property, void *r_value) { - ID *id = IDP_Id(&property); - Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr; - *(Tex **)r_value = texture; - }, - }; - return &texture_type; + ID *id = IDP_Id(&property); + Tex *texture = (id && GS(id->name) == ID_TE) ? (Tex *)id : nullptr; + *(Tex **)r_value = texture; + break; } case SOCK_MATERIAL: { - static const SocketPropertyType material_type = { - [](const bNodeSocket &socket, const char *name) { - bNodeSocketValueMaterial *value = (bNodeSocketValueMaterial *)socket.default_value; - IDPropertyTemplate idprop = {0}; - idprop.id = (ID *)value->value; - return IDP_New(IDP_ID, &idprop, name); - }, - nullptr, - nullptr, - nullptr, - nullptr, - [](const IDProperty &property) { return property.type == IDP_ID; }, - [](const IDProperty &property, void *r_value) { - ID *id = IDP_Id(&property); - Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr; - *(Material **)r_value = material; - }, - }; - return &material_type; + ID *id = IDP_Id(&property); + Material *material = (id && GS(id->name) == ID_MA) ? (Material *)id : nullptr; + *(Material **)r_value = material; + break; } default: { - return nullptr; + BLI_assert_unreachable(); + break; } } } @@ -647,32 +477,39 @@ void MOD_nodes_update_interface(Object *object, NodesModifierData *nmd) } IDProperty *old_properties = nmd->settings.properties; - { IDPropertyTemplate idprop = {0}; nmd->settings.properties = IDP_New(IDP_GROUP, &idprop, "Nodes Modifier Settings"); } - IDProperty *ui_container_group; - { - IDPropertyTemplate idprop = {0}; - ui_container_group = IDP_New(IDP_GROUP, &idprop, "_RNA_UI"); - IDP_AddToGroup(nmd->settings.properties, ui_container_group); - } - LISTBASE_FOREACH (bNodeSocket *, socket, &nmd->node_group->inputs) { - const SocketPropertyType *property_type = get_socket_property_type(*socket); - if (property_type == nullptr) { + IDProperty *new_prop = id_property_create_from_socket(*socket); + if (new_prop == nullptr) { + /* Out of the set of supported input sockets, only + * geometry sockets aren't added to the modifier. */ + BLI_assert(socket->type == SOCK_GEOMETRY); continue; } - IDProperty *new_prop = socket_add_property( - nmd->settings.properties, ui_container_group, *property_type, *socket); + new_prop->flag |= IDP_FLAG_OVERRIDABLE_LIBRARY; + if (socket->description[0] != '\0') { + IDPropertyUIData *ui_data = IDP_ui_data_ensure(new_prop); + ui_data->description = BLI_strdup(socket->description); + } + IDP_AddToGroup(nmd->settings.properties, new_prop); if (old_properties != nullptr) { IDProperty *old_prop = IDP_GetPropertyFromGroup(old_properties, socket->identifier); - if (old_prop != nullptr && property_type->is_correct_type(*old_prop)) { + if (old_prop != nullptr && id_property_type_matches_socket(*socket, *old_prop)) { + /* #IDP_CopyPropertyContent replaces the UI data as well, which we don't (we only + * want to replace the values). So release it temporarily and replace it after. */ + IDPropertyUIData *ui_data = new_prop->ui_data; + new_prop->ui_data = nullptr; IDP_CopyPropertyContent(new_prop, old_prop); + if (new_prop->ui_data != nullptr) { + IDP_ui_data_free(new_prop); + } + new_prop->ui_data = ui_data; } } } @@ -713,14 +550,8 @@ void MOD_nodes_init(Main *bmain, NodesModifierData *nmd) static void initialize_group_input(NodesModifierData &nmd, const bNodeSocket &socket, - const CPPType &cpp_type, void *r_value) { - const SocketPropertyType *property_type = get_socket_property_type(socket); - if (property_type == nullptr) { - cpp_type.copy_construct(cpp_type.default_value(), r_value); - return; - } if (nmd.settings.properties == nullptr) { socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; @@ -731,11 +562,13 @@ static void initialize_group_input(NodesModifierData &nmd, socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } - if (!property_type->is_correct_type(*property)) { + if (!id_property_type_matches_socket(socket, *property)) { socket.typeinfo->get_geometry_nodes_cpp_value(socket, r_value); return; } - property_type->init_cpp_value(*property, r_value); + + init_socket_cpp_value_from_property( + *property, static_cast<eNodeSocketDatatype>(socket.type), r_value); } static Vector<SpaceSpreadsheet *> find_spreadsheet_editors(Main *bmain) @@ -886,7 +719,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree, for (const OutputSocketRef *socket : remaining_input_sockets) { const CPPType &cpp_type = *socket->typeinfo()->get_geometry_nodes_cpp_type(); void *value_in = allocator.allocate(cpp_type.size(), cpp_type.alignment()); - initialize_group_input(*nmd, *socket->bsocket(), cpp_type, value_in); + initialize_group_input(*nmd, *socket->bsocket(), value_in); group_inputs.add_new({root_context, socket}, {cpp_type, value_in}); } } @@ -954,8 +787,7 @@ static void check_property_socket_sync(const Object *ob, ModifierData *md) continue; } - const SocketPropertyType *property_type = get_socket_property_type(*socket); - if (!property_type->is_correct_type(*property)) { + if (!id_property_type_matches_socket(*socket, *property)) { BKE_modifier_set_error( ob, md, "Property type does not match input socket \"(%s)\"", socket->name); continue; @@ -1051,17 +883,12 @@ static void draw_property_for_socket(uiLayout *layout, const IDProperty *modifier_props, const bNodeSocket &socket) { - const SocketPropertyType *property_type = get_socket_property_type(socket); - if (property_type == nullptr) { - return; - } - /* The property should be created in #MOD_nodes_update_interface with the correct type. */ IDProperty *property = IDP_GetPropertyFromGroup(modifier_props, socket.identifier); /* IDProperties can be removed with python, so there could be a situation where * there isn't a property for a socket or it doesn't have the correct type. */ - if (property == nullptr || !property_type->is_correct_type(*property)) { + if (property == nullptr || !id_property_type_matches_socket(socket, *property)) { return; } diff --git a/source/blender/python/generic/CMakeLists.txt b/source/blender/python/generic/CMakeLists.txt index dce6a7e1c91..10cc1e8b113 100644 --- a/source/blender/python/generic/CMakeLists.txt +++ b/source/blender/python/generic/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC ../../blenlib ../../gpu ../../makesdna + ../../makesrna ../../../../intern/glew-mx ../../../../intern/guardedalloc ) @@ -36,6 +37,7 @@ set(SRC blf_py_api.c bpy_threads.c idprop_py_api.c + idprop_py_ui_api.c imbuf_py_api.c py_capi_utils.c @@ -43,6 +45,7 @@ set(SRC bl_math_py_api.h blf_py_api.h idprop_py_api.h + idprop_py_ui_api.h imbuf_py_api.h py_capi_utils.h diff --git a/source/blender/python/generic/idprop_py_api.c b/source/blender/python/generic/idprop_py_api.c index 024900db691..58a947943b1 100644 --- a/source/blender/python/generic/idprop_py_api.c +++ b/source/blender/python/generic/idprop_py_api.c @@ -25,6 +25,7 @@ #include "BLI_utildefines.h" #include "idprop_py_api.h" +#include "idprop_py_ui_api.h" #include "BKE_idprop.h" @@ -2135,6 +2136,7 @@ static PyObject *BPyInit_idprop_types(void) submodule = PyModule_Create(&IDProp_types_module_def); IDProp_Init_Types(); + IDPropertyUIData_Init_Types(); /* bmesh_py_types.c */ PyModule_AddType(submodule, &BPy_IDGroup_Type); diff --git a/source/blender/python/generic/idprop_py_ui_api.c b/source/blender/python/generic/idprop_py_ui_api.c new file mode 100644 index 00000000000..92cc3b8e1dd --- /dev/null +++ b/source/blender/python/generic/idprop_py_ui_api.c @@ -0,0 +1,734 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup pygen + */ + +#include <Python.h> + +#include "MEM_guardedalloc.h" + +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "idprop_py_ui_api.h" + +#include "BKE_idprop.h" + +#include "DNA_ID.h" + +#include "RNA_access.h" +#include "RNA_enum_types.h" + +#include "../intern/bpy_rna.h" + +#define USE_STRING_COERCE + +#ifdef USE_STRING_COERCE +# include "py_capi_utils.h" +#endif + +#include "python_utildefines.h" + +/* -------------------------------------------------------------------- */ +/** \name UI Data Update + * \{ */ + +static bool args_contain_key(PyObject *kwargs, const char *name) +{ + PyObject *py_key = PyUnicode_FromString(name); + const bool result = PyDict_Contains(kwargs, py_key) == 1; + Py_DECREF(py_key); + return result; +} + +/** + * \return False when parsing fails, in which case caller should return NULL. + */ +static bool idprop_ui_data_update_base(IDPropertyUIData *ui_data, + const char *rna_subtype, + const char *description) +{ + if (rna_subtype != NULL) { + if (pyrna_enum_value_from_id(rna_enum_property_subtype_items, + rna_subtype, + &ui_data->rna_subtype, + "IDPropertyUIManager.update") == -1) { + return false; + } + } + + if (description != NULL) { + ui_data->description = BLI_strdup(description); + } + + return true; +} + +/** + * \note The default value needs special handling because for array IDProperties it can + * be a single value or an array, but for non-array properties it can only be a value. + */ +static bool idprop_ui_data_update_int_default(IDProperty *idprop, + IDPropertyUIDataInt *ui_data, + PyObject *default_value) +{ + if (PySequence_Check(default_value)) { + if (idprop->type != IDP_ARRAY) { + PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values"); + return false; + } + + Py_ssize_t len = PySequence_Size(default_value); + int *new_default_array = (int *)MEM_malloc_arrayN(len, sizeof(int), __func__); + if (PyC_AsArray( + new_default_array, sizeof(int), default_value, len, &PyLong_Type, "ui_data_update") == + -1) { + MEM_freeN(new_default_array); + return false; + } + + ui_data->default_array_len = len; + ui_data->default_array = new_default_array; + } + else { + const int value = PyC_Long_AsI32(default_value); + if ((value == -1) && PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to integer"); + return false; + } + ui_data->default_value = value; + } + + return true; +} + +/** + * \return False when parsing fails, in which case caller should return NULL. + */ +static bool idprop_ui_data_update_int(IDProperty *idprop, PyObject *args, PyObject *kwargs) +{ + const char *rna_subtype = NULL; + const char *description = NULL; + int min, max, soft_min, soft_max, step; + PyObject *default_value = NULL; + const char *kwlist[] = { + "min", "max", "soft_min", "soft_max", "step", "default", "subtype", "description", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, + kwargs, + "|$iiiiiOzz:update", + (char **)kwlist, + &min, + &max, + &soft_min, + &soft_max, + &step, + &default_value, + &rna_subtype, + &description)) { + return false; + } + + /* Write to a temporary copy of the UI data in case some part of the parsing fails. */ + IDPropertyUIDataInt *ui_data_orig = (IDPropertyUIDataInt *)idprop->ui_data; + IDPropertyUIDataInt ui_data = *ui_data_orig; + + if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) { + IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + + if (args_contain_key(kwargs, "min")) { + ui_data.min = min; + ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min); + ui_data.max = MAX2(ui_data.min, ui_data.max); + } + if (args_contain_key(kwargs, "max")) { + ui_data.max = max; + ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max); + ui_data.min = MIN2(ui_data.min, ui_data.max); + } + if (args_contain_key(kwargs, "soft_min")) { + ui_data.soft_min = soft_min; + ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min); + ui_data.soft_max = MAX2(ui_data.soft_min, ui_data.soft_max); + } + if (args_contain_key(kwargs, "soft_max")) { + ui_data.soft_max = soft_max; + ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max); + ui_data.soft_min = MIN2(ui_data.soft_min, ui_data.soft_max); + } + if (args_contain_key(kwargs, "step")) { + ui_data.step = step; + } + + if (!ELEM(default_value, NULL, Py_None)) { + if (!idprop_ui_data_update_int_default(idprop, &ui_data, default_value)) { + IDP_ui_data_free_unique_contents( + &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + } + + /* Write back to the proeprty's UI data. */ + IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); + *ui_data_orig = ui_data; + return true; +} + +/** + * \note The default value needs special handling because for array IDProperties it can + * be a single value or an array, but for non-array properties it can only be a value. + */ +static bool idprop_ui_data_update_float_default(IDProperty *idprop, + IDPropertyUIDataFloat *ui_data, + PyObject *default_value) +{ + if (PySequence_Check(default_value)) { + if (idprop->type != IDP_ARRAY) { + PyErr_SetString(PyExc_TypeError, "Only array properties can have array default values"); + return false; + } + + Py_ssize_t len = PySequence_Size(default_value); + double *new_default_array = (double *)MEM_malloc_arrayN(len, sizeof(double), __func__); + if (PyC_AsArray(new_default_array, + sizeof(double), + default_value, + len, + &PyFloat_Type, + "ui_data_update") == -1) { + MEM_freeN(new_default_array); + return false; + } + + ui_data->default_array_len = len; + ui_data->default_array = new_default_array; + } + else { + const double value = PyFloat_AsDouble(default_value); + if ((value == -1.0) && PyErr_Occurred()) { + PyErr_SetString(PyExc_ValueError, "Error converting \"default\" argument to double"); + return false; + } + ui_data->default_value = value; + } + + return true; +} + +/** + * \return False when parsing fails, in which case caller should return NULL. + */ +static bool idprop_ui_data_update_float(IDProperty *idprop, PyObject *args, PyObject *kwargs) +{ + const char *rna_subtype = NULL; + const char *description = NULL; + int precision; + double min, max, soft_min, soft_max, step; + PyObject *default_value = NULL; + const char *kwlist[] = {"min", + "max", + "soft_min", + "soft_max", + "step", + "precision", + "default", + "subtype", + "description", + NULL}; + if (!PyArg_ParseTupleAndKeywords(args, + kwargs, + "|$dddddiOzz:update", + (char **)kwlist, + &min, + &max, + &soft_min, + &soft_max, + &step, + &precision, + &default_value, + &rna_subtype, + &description)) { + return false; + } + + /* Write to a temporary copy of the UI data in case some part of the parsing fails. */ + IDPropertyUIDataFloat *ui_data_orig = (IDPropertyUIDataFloat *)idprop->ui_data; + IDPropertyUIDataFloat ui_data = *ui_data_orig; + + if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) { + IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + + if (args_contain_key(kwargs, "min")) { + ui_data.min = min; + ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min); + ui_data.max = MAX2(ui_data.min, ui_data.max); + } + if (args_contain_key(kwargs, "max")) { + ui_data.max = max; + ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max); + ui_data.min = MIN2(ui_data.min, ui_data.max); + } + if (args_contain_key(kwargs, "soft_min")) { + ui_data.soft_min = soft_min; + ui_data.soft_min = MAX2(ui_data.soft_min, ui_data.min); + ui_data.soft_max = MAX2(ui_data.soft_min, ui_data.soft_max); + } + if (args_contain_key(kwargs, "soft_max")) { + ui_data.soft_max = soft_max; + ui_data.soft_max = MIN2(ui_data.soft_max, ui_data.max); + ui_data.soft_min = MIN2(ui_data.soft_min, ui_data.soft_max); + } + if (args_contain_key(kwargs, "step")) { + ui_data.step = (float)step; + } + if (args_contain_key(kwargs, "precision")) { + ui_data.precision = precision; + } + + if (!ELEM(default_value, NULL, Py_None)) { + if (!idprop_ui_data_update_float_default(idprop, &ui_data, default_value)) { + IDP_ui_data_free_unique_contents( + &ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + } + + /* Write back to the proeprty's UI data. */ + IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); + *ui_data_orig = ui_data; + return true; +} + +/** + * \return False when parsing fails, in which case caller should return NULL. + */ +static bool idprop_ui_data_update_string(IDProperty *idprop, PyObject *args, PyObject *kwargs) +{ + const char *rna_subtype = NULL; + const char *description = NULL; + const char *default_value; + const char *kwlist[] = {"default", "subtype", "description", NULL}; + if (!PyArg_ParseTupleAndKeywords(args, + kwargs, + "|$zzz:update", + (char **)kwlist, + &default_value, + &rna_subtype, + &description)) { + return false; + } + + /* Write to a temporary copy of the UI data in case some part of the parsing fails. */ + IDPropertyUIDataString *ui_data_orig = (IDPropertyUIDataString *)idprop->ui_data; + IDPropertyUIDataString ui_data = *ui_data_orig; + + if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) { + IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + + if (default_value != NULL) { + ui_data.default_value = BLI_strdup(default_value); + } + + /* Write back to the proeprty's UI data. */ + IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); + *ui_data_orig = ui_data; + return true; +} + +/** + * \return False when parsing fails, in which case caller should return NULL. + */ +static bool idprop_ui_data_update_id(IDProperty *idprop, PyObject *args, PyObject *kwargs) +{ + const char *rna_subtype = NULL; + const char *description = NULL; + const char *kwlist[] = {"subtype", "description", NULL}; + if (!PyArg_ParseTupleAndKeywords( + args, kwargs, "|$zz:update", (char **)kwlist, &rna_subtype, &description)) { + return false; + } + + /* Write to a temporary copy of the UI data in case some part of the parsing fails. */ + IDPropertyUIDataID *ui_data_orig = (IDPropertyUIDataID *)idprop->ui_data; + IDPropertyUIDataID ui_data = *ui_data_orig; + + if (!idprop_ui_data_update_base(&ui_data.base, rna_subtype, description)) { + IDP_ui_data_free_unique_contents(&ui_data.base, IDP_ui_data_type(idprop), &ui_data_orig->base); + return false; + } + + /* Write back to the proeprty's UI data. */ + IDP_ui_data_free_unique_contents(&ui_data_orig->base, IDP_ui_data_type(idprop), &ui_data.base); + *ui_data_orig = ui_data; + return true; +} + +PyDoc_STRVAR(BPy_IDPropertyUIManager_update_doc, + ".. method:: update( " + "subtype=None, " + "min=None, " + "max=None, " + "soft_min=None, " + "soft_max=None, " + "precision=None, " + "step=None, " + "default=None, " + "description=None)\n" + "\n" + " Update the RNA information of the IDProperty used for interaction and\n" + " display in the user interface. The required types for many of the keyword\n" + " arguments depend on the type of the property.\n "); +static PyObject *BPy_IDPropertyUIManager_update(BPy_IDPropertyUIManager *self, + PyObject *args, + PyObject *kwargs) +{ + IDProperty *property = self->property; + BLI_assert(IDP_ui_data_supported(property)); + + switch (IDP_ui_data_type(property)) { + case IDP_UI_DATA_TYPE_INT: + IDP_ui_data_ensure(property); + if (!idprop_ui_data_update_int(property, args, kwargs)) { + return NULL; + } + Py_RETURN_NONE; + case IDP_UI_DATA_TYPE_FLOAT: + IDP_ui_data_ensure(property); + if (!idprop_ui_data_update_float(property, args, kwargs)) { + return NULL; + } + Py_RETURN_NONE; + case IDP_UI_DATA_TYPE_STRING: + IDP_ui_data_ensure(property); + if (!idprop_ui_data_update_string(property, args, kwargs)) { + return NULL; + } + Py_RETURN_NONE; + case IDP_UI_DATA_TYPE_ID: + IDP_ui_data_ensure(property); + if (!idprop_ui_data_update_id(property, args, kwargs)) { + return NULL; + } + Py_RETURN_NONE; + case IDP_UI_DATA_TYPE_UNSUPPORTED: + PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support RNA data", property->name); + return NULL; + } + + BLI_assert_unreachable(); + Py_RETURN_NONE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Data As Dictionary + * \{ */ + +static void idprop_ui_data_to_dict_int(IDProperty *property, PyObject *dict) +{ + IDPropertyUIDataInt *ui_data = (IDPropertyUIDataInt *)property->ui_data; + PyObject *item; + + PyDict_SetItemString(dict, "min", item = PyLong_FromLong(ui_data->min)); + Py_DECREF(item); + PyDict_SetItemString(dict, "max", item = PyLong_FromLong(ui_data->max)); + Py_DECREF(item); + PyDict_SetItemString(dict, "soft_min", item = PyLong_FromLong(ui_data->soft_min)); + Py_DECREF(item); + PyDict_SetItemString(dict, "soft_max", item = PyLong_FromLong(ui_data->soft_max)); + Py_DECREF(item); + PyDict_SetItemString(dict, "step", item = PyLong_FromLong(ui_data->step)); + Py_DECREF(item); + if (property->type == IDP_ARRAY) { + PyObject *list = PyList_New(ui_data->default_array_len); + for (int i = 0; i < ui_data->default_array_len; i++) { + PyList_SET_ITEM(list, i, PyLong_FromLong(ui_data->default_array[i])); + } + PyDict_SetItemString(dict, "default", list); + Py_DECREF(list); + } + else { + PyDict_SetItemString(dict, "default", item = PyLong_FromLong(ui_data->step)); + Py_DECREF(item); + } +} + +static void idprop_ui_data_to_dict_float(IDProperty *property, PyObject *dict) +{ + IDPropertyUIDataFloat *ui_data = (IDPropertyUIDataFloat *)property->ui_data; + PyObject *item; + + PyDict_SetItemString(dict, "min", item = PyFloat_FromDouble(ui_data->min)); + Py_DECREF(item); + PyDict_SetItemString(dict, "max", item = PyFloat_FromDouble(ui_data->max)); + Py_DECREF(item); + PyDict_SetItemString(dict, "soft_min", item = PyFloat_FromDouble(ui_data->soft_min)); + Py_DECREF(item); + PyDict_SetItemString(dict, "soft_max", item = PyFloat_FromDouble(ui_data->soft_max)); + Py_DECREF(item); + PyDict_SetItemString(dict, "step", item = PyFloat_FromDouble((double)ui_data->step)); + Py_DECREF(item); + PyDict_SetItemString(dict, "precision", item = PyFloat_FromDouble((double)ui_data->precision)); + Py_DECREF(item); + if (property->type == IDP_ARRAY) { + PyObject *list = PyList_New(ui_data->default_array_len); + for (int i = 0; i < ui_data->default_array_len; i++) { + PyList_SET_ITEM(list, i, PyFloat_FromDouble(ui_data->default_array[i])); + } + PyDict_SetItemString(dict, "default", list); + Py_DECREF(list); + } + else { + PyDict_SetItemString(dict, "default", item = PyFloat_FromDouble(ui_data->step)); + Py_DECREF(item); + } +} + +static void idprop_ui_data_to_dict_string(IDProperty *property, PyObject *dict) +{ + IDPropertyUIDataString *ui_data = (IDPropertyUIDataString *)property->ui_data; + PyObject *item; + + const char *default_value = (ui_data->default_value == NULL) ? "" : ui_data->default_value; + + PyDict_SetItemString(dict, "default", item = PyUnicode_FromString(default_value)); + Py_DECREF(item); +} + +PyDoc_STRVAR(BPy_IDPropertyUIManager_as_dict_doc, + ".. method:: as_dict()\n" + "\n" + " Return a dictionary of the property's RNA UI data. The fields in the\n" + " returned dictionary and their types will depend on the property's type.\n"); +static PyObject *BPy_IDIDPropertyUIManager_as_dict(BPy_IDPropertyUIManager *self) +{ + IDProperty *property = self->property; + BLI_assert(IDP_ui_data_supported(property)); + + IDPropertyUIData *ui_data = IDP_ui_data_ensure(property); + + PyObject *dict = PyDict_New(); + + /* RNA subtype. */ + { + const char *subtype_id = NULL; + RNA_enum_identifier(rna_enum_property_subtype_items, ui_data->rna_subtype, &subtype_id); + PyObject *item = PyUnicode_FromString(subtype_id); + PyDict_SetItemString(dict, "subtype", item); + Py_DECREF(item); + } + + /* Description. */ + if (ui_data->description != NULL) { + PyObject *item = PyUnicode_FromString(ui_data->description); + PyDict_SetItemString(dict, "description", item); + Py_DECREF(item); + } + + /* Type specific data. */ + switch (IDP_ui_data_type(property)) { + case IDP_UI_DATA_TYPE_STRING: + idprop_ui_data_to_dict_string(property, dict); + break; + case IDP_UI_DATA_TYPE_ID: + break; + case IDP_UI_DATA_TYPE_INT: + idprop_ui_data_to_dict_int(property, dict); + break; + case IDP_UI_DATA_TYPE_FLOAT: + idprop_ui_data_to_dict_float(property, dict); + break; + case IDP_UI_DATA_TYPE_UNSUPPORTED: + BLI_assert_unreachable(); + break; + } + + return dict; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Data Clear + * \{ */ + +PyDoc_STRVAR(BPy_IDPropertyUIManager_clear_doc, + ".. method:: clear()\n" + "\n" + " Remove the RNA UI data from this IDProperty.\n"); +static PyObject *BPy_IDPropertyUIManager_clear(BPy_IDPropertyUIManager *self) +{ + IDProperty *property = self->property; + BLI_assert(IDP_ui_data_supported(property)); + + if (property == NULL) { + PyErr_SetString(PyExc_RuntimeError, "IDPropertyUIManager missing property"); + BLI_assert_unreachable(); + return NULL; + } + + if (property->ui_data != NULL) { + IDP_ui_data_free(property); + } + + Py_RETURN_NONE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Data Copying + * \{ */ + +PyDoc_STRVAR( + BPy_IDPropertyUIManager_update_from_doc, + ".. method:: update_from(ui_manager_source)\n" + "\n" + " Copy UI data from an IDProperty in the source group to a property in this group.\n " + " If the source property has no UI data, the target UI data will be reset if it exists.\n" + "\n" + " :raises TypeError: If the types of the two properties don't match.\n"); +static PyObject *BPy_IDPropertyUIManager_update_from(BPy_IDPropertyUIManager *self, PyObject *args) +{ + IDProperty *property = self->property; + BLI_assert(IDP_ui_data_supported(property)); + + BPy_IDPropertyUIManager *ui_manager_src; + if (!PyArg_ParseTuple(args, "O!:update_from", &BPy_IDPropertyUIManager_Type, &ui_manager_src)) { + return NULL; + } + + if (property->ui_data != NULL) { + IDP_ui_data_free(property); + } + + property->ui_data = IDP_ui_data_copy(ui_manager_src->property); + + Py_RETURN_NONE; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name UI Data Manager Definition + * \{ */ + +static struct PyMethodDef BPy_IDPropertyUIManager_methods[] = { + {"update", + (PyCFunction)BPy_IDPropertyUIManager_update, + METH_VARARGS | METH_KEYWORDS, + BPy_IDPropertyUIManager_update_doc}, + {"as_dict", + (PyCFunction)BPy_IDIDPropertyUIManager_as_dict, + METH_NOARGS, + BPy_IDPropertyUIManager_as_dict_doc}, + {"clear", + (PyCFunction)BPy_IDPropertyUIManager_clear, + METH_NOARGS, + BPy_IDPropertyUIManager_clear_doc}, + {"update_from", + (PyCFunction)BPy_IDPropertyUIManager_update_from, + METH_VARARGS, + BPy_IDPropertyUIManager_update_from_doc}, + {NULL, NULL, 0, NULL}, +}; + +static PyObject *BPy_IDPropertyUIManager_repr(BPy_IDPropertyUIManager *self) +{ + return PyUnicode_FromFormat( + "<bpy id prop ui manager: name=\"%s\", address=%p>", self->property->name, self->property); +} + +static Py_hash_t BPy_IDPropertyUIManager_hash(BPy_IDPropertyUIManager *self) +{ + return _Py_HashPointer(self->property); +} + +PyTypeObject BPy_IDPropertyUIManager_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + /* For printing, in format "<module>.<name>" */ + "IDPropertyUIManager", /* char *tp_name; */ + sizeof(BPy_IDPropertyUIManager), /* int tp_basicsize; */ + 0, /* tp_itemsize; For allocation */ + + /* Methods to implement standard operations */ + + NULL, /* destructor tp_dealloc; */ + 0, /* tp_vectorcall_offset */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* cmpfunc tp_compare; */ + (reprfunc)BPy_IDPropertyUIManager_repr, /* reprfunc tp_repr; */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + (hashfunc)BPy_IDPropertyUIManager_hash, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + NULL, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, /* long tp_weaklistoffset; */ + + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + /*** Attribute descriptor and subclassing stuff ***/ + BPy_IDPropertyUIManager_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ +}; + +void IDPropertyUIData_Init_Types() +{ + PyType_Ready(&BPy_IDPropertyUIManager_Type); +} + +/** \} */ diff --git a/source/blender/python/generic/idprop_py_ui_api.h b/source/blender/python/generic/idprop_py_ui_api.h new file mode 100644 index 00000000000..f678e5ae9c1 --- /dev/null +++ b/source/blender/python/generic/idprop_py_ui_api.h @@ -0,0 +1,33 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup pygen + */ + +#pragma once + +struct ID; +struct IDProperty; + +extern PyTypeObject BPy_IDPropertyUIManager_Type; + +typedef struct BPy_IDPropertyUIManager { + PyObject_VAR_HEAD + struct IDProperty *property; +} BPy_IDPropertyUIManager; + +void IDPropertyUIData_Init_Types(void); diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 0e0f431cb19..7646109c1b0 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -56,6 +56,7 @@ /* external util modules */ #include "../generic/idprop_py_api.h" +#include "../generic/idprop_py_ui_api.h" #include "bpy_msgbus.h" #ifdef WITH_FREESTYLE @@ -407,6 +408,7 @@ void BPy_init_modules(struct bContext *C) } /* stand alone utility modules not related to blender directly */ IDProp_Init_Types(); /* not actually a submodule, just types */ + IDPropertyUIData_Init_Types(); #ifdef WITH_FREESTYLE Freestyle_Init(); #endif diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index e9d5ae278bb..ac1fdeb2bad 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -73,6 +73,7 @@ #include "DEG_depsgraph_query.h" #include "../generic/idprop_py_api.h" /* For IDprop lookups. */ +#include "../generic/idprop_py_ui_api.h" #include "../generic/py_capi_utils.h" #include "../generic/python_utildefines.h" @@ -4309,6 +4310,51 @@ static PyObject *pyrna_struct_id_properties_ensure(BPy_StructRNA *self) return (PyObject *)group; } +PyDoc_STRVAR(pyrna_struct_id_properties_ui_doc, + ".. method:: id_properties_ui(key)\n" + "\n" + " :return: Return an object used to manage an IDProperty's UI data.\n" + " :arg key: String name of the property.\n" + " :rtype: :class:`bpy.types.IDPropertyUIManager`\n"); +static PyObject *pyrna_struct_id_properties_ui(BPy_StructRNA *self, PyObject *args) +{ + PYRNA_STRUCT_CHECK_OBJ(self); + + if (RNA_struct_idprops_check(self->ptr.type) == 0) { + PyErr_SetString(PyExc_TypeError, "This type doesn't support IDProperties"); + return NULL; + } + + const char *key; + if (!PyArg_ParseTuple(args, "s:ui_data", &key)) { + return NULL; + } + + IDProperty *parent_group = RNA_struct_idprops(&self->ptr, true); + + /* This is a paranoid check that theoretically might not be necessary. + * It allows the possibility that some structs can't ensure IDProperties. */ + if (parent_group == NULL) { + return Py_None; + } + + IDProperty *property = IDP_GetPropertyFromGroup(parent_group, key); + if (property == NULL) { + PyErr_SetString(PyExc_KeyError, "Property not found in IDProperty group"); + return NULL; + } + + if (!IDP_ui_data_supported(property)) { + PyErr_Format(PyExc_TypeError, "IDProperty \"%s\" does not support UI data", property->name); + return NULL; + } + + BPy_IDPropertyUIManager *ui_manager = PyObject_New(BPy_IDPropertyUIManager, + &BPy_IDPropertyUIManager_Type); + ui_manager->property = property; + return (PyObject *)ui_manager; +} + PyDoc_STRVAR(pyrna_struct_id_properties_clear_doc, ".. method:: id_properties_clear()\n\n" " :return: Remove the parent group for an RNA struct's custom IDProperties.\n"); @@ -5829,6 +5875,10 @@ static struct PyMethodDef pyrna_struct_methods[] = { (PyCFunction)pyrna_struct_id_properties_clear, METH_NOARGS, pyrna_struct_id_properties_clear_doc}, + {"id_properties_ui", + (PyCFunction)pyrna_struct_id_properties_ui, + METH_VARARGS, + pyrna_struct_id_properties_ui_doc}, /* experimental */ /* unused for now */ diff --git a/source/blender/sequencer/SEQ_iterator.h b/source/blender/sequencer/SEQ_iterator.h index 055db07f646..7eab9f84b50 100644 --- a/source/blender/sequencer/SEQ_iterator.h +++ b/source/blender/sequencer/SEQ_iterator.h @@ -34,10 +34,24 @@ struct GSet; struct GSetIterator; struct Sequence; +/* Utility macro to construct an unique (within a file) variable name for iterator macro. + * Use indirect macro evaluation to ensure the `__LINE__` is expanded (rather than being + * treated as a name token), + * + * The `__LINE__` is defined at the invocation of the `SEQ_ITERATOR_FOREACH` and is not changed + * afterwards. This makes it safe to expand it several times in the `SEQ_ITERATOR_FOREACH`. + * + * This allows to have nested foreach loops. + * + * NOTE: Putting nested loop to a wrapper macro is not supported. */ +#define _SEQ_ITERATOR_NAME_JOIN(x, y) x##_##y +#define _SEQ_ITERATOR_NAME_EVALUATE(x, y) _SEQ_ITERATOR_NAME_JOIN(x, y) +#define _SEQ_ITERATOR_NAME(prefix) _SEQ_ITERATOR_NAME_EVALUATE(prefix, __LINE__) + #define SEQ_ITERATOR_FOREACH(var, collection) \ - for (SeqIterator iter = {{{NULL}}}; \ - SEQ_iterator_ensure(collection, &iter, &var) && var != NULL; \ - var = SEQ_iterator_yield(&iter)) + for (SeqIterator _SEQ_ITERATOR_NAME(iter) = {{{NULL}}}; \ + SEQ_iterator_ensure(collection, &_SEQ_ITERATOR_NAME(iter), &var) && var != NULL; \ + var = SEQ_iterator_yield(&_SEQ_ITERATOR_NAME(iter))) typedef struct SeqCollection { struct GSet *set; @@ -79,6 +93,7 @@ SeqCollection *SEQ_query_by_reference(struct Sequence *seq_reference, struct ListBase *seqbase, SeqCollection *collection)); SeqCollection *SEQ_query_selected_strips(struct ListBase *seqbase); +SeqCollection *SEQ_query_unselected_strips(struct ListBase *seqbase); SeqCollection *SEQ_query_all_strips(struct ListBase *seqbase); SeqCollection *SEQ_query_all_strips_recursive(struct ListBase *seqbase); void SEQ_query_strip_effect_chain(struct Sequence *seq_reference, diff --git a/source/blender/sequencer/SEQ_sequencer.h b/source/blender/sequencer/SEQ_sequencer.h index 54ef37b9049..15fe0be3571 100644 --- a/source/blender/sequencer/SEQ_sequencer.h +++ b/source/blender/sequencer/SEQ_sequencer.h @@ -63,6 +63,7 @@ void SEQ_tool_settings_fit_method_set(struct Scene *scene, eSeqImageFitMethod fi short SEQ_tool_settings_snap_flag_get(struct Scene *scene); short SEQ_tool_settings_snap_mode_get(struct Scene *scene); int SEQ_tool_settings_snap_distance_get(struct Scene *scene); +eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(struct Scene *scene); struct SequencerToolSettings *SEQ_tool_settings_copy(struct SequencerToolSettings *tool_settings); struct Editing *SEQ_editing_get(struct Scene *scene, bool alloc); struct Editing *SEQ_editing_ensure(struct Scene *scene); diff --git a/source/blender/sequencer/SEQ_transform.h b/source/blender/sequencer/SEQ_transform.h index 9ff827333be..1977835f627 100644 --- a/source/blender/sequencer/SEQ_transform.h +++ b/source/blender/sequencer/SEQ_transform.h @@ -41,6 +41,7 @@ bool SEQ_transform_sequence_can_be_translated(struct Sequence *seq); bool SEQ_transform_single_image_check(struct Sequence *seq); void SEQ_transform_fix_single_image_seq_offsets(struct Sequence *seq); bool SEQ_transform_test_overlap(struct ListBase *seqbasep, struct Sequence *test); +bool SEQ_transform_test_overlap_seq_seq(struct Sequence *seq1, struct Sequence *seq2); void SEQ_transform_translate_sequence(struct Scene *scene, struct Sequence *seq, int delta); bool SEQ_transform_seqbase_shuffle_ex(struct ListBase *seqbasep, struct Sequence *test, diff --git a/source/blender/sequencer/intern/effects.c b/source/blender/sequencer/intern/effects.c index d4adad9a34d..37bac523645 100644 --- a/source/blender/sequencer/intern/effects.c +++ b/source/blender/sequencer/intern/effects.c @@ -39,6 +39,7 @@ #include "BLI_utildefines.h" #include "DNA_anim_types.h" +#include "DNA_packedFile_types.h" #include "DNA_scene_types.h" #include "DNA_sequence_types.h" #include "DNA_space_types.h" @@ -3755,31 +3756,47 @@ static void init_text_effect(Sequence *seq) void SEQ_effect_text_font_unload(TextVars *data, const bool do_id_user) { - if (data) { - /* Unlink the VFont */ - if (do_id_user && data->text_font != NULL) { - id_us_min(&data->text_font->id); - data->text_font = NULL; - } + if (data == NULL) { + return; + } - /* Unload the BLF font. */ - if (data->text_blf_id >= 0) { - BLF_unload_id(data->text_blf_id); - } + /* Unlink the VFont */ + if (do_id_user && data->text_font != NULL) { + id_us_min(&data->text_font->id); + data->text_font = NULL; + } + + /* Unload the BLF font. */ + if (data->text_blf_id >= 0) { + BLF_unload_id(data->text_blf_id); } } void SEQ_effect_text_font_load(TextVars *data, const bool do_id_user) { - if (data->text_font != NULL) { - if (do_id_user) { - id_us_plus(&data->text_font->id); - } + VFont *vfont = data->text_font; + if (vfont == NULL) { + return; + } + + if (do_id_user) { + id_us_plus(&vfont->id); + } + if (vfont->packedfile != NULL) { + PackedFile *pf = vfont->packedfile; + /* Create a name that's unique between library data-blocks to avoid loading + * a font per strip which will load fonts many times. */ + char name[MAX_ID_FULL_NAME]; + BKE_id_full_name_get(name, &vfont->id, 0); + + data->text_blf_id = BLF_load_mem(name, pf->data, pf->size); + } + else { char path[FILE_MAX]; - STRNCPY(path, data->text_font->filepath); + STRNCPY(path, vfont->filepath); BLI_assert(BLI_thread_is_main()); - BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&data->text_font->id)); + BLI_path_abs(path, ID_BLEND_PATH_FROM_GLOBAL(&vfont->id)); data->text_blf_id = BLF_load(path); } @@ -3850,9 +3867,7 @@ static ImBuf *do_text_effect(const SeqRenderData *context, if (data->text_blf_id == SEQ_FONT_NOT_LOADED) { data->text_blf_id = -1; - if (data->text_font) { - data->text_blf_id = BLF_load(data->text_font->filepath); - } + SEQ_effect_text_font_load(data, false); } if (data->text_blf_id >= 0) { diff --git a/source/blender/sequencer/intern/iterator.c b/source/blender/sequencer/intern/iterator.c index a9cbf4879f1..58f68205f51 100644 --- a/source/blender/sequencer/intern/iterator.c +++ b/source/blender/sequencer/intern/iterator.c @@ -341,6 +341,24 @@ SeqCollection *SEQ_query_selected_strips(ListBase *seqbase) } /** + * Query all unselected strips in seqbase. + * + * \param seqbase: ListBase in which strips are queried + * \return strip collection + */ +SeqCollection *SEQ_query_unselected_strips(ListBase *seqbase) +{ + SeqCollection *collection = SEQ_collection_create(__func__); + LISTBASE_FOREACH (Sequence *, seq, seqbase) { + if ((seq->flag & SELECT) != 0) { + continue; + } + SEQ_collection_append_strip(seq, collection); + } + return collection; +} + +/** * Query all effect strips that are directly or indirectly connected to seq_reference. * This includes all effects of seq_reference, strips used by another inputs and their effects, so * that whole chain is fully independent of other strips. diff --git a/source/blender/sequencer/intern/sequencer.c b/source/blender/sequencer/intern/sequencer.c index 2863af8fb2e..a62b76cd832 100644 --- a/source/blender/sequencer/intern/sequencer.c +++ b/source/blender/sequencer/intern/sequencer.c @@ -323,6 +323,8 @@ SequencerToolSettings *SEQ_tool_settings_init(void) tool_settings->snap_mode = SEQ_SNAP_TO_STRIPS | SEQ_SNAP_TO_CURRENT_FRAME | SEQ_SNAP_TO_STRIP_HOLD; tool_settings->snap_distance = 15; + tool_settings->overlap_mode = SEQ_OVERLAP_SHUFFLE; + return tool_settings; } @@ -372,6 +374,12 @@ void SEQ_tool_settings_fit_method_set(Scene *scene, eSeqImageFitMethod fit_metho tool_settings->fit_method = fit_method; } +eSeqOverlapMode SEQ_tool_settings_overlap_mode_get(Scene *scene) +{ + const SequencerToolSettings *tool_settings = SEQ_tool_settings_ensure(scene); + return tool_settings->overlap_mode; +} + /** * Get seqbase that is being viewed currently. This can be main seqbase or meta strip seqbase * diff --git a/source/blender/sequencer/intern/strip_transform.c b/source/blender/sequencer/intern/strip_transform.c index 9f69f434ca0..3a5f93a72b0 100644 --- a/source/blender/sequencer/intern/strip_transform.c +++ b/source/blender/sequencer/intern/strip_transform.c @@ -208,7 +208,7 @@ bool SEQ_transform_sequence_can_be_translated(Sequence *seq) return !(seq->type & SEQ_TYPE_EFFECT) || (SEQ_effect_get_num_inputs(seq->type) == 0); } -static bool seq_overlap(Sequence *seq1, Sequence *seq2) +bool SEQ_transform_test_overlap_seq_seq(Sequence *seq1, Sequence *seq2) { return (seq1 != seq2 && seq1->machine == seq2->machine && ((seq1->enddisp <= seq2->startdisp) || (seq1->startdisp >= seq2->enddisp)) == 0); @@ -220,7 +220,7 @@ bool SEQ_transform_test_overlap(ListBase *seqbasep, Sequence *test) seq = seqbasep->first; while (seq) { - if (seq_overlap(test, seq)) { + if (SEQ_transform_test_overlap_seq_seq(test, seq)) { return true; } @@ -316,7 +316,7 @@ static int shuffle_seq_time_offset_test(SeqCollection *strips_to_shuffle, SEQ_ITERATOR_FOREACH (seq, strips_to_shuffle) { LISTBASE_FOREACH (Sequence *, seq_other, seqbasep) { - if (!seq_overlap(seq, seq_other)) { + if (!SEQ_transform_test_overlap_seq_seq(seq, seq_other)) { continue; } if (UNLIKELY(SEQ_collection_has_strip(seq_other, strips_to_shuffle))) { diff --git a/source/blender/windowmanager/xr/intern/wm_xr.c b/source/blender/windowmanager/xr/intern/wm_xr.c index 4877addbb77..297205d1e79 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr.c +++ b/source/blender/windowmanager/xr/intern/wm_xr.c @@ -148,7 +148,6 @@ bool wm_xr_events_handle(wmWindowManager *wm) wmXrRuntimeData *wm_xr_runtime_data_create(void) { wmXrRuntimeData *runtime = MEM_callocN(sizeof(*runtime), __func__); - runtime->actactionmap = runtime->selactionmap = -1; return runtime; } diff --git a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c index 673fdfcd602..8903305adb4 100644 --- a/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c +++ b/source/blender/windowmanager/xr/intern/wm_xr_actionmap.c @@ -143,14 +143,9 @@ bool WM_xr_actionmap_binding_remove(XrActionMapItem *ami, XrActionMapBinding *am if (idx != -1) { BLI_freelinkN(&ami->bindings, amb); - if (BLI_listbase_is_empty(&ami->bindings)) { - ami->selbinding = -1; - } - else { - if (idx <= ami->selbinding) { - if (--ami->selbinding < 0) { - ami->selbinding = 0; - } + if (idx <= ami->selbinding) { + if (--ami->selbinding < 0) { + ami->selbinding = 0; } } @@ -181,7 +176,7 @@ XrActionMapBinding *WM_xr_actionmap_binding_find(XrActionMapItem *ami, const cha static void wm_xr_actionmap_item_bindings_clear(XrActionMapItem *ami) { BLI_freelistN(&ami->bindings); - ami->selbinding = -1; + ami->selbinding = 0; } static void wm_xr_actionmap_item_properties_set(XrActionMapItem *ami) @@ -262,7 +257,6 @@ XrActionMapItem *WM_xr_actionmap_item_new(XrActionMap *actionmap, if (ami_prev) { WM_xr_actionmap_item_ensure_unique(actionmap, ami); } - ami->selbinding = -1; BLI_addtail(&actionmap->items, ami); @@ -355,14 +349,9 @@ bool WM_xr_actionmap_item_remove(XrActionMap *actionmap, XrActionMapItem *ami) wm_xr_actionmap_item_properties_free(ami); BLI_freelinkN(&actionmap->items, ami); - if (BLI_listbase_is_empty(&actionmap->items)) { - actionmap->selitem = -1; - } - else { - if (idx <= actionmap->selitem) { - if (--actionmap->selitem < 0) { - actionmap->selitem = 0; - } + if (idx <= actionmap->selitem) { + if (--actionmap->selitem < 0) { + actionmap->selitem = 0; } } @@ -403,7 +392,6 @@ XrActionMap *WM_xr_actionmap_new(wmXrRuntimeData *runtime, const char *name, boo if (am_prev) { WM_xr_actionmap_ensure_unique(runtime, am); } - am->selitem = -1; BLI_addtail(&runtime->actionmaps, am); @@ -487,19 +475,14 @@ bool WM_xr_actionmap_remove(wmXrRuntimeData *runtime, XrActionMap *actionmap) WM_xr_actionmap_clear(actionmap); BLI_freelinkN(&runtime->actionmaps, actionmap); - if (BLI_listbase_is_empty(&runtime->actionmaps)) { - runtime->actactionmap = runtime->selactionmap = -1; - } - else { - if (idx <= runtime->actactionmap) { - if (--runtime->actactionmap < 0) { - runtime->actactionmap = 0; - } + if (idx <= runtime->actactionmap) { + if (--runtime->actactionmap < 0) { + runtime->actactionmap = 0; } - if (idx <= runtime->selactionmap) { - if (--runtime->selactionmap < 0) { - runtime->selactionmap = 0; - } + } + if (idx <= runtime->selactionmap) { + if (--runtime->selactionmap < 0) { + runtime->selactionmap = 0; } } @@ -528,7 +511,7 @@ void WM_xr_actionmap_clear(XrActionMap *actionmap) BLI_freelistN(&actionmap->items); - actionmap->selitem = -1; + actionmap->selitem = 0; } void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) @@ -539,7 +522,7 @@ void WM_xr_actionmaps_clear(wmXrRuntimeData *runtime) BLI_freelistN(&runtime->actionmaps); - runtime->actactionmap = runtime->selactionmap = -1; + runtime->actactionmap = runtime->selactionmap = 0; } ListBase *WM_xr_actionmaps_get(wmXrRuntimeData *runtime) @@ -554,7 +537,6 @@ short WM_xr_actionmap_active_index_get(const wmXrRuntimeData *runtime) void WM_xr_actionmap_active_index_set(wmXrRuntimeData *runtime, short idx) { - BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); runtime->actactionmap = idx; } @@ -565,7 +547,6 @@ short WM_xr_actionmap_selected_index_get(const wmXrRuntimeData *runtime) void WM_xr_actionmap_selected_index_set(wmXrRuntimeData *runtime, short idx) { - BLI_assert(idx < BLI_listbase_count(&runtime->actionmaps)); runtime->selactionmap = idx; } diff --git a/tests/python/bl_pyapi_idprop.py b/tests/python/bl_pyapi_idprop.py index 38cd9d04a6b..bdf625c1c2e 100644 --- a/tests/python/bl_pyapi_idprop.py +++ b/tests/python/bl_pyapi_idprop.py @@ -246,6 +246,69 @@ class TestBufferProtocol(TestHelper, unittest.TestCase): self.assertEqual(list(view1), list(view2)) self.assertEqual(view1.tobytes(), view2.tobytes()) +class TestRNAData(TestHelper, unittest.TestCase): + + def test_custom_properties_none(self): + bpy.data.objects.new("test", None) + test_object = bpy.data.objects["test"] + + # Access default RNA data values. + test_object.id_properties_clear() + test_object["test_prop"] = 0.5 + ui_data_test_prop = test_object.id_properties_ui("test_prop") + + rna_data = ui_data_test_prop.as_dict() + self.assertTrue("min" in rna_data) + self.assertLess(rna_data["min"], -10000.0) + self.assertEqual(rna_data["subtype"], "NONE") + self.assertGreater(rna_data["soft_max"], 10000.0) + + # Change RNA data values. + ui_data_test_prop.update(subtype="TEMPERATURE", min=0, soft_min=0.1) + rna_data = ui_data_test_prop.as_dict() + self.assertEqual(rna_data["min"], 0) + self.assertEqual(rna_data["soft_min"], 0.1) + self.assertEqual(rna_data["subtype"], "TEMPERATURE") + + # Copy RNA data values from one property to another. + test_object["test_prop_2"] = 11.7 + ui_data_test_prop_2 = test_object.id_properties_ui("test_prop_2") + ui_data_test_prop_2.update_from(ui_data_test_prop) + rna_data = ui_data_test_prop_2.as_dict() + self.assertEqual(rna_data["min"], 0) + self.assertEqual(rna_data["soft_min"], 0.1) + self.assertEqual(rna_data["subtype"], "TEMPERATURE") + self.assertGreater(rna_data["soft_max"], 10000.0) + + # Copy RNA data values to another object's property. + bpy.data.objects.new("test_2", None) + test_object_2 = bpy.data.objects["test_2"] + test_object_2["test_prop_3"] = 20.1 + ui_data_test_prop_3 = test_object_2.id_properties_ui("test_prop_3") + ui_data_test_prop_3.update_from(ui_data_test_prop_2) + rna_data = ui_data_test_prop_3.as_dict() + self.assertEqual(rna_data["min"], 0) + self.assertEqual(rna_data["soft_min"], 0.1) + self.assertEqual(rna_data["subtype"], "TEMPERATURE") + self.assertGreater(rna_data["soft_max"], 10000.0) + + # Test RNA data for string property. + test_object.id_properties_clear() + test_object["test_string_prop"] = "Hello there!" + ui_data_test_prop_string = test_object.id_properties_ui("test_string_prop") + ui_data_test_prop_string.update(default="Goodbye where?") + rna_data = ui_data_test_prop_string.as_dict() + self.assertEqual(rna_data["default"], "Goodbye where?") + + # Test RNA data for array property. + test_object.id_properties_clear() + test_object["test_array_prop"] = [1, 2, 3] + ui_data_test_prop_array = test_object.id_properties_ui("test_array_prop") + ui_data_test_prop_array.update(default=[1, 2]) + rna_data = ui_data_test_prop_array.as_dict() + self.assertEqual(rna_data["default"], [1, 2]) + + if __name__ == '__main__': import sys |