GUI2: simplify various dialog's filter handling (#9650)
Closes #7868. This implements a farther-reaching cleanup without the regex, though I might revisit the regex method again later.
This commit is contained in:
parent
f113b2b7a7
commit
e037694945
12 changed files with 88 additions and 196 deletions
|
@ -39,6 +39,7 @@
|
|||
#include "serialization/string_utils.hpp"
|
||||
#include "serialization/markup.hpp"
|
||||
#include "utils/general.hpp"
|
||||
#include "utils/ci_searcher.hpp"
|
||||
#include "game_config_view.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
@ -86,7 +87,6 @@ game_load::game_load(const game_config_view& cache_config, savegame::load_game_m
|
|||
, summary_(data.summary)
|
||||
, games_()
|
||||
, cache_config_(cache_config)
|
||||
, last_words_()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -97,7 +97,7 @@ void game_load::pre_show()
|
|||
|
||||
text_box* filter = find_widget<text_box>("txtFilter", false, true);
|
||||
|
||||
filter->set_text_changed_callback(std::bind(&game_load::filter_text_changed, this, std::placeholders::_2));
|
||||
filter->set_text_changed_callback(std::bind(&game_load::apply_filter_text, this, std::placeholders::_2));
|
||||
|
||||
listbox& list = find_widget<listbox>("savegame_list");
|
||||
|
||||
|
@ -317,41 +317,10 @@ void game_load::display_savegame()
|
|||
get_window()->set_enter_disabled(!successfully_displayed_a_game);
|
||||
}
|
||||
|
||||
void game_load::filter_text_changed(const std::string& text)
|
||||
void game_load::apply_filter_text(const std::string& text)
|
||||
{
|
||||
apply_filter_text(text, false);
|
||||
}
|
||||
|
||||
void game_load::apply_filter_text(const std::string& text, bool force)
|
||||
{
|
||||
listbox& list = find_widget<listbox>("savegame_list");
|
||||
|
||||
const std::vector<std::string> words = utils::split(text, ' ');
|
||||
|
||||
if(words == last_words_ && !force)
|
||||
return;
|
||||
last_words_ = words;
|
||||
|
||||
boost::dynamic_bitset<> show_items;
|
||||
show_items.resize(list.get_item_count(), true);
|
||||
|
||||
if(!text.empty()) {
|
||||
for(unsigned int i = 0; i < list.get_item_count() && i < games_.size(); i++) {
|
||||
bool found = false;
|
||||
for(const auto & word : words)
|
||||
{
|
||||
found = translation::ci_search(games_[i].name(), word);
|
||||
if(!found) {
|
||||
// one word doesn't match, we don't reach words.end()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_items[i] = found;
|
||||
}
|
||||
}
|
||||
|
||||
list.set_row_shown(show_items);
|
||||
find_widget<listbox>("savegame_list").filter_rows_by(
|
||||
[this, match = translation::make_ci_matcher(text)](std::size_t row) { return match(games_[row].name()); });
|
||||
}
|
||||
|
||||
void game_load::evaluate_summary_string(std::stringstream& str, const config& cfg_summary)
|
||||
|
@ -541,7 +510,7 @@ void game_load::handle_dir_select()
|
|||
|
||||
populate_game_list();
|
||||
if(auto* filter = find_widget<text_box>("txtFilter", false, true)) {
|
||||
apply_filter_text(filter->get_value(), true);
|
||||
apply_filter_text(filter->get_value());
|
||||
}
|
||||
display_savegame();
|
||||
}
|
||||
|
|
|
@ -46,18 +46,16 @@ private:
|
|||
/** Update (both internally and visually) the list of games. */
|
||||
void populate_game_list();
|
||||
|
||||
void filter_text_changed(const std::string& text);
|
||||
void browse_button_callback();
|
||||
void delete_button_callback();
|
||||
void handle_dir_select();
|
||||
|
||||
/**
|
||||
* Implementation detail of filter_text_changed and handle_dir_select
|
||||
* Hides saves not matching the given filter.
|
||||
*
|
||||
* @param text Current contents of the textbox
|
||||
* @param force If true, recalculate even if the text is the same as last time
|
||||
*/
|
||||
void apply_filter_text(const std::string& text, bool force);
|
||||
void apply_filter_text(const std::string& text);
|
||||
|
||||
/** Part of display_savegame that might throw a config::error if the savegame data is corrupt. */
|
||||
void display_savegame_internal(const savegame::save_info& game);
|
||||
|
@ -77,8 +75,6 @@ private:
|
|||
|
||||
std::vector<savegame::save_info> games_;
|
||||
const game_config_view& cache_config_;
|
||||
|
||||
std::vector<std::string> last_words_;
|
||||
};
|
||||
} // namespace dialogs
|
||||
} // namespace gui2
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#include "gui/widgets/text_box.hpp"
|
||||
#include "gui/widgets/toggle_button.hpp"
|
||||
#include "gui/widgets/window.hpp"
|
||||
#include "utils/ci_searcher.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
|
||||
|
@ -32,7 +33,6 @@ REGISTER_DIALOG(log_settings)
|
|||
|
||||
log_settings::log_settings()
|
||||
: modal_dialog(window_id())
|
||||
, last_words_()
|
||||
{
|
||||
//list of names must match those in logging.cfg
|
||||
widget_id_.push_back("none");
|
||||
|
@ -95,38 +95,8 @@ void log_settings::pre_show()
|
|||
|
||||
void log_settings::filter_text_changed(const std::string& text)
|
||||
{
|
||||
listbox& list = find_widget<listbox>("logger_listbox");
|
||||
|
||||
const std::vector<std::string> words = utils::split(text, ' ');
|
||||
|
||||
if(words == last_words_) {
|
||||
return;
|
||||
}
|
||||
|
||||
last_words_ = words;
|
||||
|
||||
boost::dynamic_bitset<> show_items;
|
||||
show_items.resize(list.get_item_count(), true);
|
||||
|
||||
if(!text.empty()) {
|
||||
for(unsigned int i = 0; i < list.get_item_count(); i++) {
|
||||
assert(i < domain_list_.size());
|
||||
|
||||
bool found = false;
|
||||
|
||||
for(const auto& word : words)
|
||||
{
|
||||
found = translation::ci_search(domain_list_[i], word);
|
||||
if(!found) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_items[i] = found;
|
||||
}
|
||||
}
|
||||
|
||||
list.set_row_shown(show_items);
|
||||
find_widget<listbox>("logger_listbox").filter_rows_by(
|
||||
[this, match = translation::make_ci_matcher(text)](std::size_t row) { return match(domain_list_[row]); });
|
||||
}
|
||||
|
||||
void log_settings::post_show()
|
||||
|
|
|
@ -50,9 +50,6 @@ private:
|
|||
virtual void post_show() override;
|
||||
|
||||
void filter_text_changed(const std::string& text);
|
||||
|
||||
std::vector<std::string> last_words_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "gui/widgets/window.hpp"
|
||||
#include "gettext.hpp"
|
||||
#include "units/types.hpp"
|
||||
#include "utils/ci_searcher.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
@ -45,7 +46,6 @@ unit_create::unit_create()
|
|||
, gender_(last_gender)
|
||||
, choice_(last_chosen_type_id)
|
||||
, variation_(last_variation)
|
||||
, last_words_()
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -230,46 +230,14 @@ void unit_create::list_item_clicked()
|
|||
|
||||
void unit_create::filter_text_changed(const std::string& text)
|
||||
{
|
||||
listbox& list = find_widget<listbox>("unit_type_list");
|
||||
|
||||
const std::vector<std::string> words = utils::split(text, ' ');
|
||||
|
||||
if(words == last_words_)
|
||||
return;
|
||||
last_words_ = words;
|
||||
|
||||
boost::dynamic_bitset<> show_items;
|
||||
show_items.resize(list.get_item_count(), true);
|
||||
|
||||
if(!text.empty()) {
|
||||
for(unsigned int i = 0; i < list.get_item_count(); i++) {
|
||||
grid* row = list.get_row_grid(i);
|
||||
|
||||
// grid::iterator it = row->begin();
|
||||
label& type_label = row->find_widget<label>("unit_type");
|
||||
label& race_label = row->find_widget<label>("race");
|
||||
|
||||
assert(i < units_.size());
|
||||
const std::string& unit_type_id = units_[i] ? units_[i]->id() : "";
|
||||
|
||||
bool found = false;
|
||||
for(const auto & word : words)
|
||||
{
|
||||
found = translation::ci_search(type_label.get_label().str(), word) ||
|
||||
translation::ci_search(race_label.get_label().str(), word) ||
|
||||
translation::ci_search(unit_type_id, word);
|
||||
|
||||
if(!found) {
|
||||
// one word doesn't match, we don't reach words.end()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_items[i] = found;
|
||||
}
|
||||
}
|
||||
|
||||
list.set_row_shown(show_items);
|
||||
find_widget<listbox>("unit_type_list")
|
||||
.filter_rows_by([this, match = translation::make_ci_matcher(text)](std::size_t row) {
|
||||
return match(
|
||||
units_[row]->type_name(),
|
||||
units_[row]->race()->plural_name(),
|
||||
units_[row]->id()
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
void unit_create::gender_toggle_callback(const unit_race::GENDER val)
|
||||
|
|
|
@ -69,8 +69,6 @@ private:
|
|||
|
||||
std::string variation_;
|
||||
|
||||
std::vector<std::string> last_words_;
|
||||
|
||||
virtual const std::string& window_id() const override;
|
||||
|
||||
virtual void pre_show() override;
|
||||
|
|
|
@ -37,6 +37,7 @@
|
|||
#include "units/unit.hpp"
|
||||
#include "units/ptr.hpp"
|
||||
#include "units/types.hpp"
|
||||
#include "utils/ci_searcher.hpp"
|
||||
#include <functional>
|
||||
#include "whiteboard/manager.hpp"
|
||||
|
||||
|
@ -441,35 +442,10 @@ void unit_recall::post_show()
|
|||
|
||||
void unit_recall::filter_text_changed(const std::string& text)
|
||||
{
|
||||
listbox& list = find_widget<listbox>("recall_list");
|
||||
|
||||
const std::vector<std::string> words = utils::split(text, ' ');
|
||||
|
||||
if(words == last_words_)
|
||||
return;
|
||||
last_words_ = words;
|
||||
|
||||
boost::dynamic_bitset<> show_items;
|
||||
show_items.resize(list.get_item_count(), true);
|
||||
|
||||
if(!text.empty()) {
|
||||
for(unsigned int i = 0; i < list.get_item_count(); i++) {
|
||||
bool found = false;
|
||||
|
||||
for(const auto & word : words) {
|
||||
found = translation::ci_search(filter_options_[i], word);
|
||||
|
||||
if(!found) {
|
||||
// one word doesn't match, we don't reach words.end()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_items[i] = found;
|
||||
}
|
||||
}
|
||||
|
||||
list.set_row_shown(show_items);
|
||||
auto& list = find_widget<listbox>("recall_list");
|
||||
list.filter_rows_by([this, match = translation::make_ci_matcher(text)](std::size_t row) {
|
||||
return match(filter_options_[row]);
|
||||
});
|
||||
|
||||
// Disable rename and dismiss buttons if no units are shown
|
||||
const bool any_shown = list.any_rows_shown();
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "help/help.hpp"
|
||||
#include "team.hpp"
|
||||
#include "units/types.hpp"
|
||||
#include "utils/ci_searcher.hpp"
|
||||
|
||||
#include <functional>
|
||||
|
||||
|
@ -61,30 +62,17 @@ static inline std::string gray_if_unrecruitable(const std::string& text, const b
|
|||
// Compare unit_create::filter_text_change
|
||||
void unit_recruit::filter_text_changed(const std::string& text)
|
||||
{
|
||||
listbox& list = find_widget<listbox>("recruit_list");
|
||||
find_widget<listbox>("recruit_list")
|
||||
.filter_rows_by([this, match = translation::make_ci_matcher(text)](std::size_t row) {
|
||||
const unit_type* type = recruit_list_[row];
|
||||
if(!type) return true;
|
||||
|
||||
const std::vector<std::string> words = utils::split(text, ' ');
|
||||
const auto default_gender = !type->genders().empty() ? type->genders().front() : unit_race::MALE;
|
||||
const auto race = type->race();
|
||||
|
||||
if(words == last_words_)
|
||||
return;
|
||||
last_words_ = words;
|
||||
|
||||
boost::dynamic_bitset<> show_items;
|
||||
show_items.resize(list.get_item_count(), true);
|
||||
|
||||
if(!text.empty()) {
|
||||
for(unsigned int i = 0; i < list.get_item_count(); i++) {
|
||||
assert(i < recruit_list_.size());
|
||||
const unit_type* type = recruit_list_[i];
|
||||
if(!type) continue;
|
||||
|
||||
auto default_gender = !type->genders().empty()
|
||||
? type->genders().front() : unit_race::MALE;
|
||||
const auto* race = type->race();
|
||||
|
||||
// List of possible match criteria for this unit type. Empty values will
|
||||
// never match.
|
||||
auto criteria = std::make_tuple(
|
||||
// List of possible match criteria for this unit type.
|
||||
// Empty values will never match.
|
||||
return match(
|
||||
(game_config::debug ? type->id() : ""),
|
||||
type->type_name(),
|
||||
std::to_string(type->level()),
|
||||
|
@ -92,27 +80,7 @@ void unit_recruit::filter_text_changed(const std::string& text)
|
|||
(race ? race->name(default_gender) : ""),
|
||||
(race ? race->plural_name() : "")
|
||||
);
|
||||
|
||||
bool found = false;
|
||||
for(const auto & word : words)
|
||||
{
|
||||
// Search for the name in the local language.
|
||||
// In debug mode, also search for the type id.
|
||||
std::apply([&](auto&&... criterion) {
|
||||
found = (translation::ci_search(criterion, word) || ...);
|
||||
}, criteria);
|
||||
|
||||
if(!found) {
|
||||
// one word doesn't match, we don't reach words.end()
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
show_items[i] = found;
|
||||
}
|
||||
}
|
||||
|
||||
list.set_row_shown(show_items);
|
||||
});
|
||||
}
|
||||
|
||||
void unit_recruit::pre_show()
|
||||
|
|
|
@ -54,8 +54,6 @@ private:
|
|||
team& team_;
|
||||
|
||||
int selected_index_;
|
||||
|
||||
std::vector<std::string> last_words_;
|
||||
};
|
||||
|
||||
} // namespace dialogs
|
||||
|
|
|
@ -246,6 +246,18 @@ void listbox::set_row_shown(const boost::dynamic_bitset<>& shown)
|
|||
}
|
||||
}
|
||||
|
||||
void listbox::filter_rows_by(const std::function<bool(std::size_t)>& filter)
|
||||
{
|
||||
boost::dynamic_bitset<> mask;
|
||||
mask.resize(get_item_count(), true);
|
||||
|
||||
for(std::size_t i = 0; i < mask.size(); ++i) {
|
||||
mask[i] = std::invoke(filter, i);
|
||||
}
|
||||
|
||||
set_row_shown(mask);
|
||||
}
|
||||
|
||||
boost::dynamic_bitset<> listbox::get_rows_shown() const
|
||||
{
|
||||
return generator_->get_items_shown();
|
||||
|
|
|
@ -132,6 +132,9 @@ public:
|
|||
*/
|
||||
void set_row_shown(const boost::dynamic_bitset<>& shown);
|
||||
|
||||
/** Hides all rows for which the given predicate returns false. */
|
||||
void filter_rows_by(const std::function<bool(std::size_t)>& filter);
|
||||
|
||||
/**
|
||||
* Returns a list of visible rows
|
||||
*
|
||||
|
|
37
src/utils/ci_searcher.hpp
Normal file
37
src/utils/ci_searcher.hpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
/*
|
||||
Copyright (C) 2024
|
||||
Part of the Battle for Wesnoth Project https://www.wesnoth.org/
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY.
|
||||
|
||||
See the COPYING file for more details.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "gettext.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
||||
namespace translation
|
||||
{
|
||||
/** Returns a function which performs locale-aware case-insensitive search. */
|
||||
inline auto make_ci_matcher(std::string_view filter_text)
|
||||
{
|
||||
return [words = utils::split(filter_text, ' ')](auto&&... to_match) {
|
||||
for(const auto& word : words) {
|
||||
if(!(translation::ci_search(to_match, word) || ...)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace translation
|
Loading…
Add table
Reference in a new issue