Filtering by tags in the add-on manager

The existing PblWML specification includes a comma-separated list of tags.
Until now there are no official tag names, but some of the 1.14 add-ons already
have some unofficial ones. If this feature is included in 1.16 then I assume
many UMC authors will use the newly-official tags.

For a larger add-on list than the dev server has, use port 15014 which will
connect to the 1.14 server instead (in the connect dialog change the address to
addons.wesnoth.org:15014).
This commit is contained in:
Steve Cotton 2021-05-07 16:24:05 +02:00 committed by Steve Cotton
parent bf318a6e23
commit f7f58613f6
4 changed files with 112 additions and 0 deletions

View file

@ -1,6 +1,7 @@
## Version 1.15.12+dev
### Add-ons client
* The details panel now shows the list of tags in each add-on.
* Added a filter based on tags.
### Add-ons server
### Campaigns
### Editor

View file

@ -710,6 +710,32 @@
[/multimenu_button]
[/column]
[column]
grow_factor = 0
border = "all"
border_size = 5
horizontal_alignment = "left"
[label]
definition = "default"
label = _ "Tags:"
[/label]
[/column]
[column]
grow_factor = 0
border = "all"
border_size = 5
horizontal_alignment = "left"
[multimenu_button]
id = "tag_filter"
definition = "default"
[/multimenu_button]
[/column]
[column]
grow_factor = 0
border = "all"

View file

@ -206,6 +206,40 @@ const std::vector<addon_manager::addon_order> addon_manager::all_orders_{
[](const addon_info& a, const addon_info& b) { return a.created > b.created; }}
};
namespace
{
struct addon_tag
{
/** Text to match against addon_info.tags() */
std::string id;
/** What to show in the filter's drop-down list */
std::string label;
/** Shown when hovering over an entry in the filter's drop-down list */
std::string tooltip;
};
const std::vector<addon_tag> tag_filter_types_{
{"cooperative", N_("addon_tag^Cooperative"),
// TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
N_("addon_tag^All human players are on the same team, versus the AI")},
{"cosmetic", N_("addon_tag^Cosmetic"),
// TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
N_("addon_tag^These make the game look different, without changing gameplay")},
{"difficulty", N_("addon_tag^Difficulty"),
// TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
N_("addon_tag^Can make campaigns easier or harder")},
{"rng", N_("addon_tag^RNG"),
// TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
N_("addon_tag^Modify the randomness in the combat mechanics, or remove it entirely")},
{"survival", N_("addon_tag^Survival"),
// TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
N_("addon_tag^Fight against waves of enemies")},
{"terraforming", N_("addon_tag^Terraforming"),
// TRANSLATORS: tooltip in the drop-down menu for filtering add-ons
N_("addon_tag^Players can change the terrain")},
};
};
addon_manager::addon_manager(addons_client& client)
: orders_()
, cfg_()
@ -309,6 +343,22 @@ void addon_manager::pre_show(window& window)
connect_signal_notify_modified(status_filter,
std::bind(&addon_manager::apply_filters, this));
// The tag filter
auto& tag_filter = find_widget<multimenu_button>(&window, "tag_filter", false);
std::vector<config> tag_filter_entries;
for(const auto& f : tag_filter_types_) {
tag_filter_entries.emplace_back("label", t_string(f.label, GETTEXT_DOMAIN), "checkbox", false);
if(!f.tooltip.empty()) {
tag_filter_entries.back()["tooltip"] = t_string(f.tooltip, GETTEXT_DOMAIN);
}
}
tag_filter.set_values(tag_filter_entries);
connect_signal_notify_modified(tag_filter, std::bind(&addon_manager::apply_filters, this));
// The type filter
multimenu_button& type_filter = find_widget<multimenu_button>(&window, "type_filter", false);
std::vector<config> type_filter_entries;
@ -321,6 +371,7 @@ void addon_manager::pre_show(window& window)
connect_signal_notify_modified(type_filter,
std::bind(&addon_manager::apply_filters, this));
// Sorting order
menu_button& order_dropdown = find_widget<menu_button>(&window, "order_dropdown", false);
std::vector<config> order_dropdown_entries;
@ -555,6 +606,38 @@ boost::dynamic_bitset<> addon_manager::get_status_filter_visibility() const
return res;
}
boost::dynamic_bitset<> addon_manager::get_tag_filter_visibility() const
{
const auto& tag_filter = find_widget<const multimenu_button>(get_window(), "tag_filter", false);
const auto toggle_states = tag_filter.get_toggle_states();
if(toggle_states.none()) {
// Nothing selected. It means that all add-ons are shown.
boost::dynamic_bitset<> res_flipped(addons_.size());
return ~res_flipped;
}
std::vector<std::string> selected_tags;
for(std::size_t i = 0; i < tag_filter_types_.size(); ++i) {
if(toggle_states[i]) {
selected_tags.push_back(tag_filter_types_[i].id);
}
}
boost::dynamic_bitset<> res;
for(const auto& a : addons_) {
bool matched_tag = false;
for(const auto& id : selected_tags) {
if(utils::contains(a.second.tags, id)) {
matched_tag = true;
break;
}
}
res.push_back(matched_tag);
}
return res;
}
boost::dynamic_bitset<> addon_manager::get_type_filter_visibility() const
{
const multimenu_button& type_filter = find_widget<const multimenu_button>(get_window(), "type_filter", false);
@ -585,6 +668,7 @@ void addon_manager::apply_filters()
{
boost::dynamic_bitset<> res =
get_status_filter_visibility()
& get_tag_filter_visibility()
& get_type_filter_visibility()
& get_name_filter_visibility();
find_widget<addon_list>(get_window(), "addons", false).set_addon_shown(res);

View file

@ -156,6 +156,7 @@ private:
boost::dynamic_bitset<> get_name_filter_visibility() const;
boost::dynamic_bitset<> get_status_filter_visibility() const;
boost::dynamic_bitset<> get_tag_filter_visibility() const;
boost::dynamic_bitset<> get_type_filter_visibility() const;
void on_selected_version_change();