The Battle for Wesnoth  1.19.7+dev
faction_select.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 2024
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #define GETTEXT_DOMAIN "wesnoth-lib"
16 
18 
19 #include "formatter.hpp"
20 #include "gettext.hpp"
21 #include "gui/widgets/button.hpp"
22 #include "gui/widgets/drawing.hpp"
23 #include "gui/widgets/label.hpp"
24 #include "gui/widgets/listbox.hpp"
27 #include "gui/widgets/window.hpp"
28 #include "help/help.hpp"
29 #include "preferences/preferences.hpp" // for encountered_units
30 #include "units/types.hpp"
31 
32 #include <functional>
33 
34 namespace gui2::dialogs
35 {
36 
37 REGISTER_DIALOG(faction_select)
38 
39 faction_select::faction_select(ng::flg_manager& flg_manager, const std::string& color, const int side)
40  : modal_dialog(window_id())
41  , flg_manager_(flg_manager)
42  , tc_color_(color)
43  , side_(side)
44  , last_faction_(flg_manager.current_faction_index())
45  , last_leader_(flg_manager.current_leader_index())
46  , last_gender_(flg_manager.current_gender_index())
47 {
48 }
49 
51 {
52  find_widget<label>("starting_pos").set_label(std::to_string(side_));
53 
54  //
55  // Set up gender radio buttons
56  //
57  toggle_button& gender_rand = find_widget<toggle_button>("gender_random");
58  toggle_button& gender_male = find_widget<toggle_button>("gender_male");
59  toggle_button& gender_female = find_widget<toggle_button>("gender_female");
60 
61  gender_toggle_.add_member(&gender_rand, "random");
64 
66 
68  std::bind(&faction_select::on_gender_select, this, std::placeholders::_2));
69 
70  //
71  // Set up leader menu button
72  //
73  connect_signal_notify_modified(find_widget<menu_button>("leader_menu"),
74  std::bind(&faction_select::on_leader_select, this));
75 
76  // Leader's profile button
77  find_widget<button>("type_profile").connect_click_handler(
78  std::bind(&faction_select::profile_button_callback, this));
79 
80  //
81  // Set up faction list
82  //
83  listbox& list = find_widget<listbox>("faction_list");
84 
85  keyboard_capture(&list);
86 
88  std::bind(&faction_select::on_faction_select, this));
89 
90  for(const config *s : flg_manager_.choosable_factions()) {
91  const config& side = *s;
92 
94  widget_item item;
95 
96  const std::string name = side["name"].str();
97  // flag_rgb here is unrelated to any handling in the unit class
98  const std::string flag_rgb = !side["flag_rgb"].empty() ? side["flag_rgb"].str() : "magenta";
99 
100  item["label"] = (formatter() << side["image"] << "~RC(" << flag_rgb << ">" << tc_color_ << ")").str();
101  data.emplace("faction_image", item);
102 
103  item["label"] = name;
104  data.emplace("faction_name", item);
105 
106  list.add_row(data);
107  }
108 
110 
112 }
113 
115 {
116  const int selected_row = find_widget<listbox>("faction_list").get_selected_row();
117 
118  if(selected_row == -1) {
119  return;
120  }
121 
122  // Since set_current_faction overrides the current leader, save a copy of the previous leader index so the
123  // leader dropdown can be set to the appropriate initial selection.
124  const int previous_leader_selection = flg_manager_.current_leader_index();
125 
126  flg_manager_.set_current_faction(selected_row);
127 
128  std::vector<config> leaders;
129 
130  for(const std::string& leader : flg_manager_.choosable_leaders()) {
131  const unit_type* unit = unit_types.find(leader);
132 
133  if(unit) {
134  const std::string icon = formatter() << unit->image() << "~RC(" << unit->flag_rgb() << ">" << tc_color_ << ")";
135  leaders.emplace_back("label", unit->type_name(), "icon", icon);
136  } else if(leader == "random") {
137  leaders.emplace_back("label", _("Random"), "icon", ng::random_enemy_picture);
138  } else if(leader == "null") {
139  leaders.emplace_back("label", font::unicode_em_dash);
140  } else {
141  leaders.emplace_back("label", "?");
142  }
143  }
144 
145  menu_button& leader_dropdown = find_widget<menu_button>("leader_menu");
146 
147  leader_dropdown.set_values(leaders, std::min<int>(leaders.size() - 1, previous_leader_selection));
148  leader_dropdown.set_active(leaders.size() > 1 && !flg_manager_.is_saved_game());
149 
151 
152  // Print recruits
153  std::vector<t_string> recruit_names;
154  for(const auto& recruit : utils::split(flg_manager_.current_faction()["recruit"])) {
155  if(const unit_type* rt = unit_types.find(recruit)) {
156  recruit_names.push_back(rt->type_name());
157  }
158  }
159 
160  std::sort(recruit_names.begin(), recruit_names.end(), [](const std::string& s1, const std::string& s2) {
161  return translation::compare(s1, s2) < 0;
162  });
163 
164  find_widget<styled_widget>("recruits").set_label(utils::bullet_list(recruit_names, 0));
165 }
166 
168 {
169  flg_manager_.set_current_leader(find_widget<menu_button>("leader_menu").get_value());
170 
171  // TODO: should we decouple this from the flg manager and instead just check the unit type directly?
172  // If that's done so, we'd need to come up with a different check for Random availability.
173  gender_toggle_.set_members_enabled([this](const std::string& gender)->bool {
174  const std::vector<std::string>& genders = flg_manager_.choosable_genders();
175  return std::find(genders.begin(), genders.end(), gender) != genders.end();
176  });
177 
179 
180  // Disable the profile button if leader_type is dash or "Random"
181  button& profile_button = find_widget<button>("type_profile");
182  profile_button.set_active(unit_types.find(flg_manager_.current_leader()) != nullptr);
183 }
184 
186 {
188  prefs::get().encountered_units().insert(ut->id());
190  }
191 }
192 
193 void faction_select::on_gender_select(const std::string& val)
194 {
196 
198 }
199 
201 {
202  std::string leader_image = ng::random_enemy_picture;
203 
206  leader_image = formatter() << utg.image() << "~RC(" << utg.flag_rgb() << ">" << tc_color_ << ")";
207  }
208 
209  find_widget<drawing>("leader_image").set_label(leader_image);
210 }
211 
213 {
214  //
215  // If we're canceling, restore the previous selections. It might be worth looking
216  // into only making selections at all here in post_show, but that would require a
217  // refactor of the flg_manager class.
218  //
219  // Also, note it's important to set these in the order of faction -> leader -> gender
220  // or the saved indices will be invalid!
221  //
222  // -- vultraz, 2018-06-16
223  //
224  if(get_retval() != retval::OK) {
228  }
229 }
230 
231 } // namespace dialogs
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
bool empty() const
Definition: config.cpp:849
std::ostringstream wrapper.
Definition: formatter.hpp:40
Simple push button.
Definition: button.hpp:36
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:64
virtual void post_show() override
Actions to be taken after the window has been shown.
virtual void pre_show() override
Actions to be taken before showing the window.
group< std::string > gender_toggle_
void on_gender_select(const std::string &val)
Abstract base class for all modal dialogs.
int get_retval() const
Returns the cached window exit code.
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
Definition: group.hpp:42
void set_member_states(const T &value)
Sets the toggle values for all widgets besides the one associated with the specified value to false.
Definition: group.hpp:111
void set_callback_on_value_change(std::function< void(widget &, const T)> func)
Sets a common callback function for all members.
Definition: group.hpp:121
void set_members_enabled(std::function< bool(const T &)> predicate)
Wrapper for enabling or disabling member widgets.
Definition: group.hpp:150
The listbox class.
Definition: listbox.hpp:41
grid & add_row(const widget_item &item, const int index=-1)
When an item in the list is selected by the user we need to update the state.
Definition: listbox.cpp:92
bool select_row(const unsigned row, const bool select=true)
Selects a row.
Definition: listbox.cpp:280
void set_values(const std::vector<::config > &values, unsigned selected=0)
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:74
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
const std::vector< const config * > & choosable_factions() const
Definition: flg_manager.hpp:61
const std::string & current_gender() const
Definition: flg_manager.hpp:71
void set_current_faction(const unsigned index)
const std::vector< std::string > & choosable_genders() const
Definition: flg_manager.hpp:65
bool is_saved_game() const
Definition: flg_manager.hpp:52
int current_leader_index() const
Definition: flg_manager.hpp:79
const std::vector< std::string > & choosable_leaders() const
Definition: flg_manager.hpp:63
void set_current_leader(const unsigned index)
int current_faction_index() const
void set_current_gender(const unsigned index)
const config & current_faction() const
Definition: flg_manager.hpp:67
const std::string & current_leader() const
Definition: flg_manager.hpp:69
std::set< std::string > & encountered_units()
static prefs & get()
static const std::string s_female
Standard string id (not translatable) for FEMALE.
Definition: race.hpp:29
static const std::string s_male
Standard string id (not translatable) for MALE.
Definition: race.hpp:30
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1265
A single unit type that the player may recruit.
Definition: types.hpp:43
const unit_type & get_gender_unit_type(const std::string &gender) const
Returns a gendered variant of this unit_type.
Definition: types.cpp:455
const std::string & image() const
Definition: types.hpp:176
const std::string & flag_rgb() const
Definition: types.cpp:722
This class represents a single unit of a specific type.
Definition: unit.hpp:133
static std::string _(const char *str)
Definition: gettext.hpp:93
const t_string & type_name() const
Gets the translatable name of this unit's type.
Definition: unit.hpp:369
const std::string & flag_rgb() const
Get the source color palette to use when recoloring the unit's image.
Definition: unit.cpp:1146
This file contains the window object, this object is a top level container which has the event manage...
const std::string unicode_em_dash
Definition: constants.cpp:44
std::string flag_rgb
REGISTER_DIALOG(editor_edit_unit)
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:203
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
void show_unit_description(const unit &u)
Definition: help.cpp:71
const std::string random_enemy_picture("units/random-dice.png")
std::string bullet_list(const T &v, std::size_t indent=4, const std::string &bullet=font::unicode_bullet)
Generates a new string containing a bullet list.
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
std::string_view data
Definition: picture.cpp:178
static map_location::direction s
unit_type_data unit_types
Definition: types.cpp:1500