GUI2/Group: bunch of refactoring to fix issues when enabling/disabling members

This should fix issues in the Faction Select and Unit Create dialogs where the gender toggles weren't
updated correctly when selecting a new type.
This commit is contained in:
Charles Dang 2016-11-19 21:34:03 +11:00
parent f1686d769b
commit bd258cdcab
3 changed files with 63 additions and 44 deletions

View file

@ -174,16 +174,13 @@ void faction_select::on_leader_select(window& window)
{
flg_manager_.set_current_leader(find_widget<menu_button>(&window, "leader_menu", false).get_value());
auto gender_available = [this](const std::string& gender)->bool {
const std::vector<std::string>& genders = flg_manager_.choosable_genders();
return std::find(genders.begin(), genders.end(), gender) != genders.end();
};
// TODO: should we decouple this from the flg manager and instead just check the unit type directly?
// If that's done so, we'd need to come up with a different check for Random availability.
gender_toggle_.set_member_active(unit_race::s_male, gender_available(unit_race::s_male));
gender_toggle_.set_member_active(unit_race::s_female, gender_available(unit_race::s_female));
gender_toggle_.set_member_active("random", gender_available("random"));
gender_toggle_.set_members_enabled([this](const std::string& gender)->bool {
const std::vector<std::string>& genders = flg_manager_.choosable_genders();
return std::find(genders.begin(), genders.end(), gender) != genders.end();
});
update_leader_image(window);
}

View file

@ -202,10 +202,9 @@ void unit_create::list_item_clicked(window& window)
find_widget<unit_preview_pane>(&window, "unit_details", false)
.set_displayed_type(*units_[selected_row]);
gender_toggle.set_member_active(unit_race::GENDER::MALE,
units_[selected_row]->has_gender_variation(unit_race::GENDER::MALE));
gender_toggle.set_member_active(unit_race::GENDER::FEMALE,
units_[selected_row]->has_gender_variation(unit_race::GENDER::FEMALE));
gender_toggle.set_members_enabled([&](const unit_race::GENDER& gender)->bool {
return units_[selected_row]->has_gender_variation(gender);
});
}
void unit_create::filter_text_changed(text_box_base* textbox, const std::string& text)

View file

@ -18,33 +18,34 @@
#include "gui/widgets/control.hpp"
#include "gui/widgets/selectable.hpp"
#include "gui/widgets/widget.hpp"
#include "utils/functional.hpp"
#include <map>
#include "utils/functional.hpp"
#include <vector>
namespace gui2
{
template <class T>
template<class T>
class group
{
public:
typedef typename std::map<T, selectable_item*> group_map;
typedef typename group_map::iterator group_iterator;
typedef typename group_map::const_iterator group_iterator_const;
using group_map = std::map<T, selectable_item*>;
using order_vector = std::vector<selectable_item*>;
public:
/**
* Adds a widget/value pair to the group map. A callback is set
* that sets all members' toggle states to false when clicked. This
* happens before individual widget handlers fire, meaning that the
* clicked widget will remain the only one selected.
* Adds a widget/value pair to the group map. A callback is set that toggles each members'
* state to false when clicked. This happens before individual widget handlers fire, ensuring
* that the clicked widget will remain the only one selected.
*/
void add_member(selectable_item* w, const T& value)
{
members_.emplace(value, w);
dynamic_cast<widget*>(w)->connect_signal<event::LEFT_BUTTON_CLICK>(std::bind(
&group::group_operator, this), event::dispatcher::front_child);
dynamic_cast<widget*>(w)->connect_signal<event::LEFT_BUTTON_CLICK>(
std::bind(&group::group_operator, this), event::dispatcher::front_child);
member_order_.push_back(w);
}
/**
@ -83,8 +84,7 @@ public:
}
/**
* Returns the value paired with the currently actively toggled member
* of the group.
* Returns the value paired with the currently actively toggled member of the group.
*/
T get_active_member_value()
{
@ -111,7 +111,7 @@ public:
/**
* Sets a common callback function for all members.
*/
void set_callback_on_value_change(const std::function<void(widget&)>& func)
void set_callback_on_value_change(std::function<void(widget&)> func)
{
// Ensure this callback is only called on the member being activated
const auto callback = [func](widget& widget)->void {
@ -126,41 +126,64 @@ public:
}
/**
* Wrapper for enabling or disabling a member widget.
* If the selected widget is selected, it is deselected and the first active
* member selected instead.
* Wrapper for enabling or disabling member widgets.
* Each member widget will be enabled or disabled based on the result of the specified
* predicate, which takes its accociated value.
*
* If a selected widget is to be disabled, it is deselected and the first active member
* selected instead. The same happens if no members were previously active at all.
*/
void set_member_active(const T& value, const bool active)
void set_members_enabled(std::function<bool(const T&)> predicate)
{
if(members_.find(value) == members_.end()) {
return;
bool do_reselect = true;
for(auto& member : members_) {
const bool res = predicate(member.first);
selectable_item& w = *member.second;
dynamic_cast<styled_widget&>(w).set_active(res);
// TODO: we shouldn't need to set do_reselect twice...
if(w.get_value_bool()) {
do_reselect = false;
}
// Only select another member if this was selected
if(!res && w.get_value_bool()) {
w.set_value_bool(false);
do_reselect = true;
}
}
selectable_item& w = *members_[value];
dynamic_cast<styled_widget&>(w).set_active(active);
// Only select another member this was selected
if(!w.get_value_bool()) {
if(!do_reselect) {
return;
}
w.set_value_bool(false);
// Look for the first active member to select
for(auto& member : members_) {
if(dynamic_cast<styled_widget&>(*member.second).get_active()) {
member.second->set_value_bool(true);
for(auto& member : member_order_) {
if(dynamic_cast<styled_widget&>(*member).get_active()) {
member->set_value_bool(true);
break;
}
}
}
private:
/**
* Container map for group members, organized by member value, accociated widget.
*/
group_map members_;
/**
* The default actions to take when clicking on one of the widgets
* in the group.
* Since iterating over std::map is specified by operator< for it's key values, we can't
* guarantee the order would line up with the logical order - ie, that which the widgets
* appear in in a specific dialog. Keeping a separate vector here allows iterating over
* members in the order which they are added to the group.
*/
order_vector member_order_;
/**
* The default actions to take when clicking on one of the widgets in the group.
*/
void group_operator()
{