1919from pathlib import Path
2020import traceback
2121from operator import attrgetter
22+ from collections import namedtuple
2223
2324import gi
2425gi .require_version ('Gtk' , '3.0' )
@@ -611,6 +612,8 @@ def __init__(self, category):
611612
612613 self .add (box )
613614
615+ PkgInfoSearchCache = namedtuple ('PkgInfoSearchCache' , ['name' , 'display_name' , 'keywords' , 'summary' , 'description' ])
616+
614617class Application (Gtk .Application ):
615618 (ACTION_TAB , PROGRESS_TAB , SPINNER_TAB ) = list (range (3 ))
616619
@@ -1072,6 +1075,8 @@ def on_installer_ready(self):
10721075 # Can take some time, don't block for it (these are categorizing packages based on apt info, not our listings)
10731076 GLib .idle_add (self .process_unmatched_packages )
10741077
1078+ GLib .idle_add (self .pregenerate_search_cache )
1079+
10751080 housekeeping .run ()
10761081
10771082 self .refresh_cache_menuitem .set_sensitive (True )
@@ -1397,11 +1402,11 @@ def on_process_exited(proc, result):
13971402 # Add a callback when we exit mintsources
13981403 p .wait_async (None , on_process_exited )
13991404
1400- def should_show_pkginfo (self , pkginfo ):
1405+ def should_show_pkginfo (self , pkginfo , allow_unverified_flatpaks ):
14011406 if pkginfo .pkg_hash .startswith ("apt" ):
14021407 return True
14031408
1404- if not self . settings . get_boolean ( prefs . ALLOW_UNVERIFIED_FLATPAKS ) :
1409+ if not allow_unverified_flatpaks :
14051410 return pkginfo .verified
14061411
14071412 return pkginfo .refid .startswith ("app/" )
@@ -1753,6 +1758,10 @@ def on_search_changed(self, searchentry):
17531758 self .show_category (self .current_category )
17541759 elif terms != "" and len (terms ) >= 3 :
17551760 self .show_search_results (terms )
1761+ elif terms == "" :
1762+ page = self .page_stack .get_visible_child_name ()
1763+ if page == self .PAGE_LIST or page == self .PAGE_SEARCHING :
1764+ self .go_back_action ()
17561765
17571766 self .search_changed_timer = 0
17581767 return False
@@ -2185,6 +2194,11 @@ def show_active_tasks(self):
21852194 def on_back_button_clicked (self , button ):
21862195 self .go_back_action ()
21872196
2197+ def cancel_running_search (self ):
2198+ if self .search_idle_timer > 0 :
2199+ GLib .source_remove (self .search_idle_timer )
2200+ self .search_idle_timer = 0
2201+
21882202 def go_back_action (self ):
21892203 XApp .set_window_progress (self .main_window , 0 )
21902204 self .stop_progress_pulse ()
@@ -2202,6 +2216,8 @@ def go_back_action(self):
22022216 self .installer .cancel_task (self .current_task )
22032217 self .current_task = None
22042218
2219+ self .cancel_running_search ()
2220+
22052221 if self .page_stack .get_visible_child_name () == self .PAGE_PREFS :
22062222 self .search_tool_item .set_sensitive (True )
22072223
@@ -2299,6 +2315,37 @@ def get_application_icon(self, pkginfo, size):
22992315
23002316 return imaging .get_icon (icon_string , size )
23012317
2318+ def update_package_search_cache (self , pkginfo , search_in_description ):
2319+ if not hasattr (pkginfo , "search_cache" ):
2320+ pkginfo .search_cache = PkgInfoSearchCache (
2321+ name = pkginfo .name .upper (),
2322+ display_name = pkginfo .get_display_name ().upper (),
2323+ keywords = pkginfo .get_keywords ().upper (),
2324+ summary = pkginfo .get_summary ().upper (),
2325+ description = None
2326+ if not search_in_description
2327+ else self .installer .get_description (pkginfo , for_search = True ).upper ()
2328+ )
2329+
2330+ # installer.get_description() is very slow, so we only fetch it if it's required
2331+ if search_in_description and pkginfo .search_cache .description is None :
2332+ description = self .installer .get_description (pkginfo , True ).upper ()
2333+ pkginfo .search_cache = pkginfo .search_cache ._replace (description = description )
2334+
2335+ def pregenerate_search_cache (self ):
2336+ search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
2337+ pkginfos = self .installer .cache .values ()
2338+
2339+ def generate_package_cache (pkginfos ):
2340+ try :
2341+ pkginfo = next (pkginfos )
2342+ self .update_package_search_cache (pkginfo , search_in_description )
2343+ return True
2344+ except StopIteration :
2345+ return False
2346+
2347+ GLib .idle_add (generate_package_cache , iter (pkginfos ))
2348+
23022349 @print_timing
23032350 def show_search_results (self , terms ):
23042351 if not self .gui_ready :
@@ -2333,53 +2380,65 @@ def show_search_results(self, terms):
23332380
23342381 searched_packages = []
23352382
2336- if self .search_idle_timer > 0 :
2337- GLib .source_remove (self .search_idle_timer )
2338- self .search_idle_timer = 0
2383+ self .cancel_running_search ()
23392384
23402385 search_in_summary = self .settings .get_boolean (prefs .SEARCH_IN_SUMMARY )
23412386 search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
23422387
23432388 package_type_preference = self .settings .get_string (prefs .PACKAGE_TYPE_PREFERENCE )
23442389 hidden_packages = set ()
2390+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
23452391
23462392 def idle_search_one_package (pkginfos ):
23472393 try :
2348- pkginfo = pkginfos . pop ( 0 )
2349- except IndexError :
2394+ pkginfo = next ( pkginfos )
2395+ except StopIteration :
23502396 self .search_idle_timer = 0
2397+
2398+ if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2399+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2400+ elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2401+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2402+ else :
2403+ results = searched_packages
2404+
2405+ GLib .idle_add (self .on_search_results_complete , results )
23512406 return False
23522407
23532408 flatpak = pkginfo .pkg_hash .startswith ("f" )
23542409 is_match = False
23552410
23562411 while True :
2357- if not self .should_show_pkginfo (pkginfo ):
2412+ if not self .should_show_pkginfo (pkginfo , allow_unverified_flatpaks ):
23582413 break
23592414
2360- if all (piece in pkginfo .name .upper () for piece in termsSplit ):
2415+ self .update_package_search_cache (pkginfo , search_in_description )
2416+
2417+ if all (piece in pkginfo .search_cache .name for piece in termsSplit ):
23612418 is_match = True
23622419 pkginfo .search_tier = 0
23632420 break
2421+
23642422 # pkginfo.name for flatpaks is their id (org.foo.BarMaker), which
23652423 # may not actually contain the app's name. In this case their display
23662424 # names are better. The 'name' is still checked first above, because
23672425 # it's static - get_display_name() may involve a lookup with appstream.
2368- if flatpak and all (piece in pkginfo .get_display_name (). upper () for piece in termsSplit ):
2426+ if flatpak and all (piece in pkginfo .search_cache . display_name for piece in termsSplit ):
23692427 is_match = True
23702428 pkginfo .search_tier = 0
23712429 break
23722430
2373- if termsUpper in pkginfo .get_keywords (). upper () :
2431+ if termsUpper in pkginfo .search_cache . keywords :
23742432 is_match = True
23752433 pkginfo .search_tier = 50
23762434 break
23772435
2378- if (search_in_summary and termsUpper in pkginfo .get_summary (). upper () ):
2436+ if (search_in_summary and termsUpper in pkginfo .search_cache . summary ):
23792437 is_match = True
23802438 pkginfo .search_tier = 100
23812439 break
2382- if (search_in_description and termsUpper in self .installer .get_description (pkginfo ).upper ()):
2440+
2441+ if (search_in_description and termsUpper in pkginfo .search_cache .description ):
23832442 is_match = True
23842443 pkginfo .search_tier = 200
23852444 break
@@ -2392,23 +2451,9 @@ def idle_search_one_package(pkginfos):
23922451 elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK and flatpak :
23932452 hidden_packages .add (DEB_EQUIVS .get (pkginfo .name ))
23942453
2395- # Repeat until empty
2396- if len (pkginfos ) > 0 :
2397- return True
2398-
2399- self .search_idle_timer = 0
2400-
2401- if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2402- results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2403- elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2404- results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2405- else :
2406- results = searched_packages
2407-
2408- GLib .idle_add (self .on_search_results_complete , results )
2409- return False
2454+ return True
24102455
2411- self .search_idle_timer = GLib .idle_add (idle_search_one_package , list (listing ))
2456+ self .search_idle_timer = GLib .idle_add (idle_search_one_package , iter (listing ))
24122457
24132458 def on_search_results_complete (self , results ):
24142459 self .page_stack .set_visible_child_name (self .PAGE_LIST )
@@ -2479,6 +2524,7 @@ def sort_packages(self, pkgs, key_func):
24792524
24802525 def show_packages (self , pkginfos , from_search = False ):
24812526 self .stop_slideshow_timer ()
2527+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
24822528
24832529 if self .one_package_idle_timer > 0 :
24842530 GLib .source_remove (self .one_package_idle_timer )
@@ -2515,7 +2561,7 @@ def show_packages(self, pkginfos, from_search=False):
25152561 apps = [info for info in pkginfos ] # should_show_pkginfo was applied during search matching
25162562 apps = self .sort_packages (apps , attrgetter ("unverified" , "search_tier" , "score_desc" , "name" ))
25172563 else :
2518- apps = [info for info in pkginfos if self .should_show_pkginfo (info )]
2564+ apps = [info for info in pkginfos if self .should_show_pkginfo (info , allow_unverified_flatpaks )]
25192565 apps = self .sort_packages (apps , attrgetter ("unverified" , "score_desc" , "name" ))
25202566 apps = apps [0 :201 ]
25212567
@@ -2845,10 +2891,11 @@ def on_package_type_button_clicked(self, button, pkginfo):
28452891 self .show_package (pkginfo , self .previous_page )
28462892
28472893 def get_flatpak_for_deb (self , pkginfo ):
2894+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
28482895 try :
28492896 fp_name = FLATPAK_EQUIVS [pkginfo .name ]
28502897 flatpak_pkginfo = self .installer .find_pkginfo (fp_name , installer .PKG_TYPE_FLATPAK )
2851- if self .should_show_pkginfo (flatpak_pkginfo ):
2898+ if self .should_show_pkginfo (flatpak_pkginfo , allow_unverified_flatpaks ):
28522899 return flatpak_pkginfo
28532900 except :
28542901 return None
0 commit comments