Add version sorting for plugins

Enhance plugin dialog with semver-aware sorting
by version, enabling users to sort plugins based
on version comparisons.
This commit is contained in:
Andrew
2026-07-06 13:01:21 +08:00
parent 714fe54f77
commit a11e442f1d
5 changed files with 52 additions and 2 deletions

View File

@@ -3,6 +3,8 @@
#include "PluginSource.hpp"
#include "PluginStatus.hpp"
#include "libslic3r/Semver.hpp"
#include <algorithm>
#include <cctype>
#include <string>
@@ -16,6 +18,7 @@ namespace Slic3r::GUI
Status,
Name,
Source,
Version,
// why: neutral "no column selected" state - clearing a header sort returns here and the
// list falls to compare_plugin_base_order only. Header UI reaches it via the asc/desc/clear cycle.
None
@@ -34,6 +37,7 @@ namespace Slic3r::GUI
case PluginSortKey::Status: return "status";
case PluginSortKey::Name: return "name";
case PluginSortKey::Source: return "source";
case PluginSortKey::Version: return "version";
case PluginSortKey::None: return "none";
}
@@ -53,6 +57,8 @@ namespace Slic3r::GUI
return PluginSortKey::Name;
if (sort_key == "source")
return PluginSortKey::Source;
if (sort_key == "version")
return PluginSortKey::Version;
if (sort_key == "none")
return PluginSortKey::None;
return fallback;
@@ -141,6 +147,23 @@ namespace Slic3r::GUI
return lhs.plugin_key.compare(rhs.plugin_key);
}
// Compares two version strings returning -1 / 0 / +1. Uses Slic3r::Semver (the same parser the
// plugin catalog's update-available check uses); on unparseable input falls back to the natural
// compare so the order stays deterministic.
// e.g. "1.2.0" < "1.10.0" (numeric), "1.0.0-rc1" < "1.0.0" (semver prerelease rule).
inline int compare_plugin_version(const std::string& lhs, const std::string& rhs)
{
const auto lhs_semver = Semver::parse(lhs);
const auto rhs_semver = Semver::parse(rhs);
if (lhs_semver && rhs_semver)
{
if (*lhs_semver < *rhs_semver) return -1;
if (*rhs_semver < *lhs_semver) return 1;
return 0;
}
return compare_ascii_case_insensitive_natural(lhs, rhs);
}
// Compares two items by the chosen primary key, returning -1 / 0 / +1. Status and Source
// rank by enum ordinal (the declared dialog priority); Name uses the natural compare above.
// e.g. Status: an enabled item (lower ordinal) sorts before a disabled one.
@@ -157,6 +180,8 @@ namespace Slic3r::GUI
return compare_ascii_case_insensitive_natural(lhs.display_name, rhs.display_name);
case PluginSortKey::Source:
return static_cast<int>(lhs.source) - static_cast<int>(rhs.source);
case PluginSortKey::Version:
return compare_plugin_version(lhs.sort_version, rhs.sort_version);
case PluginSortKey::None:
// why: no primary key - every pair ties here so sort_plugin_items_for_dialog falls
// straight to the ascending base order (direction is irrelevant for the baseline).

View File

@@ -89,6 +89,7 @@ struct PluginDialogItem
std::string version;
std::string installed_version;
std::string latest_version;
std::string sort_version; // Version shown in the row (installed if installed, else latest); used by the Version sort.
std::string type_label;
std::string type_key;
std::string sharing_token;
@@ -319,6 +320,9 @@ PluginDialogItem build_plugin_dialog_item(const PluginDescriptor& descriptor)
(descriptor.installed_version.empty() ? descriptor.version : descriptor.installed_version) :
std::string{};
item.latest_version = descriptor.latest_available_version();
// why: sort by the same version the row displays (GetDisplayVersion in index.js) - installed when
// installed, otherwise latest - so the Version sort matches what the user sees.
item.sort_version = item.installed_version.empty() ? item.latest_version : item.installed_version;
item.type_label = descriptor.type_label();
item.type_key = plugin_capability_type_to_string(descriptor.primary_capability_type());
// "types" is the display-only compatibility list. Cloud plugins show the raw labels the