diff --git a/bpkg/__init__.py b/bpkg/__init__.py index 50b990b..b89d78c 100644 --- a/bpkg/__init__.py +++ b/bpkg/__init__.py @@ -402,8 +402,17 @@ class USERPREF_PT_packages(bpy.types.Panel): return (userpref.active_section == 'PACKAGES') def draw(self, context): - layout = self.layout + wm = context.window_manager + + # try: + # wm['package_last_used_filters'] + # except KeyError: + # wm['package_last_used_filters'] = {} + # try: + # wm['package_displayed_packages'] + # except KeyError: + # wm['package_displayed_packages'] = [] main = layout.row() spl = main.split(.12) @@ -411,42 +420,66 @@ class USERPREF_PT_packages(bpy.types.Panel): pkgzone = spl.column() sidebar.label(text="Category") - sidebar.prop(context.window_manager, "addon_filter", text="") + sidebar.prop(wm, "addon_filter", text="") top = pkgzone.row() spl = top.split(.6) - spl.prop(context.window_manager, "package_search", text="", icon='VIEWZOOM') + spl.prop(wm, "package_search", text="", icon='VIEWZOOM') spl_r = spl.row() - spl_r.prop(context.window_manager, "package_install_filter", expand=True) + spl_r.prop(wm, "package_install_filter", expand=True) - #TODO: more advanced filter/sorting; sort matches which match the filter string from the start higher - #Also some caching of this would be nice, this only needs to be re-run when any of the filters change. - def filter_package(package):# {{{ - """Returns true if the given package matches all filters""" - filterstr = bpy.context.window_manager.package_search - category = bpy.context.window_manager.addon_filter - blinfo = package['bl_info'] + #TODO: if filters are only added to, we can filter on existing filtered list rather than redoing whole list + # def filters_changed(filters: dict) -> bool: + # if filters['search'] != wm['package_last_used_filters'].get('search'): + # return True + # if filters['category'] != wm['package_last_used_filters'].get('category'): + # return True + # return False - def match_search() -> bool: - if len(filterstr) == 0: - return True - if blinfo['name'].lower().__contains__(filterstr.lower()): + def filtered(filters: dict, packages: list) -> list: + """Returns filtered and sorted list of packages which match filters defined in dict""" + + #TODO: using lower() for case-insensitive comparison doesn't work in some languages + def match_contains(blinfo) -> bool: + if blinfo['name'].lower().__contains__(filters['search'].lower()): return True return False - def match_category() -> bool: - if category.upper() == 'ALL': + def match_startswith(blinfo) -> bool: + if blinfo['name'].lower().startswith(filters['search'].lower()): + return True + return False + + def match_category(blinfo) -> bool: + if filters['category'].lower() == 'all': return True if 'category' not in blinfo: - return True - if blinfo['category'].upper() == category.upper(): + return False + if blinfo['category'].lower() == filters['category'].lower(): return True return False - if match_search() and match_category(): - return True - return False# }}} + # use two lists as a simple way of putting matches from the start on top + contains = [] + startswith = [] + + for pkg in packages: + blinfo = pkg['bl_info'] + if match_category(blinfo): + if len(filters['search']) == 0: + startswith.append(pkg) + continue + if match_startswith(blinfo): + startswith.append(pkg) + continue + if match_contains(blinfo): + contains.append(pkg) + continue + + return startswith + contains + + def draw_package(pkg, layout):# {{{ """Draws the given package""" @@ -533,7 +566,7 @@ class USERPREF_PT_packages(bpy.types.Panel): #TODO either store repos in windowmanager and reload from disk every time, or store them in the .blend. Not both repo = context.user_preferences.addons[__package__].preferences['repo'] except KeyError: - center_message("Loading Repositories...") + center_message(pkgzone, "Loading Repositories...") import pathlib # TODO: read repository synchronously for now; can't run an operator to do async monitoring from draw code @@ -544,14 +577,25 @@ class USERPREF_PT_packages(bpy.types.Panel): return if repo is None: - center_message("No repository found. Add one in the addon preferences.") + center_message(pkgzone, "No repository found. Add one in the addon preferences.") return - for pkg in repo['packages']: - if filter_package(pkg): - row = pkgzone.row() - draw_package(pkg, row) + filters = { + 'category': bpy.context.window_manager.addon_filter, + 'search': bpy.context.window_manager.package_search, + } + # if filters_changed(filters): + # self.log.debug("Re-executing filters") + # wm['package_displayed_packages'] = filtered(filters, repo['packages']) + filtered_packages = filtered(filters, repo['packages']) + + # for key, item in filters.items(): + # wm['package_last_used_filters'][key] = item + + for pkg in filtered_packages: + row = pkgzone.row() + draw_package(pkg, row) class WM_OT_package_toggle_expand(bpy.types.Operator): @@ -623,6 +667,8 @@ def register(): name="Install filter", default='AVAILABLE', ) + # bpy.types.WindowManager.package_last_used_filters = dict() + # bpy.types.WindowManager.package_displayed_packages = [] bpy.utils.register_class(PackageManagerPreferences) @@ -635,4 +681,6 @@ def unregister(): bpy.utils.unregister_class(WM_OT_package_toggle_expand) del bpy.types.WindowManager.package_search del bpy.types.WindowManager.package_install_filter + # del bpy.types.WindowManager.package_displayed_packages + # del bpy.types.WindowManager.package_last_used_filters bpy.utils.unregister_class(PackageManagerPreferences)