From 2a8107c7a2bc1b7706f8aa3106fc6c442ee8d42c Mon Sep 17 00:00:00 2001 From: Leonhard Kargl Date: Thu, 20 Jun 2024 15:00:14 +0200 Subject: [PATCH] Prototype fast search --- src/Core/FlatpakBackend.vala | 24 +++++ src/Core/Package.vala | 27 ++++++ src/Core/SearchManager.vala | 32 +++++++ src/Views/SearchView.vala | 93 +++++++++++-------- .../AppContainers/ListPackageRowGrid.vala | 9 +- src/meson.build | 1 + 6 files changed, 144 insertions(+), 42 deletions(-) create mode 100644 src/Core/SearchManager.vala diff --git a/src/Core/FlatpakBackend.vala b/src/Core/FlatpakBackend.vala index 14e3cc839..4e593ca3f 100644 --- a/src/Core/FlatpakBackend.vala +++ b/src/Core/FlatpakBackend.vala @@ -440,7 +440,31 @@ public class AppCenterCore.FlatpakBackend : Object { return apps.values; } + public SearchManager get_search_manager () { + return new SearchManager (package_list.values.to_array ()); + } + + public Gee.Collection search_applications_fast (string query, AppStream.Category? category) { + warning ("SEARCHING FAST"); + var tokens = user_appstream_pool.build_search_tokens (query); + var packages = new ListStore (typeof (Package)); + var arr = package_list.values.to_array (); + packages.splice (0, 0, arr); + var filter = new Gtk.FilterListModel (packages, new Gtk.CustomFilter ((obj) => { + return ((Package) obj).matches_search (query) > 0; + })) { + incremental = true + }; + var result = new Gee.TreeSet (); + for (int i = 0; i < filter.n_items; i++) { + result.add ((Package) filter.get_item (i)); + } + warning ("SEARCHED FAST"); + return result; + } + public Gee.Collection search_applications (string query, AppStream.Category? category) { + return search_applications_fast (query, category); var results = new Gee.TreeSet (); var comps = user_appstream_pool.search (query); if (category == null) { diff --git a/src/Core/Package.vala b/src/Core/Package.vala index 3f9fb8751..30f16c997 100644 --- a/src/Core/Package.vala +++ b/src/Core/Package.vala @@ -766,6 +766,33 @@ public class AppCenterCore.Package : Object { } } + public uint matches_search (string search_term) { + return component.search_matches (search_term); + // if (search_term == "") { + // return true; + // } + + // var _search_term = search_term.down (); + + // if (_search_term in get_name ().down ()) { + // return true; + // } + + // if (_search_term in get_summary ().down ()) { + // return true; + // } + + // if (_search_term in get_description ().down ()) { + // return true; + // } + + // if (_search_term in component.get_keywords_table ()) { + // return true; + // } + + // return false; + } + private string convert_version (string version) { if (is_runtime_updates) { return version; diff --git a/src/Core/SearchManager.vala b/src/Core/SearchManager.vala new file mode 100644 index 000000000..96dd150eb --- /dev/null +++ b/src/Core/SearchManager.vala @@ -0,0 +1,32 @@ +public class AppCenterCore.SearchManager : Object { + private ListStore packages; + + public ListModel results { get; construct; } + + public string query { get; set; } + + public SearchManager (Package[] packages) { + this.packages.splice (0, 0, packages); + } + + construct { + packages = new ListStore (typeof (Package)); + + var filter_model = new Gtk.FilterListModel (packages, new Gtk.CustomFilter ((obj) => { + return ((Package) obj).matches_search (query) > 0; + })); + + var sort_model = new Gtk.SortListModel (filter_model, new Gtk.CustomSorter ((obj1, obj2) => { + var package1 = (Package) obj1; + var package2 = (Package) obj2; + return (int) (package2.matches_search (query) - package1.matches_search (query)); + })); + + results = sort_model; + } + + public void search (string query) { + this.query = query; + packages.items_changed (0, packages.n_items, packages.n_items); + } +} diff --git a/src/Views/SearchView.vala b/src/Views/SearchView.vala index 8a3274888..4c70ef361 100644 --- a/src/Views/SearchView.vala +++ b/src/Views/SearchView.vala @@ -27,7 +27,8 @@ public class AppCenter.SearchView : Adw.NavigationPage { public string search_term { get; construct; } public bool mimetype { get; set; default = false; } - private GLib.ListStore list_store; + private AppCenterCore.SearchManager search_manager; + private GLib.ListModel list_store; private Gtk.SearchEntry search_entry; private Granite.Placeholder alert_view; @@ -63,16 +64,29 @@ public class AppCenter.SearchView : Adw.NavigationPage { list_store = new GLib.ListStore (typeof (AppCenterCore.Package)); - var list_box = new Gtk.ListBox () { - activate_on_single_click = true, + search_manager = AppCenterCore.FlatpakBackend.get_default ().get_search_manager (); + + var selection_model = new Gtk.NoSelection (search_manager.results); + + var factory = new Gtk.SignalListItemFactory (); + factory.setup.connect ((obj) => { + var list_item = (Gtk.ListItem) obj; + list_item.child = new Widgets.ListPackageRowGrid (null); + }); + factory.bind.connect ((obj) => { + var list_item = (Gtk.ListItem) obj; + ((Widgets.ListPackageRowGrid) list_item.child).bind ((AppCenterCore.Package) list_item.item); + }); + + var list_view = new Gtk.ListView (selection_model, factory) { hexpand = true, vexpand = true }; - list_box.bind_model (list_store, create_row_from_package); - list_box.set_placeholder (alert_view); + // list_box.bind_model (list_store, create_row_from_package); + // list_box.set_placeholder (alert_view); var scrolled = new Gtk.ScrolledWindow () { - child = list_box, + child = list_view, hscrollbar_policy = Gtk.PolicyType.NEVER }; @@ -91,10 +105,8 @@ public class AppCenter.SearchView : Adw.NavigationPage { search_entry.grab_focus (); }); - list_box.row_activated.connect ((row) => { - if (row is Widgets.PackageRow) { - show_app (((Widgets.PackageRow) row).get_package ()); - } + list_view.activate.connect ((index) => { + show_app ((AppCenterCore.Package) list_store.get_item (index)); }); search_entry.search_changed.connect (search); @@ -114,33 +126,34 @@ public class AppCenter.SearchView : Adw.NavigationPage { } private void search () { - list_store.remove_all (); + search_manager.search (search_entry.text); + // list_store.remove_all (); - if (search_entry.text.length >= VALID_QUERY_LENGTH) { - var dyn_flathub_link = "%s".printf (search_entry.text, _("Flathub")); - alert_view.description = _("Try changing search terms. You can also sideload Flatpak apps e.g. from %s").printf (dyn_flathub_link); + // if (search_entry.text.length >= VALID_QUERY_LENGTH) { + // var dyn_flathub_link = "%s".printf (search_entry.text, _("Flathub")); + // alert_view.description = _("Try changing search terms. You can also sideload Flatpak apps e.g. from %s").printf (dyn_flathub_link); - unowned var flatpak_backend = AppCenterCore.FlatpakBackend.get_default (); + // unowned var flatpak_backend = AppCenterCore.FlatpakBackend.get_default (); - Gee.Collection found_apps; + // Gee.Collection found_apps; - if (mimetype) { - found_apps = flatpak_backend.search_applications_mime (search_entry.text); - add_packages (found_apps); - } else { - var category = update_category (); + // if (mimetype) { + // found_apps = flatpak_backend.search_applications_mime (search_entry.text); + // add_packages (found_apps); + // } else { + // var category = update_category (); - found_apps = flatpak_backend.search_applications (search_entry.text, category); - add_packages (found_apps); - } + // found_apps = flatpak_backend.search_applications (search_entry.text, category); + // add_packages (found_apps); + // } - } else { - alert_view.description = _("The search term must be at least 3 characters long."); - } + // } else { + // alert_view.description = _("The search term must be at least 3 characters long."); + // } - if (mimetype) { - mimetype = false; - } + // if (mimetype) { + // mimetype = false; + // } } private AppStream.Category? update_category () { @@ -158,16 +171,16 @@ public class AppCenter.SearchView : Adw.NavigationPage { } public void add_packages (Gee.Collection packages) { - foreach (var package in packages) { - // Don't show plugins or fonts in search and category views - if (package.kind != AppStream.ComponentKind.ADDON && package.kind != AppStream.ComponentKind.FONT) { - GLib.CompareDataFunc sort_fn = (a, b) => { - return compare_packages (a, b); - }; - - list_store.insert_sorted (package, sort_fn); - } - } + // foreach (var package in packages) { + // // Don't show plugins or fonts in search and category views + // if (package.kind != AppStream.ComponentKind.ADDON && package.kind != AppStream.ComponentKind.FONT) { + // GLib.CompareDataFunc sort_fn = (a, b) => { + // return compare_packages (a, b); + // }; + + // list_store.insert_sorted (package, sort_fn); + // } + // } } private Gtk.Widget create_row_from_package (Object object) { diff --git a/src/Widgets/AppContainers/ListPackageRowGrid.vala b/src/Widgets/AppContainers/ListPackageRowGrid.vala index c7bbfa5e3..51d269ccd 100644 --- a/src/Widgets/AppContainers/ListPackageRowGrid.vala +++ b/src/Widgets/AppContainers/ListPackageRowGrid.vala @@ -20,12 +20,12 @@ public class AppCenter.Widgets.ListPackageRowGrid : AbstractPackageRowGrid { private Gtk.Label package_summary; - public ListPackageRowGrid (AppCenterCore.Package package) { + public ListPackageRowGrid (AppCenterCore.Package? package) { Object (package: package); } construct { - var package_name = new Gtk.Label (package.get_name ()) { + package_name = new Gtk.Label (package.get_name ()) { ellipsize = Pango.EllipsizeMode.END, lines = 2, max_width_chars = 1, @@ -62,4 +62,9 @@ public class AppCenter.Widgets.ListPackageRowGrid : AbstractPackageRowGrid { append (grid); } + + public void bind (AppCenterCore.Package package) { + package_name.label = package.get_name (); + package_summary.label = package.get_summary (); + } } diff --git a/src/meson.build b/src/meson.build index 28d93cdce..faf79f764 100644 --- a/src/meson.build +++ b/src/meson.build @@ -12,6 +12,7 @@ appcenter_files = files( 'Core/Houston.vala', 'Core/Package.vala', 'Core/ScreenshotCache.vala', + 'Core/SearchManager.vala', 'Core/SoupClient.vala', 'Core/Stripe.vala', 'Core/UpdateManager.vala',