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
@@ -663,6 +666,7 @@ def __init__(self):
663666 self .installer_pulse_timer = 0
664667 self .search_changed_timer = 0
665668 self .search_idle_timer = 0
669+ self .generate_search_cache_idle_timer = 0
666670
667671 self .action_button_signal_id = 0
668672 self .launch_button_signal_id = 0
@@ -1052,6 +1056,9 @@ def on_appstream_changed(self, installer):
10521056 if self .banner_tile is not None :
10531057 self .banner_tile .repopulate_tile ()
10541058
1059+ GLib .idle_add (self .pregenerate_search_cache )
1060+
1061+
10551062 def on_installer_ready (self ):
10561063 self .page_stack .set_visible_child_name (self .PAGE_LOADING )
10571064 try :
@@ -1072,10 +1079,14 @@ def on_installer_ready(self):
10721079 # Can take some time, don't block for it (these are categorizing packages based on apt info, not our listings)
10731080 GLib .idle_add (self .process_unmatched_packages )
10741081
1082+ if not self .installer .have_flatpak :
1083+ GLib .idle_add (self .pregenerate_search_cache )
1084+
10751085 housekeeping .run ()
10761086
10771087 self .refresh_cache_menuitem .set_sensitive (True )
10781088 self .print_startup_time ()
1089+
10791090 except Exception as e :
10801091 print ("Loading error: %s" % e )
10811092 traceback .print_tb (e .__traceback__ )
@@ -1397,11 +1408,11 @@ def on_process_exited(proc, result):
13971408 # Add a callback when we exit mintsources
13981409 p .wait_async (None , on_process_exited )
13991410
1400- def should_show_pkginfo (self , pkginfo ):
1411+ def should_show_pkginfo (self , pkginfo , allow_unverified_flatpaks ):
14011412 if pkginfo .pkg_hash .startswith ("apt" ):
14021413 return True
14031414
1404- if not self . settings . get_boolean ( prefs . ALLOW_UNVERIFIED_FLATPAKS ) :
1415+ if not allow_unverified_flatpaks :
14051416 return pkginfo .verified
14061417
14071418 return pkginfo .refid .startswith ("app/" )
@@ -1753,6 +1764,10 @@ def on_search_changed(self, searchentry):
17531764 self .show_category (self .current_category )
17541765 elif terms != "" and len (terms ) >= 3 :
17551766 self .show_search_results (terms )
1767+ elif terms == "" :
1768+ page = self .page_stack .get_visible_child_name ()
1769+ if page == self .PAGE_LIST or page == self .PAGE_SEARCHING :
1770+ self .go_back_action ()
17561771
17571772 self .search_changed_timer = 0
17581773 return False
@@ -2185,6 +2200,11 @@ def show_active_tasks(self):
21852200 def on_back_button_clicked (self , button ):
21862201 self .go_back_action ()
21872202
2203+ def cancel_running_search (self ):
2204+ if self .search_idle_timer > 0 :
2205+ GLib .source_remove (self .search_idle_timer )
2206+ self .search_idle_timer = 0
2207+
21882208 def go_back_action (self ):
21892209 XApp .set_window_progress (self .main_window , 0 )
21902210 self .stop_progress_pulse ()
@@ -2202,6 +2222,8 @@ def go_back_action(self):
22022222 self .installer .cancel_task (self .current_task )
22032223 self .current_task = None
22042224
2225+ self .cancel_running_search ()
2226+
22052227 if self .page_stack .get_visible_child_name () == self .PAGE_PREFS :
22062228 self .search_tool_item .set_sensitive (True )
22072229
@@ -2299,6 +2321,42 @@ def get_application_icon(self, pkginfo, size):
22992321
23002322 return imaging .get_icon (icon_string , size )
23012323
2324+ def update_package_search_cache (self , pkginfo , search_in_description ):
2325+ if not hasattr (pkginfo , "search_cache" ):
2326+ pkginfo .search_cache = PkgInfoSearchCache (
2327+ name = pkginfo .name .upper (),
2328+ display_name = pkginfo .get_display_name ().upper (),
2329+ keywords = pkginfo .get_keywords ().upper (),
2330+ summary = pkginfo .get_summary ().upper (),
2331+ description = None
2332+ if not search_in_description
2333+ else self .installer .get_description (pkginfo , for_search = True ).upper ()
2334+ )
2335+
2336+ # installer.get_description() is very slow, so we only fetch it if it's required
2337+ if search_in_description and pkginfo .search_cache .description is None :
2338+ description = self .installer .get_description (pkginfo , for_search = True ).upper ()
2339+ pkginfo .search_cache = pkginfo .search_cache ._replace (description = description )
2340+
2341+ def pregenerate_search_cache (self ):
2342+ if self .generate_search_cache_idle_timer > 0 :
2343+ GLib .source_remove (self .generate_search_cache_idle_timer )
2344+ self .generate_search_cache_idle_timer = 0
2345+
2346+ search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
2347+ pkginfos = self .installer .cache .values ()
2348+
2349+ def generate_package_cache (pkginfos_iter ):
2350+ try :
2351+ pkginfo = next (pkginfos_iter )
2352+ self .update_package_search_cache (pkginfo , search_in_description )
2353+ return True
2354+ except StopIteration :
2355+ self .generate_search_cache_idle_timer = 0
2356+ return False
2357+
2358+ self .generate_search_cache_idle_timer = GLib .idle_add (generate_package_cache , iter (pkginfos ))
2359+
23022360 @print_timing
23032361 def show_search_results (self , terms ):
23042362 if not self .gui_ready :
@@ -2333,53 +2391,65 @@ def show_search_results(self, terms):
23332391
23342392 searched_packages = []
23352393
2336- if self .search_idle_timer > 0 :
2337- GLib .source_remove (self .search_idle_timer )
2338- self .search_idle_timer = 0
2394+ self .cancel_running_search ()
23392395
23402396 search_in_summary = self .settings .get_boolean (prefs .SEARCH_IN_SUMMARY )
23412397 search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
23422398
23432399 package_type_preference = self .settings .get_string (prefs .PACKAGE_TYPE_PREFERENCE )
23442400 hidden_packages = set ()
2401+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
23452402
23462403 def idle_search_one_package (pkginfos ):
23472404 try :
2348- pkginfo = pkginfos . pop ( 0 )
2349- except IndexError :
2405+ pkginfo = next ( pkginfos )
2406+ except StopIteration :
23502407 self .search_idle_timer = 0
2408+
2409+ if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2410+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2411+ elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2412+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2413+ else :
2414+ results = searched_packages
2415+
2416+ GLib .idle_add (self .on_search_results_complete , results )
23512417 return False
23522418
23532419 flatpak = pkginfo .pkg_hash .startswith ("f" )
23542420 is_match = False
23552421
23562422 while True :
2357- if not self .should_show_pkginfo (pkginfo ):
2423+ if not self .should_show_pkginfo (pkginfo , allow_unverified_flatpaks ):
23582424 break
23592425
2360- if all (piece in pkginfo .name .upper () for piece in termsSplit ):
2426+ self .update_package_search_cache (pkginfo , search_in_description )
2427+
2428+ if all (piece in pkginfo .search_cache .name for piece in termsSplit ):
23612429 is_match = True
23622430 pkginfo .search_tier = 0
23632431 break
2432+
23642433 # pkginfo.name for flatpaks is their id (org.foo.BarMaker), which
23652434 # may not actually contain the app's name. In this case their display
23662435 # names are better. The 'name' is still checked first above, because
23672436 # 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 ):
2437+ if flatpak and all (piece in pkginfo .search_cache . display_name for piece in termsSplit ):
23692438 is_match = True
23702439 pkginfo .search_tier = 0
23712440 break
23722441
2373- if termsUpper in pkginfo .get_keywords (). upper () :
2442+ if termsUpper in pkginfo .search_cache . keywords :
23742443 is_match = True
23752444 pkginfo .search_tier = 50
23762445 break
23772446
2378- if (search_in_summary and termsUpper in pkginfo .get_summary (). upper () ):
2447+ if (search_in_summary and termsUpper in pkginfo .search_cache . summary ):
23792448 is_match = True
23802449 pkginfo .search_tier = 100
23812450 break
2382- if (search_in_description and termsUpper in self .installer .get_description (pkginfo ).upper ()):
2451+
2452+ if (search_in_description and termsUpper in pkginfo .search_cache .description ):
23832453 is_match = True
23842454 pkginfo .search_tier = 200
23852455 break
@@ -2392,23 +2462,9 @@ def idle_search_one_package(pkginfos):
23922462 elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK and flatpak :
23932463 hidden_packages .add (DEB_EQUIVS .get (pkginfo .name ))
23942464
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
2465+ return True
24102466
2411- self .search_idle_timer = GLib .idle_add (idle_search_one_package , list (listing ))
2467+ self .search_idle_timer = GLib .idle_add (idle_search_one_package , iter (listing ))
24122468
24132469 def on_search_results_complete (self , results ):
24142470 self .page_stack .set_visible_child_name (self .PAGE_LIST )
@@ -2479,6 +2535,7 @@ def sort_packages(self, pkgs, key_func):
24792535
24802536 def show_packages (self , pkginfos , from_search = False ):
24812537 self .stop_slideshow_timer ()
2538+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
24822539
24832540 if self .one_package_idle_timer > 0 :
24842541 GLib .source_remove (self .one_package_idle_timer )
@@ -2515,7 +2572,7 @@ def show_packages(self, pkginfos, from_search=False):
25152572 apps = [info for info in pkginfos ] # should_show_pkginfo was applied during search matching
25162573 apps = self .sort_packages (apps , attrgetter ("unverified" , "search_tier" , "score_desc" , "name" ))
25172574 else :
2518- apps = [info for info in pkginfos if self .should_show_pkginfo (info )]
2575+ apps = [info for info in pkginfos if self .should_show_pkginfo (info , allow_unverified_flatpaks )]
25192576 apps = self .sort_packages (apps , attrgetter ("unverified" , "score_desc" , "name" ))
25202577 apps = apps [0 :201 ]
25212578
@@ -2845,10 +2902,11 @@ def on_package_type_button_clicked(self, button, pkginfo):
28452902 self .show_package (pkginfo , self .previous_page )
28462903
28472904 def get_flatpak_for_deb (self , pkginfo ):
2905+ allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
28482906 try :
28492907 fp_name = FLATPAK_EQUIVS [pkginfo .name ]
28502908 flatpak_pkginfo = self .installer .find_pkginfo (fp_name , installer .PKG_TYPE_FLATPAK )
2851- if self .should_show_pkginfo (flatpak_pkginfo ):
2909+ if self .should_show_pkginfo (flatpak_pkginfo , allow_unverified_flatpaks ):
28522910 return flatpak_pkginfo
28532911 except :
28542912 return None
0 commit comments