diff --git a/src/UniGetUI.Core.Classes/SortableObservableCollection.cs b/src/UniGetUI.Core.Classes/SortableObservableCollection.cs index 67492e6bc..37d187d36 100644 --- a/src/UniGetUI.Core.Classes/SortableObservableCollection.cs +++ b/src/UniGetUI.Core.Classes/SortableObservableCollection.cs @@ -14,22 +14,21 @@ public class SortableObservableCollection : ObservableCollection where T : protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { - if (!BlockSorting) - { - base.OnCollectionChanged(e); - if (SortingSelector is null - || e.Action == NotifyCollectionChangedAction.Remove - || e.Action == NotifyCollectionChangedAction.Reset) - { - return; - } - - Sort(); - } + if (BlockSorting) + return; + + base.OnCollectionChanged(e); + if (SortingSelector is null || e.Action is NotifyCollectionChangedAction.Remove or NotifyCollectionChangedAction.Reset) + return; + + Sort(); } public void Sort() { + if (BlockSorting) + return; + BlockSorting = true; if (SortingSelector is null) @@ -45,7 +44,7 @@ public void Sort() for (int i = 0; i < Count; i++) { - this[i].Index = i; + this.ElementAt(i).Index = i; } BlockSorting = false; diff --git a/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json b/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json index c4c60f64d..940c63b49 100644 --- a/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json +++ b/src/UniGetUI.Core.LanguageEngine/Assets/Data/LanguagesReference.json @@ -14,6 +14,7 @@ "es": "Spanish - Castellano", "fa": "Persian - فارسی‎", "fi": "Finnish - Suomi", + "fil": "Filipino - Filipino", "fr": "French - Français", "gu": "Gujarati - ગુજરાતી", "hi": "Hindi - हिंदी", diff --git a/src/UniGetUI.Core.Tools/Tools.cs b/src/UniGetUI.Core.Tools/Tools.cs index 88c5e0da3..29af99e62 100644 --- a/src/UniGetUI.Core.Tools/Tools.cs +++ b/src/UniGetUI.Core.Tools/Tools.cs @@ -209,6 +209,7 @@ public static string RandomString(int length) public static void ReportFatalException(Exception e) { + Debugger.Break(); string LangName = "Unknown"; try { diff --git a/src/UniGetUI.PackageEngine.PackageLoader/AbstractPackageLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/AbstractPackageLoader.cs index 8330c14b3..f0e2543a5 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/AbstractPackageLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/AbstractPackageLoader.cs @@ -4,6 +4,20 @@ namespace UniGetUI.PackageEngine.PackageLoader { + public class PackagesChangedEvent + { + public PackagesChangedEvent(bool proceduralChange, IReadOnlyList addedPackages, IReadOnlyList removedPackages) + { + ProceduralChange = proceduralChange; + AddedPackages = addedPackages; + RemovedPackages = removedPackages; + } + + public readonly bool ProceduralChange; + public readonly IReadOnlyList AddedPackages; + public readonly IReadOnlyList RemovedPackages; + } + public abstract class AbstractPackageLoader { /// @@ -34,7 +48,7 @@ public List Packages /// /// Fires when a block of packages (one package or more) is added or removed to the loader /// - public event EventHandler? PackagesChanged; + public event EventHandler? PackagesChanged; /// /// Fires when the loader finishes fetching packages @@ -85,9 +99,9 @@ public void StopLoading(bool emitFinishSignal = true) if (emitFinishSignal) InvokeFinishedLoadingEvent(); } - protected void InvokePackagesChangedEvent() + protected void InvokePackagesChangedEvent(bool proceduralChange, IReadOnlyList toAdd, IReadOnlyList toRemove) { - PackagesChanged?.Invoke(this, EventArgs.Empty); + PackagesChanged?.Invoke(this, new(proceduralChange, toAdd, toRemove)); } protected void InvokeStartedLoadingEvent() @@ -107,7 +121,7 @@ public virtual async Task ReloadPackages() { if (DISABLE_RELOAD) { - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(false, [], []); return; } @@ -156,7 +170,7 @@ public virtual async Task ReloadPackages() AddPackage(package); await WhenAddingPackage(package); } - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(true, task.Result.ToArray(), []); } tasks.Remove(task); } @@ -180,7 +194,7 @@ public void ClearPackages(bool emitFinishSignal = true) PackageReference.Clear(); IsLoaded = false; IsLoading = false; - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(false, [], []); } /// @@ -245,7 +259,7 @@ public void AddForeign(IPackage? package) } AddPackage(package); - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(true, [package], []); } /// @@ -264,7 +278,7 @@ public void Remove(IPackage? package) } PackageReference.Remove(HashPackage(package), out IPackage? _); - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(true, [], [package]); } /// diff --git a/src/UniGetUI.PackageEngine.PackageLoader/InstalledPackagesLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/InstalledPackagesLoader.cs index 18be7c511..9760c9daa 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/InstalledPackagesLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/InstalledPackagesLoader.cs @@ -82,7 +82,7 @@ public async Task ReloadPackagesSilently() await WhenAddingPackage(package); } } - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(true, task.Result.ToArray(), []); } tasks.Remove(task); } diff --git a/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs b/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs index a16135565..2540ecbb1 100644 --- a/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs +++ b/src/UniGetUI.PackageEngine.PackageLoader/PackageBundlesLoader.cs @@ -73,7 +73,7 @@ public async Task AddPackagesAsync(IReadOnlyList foreign_packages) } if (package is not null && !Contains(package)) AddPackage(package); } - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(true, foreign_packages, []); } public void RemoveRange(IReadOnlyList packages) @@ -83,7 +83,7 @@ public void RemoveRange(IReadOnlyList packages) if (!Contains(package)) continue; PackageReference.Remove(HashPackage(package), out IPackage? _); } - InvokePackagesChangedEvent(); + InvokePackagesChangedEvent(true, [], packages); } } } diff --git a/src/UniGetUI/CLIHandler.cs b/src/UniGetUI/CLIHandler.cs index cd534207d..6e24ef7da 100644 --- a/src/UniGetUI/CLIHandler.cs +++ b/src/UniGetUI/CLIHandler.cs @@ -8,6 +8,8 @@ public static class CLIHandler public const string HELP = "--help"; public const string DAEMON = "--daemon"; public const string MIGRATE_WINGETUI_TO_UNIGETUI = "--migrate-wingetui-to-unigetui"; + public const string UNINSTALL_WINGETUI = "--uninstall-wingetui"; + public const string UNINSTALL_UNIGETUI = "--uninstall-unigetui"; public const string IMPORT_SETTINGS = "--import-settings"; public const string EXPORT_SETTINGS = "--export-settings"; @@ -221,4 +223,10 @@ public static int WingetUIToUniGetUIMigrator() return ex.HResult; } } + + public static int UninstallUniGetUI() + { + // There is currently no uninstall logic. However, this needs to be maintained, or otherwhise UniGetUI will launch on uninstall + return 0; + } } diff --git a/src/UniGetUI/Controls/ObservablePackageCollection.cs b/src/UniGetUI/Controls/ObservablePackageCollection.cs index 41d717057..bf5f0179e 100644 --- a/src/UniGetUI/Controls/ObservablePackageCollection.cs +++ b/src/UniGetUI/Controls/ObservablePackageCollection.cs @@ -1,5 +1,4 @@ using UniGetUI.Core.Classes; -using UniGetUI.Interface; using UniGetUI.PackageEngine.Interfaces; namespace UniGetUI.PackageEngine.PackageClasses @@ -26,14 +25,22 @@ public ObservablePackageCollection() SortingSelector = x => x.Package.Name; } - /// - /// Add a package to the collection - /// - public void Add(IPackage p, AbstractPackagesPage page) + public void FromRange(IReadOnlyList packages) { - base.Add(new PackageWrapper(p, page)); + BlockSorting = true; + + // Clear the list + Clear(); + + // Add all packages + foreach (var w in packages) + Add(w); + + BlockSorting = false; + Sort(); } + /// /// Sets the property with which to filter the package and sorts the collection /// @@ -120,7 +127,7 @@ public void SelectAll() { foreach (PackageWrapper wrapper in this) { - wrapper.Package.IsChecked = true; + wrapper.IsChecked = true; } } @@ -131,7 +138,7 @@ public void ClearSelection() { foreach (PackageWrapper wrapper in this) { - wrapper.Package.IsChecked = false; + wrapper.IsChecked = false; } } } diff --git a/src/UniGetUI/Controls/PackageWrapper.cs b/src/UniGetUI/Controls/PackageWrapper.cs index 629f6eb91..81b481718 100644 --- a/src/UniGetUI/Controls/PackageWrapper.cs +++ b/src/UniGetUI/Controls/PackageWrapper.cs @@ -27,7 +27,11 @@ public static void ResetIconCache() public bool IsChecked { get => Package.IsChecked; - set => Package.IsChecked = value; + set + { + Package.IsChecked = value; + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(IsChecked))); + } } public bool IconWasLoaded; diff --git a/src/UniGetUI/EntryPoint.cs b/src/UniGetUI/EntryPoint.cs index 907001df6..2e19ee3b2 100644 --- a/src/UniGetUI/EntryPoint.cs +++ b/src/UniGetUI/EntryPoint.cs @@ -24,6 +24,11 @@ private static void Main(string[] args) int ret = CLIHandler.WingetUIToUniGetUIMigrator(); Environment.Exit(ret); } + else if (args.Contains(CLIHandler.UNINSTALL_UNIGETUI) || args.Contains(CLIHandler.UNINSTALL_WINGETUI)) + { + int ret = CLIHandler.UninstallUniGetUI(); + Environment.Exit(ret); + } else if (args.Contains(CLIHandler.IMPORT_SETTINGS)) { int ret = CLIHandler.ImportSettings(); diff --git a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs b/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs index b46822b09..f86fb9b84 100644 --- a/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs +++ b/src/UniGetUI/Pages/SoftwarePages/AbstractPackagesPage.xaml.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using System.Collections.ObjectModel; using System.Diagnostics; using Microsoft.UI.Input; using Microsoft.UI.Xaml; @@ -62,6 +63,63 @@ protected enum ReloadReason External } + static class FilterHelpers + { + public static string NormalizeCase(string input) + => input.ToLower(); + + public static string NormalizeSpecialCharacters(string input) + { + input = input.Replace("-", "").Replace("_", "").Replace(" ", "").Replace("@", "").Replace("\t", "").Replace(".", "").Replace(",", "").Replace(":", ""); + foreach (KeyValuePair entry in new Dictionary + { + {'a', "àáäâ"}, + {'e', "èéëê"}, + {'i', "ìíïî"}, + {'o', "òóöô"}, + {'u', "ùúüû"}, + {'y', "ýÿ"}, + {'c', "ç"}, + {'ñ', "n"}, + }) + { + foreach (char InvalidChar in entry.Value) + { + input = input.Replace(InvalidChar, entry.Key); + } + } + return input; + } + + public static bool NameContains(IPackage pkg, string query, List> filters) + { + string treatedName = pkg.Name; + foreach (var filter in filters) treatedName = filter(treatedName); + return treatedName.Contains(query); + } + + public static bool IdContains(IPackage pkg, string query, List> filters) + { + string treatedId = pkg.Id; + foreach (var filter in filters) treatedId = filter(treatedId); + return treatedId.Contains(query); + } + + public static bool NameOrIdContains(IPackage pkg, string query, List> filters) + => NameContains(pkg, query, filters) || IdContains(pkg, query, filters); + + public static bool NameOrIdExactMatch(IPackage pkg, string query, List> filters) + { + string treatedId = pkg.Id; + foreach (var filter in filters) treatedId = filter(treatedId); + if (query == treatedId) return true; + + string treatedName = pkg.Name; + foreach (var filter in filters) treatedName = filter(treatedName); + return query == treatedName; + } + } + protected readonly bool DISABLE_AUTOMATIC_PACKAGE_LOAD_ON_START; protected readonly bool MEGA_QUERY_BOX_ENABLED; protected readonly bool SHOW_LAST_CHECKED_TIME; @@ -87,12 +145,17 @@ protected ItemsView CurrentPackageList } protected AbstractPackageLoader Loader; - public ObservablePackageCollection FilteredPackages = []; + + public readonly ObservablePackageCollection FilteredPackages = []; + private readonly ObservableCollection WrappedPackages = []; + private IEnumerable? LastQueryResult; + + protected List UsedManagers = []; protected ConcurrentDictionary> UsedSourcesForManager = []; protected ConcurrentDictionary RootNodeForManager = []; protected ConcurrentDictionary NodesForSources = []; - private readonly TreeViewNode LocalPackagesNode; + private readonly TreeViewNode LocalPackagesNode = new(); public readonly int NewVersionLabelWidth; public readonly int NewVersionIconWidth; @@ -171,6 +234,9 @@ protected AbstractPackagesPage(PackagesPageData data) Loader.FinishedLoading += Loader_FinishedLoading; Loader.PackagesChanged += Loader_PackagesChanged; + // Clear cached filtering result + WrappedPackages.CollectionChanged += (_, _) => LastQueryResult = null; + if (Loader.IsLoading) { Loader_StartedLoading(this, EventArgs.Empty); @@ -180,13 +246,11 @@ protected AbstractPackagesPage(PackagesPageData data) Loader_FinishedLoading(this, EventArgs.Empty); FilterPackages(); } + Loader_PackagesChanged(this, new(false, [], [])); LastPackageLoadTime = DateTime.Now; - LocalPackagesNode = new TreeViewNode - { - Content = CoreTools.Translate("Local"), - IsExpanded = false - }; + LocalPackagesNode.Content = CoreTools.Translate("Local"); + LocalPackagesNode.IsExpanded = false; ReloadButton.Click += async (_, _) => await LoadPackages(); @@ -194,7 +258,7 @@ protected AbstractPackagesPage(PackagesPageData data) FindButton.Click += (_, _) => { MegaQueryBlockGrid.Visibility = Visibility.Collapsed; - FilterPackages(); + FilterPackages(true); }; // Handle Enter pressed on the QueryBlock @@ -207,7 +271,7 @@ protected AbstractPackagesPage(PackagesPageData data) MegaQueryBlockGrid.Visibility = Visibility.Collapsed; if (!DISABLE_FILTER_ON_QUERY_CHANGE) - FilterPackages(); + FilterPackages(true); }; // Handle showing the MegaQueryBlock @@ -216,7 +280,7 @@ protected AbstractPackagesPage(PackagesPageData data) if (InstantSearchCheckbox.IsChecked == true) { if (!DISABLE_FILTER_ON_QUERY_CHANGE) - FilterPackages(); + FilterPackages(true); } if (MEGA_QUERY_BOX_ENABLED && QueryBlock.Text.Trim() == "") @@ -224,8 +288,9 @@ protected AbstractPackagesPage(PackagesPageData data) MegaQueryBlockGrid.Visibility = Visibility.Visible; Loader.StopLoading(); BackgroundText.Visibility = Visibility.Collapsed; - ClearPackageList(); - UpdatePackageCount(); + ClearSourcesList(); + WrappedPackages.Clear(); + FilterPackages(true); MegaQueryBlock.Focus(FocusState.Programmatic); MegaQueryBlock.Text = ""; } @@ -242,7 +307,7 @@ protected AbstractPackagesPage(PackagesPageData data) MegaQueryBlockGrid.Visibility = Visibility.Collapsed; QueryBlock.Text = MegaQueryBlock.Text.Trim(); if (!DISABLE_FILTER_ON_QUERY_CHANGE) - FilterPackages(); + FilterPackages(true); }; // Hande the MegaQueryBlock search button click @@ -250,7 +315,7 @@ protected AbstractPackagesPage(PackagesPageData data) { MegaQueryBlockGrid.Visibility = Visibility.Collapsed; QueryBlock.Text = MegaQueryBlock.Text.Trim(); - FilterPackages(); + FilterPackages(true); }; // Handle when a source is clicked @@ -332,30 +397,47 @@ protected AbstractPackagesPage(PackagesPageData data) UpdateSortingMenu(); } - private void Loader_PackagesChanged(object? sender, EventArgs e) + private void Loader_PackagesChanged(object? sender, PackagesChangedEvent packagesChangedEvent) { // Ensure we are in the UI thread if (Microsoft.UI.Dispatching.DispatcherQueue.GetForCurrentThread() is null) { - DispatcherQueue.TryEnqueue(() => Loader_PackagesChanged(sender, e)); + DispatcherQueue.TryEnqueue(() => Loader_PackagesChanged(sender, packagesChangedEvent)); return; } - - if (Loader.Count() == 0) + // Procedural package upgrade + if (packagesChangedEvent.ProceduralChange) { - ClearPackageList(); + // Add added packages + foreach (var package in packagesChangedEvent.AddedPackages) + { + if (WrappedPackages.Where(w => w.Package.Equals(package)).Any()) + continue; + + WrappedPackages.Add(new PackageWrapper(package, this)); + AddPackageToSourcesList(package); + } + + // Remove removed packages + foreach (var package in packagesChangedEvent.RemovedPackages) + foreach (var match in WrappedPackages.Where(w => w.Package.Equals(package))) + { + WrappedPackages.Remove(match); + } } else { - foreach (IPackage package in Loader.Packages) + // Reset internal package cache, and update from loader + WrappedPackages.Clear(); + ClearSourcesList(); + foreach (var package in Loader.Packages) { + WrappedPackages.Add(new PackageWrapper(package, this)); AddPackageToSourcesList(package); } } FilterPackages(); - if (!Settings.Get("DisableIconsOnPackageLists")) - _ = LoadIconsForNewPackages(); } @@ -371,7 +453,6 @@ private void Loader_FinishedLoading(object? sender, EventArgs e) LoadingProgressBar.Visibility = Visibility.Collapsed; LastPackageLoadTime = DateTime.Now; WhenPackagesLoaded(ReloadReason.External); - FilterPackages(); } private void Loader_StartedLoading(object? sender, EventArgs e) @@ -469,26 +550,26 @@ private void FilterOptionsChanged(object sender, RoutedEventArgs e) return; } - FilterPackages(); + FilterPackages(true); } private void InstantSearchValueChanged(object sender, RoutedEventArgs e) - { Settings.SetDictionaryItem("DisableInstantSearch", PAGE_NAME, !InstantSearchCheckbox.IsChecked); } + => Settings.SetDictionaryItem("DisableInstantSearch", PAGE_NAME, !InstantSearchCheckbox.IsChecked); + private void SourcesTreeView_SelectionChanged(TreeView sender, TreeViewSelectionChangedEventArgs args) - { FilterPackages(); } + => FilterPackages(); public virtual async Task LoadPackages() - { await LoadPackages(ReloadReason.External); } + => await LoadPackages(ReloadReason.External); - protected void ClearPackageList() + protected void ClearSourcesList() { - FilteredPackages.Clear(); UsedManagers.Clear(); - SourcesTreeView.RootNodes.Clear(); + SourcesTreeView?.RootNodes?.Clear(); UsedSourcesForManager.Clear(); RootNodeForManager.Clear(); NodesForSources.Clear(); - LocalPackagesNode.Children.Clear(); + LocalPackagesNode?.Children?.Clear(); } /// @@ -502,7 +583,6 @@ protected async Task LoadPackages(ReloadReason reason) Loader.ClearPackages(emitFinishSignal: false); await Loader.ReloadPackages(); } - Loader_PackagesChanged(this, EventArgs.Empty); } private void SelectAndScrollTo(int index, bool focus) @@ -664,13 +744,13 @@ public void PackageList_CharacterReceived(object sender, CharacterReceivedRouted /// Will filter the packages with the query on QueryBlock.Text and put the /// resulting packages on the ItemsView /// - public void FilterPackages() + + public void FilterPackages(bool forceQueryUpdate = false) { - PackageWrapper? previousSelection = CurrentPackageList.SelectedItem as PackageWrapper; - FilteredPackages.Clear(); + var previousSelection = CurrentPackageList.SelectedItem as PackageWrapper; - List VisibleSources = []; - List VisibleManagers = []; + List visibleSources = []; + List visibleManagers = []; if (SourcesTreeView.SelectedNodes.Count > 0) { @@ -678,99 +758,64 @@ public void FilterPackages() { if (NodesForSources.Values.Contains(node)) { - VisibleSources.Add(NodesForSources.First(x => x.Value == node).Key); + visibleSources.Add(NodesForSources.First(x => x.Value == node).Key); } else if (RootNodeForManager.Values.Contains(node)) { IPackageManager manager = RootNodeForManager.First(x => x.Value == node).Key; - VisibleManagers.Add(manager); - if (manager.Capabilities.SupportsCustomSources) - { - foreach (IManagerSource source in manager.SourcesHelper.Factory.GetAvailableSources()) - { - if (!VisibleSources.Contains(source)) VisibleSources.Add(source); - } - } + visibleManagers.Add(manager); + if (!manager.Capabilities.SupportsCustomSources) + continue; + + foreach (IManagerSource source in manager.SourcesHelper.Factory.GetAvailableSources()) + if (!visibleSources.Contains(source)) + visibleSources.Add(source); } } } - Func CaseFunc; - if (UpperLowerCaseCheckbox.IsChecked == true) - { - CaseFunc = (x) => { return x; }; - } - else + // Filter only by query when needed + if (forceQueryUpdate || LastQueryResult is null) { - CaseFunc = (x) => { return x.ToLower(); }; - } + // Load applied filters and prepare query + List> appliedFilters = []; + if (UpperLowerCaseCheckbox.IsChecked is false) appliedFilters.Add(FilterHelpers.NormalizeCase); + if (IgnoreSpecialCharsCheckbox.IsChecked is true) + appliedFilters.Add(FilterHelpers.NormalizeSpecialCharacters); - Func CharsFunc; - if (IgnoreSpecialCharsCheckbox.IsChecked == true) - { - CharsFunc = (x) => - { - string temp_x = CaseFunc(x).Replace("-", "").Replace("_", "").Replace(" ", "").Replace("@", "").Replace("\t", "").Replace(".", "").Replace(",", "").Replace(":", ""); - foreach (KeyValuePair entry in new Dictionary - { - {'a', "àáäâ"}, - {'e', "èéëê"}, - {'i', "ìíïî"}, - {'o', "òóöô"}, - {'u', "ùúüû"}, - {'y', "ýÿ"}, - {'c', "ç"}, - {'ñ', "n"}, - }) - { - foreach (char InvalidChar in entry.Value) - { - x = x.Replace(InvalidChar, entry.Key); - } - } - return temp_x; - }; - } - else - { - CharsFunc = (x) => { return CaseFunc(x); }; - } - - string treatedQuery = CharsFunc(QueryBlock.Text.Trim()); - IEnumerable MatchingList; + string treatedQuery = QueryBlock.Text.Trim(); + foreach (var filter in appliedFilters) treatedQuery = filter(treatedQuery); + // treatedQuery now has the appropiate content - if (QueryIdRadio.IsChecked == true) - { - MatchingList = Loader.Packages.Where(x => CharsFunc(x.Name).Contains(treatedQuery)); - } - else if (QueryNameRadio.IsChecked == true) - { - MatchingList = Loader.Packages.Where(x => CharsFunc(x.Id).Contains(treatedQuery)); - } - else if (QueryBothRadio.IsChecked == true) - { - MatchingList = Loader.Packages.Where(x => CharsFunc(x.Name).Contains(treatedQuery) | CharsFunc(x.Id).Contains(treatedQuery)); - } - else if (QueryExactMatch.IsChecked == true) - { - MatchingList = Loader.Packages.Where(x => CharsFunc(x.Name) == treatedQuery | CharsFunc(x.Id) == treatedQuery); - } - else // QuerySimilarResultsRadio == true - { - MatchingList = Loader.Packages; + if (QueryIdRadio.IsChecked is true) + LastQueryResult = WrappedPackages.Where(wrapper => + FilterHelpers.NameContains(wrapper.Package, treatedQuery, appliedFilters)); + else if (QueryNameRadio.IsChecked is true) + LastQueryResult = WrappedPackages.Where(wrapper => + FilterHelpers.IdContains(wrapper.Package, treatedQuery, appliedFilters)); + else if (QueryBothRadio.IsChecked is true) + LastQueryResult = WrappedPackages.Where(wrapper => + FilterHelpers.NameOrIdContains(wrapper.Package, treatedQuery, appliedFilters)); + else if (QueryExactMatch.IsChecked == true) + LastQueryResult = WrappedPackages.Where(wrapper => + FilterHelpers.NameOrIdExactMatch(wrapper.Package, treatedQuery, appliedFilters)); + else // QuerySimilarResultsRadio == true + LastQueryResult = WrappedPackages; } - FilteredPackages.BlockSorting = true; + List matchingList_selectedSources = []; - foreach (IPackage match in MatchingList) + foreach (var match in LastQueryResult) { - if (VisibleSources.Contains(match.Source) || (!match.Manager.Capabilities.SupportsCustomSources && VisibleManagers.Contains(match.Manager))) + if (visibleSources.Contains(match.Package.Source) || + (!match.Package.Manager.Capabilities.SupportsCustomSources && + visibleManagers.Contains(match.Package.Manager))) { - FilteredPackages.Add(match, this); + matchingList_selectedSources.Add(match); } } - FilteredPackages.BlockSorting = false; - FilteredPackages.Sort(); + + FilteredPackages.FromRange(matchingList_selectedSources); UpdatePackageCount(); if (previousSelection is not null) @@ -789,6 +834,8 @@ public void FilterPackages() ForceRedrawByScroll(); } + if (!Settings.Get("DisableIconsOnPackageLists")) + _ = LoadIconsForNewPackages(); } /// @@ -1133,8 +1180,6 @@ private async Task LoadIconsForNewPackages() var icon = await Task.Run(wrapper.Package.GetIconUrlIfAny); if (icon is not null) wrapper.PackageIcon = icon; } - - FilterPackages(); } public void OnEnter() @@ -1214,7 +1259,6 @@ public void PackageItemContainer_PreviewKeyDown(object sender, KeyRoutedEventArg } } - private async void SetFilterMode_Overlay() { if (_filterPanelCurrentMode == SplitViewDisplayMode.Overlay)