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
This commit is contained in:
Julian Eisel 2024-03-22 12:25:39 +01:00
parent 365f5f8823
commit 303014bfac
6 changed files with 38 additions and 18 deletions

View File

@ -136,8 +136,9 @@ class MESH_UL_vgroups_slow(bpy.types.UIList):
def filter_items(self, context, data, propname): def filter_items(self, context, data, propname):
# This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: # 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 # * 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 # matching item as filtered (i.e. to be shown). The upper 16 bits (including self.bitflag_filter_item) are
# first one to mark VGROUP_EMPTY. # 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 # * 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). # 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). # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).

View File

@ -30,8 +30,9 @@ class MESH_UL_mylist(bpy.types.UIList):
def filter_items(self, context, data, propname): def filter_items(self, context, data, propname):
# This function gets the collection property (as the usual tuple (data, propname)), and must return two lists: # 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 # * 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 # matching item as filtered (i.e. to be shown). The upper 16 bits (including self.bitflag_filter_item) are
# first one to mark VGROUP_EMPTY. # 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 # * 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). # 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). # Please note that the default UI_UL_list defines helper functions for common tasks (see its doc for more info).

View File

@ -9774,7 +9774,8 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *regi
for (int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
if (!dyn_data->items_filter_flags || 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; org_order[new_order ? new_order[++org_idx] : ++org_idx] = i;
if (i == value) { if (i == value) {

View File

@ -250,7 +250,7 @@ void UI_list_filter_and_sort_items(uiList *ui_list,
const eUIListFilterResult filter_result = item_filter_fn(itemptr, name, i); const eUIListFilterResult filter_result = item_filter_fn(itemptr, name, i);
if (filter_result == UI_LIST_ITEM_NEVER_SHOW) { 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) { else if (filter_result == UI_LIST_ITEM_FILTER_MATCHES) {
dyn_data->items_filter_flags[i] = UILST_FLT_ITEM; 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) { RNA_PROP_BEGIN (list_ptr, itemptr, list_prop) {
if (!dyn_data->items_filter_flags || 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; int new_order_idx;
if (dyn_data->items_filter_neworder) { if (dyn_data->items_filter_neworder) {

View File

@ -275,7 +275,9 @@ typedef struct uiListDyn {
void *customdata; void *customdata;
/* Filtering data. */ /* 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; int *items_filter_flags;
/** Org_idx -> new_idx, items_len length. */ /** Org_idx -> new_idx, items_len length. */
int *items_filter_neworder; 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. */ /** Value (in number of items) we have to go below minimum shown items to enable auto size. */
#define UI_LIST_AUTO_SIZE_THRESHOLD 1 #define UI_LIST_AUTO_SIZE_THRESHOLD 1
/* uiList filter flags (dyn_data) */ /** 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. */ * \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 { 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. */ UILST_FLT_ITEM = 1 << 30, /* This item has passed the filter process successfully. */
}; };

View File

@ -625,7 +625,10 @@ static void uilist_filter_items(uiList *ui_list,
int t_idx, t_ni, prev_ni; int t_idx, t_ni, prev_ni;
flt_data->items_shown = 0; flt_data->items_shown = 0;
for (i = 0, shown_idx = 0; i < len; i++) { 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]; 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... */ /* we still have to set flt_data->items_shown... */
flt_data->items_shown = 0; flt_data->items_shown = 0;
for (i = 0; i < len; i++) { 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++; 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); prop = RNA_def_property(func, "filter_flags", PROP_INT, PROP_UNSIGNED);
RNA_def_property_flag(prop, PropertyFlag(PARM_REQUIRED | PROP_DYNAMIC)); 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_array(prop, 1); /* XXX Dummy value, default 0 does not work */
RNA_def_property_ui_text( RNA_def_property_ui_text(prop,
prop, "",
"", "An array of filter flags, one for each item in the collection (NOTE: "
"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 "
"FILTER_ITEM bit is reserved, it defines whether the item is shown or not)"); "lower 16 bits for custom usages).");
RNA_def_function_output(func, prop); RNA_def_function_output(func, prop);
prop = RNA_def_property(func, "filter_neworder", PROP_INT, PROP_UNSIGNED); prop = RNA_def_property(func, "filter_neworder", PROP_INT, PROP_UNSIGNED);
RNA_def_property_flag(prop, PropertyFlag(PARM_REQUIRED | PROP_DYNAMIC)); RNA_def_property_flag(prop, PropertyFlag(PARM_REQUIRED | PROP_DYNAMIC));