From 303014bfac01309eb3b533110e7483bd02bdc139 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 22 Mar 2024 12:25:39 +0100 Subject: [PATCH] Fix #108078: Crash when inverting results in pose library sidebar a958ae36e8 introduced support for UI lists to reference items that would never be shown, regardless of filter settings. This was to skip assets in the asset view template that were not of the requested type. UI list sorting code wasn't updated to account for such items that should be entirely ignored. Pull Request: https://projects.blender.org/blender/blender/pulls/109157 --- doc/python_api/examples/bpy.types.UIList.2.py | 5 +++-- scripts/templates_py/ui_list.py | 5 +++-- .../editors/interface/interface_handlers.cc | 3 ++- .../interface/interface_template_list.cc | 5 +++-- source/blender/makesdna/DNA_screen_types.h | 18 +++++++++++++---- source/blender/makesrna/intern/rna_ui.cc | 20 ++++++++++++------- 6 files changed, 38 insertions(+), 18 deletions(-) diff --git a/doc/python_api/examples/bpy.types.UIList.2.py b/doc/python_api/examples/bpy.types.UIList.2.py index 05c9e9b3a17..5a36036803e 100644 --- a/doc/python_api/examples/bpy.types.UIList.2.py +++ b/doc/python_api/examples/bpy.types.UIList.2.py @@ -136,8 +136,9 @@ class MESH_UL_vgroups_slow(bpy.types.UIList): def filter_items(self, context, data, propname): # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the - # matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the - # first one to mark VGROUP_EMPTY. + # matching item as filtered (i.e. to be shown). The upper 16 bits (including self.bitflag_filter_item) are + # reserved for internal use, the lower 16 bits are free for custom use. Here we use the first bit to mark + # VGROUP_EMPTY. # * The second one is for reordering, it must return a list containing the new indices of the items (which # gives us a mapping org_idx -> new_idx). # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info). diff --git a/scripts/templates_py/ui_list.py b/scripts/templates_py/ui_list.py index 9727a1b3678..c5700e9970c 100644 --- a/scripts/templates_py/ui_list.py +++ b/scripts/templates_py/ui_list.py @@ -30,8 +30,9 @@ class MESH_UL_mylist(bpy.types.UIList): def filter_items(self, context, data, propname): # This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: # * The first one is for filtering, it must contain 32bit integers were self.bitflag_filter_item marks the - # matching item as filtered (i.e. to be shown), and 31 other bits are free for custom needs. Here we use the - # first one to mark VGROUP_EMPTY. + # matching item as filtered (i.e. to be shown). The upper 16 bits (including self.bitflag_filter_item) are + # reseverd for internal use, the lower 16 bits are free for custom use. Here we use the first bit to mark + # VGROUP_EMPTY. # * The second one is for reordering, it must return a list containing the new indices of the items (which # gives us a mapping org_idx -> new_idx). # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info). diff --git a/source/blender/editors/interface/interface_handlers.cc b/source/blender/editors/interface/interface_handlers.cc index 493c73479c1..646f614430b 100644 --- a/source/blender/editors/interface/interface_handlers.cc +++ b/source/blender/editors/interface/interface_handlers.cc @@ -9774,7 +9774,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi for (int i = 0; i < len; i++) { if (!dyn_data->items_filter_flags || - ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) + (((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM_NEVER_SHOW) == 0) && + (dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) { org_order[new_order ? new_order[++org_idx] : ++org_idx] = i; if (i == value) { diff --git a/source/blender/editors/interface/interface_template_list.cc b/source/blender/editors/interface/interface_template_list.cc index af6c7f7a577..8d18ac9d55b 100644 --- a/source/blender/editors/interface/interface_template_list.cc +++ b/source/blender/editors/interface/interface_template_list.cc @@ -250,7 +250,7 @@ void UI_list_filter_and_sort_items(uiList *ui_list, const eUIListFilterResult filter_result = item_filter_fn(itemptr, name, i); if (filter_result == UI_LIST_ITEM_NEVER_SHOW) { - /* Pass. */ + dyn_data->items_filter_flags[i] = UILST_FLT_ITEM_NEVER_SHOW; } else if (filter_result == UI_LIST_ITEM_FILTER_MATCHES) { dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; @@ -431,7 +431,8 @@ static void ui_template_list_collect_items(PointerRNA *list_ptr, RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) { if (!dyn_data->items_filter_flags || - ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude)) + (((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM_NEVER_SHOW) == 0) && + ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude))) { int new_order_idx; if (dyn_data->items_filter_neworder) { diff --git a/source/blender/makesdna/DNA_screen_types.h b/source/blender/makesdna/DNA_screen_types.h index 7bd59a3c1fd..71d4d52b3cf 100644 --- a/source/blender/makesdna/DNA_screen_types.h +++ b/source/blender/makesdna/DNA_screen_types.h @@ -275,7 +275,9 @@ typedef struct uiListDyn { void *customdata; /* Filtering data. */ - /** Items_len length. */ + /** This bitfield is effectively exposed in Python, and scripts are explicitly allowed to assign + * any own meaning to the lower 16 ones. + * #items_len length. */ int *items_filter_flags; /** Org_idx -> new_idx, items_len length. */ int *items_filter_neworder; @@ -626,10 +628,18 @@ enum { /** Value (in number of items) we have to go below minimum shown items to enable auto size. */ #define UI_LIST_AUTO_SIZE_THRESHOLD 1 -/* uiList filter flags (dyn_data) */ -/* WARNING! Those values are used by integer RNA too, which does not handle well values > INT_MAX. - * So please do not use 32nd bit here. */ +/** uiList filter flags (dyn_data) + * + * \warning Lower 16 bits are meant for custom use in Python, don't use them here! Only use the + * higher 16 bits. + * \warning Those values are used by integer RNA too, which does not handle well values > INT_MAX. + * So please do not use 32nd bit here. + */ enum { + /* Don't use (1 << 0) to (1 << 15) here! See warning above. */ + + /* Filtering returned #UI_LIST_ITEM_NEVER_SHOW. */ + UILST_FLT_ITEM_NEVER_SHOW = (1 << 16), UILST_FLT_ITEM = 1 << 30, /* This item has passed the filter process successfully. */ }; diff --git a/source/blender/makesrna/intern/rna_ui.cc b/source/blender/makesrna/intern/rna_ui.cc index 085b4773937..a79c933fed6 100644 --- a/source/blender/makesrna/intern/rna_ui.cc +++ b/source/blender/makesrna/intern/rna_ui.cc @@ -625,7 +625,10 @@ static void uilist_filter_items(uiList *ui_list, int t_idx, t_ni, prev_ni; flt_data->items_shown = 0; for (i = 0, shown_idx = 0; i < len; i++) { - if ((filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude) { + if (filter_flags[i] & UILST_FLT_ITEM_NEVER_SHOW) { + BLI_assert_msg(false, "Bit reserved for internal use"); + } + else if ((filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude) { filter_neworder[shown_idx++] = filter_neworder[i]; } } @@ -653,7 +656,10 @@ static void uilist_filter_items(uiList *ui_list, /* we still have to set flt_data->items_shown... */ flt_data->items_shown = 0; for (i = 0; i < len; i++) { - if ((filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude) { + if (filter_flags[i] & UILST_FLT_ITEM_NEVER_SHOW) { + /* Pass. */ + } + else if ((filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude) { flt_data->items_shown++; } } @@ -2073,11 +2079,11 @@ static void rna_def_uilist(BlenderRNA *brna) prop = RNA_def_property(func, "filter_flags", PROP_INT, PROP_UNSIGNED); RNA_def_property_flag(prop, PropertyFlag(PARM_REQUIRED | PROP_DYNAMIC)); RNA_def_property_array(prop, 1); /* XXX Dummy value, default 0 does not work */ - RNA_def_property_ui_text( - prop, - "", - "An array of filter flags, one for each item in the collection (NOTE: " - "FILTER_ITEM bit is reserved, it defines whether the item is shown or not)"); + RNA_def_property_ui_text(prop, + "", + "An array of filter flags, one for each item in the collection (NOTE: " + "The upper 16 bits, including FILTER_ITEM, are reserved, only use the " + "lower 16 bits for custom usages)."); RNA_def_function_output(func, prop); prop = RNA_def_property(func, "filter_neworder", PROP_INT, PROP_UNSIGNED); RNA_def_property_flag(prop, PropertyFlag(PARM_REQUIRED | PROP_DYNAMIC));