@@ -614,6 +614,69 @@ def __init__(self, category):
614
614
615
615
PkgInfoSearchCache = namedtuple ('PkgInfoSearchCache' , ['name' , 'display_name' , 'keywords' , 'summary' , 'description' ])
616
616
617
+
618
+ class CooperativeIterator :
619
+ """
620
+ Iterates over items cooperatively within the GLib main loop, yielding periodically to keep the UI responsive.
621
+ """
622
+ def __init__ (self , iterable , on_per_item , * , on_progress = None , on_finish = None , on_error = None , max_duration_ms = 16 , ** kwargs ):
623
+ self ._size = len (iterable ) if hasattr (iterable , '__len__' ) else None
624
+ self ._iterator = iter (iterable )
625
+ self ._on_per_item = on_per_item
626
+ self ._on_progress = on_progress
627
+ self ._on_finish = on_finish
628
+ self ._on_error = on_error
629
+ self ._kwargs = kwargs
630
+ self ._max_duration = max_duration_ms / 1000.0
631
+ self ._cancelled = False
632
+
633
+ def run (self ):
634
+ self ._start_time = time .monotonic ()
635
+ self ._current_index = 0
636
+ GLib .idle_add (self ._process )
637
+
638
+ def _process (self ):
639
+ if self ._cancelled :
640
+ return False
641
+
642
+ start_time = time .monotonic ()
643
+
644
+ try :
645
+ while not self ._cancelled :
646
+ item = next (self ._iterator )
647
+
648
+ self ._on_per_item (item , ** self ._kwargs )
649
+
650
+ self ._current_index += 1
651
+ if self ._on_progress and self ._size is not None :
652
+ self ._on_progress (self ._current_index / self ._size )
653
+
654
+ if (time .monotonic () - start_time ) >= self ._max_duration :
655
+ return True
656
+
657
+ except StopIteration :
658
+ if os .getenv ("DEBUG" , False ):
659
+ elapsed_time = time .monotonic () - self ._start_time
660
+ print (f"CooperativeIterator: Finished processing { self ._on_per_item .__name__ } in { elapsed_time :.3f} seconds." )
661
+
662
+ if self ._on_finish :
663
+ try :
664
+ self ._on_finish (** self ._kwargs )
665
+ except Exception as e :
666
+ print (f"CooperativeIterator: Error in on_finish: { e } " )
667
+ return False
668
+
669
+ except RuntimeError :
670
+ if self ._on_error :
671
+ try :
672
+ self ._on_error ()
673
+ except Exception as e :
674
+ print (f"CooperativeIterator: Error in on_error: { e } " )
675
+ return False
676
+
677
+ def cancel (self ):
678
+ self ._cancelled = True
679
+
617
680
class Application (Gtk .Application ):
618
681
(ACTION_TAB , PROGRESS_TAB , SPINNER_TAB ) = list (range (3 ))
619
682
@@ -665,7 +728,7 @@ def __init__(self):
665
728
self .one_package_idle_timer = 0
666
729
self .installer_pulse_timer = 0
667
730
self .search_changed_timer = 0
668
- self .search_idle_timer = 0
731
+ self .search_iterator = None
669
732
self .generate_search_cache_idle_timer = 0
670
733
671
734
self .action_button_signal_id = 0
@@ -2210,9 +2273,9 @@ def on_back_button_clicked(self, button):
2210
2273
self .go_back_action ()
2211
2274
2212
2275
def cancel_running_search (self ):
2213
- if self .search_idle_timer > 0 :
2214
- GLib . source_remove ( self .search_idle_timer )
2215
- self .search_idle_timer = 0
2276
+ if self .search_iterator :
2277
+ self .search_iterator . cancel ( )
2278
+ self .search_iterator = None
2216
2279
2217
2280
def go_back_action (self ):
2218
2281
XApp .set_window_progress (self .main_window , 0 )
@@ -2412,35 +2475,34 @@ def show_search_results(self, terms):
2412
2475
search_in_description = self .settings .get_boolean (prefs .SEARCH_IN_DESCRIPTION )
2413
2476
2414
2477
package_type_preference = self .settings .get_string (prefs .PACKAGE_TYPE_PREFERENCE )
2415
- hidden_packages = set ()
2416
2478
allow_unverified_flatpaks = self .settings .get_boolean (prefs .ALLOW_UNVERIFIED_FLATPAKS )
2417
2479
2418
- list_size = len (listing )
2419
- self .search_progress = 0
2420
-
2421
- def idle_search_one_package (pkginfos , list_size ):
2422
- try :
2423
- pkginfo = next (pkginfos )
2424
- except StopIteration :
2425
- self .search_idle_timer = 0
2426
- self .search_progress = 0
2480
+ def on_finish (list_size , searched_packages , hidden_packages , package_type_preference , ** kwargs ):
2481
+ self .search_iterator = None
2427
2482
2428
- if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2429
- results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2430
- elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2431
- results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2432
- else :
2433
- results = searched_packages
2483
+ if package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_APT :
2484
+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("f" ) and p .name in hidden_packages )]
2485
+ elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK :
2486
+ results = [p for p in searched_packages if not (p .pkg_hash .startswith ("a" ) and p .name in hidden_packages )]
2487
+ else :
2488
+ results = searched_packages
2489
+ self .on_search_results_complete (results )
2434
2490
2435
- GLib .idle_add (self .on_search_results_complete , results )
2436
- return False
2437
- except RuntimeError : # dictionary changed size during iteration
2438
- self .search_idle_timer = 0
2439
- self .search_progress = 0
2491
+ def on_error ():
2492
+ self .search_iterator = None
2440
2493
2441
- self .go_back_action ()
2442
- return False
2494
+ self .go_back_action ()
2443
2495
2496
+ def search_one_package (
2497
+ pkginfo ,
2498
+ list_size ,
2499
+ searched_packages ,
2500
+ hidden_packages ,
2501
+ allow_unverified_flatpaks ,
2502
+ package_type_preference ,
2503
+ search_in_summary ,
2504
+ search_in_description
2505
+ ):
2444
2506
flatpak = pkginfo .pkg_hash .startswith ("f" )
2445
2507
is_match = False
2446
2508
@@ -2487,21 +2549,29 @@ def idle_search_one_package(pkginfos, list_size):
2487
2549
elif package_type_preference == prefs .PACKAGE_TYPE_PREFERENCE_FLATPAK and flatpak :
2488
2550
hidden_packages .add (DEB_EQUIVS .get (pkginfo .name ))
2489
2551
2490
- self .search_progress = self .search_progress + 1
2491
- self .update_progress (self .search_progress / list_size )
2492
-
2493
- return True
2552
+ def on_progress (progress ):
2553
+ self .update_progress (progress )
2554
+
2555
+ self .search_iterator = CooperativeIterator (
2556
+ listing ,
2557
+ search_one_package ,
2558
+ on_finish = on_finish ,
2559
+ on_error = on_error ,
2560
+ on_progress = on_progress ,
2561
+ list_size = len (listing ),
2562
+ searched_packages = [],
2563
+ hidden_packages = set (),
2564
+ allow_unverified_flatpaks = allow_unverified_flatpaks ,
2565
+ package_type_preference = package_type_preference ,
2566
+ search_in_summary = search_in_summary ,
2567
+ search_in_description = search_in_description
2568
+ )
2569
+ self .search_iterator .run ()
2494
2570
2495
- self .search_idle_timer = GLib .idle_add (idle_search_one_package , iter (listing ), list_size )
2496
2571
2497
2572
def update_progress (self , progress ):
2498
2573
progress = max (0.0 , min (1.0 , progress ))
2499
-
2500
- def update_progress_ui ():
2501
- self .progress_bar .set_fraction (progress )
2502
- return False
2503
-
2504
- GLib .idle_add (update_progress_ui )
2574
+ self .progress_bar .set_fraction (progress )
2505
2575
2506
2576
def on_search_results_complete (self , results ):
2507
2577
self .page_stack .set_visible_child_name (self .PAGE_LIST )
0 commit comments