Last uiList patch (for now!): filtering and reordering of shown elements.
authorBastien Montagne <montagne29@wanadoo.fr>
Thu, 29 Aug 2013 12:55:31 +0000 (12:55 +0000)
committerBastien Montagne <montagne29@wanadoo.fr>
Thu, 29 Aug 2013 12:55:31 +0000 (12:55 +0000)
Thanks to Brecht for the reviews. :)

This commit adds a show/hide extension below each uiList, containing by default an option to filter and/or reorder items by name (and to reverse those filtering and reordering).

Each derived uiList class in Python can define more specific filtering by implementing callbacks: the draw_filter() function to draw options in UI, and the filter_items() function to effectively filter/reorder items.

Note: the advanced options for vgroups shown as "proof od concept" in patches do not go in trunk for now, we have to find a better way to get those vgroups info for UI code, we can't afford to loop over each vertex here!

And doc (release notes and uiList example) is still to be updated, will do this in next days.

release/scripts/startup/bl_ui/__init__.py
source/blender/blenkernel/BKE_screen.h
source/blender/blenkernel/intern/screen.c
source/blender/blenloader/intern/readfile.c
source/blender/blenloader/intern/writefile.c
source/blender/editors/interface/interface_handlers.c
source/blender/editors/interface/interface_templates.c
source/blender/makesdna/DNA_screen_types.h
source/blender/makesrna/intern/rna_ui.c

index df6247f65f57c8049ae78581c1e430bb1481ac83..ba256bdb6c894e1ca00278124abcccb1d51daf68 100644 (file)
@@ -142,6 +142,61 @@ def unregister():
 # Define a default UIList, when a list does not need any custom drawing...
 # Keep in sync with its #defined name in UI_interface.h
 class UI_UL_list(bpy.types.UIList):
-    pass
+    # These are common filtering or ordering operations (same as the default C ones!).
+    @staticmethod
+    def filter_items_by_name(pattern, bitflag, items, propname="name", flags=None, reverse=False):
+        """
+        Set FILTER_ITEM for items which name matches filter_name one (case-insensitive).
+        pattern is the filtering pattern.
+        propname is the name of the string property to use for filtering.
+        flags must be a list of integers the same length as items, or None!
+        return a list of flags (based on given flags if not None),
+               or an empty list if no flags were given and no filtering has been done.
+        """
+        import fnmatch
+
+        if not pattern:  # Empty pattern = no filtering!
+            return flags or []
+
+        if flags is None:
+            flags = [0] * len(items)
+        for idx, it in enumerate(items):
+            name = getattr(it, propname, None)
+            # Implicitly add heading/trailing wildcards if needed.
+            if pattern[0] != "*":
+                pattern = "*" + pattern
+            if pattern[-1] != "*":
+                pattern = pattern + "*"
+            # This is similar to a logical xor
+            if bool(name and fnmatch.fnmatch(name.lower(), pattern.lower())) is not bool(reverse):
+                flags[idx] |= bitflag
+        return flags
+
+    @staticmethod
+    def sort_items_helper(sort_data, key, reverse=False):
+        """
+        Common sorting utility. Returns a neworder list mapping org_idx -> new_idx.
+        sort_data must be an (unordered) list of tuples [(org_idx, ...), (org_idx, ...), ...].
+        key must be the same kind of callable you would use for sorted() builtin function.
+        reverse will reverse the sorting!
+        """
+        sort_data.sort(key=key, reverse=reverse)
+        neworder = [None] * len(sort_data)
+        for newidx, (orgidx, *_) in enumerate(sort_data):
+            neworder[orgidx] = newidx
+        return neworder
+
+    @classmethod
+    def sort_items_by_name(cls, items, propname="name"):
+        """
+        Re-order items using their names (case-insensitive).
+        propname is the name of the string property to use for sorting.
+        return a list mapping org_idx -> new_idx,
+               or an empty list if no sorting has been done.
+        """
+        neworder = [None] * len(items)
+        _sort = [(idx, getattr(it, propname, "")) for idx, it in enumerate(items)]
+        return cls.sort_items_helper(_sort, lambda e: e[1].lower())
+
 
 bpy.utils.register_class(UI_UL_list)
index c883bdf74e06d4ad1dc74eaeeb387c98cca22c84..3c0928d38a06a32f30daa2eb37cce3ac379221e9 100644 (file)
@@ -189,9 +189,15 @@ typedef struct PanelType {
 
 /* uilist types */
 
-/* draw an item in the uiList */
+/* Draw an item in the uiList */
 typedef void (*uiListDrawItemFunc)(struct uiList *, struct bContext *, struct uiLayout *, struct PointerRNA *,
-                                   struct PointerRNA *, int, struct PointerRNA *, const char *, int);
+                                   struct PointerRNA *, int, struct PointerRNA *, const char *, int, int);
+
+/* Draw the filtering part of an uiList */
+typedef void (*uiListDrawFilterFunc)(struct uiList *, struct bContext *, struct uiLayout *);
+
+/* Filter items of an uiList */
+typedef void (*uiListFilterItemsFunc)(struct uiList *, struct bContext *, struct PointerRNA *, const char *);
 
 typedef struct uiListType {
        struct uiListType *next, *prev;
@@ -199,6 +205,8 @@ typedef struct uiListType {
        char idname[BKE_ST_MAXNAME];            /* unique name */
 
        uiListDrawItemFunc draw_item;
+       uiListDrawFilterFunc draw_filter;
+       uiListFilterItemsFunc filter_items;
 
        /* RNA integration */
        ExtensionRNA ext;
index 924cdff798d1ffb7d9825dd3990fc5026ae581b2..efcbcacf9748d232e578dda99c9e83937f1a7959 100644 (file)
@@ -47,6 +47,7 @@
 #include "BLI_listbase.h"
 #include "BLI_utildefines.h"
 
+#include "BKE_idprop.h"
 #include "BKE_screen.h"
 
 /* ************ Spacetype/regiontype handling ************** */
@@ -290,7 +291,18 @@ void BKE_area_region_free(SpaceType *st, ARegion *ar)
 
        for (uilst = ar->ui_lists.first; uilst; uilst = uilst->next) {
                if (uilst->dyn_data) {
-                       MEM_freeN(uilst->dyn_data);
+                       uiListDyn *dyn_data = uilst->dyn_data;
+                       if (dyn_data->items_filter_flags) {
+                               MEM_freeN(dyn_data->items_filter_flags);
+                       }
+                       if (dyn_data->items_filter_neworder) {
+                               MEM_freeN(dyn_data->items_filter_neworder);
+                       }
+                       MEM_freeN(dyn_data);
+               }
+               if (uilst->properties) {
+                       IDP_FreeProperty(uilst->properties);
+                       MEM_freeN(uilst->properties);
                }
        }
        BLI_freelistN(&ar->ui_lists);
index ff44bcc4cfefb87c5a3be8cc16f711613f260bee..76ed06ce57f9d2cbd822166aa257327d90114d0e 100644 (file)
@@ -6128,6 +6128,9 @@ static void direct_link_region(FileData *fd, ARegion *ar, int spacetype)
        for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next) {
                ui_list->type = NULL;
                ui_list->dyn_data = NULL;
+               ui_list->properties = newdataadr(fd, ui_list->properties);
+               if (ui_list->properties)
+                       IDP_DirectLinkProperty(ui_list->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
        }
 
        if (spacetype == SPACE_EMPTY) {
index b7254ffe1a9db20a0585e749aefcdebf8e62552e..83982781d8c9bfbd8ff5b206accd72c3abbd6691 100644 (file)
@@ -2394,6 +2394,15 @@ static void write_region(WriteData *wd, ARegion *ar, int spacetype)
        }
 }
 
+static void write_uilist(WriteData *wd, uiList *ui_list)
+{
+       writestruct(wd, DATA, "uiList", 1, ui_list);
+
+       if (ui_list->properties) {
+               IDP_WriteProperty(ui_list->properties, wd);
+       }
+}
+
 static void write_soops(WriteData *wd, SpaceOops *so, LinkNode **tmp_mem_list)
 {
        BLI_mempool *ts = so->treestore;
@@ -2475,7 +2484,7 @@ static void write_screens(WriteData *wd, ListBase *scrbase)
                                        writestruct(wd, DATA, "Panel", 1, pa);
                                
                                for (ui_list = ar->ui_lists.first; ui_list; ui_list = ui_list->next)
-                                       writestruct(wd, DATA, "uiList", 1, ui_list);
+                                       write_uilist(wd, ui_list);
                        }
                        
                        sl= sa->spacedata.first;
index e4e756a8892d08ad758343085b084cc39e2039a6..90040e221b09605a2b9f6ec63c28008243b2613c 100644 (file)
@@ -6585,15 +6585,55 @@ static int ui_handle_list_event(bContext *C, const wmEvent *event, ARegion *ar)
                            ((ELEM(type, WHEELUPMOUSE, WHEELDOWNMOUSE) && event->alt)))
                        {
                                const int value_orig = RNA_property_int_get(&but->rnapoin, but->rnaprop);
-                               int value, min, max;
+                               int value, min, max, inc;
 
                                /* activate up/down the list */
                                value = value_orig;
+                               if ((ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_REVERSE) != 0) {
+                                       inc = ELEM(type, UPARROWKEY, WHEELUPMOUSE) ? 1 : -1;
+                               }
+                               else {
+                                       inc = ELEM(type, UPARROWKEY, WHEELUPMOUSE) ? -1 : 1;
+                               }
 
-                               if (ELEM(type, UPARROWKEY, WHEELUPMOUSE))
-                                       value--;
-                               else
-                                       value++;
+                               if (dyn_data->items_filter_neworder || dyn_data->items_filter_flags) {
+                                       /* If we have a display order different from collection order, we have some work! */
+                                       int *org_order = MEM_mallocN(dyn_data->items_shown * sizeof(int), AT);
+                                       int *new_order = dyn_data->items_filter_neworder;
+                                       int i, org_idx = -1, len = dyn_data->items_len;
+                                       int current_idx = -1;
+                                       int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
+
+                                       for (i = 0; i < len; i++) {
+                                               if (!dyn_data->items_filter_flags ||
+                                                       ((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) {
+                                                               current_idx = new_order ? new_order[org_idx] : org_idx;
+                                                       }
+                                               }
+                                               else if (i == value && org_idx >= 0) {
+                                                       current_idx = -(new_order ? new_order[org_idx] : org_idx) - 1;
+                                               }
+                                       }
+                                       /* Now, org_order maps displayed indices to real indices,
+                                        * and current_idx either contains the displayed index of active value (positive),
+                                        *                 or its more-nearest one (negated).
+                                        */
+                                       if (current_idx < 0) {
+                                               current_idx = (current_idx * -1) + (inc < 0 ? inc : inc - 1);
+                                       }
+                                       else {
+                                               current_idx += inc;
+                                       }
+                                       CLAMP(current_idx, 0, dyn_data->items_shown - 1);
+                                       value = org_order[current_idx];
+                                       MEM_freeN(org_order);
+                               }
+                               else {
+                                       value += inc;
+                               }
 
                                CLAMP(value, 0, dyn_data->items_len - 1);
 
index 2098d881d4e611b248b56c5a0f8adcf6bc73f7f1..7c84a676827b3b2b1c1f4767341173fc3a7f9d7c 100644 (file)
@@ -44,6 +44,7 @@
 #include "BLI_rect.h"
 #include "BLI_math.h"
 #include "BLI_listbase.h"
+#include "BLI_fnmatch.h"
 
 #include "BLF_api.h"
 #include "BLF_translation.h"
@@ -2471,7 +2472,7 @@ void uiTemplateGameStates(uiLayout *layout, PointerRNA *ptr, const char *propnam
 static void uilist_draw_item_default(struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout,
                                      struct PointerRNA *UNUSED(dataptr), struct PointerRNA *itemptr, int icon,
                                      struct PointerRNA *UNUSED(active_dataptr), const char *UNUSED(active_propname),
-                                     int UNUSED(index))
+                                     int UNUSED(index), int UNUSED(flt_flag))
 {
        char *namebuf;
        const char *name;
@@ -2497,6 +2498,153 @@ static void uilist_draw_item_default(struct uiList *ui_list, struct bContext *UN
        }
 }
 
+static void uilist_draw_filter_default(struct uiList *ui_list, struct bContext *UNUSED(C), struct uiLayout *layout)
+{
+       PointerRNA listptr;
+       uiLayout *row, *subrow;
+
+       RNA_pointer_create(NULL, &RNA_UIList, ui_list, &listptr);
+
+       row = uiLayoutRow(layout, FALSE);
+
+       subrow = uiLayoutRow(row, TRUE);
+       uiItemR(subrow, &listptr, "filter_name", 0, "", ICON_NONE);
+       uiItemR(subrow, &listptr, "use_filter_invert", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "",
+               (ui_list->filter_flag & UILST_FLT_EXCLUDE) ? ICON_ZOOM_OUT : ICON_ZOOM_IN);
+
+       subrow = uiLayoutRow(row, TRUE);
+       uiItemR(subrow, &listptr, "use_filter_orderby_name", UI_ITEM_R_TOGGLE, NULL, ICON_NONE);
+       uiItemR(subrow, &listptr, "use_filter_orderby_invert", UI_ITEM_R_TOGGLE | UI_ITEM_R_ICON_ONLY, "",
+               (ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_REVERSE) ? ICON_TRIA_UP : ICON_TRIA_DOWN);
+}
+
+typedef struct {
+       char name[MAX_IDPROP_NAME];
+       int org_idx;
+} StringCmp;
+
+static int cmpstringp(const void *p1, const void *p2)
+{
+       /* Case-insensitive comparison. */
+       return strcasecmp(((StringCmp *) p1)->name, ((StringCmp *) p2)->name);
+}
+
+static void uilist_filter_items_default(struct uiList *ui_list, struct bContext *UNUSED(C), struct PointerRNA *dataptr,
+                                        const char *propname)
+{
+       uiListDyn *dyn_data = ui_list->dyn_data;
+       PropertyRNA *prop = RNA_struct_find_property(dataptr, propname);
+
+       const char *filter_raw = ui_list->filter_byname;
+       char *filter = (char *)filter_raw, filter_buff[32], *filter_dyn = NULL;
+       bool filter_exclude = (ui_list->filter_flag & UILST_FLT_EXCLUDE) != 0;
+       bool order_by_name = (ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_NAME) != 0;
+       int len = RNA_property_collection_length(dataptr, prop);
+
+       dyn_data->items_shown = dyn_data->items_len = len;
+
+       if (len && (order_by_name || filter_raw[0])) {
+               StringCmp *names = NULL;
+               int order_idx = 0, i = 0;
+
+               if (order_by_name) {
+                       names = MEM_callocN(sizeof(StringCmp) * len, AT);
+               }
+               if (filter_raw[0]) {
+                       size_t idx = 0, slen = strlen(filter_raw);
+
+                       dyn_data->items_filter_flags = MEM_callocN(sizeof(int) * len, AT);
+                       dyn_data->items_shown = 0;
+
+                       /* Implicitly add heading/trailing wildcards if needed. */
+                       if (len + 3 <= 32) {
+                               filter = filter_buff;
+                       }
+                       else {
+                               filter = filter_dyn = MEM_mallocN((slen + 3) * sizeof(char), AT);
+                       }
+                       if (filter_raw[idx] != '*') {
+                               filter[idx++] = '*';
+                       }
+                       memcpy(filter + idx, filter_raw, slen);
+                       idx += slen;
+                       if (filter[idx - 1] != '*') {
+                               filter[idx++] = '*';
+                       }
+                       filter[idx] = '\0';
+               }
+
+               RNA_PROP_BEGIN (dataptr, itemptr, prop)
+               {
+                       char *namebuf;
+                       const char *name;
+                       bool do_order = false;
+
+                       namebuf = RNA_struct_name_get_alloc(&itemptr, NULL, 0, NULL);
+                       name = namebuf ? namebuf : "";
+
+                       if (filter[0]) {
+                               /* Case-insensitive! */
+                               if (fnmatch(filter, name, FNM_CASEFOLD) == 0) {
+                                       dyn_data->items_filter_flags[i] = UILST_FLT_ITEM;
+                                       if (!filter_exclude) {
+                                               dyn_data->items_shown++;
+                                               do_order = order_by_name;
+                                       }
+                                       //printf("%s: '%s' matches '%s'\n", __func__, name, filter);
+                               }
+                               else if (filter_exclude) {
+                                       dyn_data->items_shown++;
+                                       do_order = order_by_name;
+                               }
+                       }
+                       else {
+                               do_order = order_by_name;
+                       }
+
+                       if (do_order) {
+                               names[order_idx].org_idx = order_idx;
+                               BLI_strncpy(names[order_idx++].name, name, MAX_IDPROP_NAME);
+                       }
+
+                       /* free name */
+                       if (namebuf) {
+                               MEM_freeN(namebuf);
+                       }
+                       i++;
+               }
+               RNA_PROP_END;
+
+               if (order_by_name) {
+                       int new_idx;
+                       /* note: order_idx equals either to ui_list->items_len if no filtering done,
+                        *       or to ui_list->items_shown if filter is enabled,
+                        *       or to (ui_list->items_len - ui_list->items_shown) if filtered items are excluded.
+                        *       This way, we only sort items we actually intend to draw!
+                        */
+                       qsort(names, order_idx, sizeof(StringCmp), cmpstringp);
+
+                       dyn_data->items_filter_neworder = MEM_mallocN(sizeof(int) * order_idx, AT);
+                       for (new_idx = 0; new_idx < order_idx; new_idx++) {
+                               dyn_data->items_filter_neworder[names[new_idx].org_idx] = new_idx;
+                       }
+               }
+
+               if (filter_dyn) {
+                       MEM_freeN(filter_dyn);
+               }
+               if (names) {
+                       MEM_freeN(names);
+               }
+       }
+}
+
+typedef struct {
+       PointerRNA item;
+       int org_idx;
+       int flt_flag;
+} _uilist_item;
+
 typedef struct {
        int visual_items;  /* Visual number of items (i.e. number of items we have room to display). */
        int start_idx;     /* Index of first item to display. */
@@ -2572,9 +2720,12 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
        uiListDyn *dyn_data;
        ARegion *ar;
        uiListDrawItemFunc draw_item;
+       uiListDrawFilterFunc draw_filter;
+       uiListFilterItemsFunc filter_items;
 
        PropertyRNA *prop = NULL, *activeprop;
        PropertyType type, activetype;
+       _uilist_item *items_ptr = NULL;
        StructRNA *ptype;
        uiLayout *glob = NULL, *box, *row, *col, *subrow, *sub, *overlap;
        uiBlock *block, *subblock;
@@ -2586,7 +2737,6 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
        int rnaicon = ICON_NONE, icon = ICON_NONE;
        int i = 0, activei = 0;
        int len = 0;
-       int found;
 
        /* validate arguments */
        /* Forbid default UI_UL_DEFAULT_CLASS_NAME list class without a custom list_id! */
@@ -2649,6 +2799,8 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
        }
 
        draw_item = ui_list_type->draw_item ? ui_list_type->draw_item : uilist_draw_item_default;
+       draw_filter = ui_list_type->draw_filter ? ui_list_type->draw_filter : uilist_draw_filter_default;
+       filter_items = ui_list_type->filter_items ? ui_list_type->filter_items : uilist_filter_items_default;
 
        /* Find or add the uiList to the current Region. */
        /* We tag the list id with the list type... */
@@ -2672,8 +2824,89 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
        ui_list->type = ui_list_type;
        ui_list->layout_type = layout_type;
 
-       if (dataptr->data && prop)
-               dyn_data->items_len = dyn_data->items_shown = len = RNA_property_collection_length(dataptr, prop);
+       /* Reset filtering data. */
+       if (dyn_data->items_filter_flags) {
+               MEM_freeN(dyn_data->items_filter_flags);
+               dyn_data->items_filter_flags = NULL;
+       }
+       if (dyn_data->items_filter_neworder) {
+               MEM_freeN(dyn_data->items_filter_neworder);
+               dyn_data->items_filter_neworder = NULL;
+       }
+       dyn_data->items_len = dyn_data->items_shown = -1;
+
+       /* Filter list items! (not for compact layout, though) */
+       if (dataptr->data && prop) {
+               int filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
+               bool order_reverse = (ui_list->filter_orderby_flag & UILST_FLT_ORDERBY_REVERSE) != 0;
+               int items_shown, idx = 0;
+#if 0
+               int prev_ii = -1, prev_i;
+#endif
+
+               if (layout_type == UILST_LAYOUT_COMPACT) {
+                       dyn_data->items_len = dyn_data->items_shown = RNA_property_collection_length(dataptr, prop);
+               }
+               else {
+                       //printf("%s: filtering...\n", __func__);
+                       filter_items(ui_list, C, dataptr, propname);
+                       //printf("%s: filtering done.\n", __func__);
+               }
+
+               items_shown = dyn_data->items_shown;
+               if (items_shown >= 0) {
+                       items_ptr = MEM_mallocN(sizeof(_uilist_item) * items_shown, AT);
+                       //printf("%s: items shown: %d.\n", __func__, items_shown);
+                       RNA_PROP_BEGIN (dataptr, itemptr, prop)
+                       {
+                               if (!dyn_data->items_filter_flags ||
+                                       ((dyn_data->items_filter_flags[i] & UILST_FLT_ITEM) ^ filter_exclude))
+                               {
+                                       int ii;
+                                       if (dyn_data->items_filter_neworder) {
+                                               ii = dyn_data->items_filter_neworder[idx++];
+                                               ii = order_reverse ? items_shown - ii - 1 : ii;
+                                       }
+                                       else {
+                                               ii = order_reverse ? items_shown - ++idx : idx++;
+                                       }
+                                       //printf("%s: ii: %d\n", __func__, ii);
+                                       items_ptr[ii].item = itemptr;
+                                       items_ptr[ii].org_idx = i;
+                                       items_ptr[ii].flt_flag = dyn_data->items_filter_flags ? dyn_data->items_filter_flags[i] : 0;
+
+                                       if (activei == i) {
+                                               activei = ii;
+                                       }
+# if 0 /* For now, do not alter active element, even if it will be hidden... */
+                                       else if (activei < i) {
+                                               /* We do not want an active but invisible item!
+                                                * Only exception is when all items are filtered out...
+                                                */
+                                               if (prev_ii >= 0) {
+                                                       activei = prev_ii;
+                                                       RNA_property_int_set(active_dataptr, activeprop, prev_i);
+                                               }
+                                               else {
+                                                       activei = ii;
+                                                       RNA_property_int_set(active_dataptr, activeprop, i);
+                                               }
+                                       }
+                                       prev_i = i;
+                                       prev_ii = ii;
+#endif
+                               }
+                               i++;
+                       }
+                       RNA_PROP_END;
+               }
+               if (dyn_data->items_shown >= 0) {
+                       len = dyn_data->items_shown;
+               }
+               else {
+                       len = dyn_data->items_len;
+               }
+       }
 
        switch (layout_type) {
                case UILST_LAYOUT_DEFAULT:
@@ -2688,38 +2921,39 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
 
                        if (dataptr->data && prop) {
                                /* create list items */
-                               RNA_PROP_BEGIN (dataptr, itemptr, prop)
-                               {
-                                       if (i >= layoutdata.start_idx && i < layoutdata.end_idx) {
-                                               subblock = uiLayoutGetBlock(col);
-                                               overlap = uiLayoutOverlap(col);
+                               for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
+                                       PointerRNA *itemptr = &items_ptr[i].item;
+                                       int org_i = items_ptr[i].org_idx;
+                                       int flt_flag = items_ptr[i].flt_flag;
+                                       subblock = uiLayoutGetBlock(col);
 
-                                               uiBlockSetFlag(subblock, UI_BLOCK_LIST_ITEM);
+                                       overlap = uiLayoutOverlap(col);
 
-                                               /* list item behind label & other buttons */
-                                               sub = uiLayoutRow(overlap, FALSE);
+                                       uiBlockSetFlag(subblock, UI_BLOCK_LIST_ITEM);
 
-                                               but = uiDefButR_prop(subblock, LISTROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y,
-                                                                    active_dataptr, activeprop, 0, 0, i, 0, 0, NULL);
-                                               uiButSetFlag(but, UI_BUT_NO_TOOLTIP);
+                                       /* list item behind label & other buttons */
+                                       sub = uiLayoutRow(overlap, FALSE);
 
-                                               sub = uiLayoutRow(overlap, FALSE);
+                                       but = uiDefButR_prop(subblock, LISTROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y,
+                                                            active_dataptr, activeprop, 0, 0, org_i, 0, 0, NULL);
+                                       uiButSetFlag(but, UI_BUT_NO_TOOLTIP);
 
-                                               icon = UI_rnaptr_icon_get(C, &itemptr, rnaicon, false);
-                                               if (icon == ICON_DOT)
-                                                       icon = ICON_NONE;
-                                               draw_item(ui_list, C, sub, dataptr, &itemptr, icon, active_dataptr, active_propname, i);
+                                       sub = uiLayoutRow(overlap, FALSE);
+
+                                       icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false);
+                                       if (icon == ICON_DOT)
+                                               icon = ICON_NONE;
+                                       draw_item(ui_list, C, sub, dataptr, itemptr, icon, active_dataptr, active_propname,
+                                                 org_i, flt_flag);
 
-                                               /* If we are "drawing" active item, set all labels as active. */
-                                               if (i == activei) {
-                                                       ui_layout_list_set_labels_active(sub);
-                                               }
 
-                                               uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM);
+                                       /* If we are "drawing" active item, set all labels as active. */
+                                       if (i == activei) {
+                                               ui_layout_list_set_labels_active(sub);
                                        }
-                                       i++;
+
+                                       uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM);
                                }
-                               RNA_PROP_END;
                        }
 
                        /* add dummy buttons to fill space */
@@ -2738,33 +2972,25 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                case UILST_LAYOUT_COMPACT:
                        row = uiLayoutRow(layout, TRUE);
 
-                       if (dataptr->data && prop) {
-                               /* create list items */
-                               RNA_PROP_BEGIN (dataptr, itemptr, prop)
-                               {
-                                       found = (activei == i);
+                       if (dataptr->data && prop && dyn_data->items_shown > 0) {
+                               PointerRNA *itemptr = &items_ptr[activei].item;
+                               int org_i = items_ptr[activei].org_idx;
 
-                                       if (found) {
-                                               icon = UI_rnaptr_icon_get(C, &itemptr, rnaicon, false);
-                                               if (icon == ICON_DOT)
-                                                       icon = ICON_NONE;
-                                               draw_item(ui_list, C, row, dataptr, &itemptr, icon, active_dataptr, active_propname, i);
-                                       }
-
-                                       i++;
-                               }
-                               RNA_PROP_END;
+                               icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false);
+                               if (icon == ICON_DOT)
+                                       icon = ICON_NONE;
+                               draw_item(ui_list, C, row, dataptr, itemptr, icon, active_dataptr, active_propname, org_i, 0);
                        }
-
                        /* if list is empty, add in dummy button */
-                       if (i == 0)
+                       else {
                                uiItemL(row, "", ICON_NONE);
+                       }
 
                        /* next/prev button */
-                       BLI_snprintf(numstr, sizeof(numstr), "%d :", i);
+                       BLI_snprintf(numstr, sizeof(numstr), "%d :", dyn_data->items_shown);
                        but = uiDefIconTextButR_prop(block, NUM, 0, 0, numstr, 0, 0, UI_UNIT_X * 5, UI_UNIT_Y,
                                                     active_dataptr, activeprop, 0, 0, 0, 0, 0, "");
-                       if (i == 0)
+                       if (dyn_data->items_shown == 0)
                                uiButSetFlag(but, UI_BUT_DISABLED);
                        break;
                case UILST_LAYOUT_GRID:
@@ -2778,9 +3004,11 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
 
                        if (dataptr->data && prop) {
                                /* create list items */
-                               RNA_PROP_BEGIN (dataptr, itemptr, prop)
-                               {
-                                       if (i >= layoutdata.start_idx && i < layoutdata.end_idx) {
+                               for (i = layoutdata.start_idx; i < layoutdata.end_idx; i++) {
+                                       PointerRNA *itemptr = &items_ptr[i].item;
+                                       int org_i = items_ptr[i].org_idx;
+                                       int flt_flag = items_ptr[i].flt_flag;
+
                                        /* create button */
                                        if (!(i % columns))
                                                subrow = uiLayoutRow(col, FALSE);
@@ -2794,13 +3022,14 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                                        sub = uiLayoutRow(overlap, FALSE);
 
                                        but = uiDefButR_prop(subblock, LISTROW, 0, "", 0, 0, UI_UNIT_X * 10, UI_UNIT_Y,
-                                                            active_dataptr, activeprop, 0, 0, i, 0, 0, NULL);
+                                                            active_dataptr, activeprop, 0, 0, org_i, 0, 0, NULL);
                                        uiButSetFlag(but, UI_BUT_NO_TOOLTIP);
 
                                        sub = uiLayoutRow(overlap, FALSE);
 
-                                       icon = UI_rnaptr_icon_get(C, &itemptr, rnaicon, false);
-                                       draw_item(ui_list, C, sub, dataptr, &itemptr, icon, active_dataptr, active_propname, i);
+                                       icon = UI_rnaptr_icon_get(C, itemptr, rnaicon, false);
+                                       draw_item(ui_list, C, sub, dataptr, itemptr, icon, active_dataptr, active_propname,
+                                                 org_i, flt_flag);
 
                                        /* If we are "drawing" active item, set all labels as active. */
                                        if (i == activei) {
@@ -2808,10 +3037,7 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                                        }
 
                                        uiBlockClearFlag(subblock, UI_BLOCK_LIST_ITEM);
-                                       }
-                                       i++;
                                }
-                               RNA_PROP_END;
                        }
 
                        /* add dummy buttons to fill space */
@@ -2837,11 +3063,40 @@ void uiTemplateList(uiLayout *layout, bContext *C, const char *listtype_name, co
                subblock = uiLayoutGetBlock(row);
                uiBlockSetEmboss(subblock, UI_EMBOSSN);
 
-               but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.4f, ui_list,
-                                  0.0, 0.0, 0, -1, "");
-               uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+               if (ui_list->filter_flag & UILST_FLT_SHOW) {
+                       but = uiDefIconButBitI(subblock, TOG, UILST_FLT_SHOW, 0, ICON_DISCLOSURE_TRI_DOWN, 0, 0,
+                                              UI_UNIT_X, UI_UNIT_Y * 0.6f, &(ui_list->filter_flag), 0, 0, 0, 0,
+                                              TIP_("Hide filtering options"));
+                       uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+                       but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.6f, ui_list,
+                                          0.0, 0.0, 0, -1, "");
+                       uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+                       uiBlockSetEmboss(subblock, UI_EMBOSS);
+
+                       col = uiLayoutColumn(glob, FALSE);
+                       subblock = uiLayoutGetBlock(col);
+                       uiDefBut(subblock, SEPR, 0, "", 0, 0, UI_UNIT_X, UI_UNIT_Y * 0.05f, NULL, 0.0, 0.0, 0, 0, "");
+
+                       draw_filter(ui_list, C, col);
+               }
+               else {
+                       but = uiDefIconButBitI(subblock, TOG, UILST_FLT_SHOW, 0, ICON_DISCLOSURE_TRI_RIGHT, 0, 0,
+                                              UI_UNIT_X, UI_UNIT_Y * 0.6f, &(ui_list->filter_flag), 0, 0, 0, 0,
+                                              TIP_("Show filtering options"));
+                       uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+                       but = uiDefIconBut(subblock, BUT, 0, ICON_GRIP, 0, 0, UI_UNIT_X * 10.0f, UI_UNIT_Y * 0.6f, ui_list,
+                                          0.0, 0.0, 0, -1, "");
+                       uiButClearFlag(but, UI_BUT_UNDO); /* skip undo on screen buttons */
+
+                       uiBlockSetEmboss(subblock, UI_EMBOSS);
+               }
+       }
 
-               uiBlockSetEmboss(subblock, UI_EMBOSS);
+       if (items_ptr) {
+               MEM_freeN(items_ptr);
        }
 }
 
index 8e18a84060a568abcebef692a615068f5cf87c04..435f5c634a9e52017c0857f554710f435aa1ae27 100644 (file)
@@ -123,6 +123,10 @@ typedef struct uiListDyn {
 
        int items_len;                /* Number of items in collection. */
        int items_shown;              /* Number of items actually visible after filtering. */
+
+       /* Filtering data. */
+       int *items_filter_flags;      /* items_len length. */
+       int *items_filter_neworder;   /* org_idx -> new_idx, items_len length. */
 } uiListDyn;
 
 typedef struct uiList {           /* some list UI data need to be saved in file */
@@ -140,6 +144,14 @@ typedef struct uiList {           /* some list UI data need to be saved in file
        int list_last_len;
        int padi1;
 
+       /* Filtering data. */
+       char filter_byname[64];       /* defined as UI_MAX_NAME_STR */
+       int filter_flag;
+       int filter_orderby_flag;
+
+       /* Custom sub-classes properties. */
+       IDProperty *properties;
+
        /* Dynamic data (runtime). */
        uiListDyn *dyn_data;
 } uiList;
@@ -259,6 +271,25 @@ enum {
        UILST_RESIZING                = 1 << 1,          /* We are currently resizing, deactivate autosize! */
 };
 
+/* uiList filter flags (dyn_data) */
+enum {
+       UILST_FLT_ITEM      = 1 << 31,  /* This item has passed the filter process successfully. */
+};
+
+/* uiList filter options */
+enum {
+       UILST_FLT_SHOW      = 1 << 0,          /* Show filtering UI. */
+       UILST_FLT_EXCLUDE   = UILST_FLT_ITEM,  /* Exclude filtered items, *must* use this same value. */
+};
+
+/* uiList filter orderby type */
+enum {
+       UILST_FLT_ORDERBY_NAME         = 1 << 0,
+       UILST_FLT_ORDERBY_REVERSE      = 1 << 31  /* Special value, bitflag used to reverse order! */
+};
+
+#define UILST_FLT_ORDERBY_MASK (((unsigned int)UILST_FLT_ORDERBY_REVERSE) - 1)
+
 /* regiontype, first two are the default set */
 /* Do NOT change order, append on end. Types are hardcoded needed */
 enum {
index 9efe3d9f1d6e5796b6382b1622b75448d0766e76..dbddf54fe0417d3561c264c89776b1e77f27eaea 100644 (file)
@@ -31,6 +31,8 @@
 
 #include "BLF_translation.h"
 
+#include "BKE_idprop.h"
+
 #include "RNA_define.h"
 
 #include "rna_internal.h"
@@ -58,12 +60,9 @@ EnumPropertyItem operator_context_items[] = {
 };
 
 EnumPropertyItem uilist_layout_type_items[] = {
-       {UILST_LAYOUT_DEFAULT, "DEFAULT", 0, "Default Layout",
-                              "Use the default, multi-rows layout"},
-       {UILST_LAYOUT_COMPACT, "COMPACT", 0, "Compact Layout",
-                              "Use the compact, single-row layout"},
-       {UILST_LAYOUT_GRID, "GRID", 0, "Grid Layout",
-                           "Use the grid-based layout"},
+       {UILST_LAYOUT_DEFAULT, "DEFAULT", 0, "Default Layout", "Use the default, multi-rows layout"},
+       {UILST_LAYOUT_COMPACT, "COMPACT", 0, "Compact Layout", "Use the compact, single-row layout"},
+       {UILST_LAYOUT_GRID, "GRID", 0, "Grid Layout", "Use the grid-based layout"},
        {0, NULL, 0, NULL, NULL}
 };
 
@@ -269,8 +268,24 @@ static StructRNA *rna_Panel_refine(PointerRNA *ptr)
 }
 
 /* UIList */
+static unsigned int rna_UIList_filter_const_FILTER_ITEM_get(PointerRNA *ptr)
+{
+       return UILST_FLT_ITEM;
+}
+
+static IDProperty *rna_UIList_idprops(PointerRNA *ptr, bool create)
+{
+       uiList *ui_list = (uiList *)ptr->data;
+       if (create && !ui_list->properties) {
+               IDPropertyTemplate val = {0};
+               ui_list->properties = IDP_New(IDP_GROUP, &val, "RNA_UIList IDproperties group");
+       }
+
+       return ui_list->properties;
+}
+
 static void uilist_draw_item(uiList *ui_list, bContext *C, uiLayout *layout, PointerRNA *dataptr, PointerRNA *itemptr,
-                             int icon, PointerRNA *active_dataptr, const char *active_propname, int index)
+                             int icon, PointerRNA *active_dataptr, const char *active_propname, int index, int flt_flag)
 {
        extern FunctionRNA rna_UIList_draw_item_func;
 
@@ -290,11 +305,139 @@ static void uilist_draw_item(uiList *ui_list, bContext *C, uiLayout *layout, Poi
        RNA_parameter_set_lookup(&list, "active_data", active_dataptr);
        RNA_parameter_set_lookup(&list, "active_property", &active_propname);
        RNA_parameter_set_lookup(&list, "index", &index);
+       RNA_parameter_set_lookup(&list, "flt_flag", &flt_flag);
+       ui_list->type->ext.call((bContext *)C, &ul_ptr, func, &list);
+
+       RNA_parameter_list_free(&list);
+}
+
+static void uilist_draw_filter(uiList *ui_list, bContext *C, uiLayout *layout)
+{
+       extern FunctionRNA rna_UIList_draw_filter_func;
+
+       PointerRNA ul_ptr;
+       ParameterList list;
+       FunctionRNA *func;
+
+       RNA_pointer_create(&CTX_wm_screen(C)->id, ui_list->type->ext.srna, ui_list, &ul_ptr);
+       func = &rna_UIList_draw_filter_func; /* RNA_struct_find_function(&ul_ptr, "draw_filter"); */
+
+       RNA_parameter_list_create(&list, &ul_ptr, func);
+       RNA_parameter_set_lookup(&list, "context", &C);
+       RNA_parameter_set_lookup(&list, "layout", &layout);
        ui_list->type->ext.call((bContext *)C, &ul_ptr, func, &list);
 
        RNA_parameter_list_free(&list);
 }
 
+static void uilist_filter_items(uiList *ui_list, bContext *C, PointerRNA *dataptr, const char *propname)
+{
+       extern FunctionRNA rna_UIList_filter_items_func;
+
+       PointerRNA ul_ptr;
+       ParameterList list;
+       FunctionRNA *func;
+       PropertyRNA *parm;
+
+       uiListDyn *flt_data = ui_list->dyn_data;
+       int *filter_flags, *filter_neworder;
+       void *ret1, *ret2;
+       int ret_len;
+       int len = flt_data->items_len = RNA_collection_length(dataptr, propname);
+
+       RNA_pointer_create(&CTX_wm_screen(C)->id, ui_list->type->ext.srna, ui_list, &ul_ptr);
+       func = &rna_UIList_filter_items_func; /* RNA_struct_find_function(&ul_ptr, "filter_items"); */
+
+       RNA_parameter_list_create(&list, &ul_ptr, func);
+       RNA_parameter_set_lookup(&list, "context", &C);
+       RNA_parameter_set_lookup(&list, "data", dataptr);
+       RNA_parameter_set_lookup(&list, "property", &propname);
+
+       ui_list->type->ext.call((bContext *)C, &ul_ptr, func, &list);
+
+       parm = RNA_function_find_parameter(NULL, func, "filter_flags");
+       ret_len = RNA_parameter_dynamic_length_get(&list, parm);
+       if (ret_len != len && ret_len != 0) {
+               printf("%s: Error, py func returned %d items in %s, %d or none were expected.\n", AT,
+                      RNA_parameter_dynamic_length_get(&list, parm), "filter_flags", len);
+               RNA_parameter_list_free(&list);
+               return;
+       }
+       RNA_parameter_get(&list, parm, &ret1);
+       filter_flags = (int *)ret1;
+
+       parm = RNA_function_find_parameter(NULL, func, "filter_neworder");
+       ret_len = RNA_parameter_dynamic_length_get(&list, parm);
+       if (ret_len != len && ret_len != 0) {
+               printf("%s: Error, py func returned %d items in %s, %d or none were expected.\n", AT,
+                      RNA_parameter_dynamic_length_get(&list, parm), "filter_neworder", len);
+               RNA_parameter_list_free(&list);
+               return;
+       }
+       RNA_parameter_get(&list, parm, &ret2);
+       filter_neworder = (int *)ret2;
+
+       /* We have to do some final checks and transforms... */
+       {
+               int i, filter_exclude = ui_list->filter_flag & UILST_FLT_EXCLUDE;
+               if (filter_flags) {
+                       flt_data->items_filter_flags = MEM_mallocN(sizeof(int) * len, AT);
+                       memcpy(flt_data->items_filter_flags, filter_flags, sizeof(int) * len);
+
+                       if (filter_neworder) {
+                               /* For sake of simplicity, py filtering is expected to filter all items, but we actually only want
+                                * reordering data for shown items!
+                                */
+                               int items_shown, shown_idx;
+                               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) {
+                                               filter_neworder[shown_idx++] = filter_neworder[i];
+                                       }
+                               }
+                               items_shown = flt_data->items_shown = shown_idx;
+                               flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * items_shown, AT);
+                               /* And now, bring back new indices into the [0, items_shown[ range!
+                                * XXX This is O(N²)... :/
+                                */
+                               for (shown_idx = 0, prev_ni = -1; shown_idx < items_shown; shown_idx++) {
+                                       for (i = 0, t_ni = len, t_idx = -1; i < items_shown; i++) {
+                                               int ni = filter_neworder[i];
+                                               if (ni > prev_ni && ni < t_ni) {
+                                                       t_idx = i;
+                                                       t_ni = ni;
+                                               }
+                                       }
+                                       if (t_idx >= 0) {
+                                               prev_ni = t_ni;
+                                               flt_data->items_filter_neworder[t_idx] = shown_idx;
+                                       }
+                               }
+                       }
+                       else {
+                               /* 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) {
+                                               flt_data->items_shown++;
+                                       }
+                               }
+                       }
+               }
+               else {
+                       flt_data->items_shown = len;
+
+                       if (filter_neworder) {
+                               flt_data->items_filter_neworder = MEM_mallocN(sizeof(int) * len, AT);
+                               memcpy(flt_data->items_filter_neworder, filter_neworder, sizeof(int) * len);
+                       }
+               }
+       }
+
+       RNA_parameter_list_free(&list);
+}
+
 static void rna_UIList_unregister(Main *UNUSED(bmain), StructRNA *type)
 {
        uiListType *ult = RNA_struct_blender_type_get(type);
@@ -318,7 +461,7 @@ static StructRNA *rna_UIList_register(Main *bmain, ReportList *reports, void *da
        uiListType *ult, dummyult = {NULL};
        uiList dummyuilist = {NULL};
        PointerRNA dummyul_ptr;
-       int have_function[1];
+       int have_function[3];
        size_t over_alloc = 0; /* warning, if this becomes a bess, we better do another alloc */
 
        /* setup dummy menu & menu type to store static properties in */
@@ -349,9 +492,10 @@ static StructRNA *rna_UIList_register(Main *bmain, ReportList *reports, void *da
        ult->ext.call = call;
        ult->ext.free = free;
        RNA_struct_blender_type_set(ult->ext.srna, ult);
-       RNA_def_struct_flag(ult->ext.srna, STRUCT_NO_IDPROPERTIES);
 
        ult->draw_item = (have_function[0]) ? uilist_draw_item : NULL;
+       ult->draw_filter = (have_function[1]) ? uilist_draw_filter : NULL;
+       ult->filter_items = (have_function[2]) ? uilist_filter_items : NULL;
 
        WM_uilisttype_add(ult);
 
@@ -874,8 +1018,45 @@ static void rna_def_uilist(BlenderRNA *brna)
        RNA_def_struct_sdna(srna, "uiList");
        RNA_def_struct_refine_func(srna, "rna_UIList_refine");
        RNA_def_struct_register_funcs(srna, "rna_UIList_register", "rna_UIList_unregister", NULL);
+       RNA_def_struct_idprops_func(srna, "rna_UIList_idprops");
 
-       /* draw */
+       /* Registration */
+       prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
+       RNA_def_property_string_sdna(prop, NULL, "type->idname");
+       RNA_def_property_flag(prop, PROP_REGISTER | PROP_NEVER_CLAMP);
+       RNA_def_property_ui_text(prop, "ID Name",
+                                "If this is set, the uilist gets a custom ID, otherwise it takes the "
+                                "name of the class used to define the uilist (for example, if the "
+                                "class name is \"OBJECT_UL_vgroups\", and bl_idname is not set by the "
+                                "script, then bl_idname = \"OBJECT_UL_vgroups\")");
+
+       /* Data */
+       prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE);
+       RNA_def_property_enum_items(prop, uilist_layout_type_items);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+
+       /* Filter options */
+       prop = RNA_def_property(srna, "use_filter_show", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", UILST_FLT_SHOW);
+       RNA_def_property_ui_text(prop, "Show Filter", "Show filtering options");
+
+       prop = RNA_def_property(srna, "filter_name", PROP_STRING, PROP_NONE);
+       RNA_def_property_string_sdna(prop, NULL, "filter_byname");
+       RNA_def_property_ui_text(prop, "Filter by Name", "Only show items matching this name (use '*' as wildcard)");
+
+       prop = RNA_def_property(srna, "use_filter_invert", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "filter_flag", UILST_FLT_EXCLUDE);
+       RNA_def_property_ui_text(prop, "Invert", "Invert filtering (show hidden items, and vice-versa)");
+
+       prop = RNA_def_property(srna, "use_filter_orderby_name", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "filter_orderby_flag", UILST_FLT_ORDERBY_NAME);
+       RNA_def_property_ui_text(prop, "Order by Name", "Order shown items by their names");
+
+       prop = RNA_def_property(srna, "use_filter_orderby_invert", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "filter_orderby_flag", UILST_FLT_ORDERBY_REVERSE);
+       RNA_def_property_ui_text(prop, "Invert", "Invert the order of shown items");
+
+       /* draw_item */
        func = RNA_def_function(srna, "draw_item", NULL);
        RNA_def_function_ui_description(func, "Draw an item in the list (NOTE: when you define your own draw_item "
                                              "function, you may want to check given 'item' is of the right type...)");
@@ -897,21 +1078,52 @@ static void rna_def_uilist(BlenderRNA *brna)
                              "Identifier of property in active_data, for the active element");
        RNA_def_property_flag(parm, PROP_REQUIRED);
        RNA_def_int(func, "index", 0, 0, INT_MAX, "", "Index of the item in the collection", 0, INT_MAX);
+       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_PYFUNC_OPTIONAL);
+       prop = RNA_def_property(func, "flt_flag", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_ui_text(prop, "", "The filter-flag result for this item");
+       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_PYFUNC_OPTIONAL);
+
+       /* draw_filter */
+       func = RNA_def_function(srna, "draw_filter", NULL);
+       RNA_def_function_ui_description(func, "Draw filtering options");
+       RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL);
+       parm = RNA_def_pointer(func, "context", "Context", "", "");
        RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_pointer(func, "layout", "UILayout", "", "Layout to draw the item");
+       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_NEVER_NULL);
 
-       prop = RNA_def_property(srna, "layout_type", PROP_ENUM, PROP_NONE);
-       RNA_def_property_enum_items(prop, uilist_layout_type_items);
-       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       /* filter */
+       func = RNA_def_function(srna, "filter_items", NULL);
+       RNA_def_function_ui_description(func, "Filter and/or re-order items of the collection (output filter results in "
+                                             "filter_flags, and reorder results in filter_neworder arrays)");
+       RNA_def_function_flag(func, FUNC_REGISTER_OPTIONAL);
+       parm = RNA_def_pointer(func, "context", "Context", "", "");
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       parm = RNA_def_pointer(func, "data", "AnyType", "", "Data from which to take Collection property");
+       RNA_def_property_flag(parm, PROP_REQUIRED | PROP_RNAPTR);
+       parm = RNA_def_string(func, "property", "", 0, "", "Identifier of property in data, for the collection");
+       RNA_def_property_flag(parm, PROP_REQUIRED);
+       prop = RNA_def_property(func, "filter_flags", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_flag(prop, PROP_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_function_output(func, prop);
+       prop = RNA_def_property(func, "filter_neworder", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_flag(prop, PROP_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 indices, one for each item in the collection, mapping the org "
+                                          "index to the new one");
+       RNA_def_function_output(func, prop);
+
+       /* "Constants"! */
+       RNA_define_verify_sdna(0); /* not in sdna */
 
-       /* registration */
-       prop = RNA_def_property(srna, "bl_idname", PROP_STRING, PROP_NONE);
-       RNA_def_property_string_sdna(prop, NULL, "type->idname");
-       RNA_def_property_flag(prop, PROP_REGISTER | PROP_NEVER_CLAMP);
-       RNA_def_property_ui_text(prop, "ID Name",
-                                "If this is set, the uilist gets a custom ID, otherwise it takes the "
-                                "name of the class used to define the uilist (for example, if the "
-                                "class name is \"OBJECT_UL_vgroups\", and bl_idname is not set by the "
-                                "script, then bl_idname = \"OBJECT_UL_vgroups\")");
+       prop = RNA_def_property(srna, "bitflag_filter_item", PROP_INT, PROP_UNSIGNED);
+       RNA_def_property_ui_text(prop, "FILTER_ITEM",
+                                      "The value of the reserved bitfalg 'FILTER_ITEM' (in filter_flags values)");
+       RNA_def_property_int_funcs(prop, "rna_UIList_filter_const_FILTER_ITEM_get", NULL, NULL);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
 }
 
 static void rna_def_header(BlenderRNA *brna)