Add new default 'search score' sorting method for plugin search (#1882)

This commit is contained in:
nebel 2024-07-04 01:37:25 +09:00 committed by GitHub
parent a4c234c4fa
commit 583c007253
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -124,6 +124,7 @@ internal class PluginInstallerWindow : Window, IDisposable
private PluginSortKind sortKind = PluginSortKind.Alphabetical; private PluginSortKind sortKind = PluginSortKind.Alphabetical;
private string filterText = Locs.SortBy_Alphabetical; private string filterText = Locs.SortBy_Alphabetical;
private bool adaptiveSort = true;
private OperationStatus installStatus = OperationStatus.Idle; private OperationStatus installStatus = OperationStatus.Idle;
private OperationStatus updateStatus = OperationStatus.Idle; private OperationStatus updateStatus = OperationStatus.Idle;
@ -208,6 +209,7 @@ internal class PluginInstallerWindow : Window, IDisposable
NotInstalled, NotInstalled,
EnabledDisabled, EnabledDisabled,
ProfileOrNot, ProfileOrNot,
SearchScore,
} }
[Flags] [Flags]
@ -280,6 +282,7 @@ internal class PluginInstallerWindow : Window, IDisposable
if (!this.isSearchTextPrefilled) this.searchText = string.Empty; if (!this.isSearchTextPrefilled) this.searchText = string.Empty;
this.sortKind = PluginSortKind.Alphabetical; this.sortKind = PluginSortKind.Alphabetical;
this.filterText = Locs.SortBy_Alphabetical; this.filterText = Locs.SortBy_Alphabetical;
this.adaptiveSort = true;
if (this.updateStatus == OperationStatus.Complete || this.updateStatus == OperationStatus.Idle) if (this.updateStatus == OperationStatus.Complete || this.updateStatus == OperationStatus.Idle)
{ {
@ -599,6 +602,7 @@ internal class PluginInstallerWindow : Window, IDisposable
var sortByTextWidth = ImGui.CalcTextSize(sortByText).X; var sortByTextWidth = ImGui.CalcTextSize(sortByText).X;
var sortSelectables = new (string Localization, PluginSortKind SortKind)[] var sortSelectables = new (string Localization, PluginSortKind SortKind)[]
{ {
(Locs.SortBy_SearchScore, PluginSortKind.SearchScore),
(Locs.SortBy_Alphabetical, PluginSortKind.Alphabetical), (Locs.SortBy_Alphabetical, PluginSortKind.Alphabetical),
(Locs.SortBy_DownloadCounts, PluginSortKind.DownloadCount), (Locs.SortBy_DownloadCounts, PluginSortKind.DownloadCount),
(Locs.SortBy_LastUpdate, PluginSortKind.LastUpdate), (Locs.SortBy_LastUpdate, PluginSortKind.LastUpdate),
@ -651,7 +655,29 @@ internal class PluginInstallerWindow : Window, IDisposable
} }
if (searchTextChanged) if (searchTextChanged)
{
if (this.adaptiveSort)
{
if (string.IsNullOrWhiteSpace(this.searchText))
{
this.sortKind = PluginSortKind.Alphabetical;
this.filterText = Locs.SortBy_Alphabetical;
}
else
{
this.sortKind = PluginSortKind.SearchScore;
this.filterText = Locs.SortBy_SearchScore;
}
this.ResortPlugins();
}
else if (this.sortKind == PluginSortKind.SearchScore)
{
this.ResortPlugins();
}
this.UpdateCategoriesOnSearchChange(prevSearchText); this.UpdateCategoriesOnSearchChange(prevSearchText);
}
} }
// Disable sort if changelogs or profile editor // Disable sort if changelogs or profile editor
@ -664,10 +690,14 @@ internal class PluginInstallerWindow : Window, IDisposable
{ {
foreach (var selectable in sortSelectables) foreach (var selectable in sortSelectables)
{ {
if (selectable.SortKind == PluginSortKind.SearchScore && string.IsNullOrWhiteSpace(this.searchText))
continue;
if (ImGui.Selectable(selectable.Localization)) if (ImGui.Selectable(selectable.Localization))
{ {
this.sortKind = selectable.SortKind; this.sortKind = selectable.SortKind;
this.filterText = selectable.Localization; this.filterText = selectable.Localization;
this.adaptiveSort = false;
lock (this.listLock) lock (this.listLock)
{ {
@ -3492,8 +3522,6 @@ internal class PluginInstallerWindow : Window, IDisposable
private bool IsManifestFiltered(IPluginManifest manifest) private bool IsManifestFiltered(IPluginManifest manifest)
{ {
var searchString = this.searchText.ToLowerInvariant();
var matcher = new FuzzyMatcher(searchString, MatchMode.FuzzyParts);
var hasSearchString = !string.IsNullOrWhiteSpace(this.searchText); var hasSearchString = !string.IsNullOrWhiteSpace(this.searchText);
var oldApi = (manifest.TestingDalamudApiLevel == null var oldApi = (manifest.TestingDalamudApiLevel == null
|| manifest.TestingDalamudApiLevel < PluginManager.DalamudApiLevel) || manifest.TestingDalamudApiLevel < PluginManager.DalamudApiLevel)
@ -3503,12 +3531,30 @@ internal class PluginInstallerWindow : Window, IDisposable
if (oldApi && !hasSearchString && !installed) if (oldApi && !hasSearchString && !installed)
return true; return true;
return hasSearchString && !( if (!hasSearchString)
(!manifest.Name.IsNullOrEmpty() && matcher.Matches(manifest.Name.ToLowerInvariant()) > 0) || return false;
(!manifest.InternalName.IsNullOrEmpty() && matcher.Matches(manifest.InternalName.ToLowerInvariant()) > 0) ||
(!manifest.Author.IsNullOrEmpty() && matcher.Matches(manifest.Author.ToLowerInvariant()) > 0) || return this.GetManifestSearchScore(manifest) < 1;
(!manifest.Punchline.IsNullOrEmpty() && manifest.Punchline.ToLowerInvariant().Contains(searchString)) || }
(manifest.Tags != null && matcher.MatchesAny(manifest.Tags.Select(term => term.ToLowerInvariant()).ToArray()) > 0));
private int GetManifestSearchScore(IPluginManifest manifest)
{
var searchString = this.searchText.ToLowerInvariant();
var matcher = new FuzzyMatcher(searchString, MatchMode.FuzzyParts);
var scores = new List<int> { 0 };
if (!manifest.Name.IsNullOrEmpty())
scores.Add(matcher.Matches(manifest.Name.ToLowerInvariant()) * 110);
if (!manifest.InternalName.IsNullOrEmpty())
scores.Add(matcher.Matches(manifest.InternalName.ToLowerInvariant()) * 105);
if (!manifest.Author.IsNullOrEmpty())
scores.Add(matcher.Matches(manifest.Author.ToLowerInvariant()) * 100);
if (!manifest.Punchline.IsNullOrEmpty())
scores.Add(matcher.Matches(manifest.Punchline.ToLowerInvariant()) * 100);
if (manifest.Tags != null)
scores.Add(matcher.MatchesAny(manifest.Tags.ToArray()) * 100);
return scores.Max();
} }
private (bool IsInstalled, LocalPlugin Plugin) IsManifestInstalled(IPluginManifest? manifest) private (bool IsInstalled, LocalPlugin Plugin) IsManifestInstalled(IPluginManifest? manifest)
@ -3609,6 +3655,10 @@ internal class PluginInstallerWindow : Window, IDisposable
var profman = Service<ProfileManager>.Get(); var profman = Service<ProfileManager>.Get();
this.pluginListInstalled.Sort((p1, p2) => profman.IsInDefaultProfile(p1.EffectiveWorkingPluginId).CompareTo(profman.IsInDefaultProfile(p2.EffectiveWorkingPluginId))); this.pluginListInstalled.Sort((p1, p2) => profman.IsInDefaultProfile(p1.EffectiveWorkingPluginId).CompareTo(profman.IsInDefaultProfile(p2.EffectiveWorkingPluginId)));
break; break;
case PluginSortKind.SearchScore:
this.pluginListAvailable = this.pluginListAvailable.OrderByDescending(this.GetManifestSearchScore).ThenBy(m => m.Name).ToList();
this.pluginListInstalled = this.pluginListInstalled.OrderByDescending(p => this.GetManifestSearchScore(p.Manifest)).ThenBy(m => m.Name).ToList();
break;
default: default:
throw new InvalidEnumArgumentException("Unknown plugin sort type."); throw new InvalidEnumArgumentException("Unknown plugin sort type.");
} }
@ -3754,6 +3804,8 @@ internal class PluginInstallerWindow : Window, IDisposable
#region SortBy #region SortBy
public static string SortBy_SearchScore => Loc.Localize("InstallerSearchScore", "Search score");
public static string SortBy_Alphabetical => Loc.Localize("InstallerAlphabetical", "Alphabetical"); public static string SortBy_Alphabetical => Loc.Localize("InstallerAlphabetical", "Alphabetical");
public static string SortBy_DownloadCounts => Loc.Localize("InstallerDownloadCount", "Download Count"); public static string SortBy_DownloadCounts => Loc.Localize("InstallerDownloadCount", "Download Count");