The Battle for Wesnoth  1.17.23+dev
unit_create.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2023
3  by Iris Morelle <shadowm2006@gmail.com>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #define GETTEXT_DOMAIN "wesnoth-lib"
17 
19 
21 #include "gui/core/log.hpp"
22 #include "gui/widgets/listbox.hpp"
23 #include "gui/widgets/settings.hpp"
24 #include "gui/widgets/button.hpp"
25 #include "gui/widgets/image.hpp"
26 #include "gui/widgets/label.hpp"
27 #include "gui/widgets/grid.hpp"
29 #include "gui/widgets/text_box.hpp"
32 #include "gui/widgets/window.hpp"
33 #include "help/help.hpp"
34 #include "gettext.hpp"
35 #include "play_controller.hpp"
36 #include "units/types.hpp"
37 
38 #include <functional>
39 #include <boost/dynamic_bitset.hpp>
40 
41 static std::string last_chosen_type_id = "";
42 static std::string last_variation = "";
44 
45 namespace gui2::dialogs
46 {
47 
48 REGISTER_DIALOG(unit_create)
49 
51  : modal_dialog(window_id())
52  , gender_(last_gender)
53  , choice_(last_chosen_type_id)
54  , variation_(last_variation)
55  , last_words_()
56 {
57 }
58 
60 {
61  toggle_button& male_toggle
62  = find_widget<toggle_button>(&window, "male_toggle", false);
63  toggle_button& female_toggle
64  = find_widget<toggle_button>(&window, "female_toggle", false);
65 
68 
70 
72  std::bind(&unit_create::gender_toggle_callback, this, std::placeholders::_2));
73 
74  menu_button& var_box = find_widget<menu_button>(&window, "variation_box", false);
75 
77 
78  listbox& list = find_widget<listbox>(&window, "unit_type_list", false);
79 
80  text_box* filter
81  = find_widget<text_box>(&window, "filter_box", false, true);
82 
84  std::bind(&unit_create::filter_text_changed, this, std::placeholders::_2));
85 
86  window.keyboard_capture(filter);
88 
90 
91  list.clear();
92 
93  for(const auto & i : unit_types.types())
94  {
95  // Make sure this unit type is built with the data we need.
97 
98  units_.push_back(&i.second);
99 
100  widget_data row_data;
101  widget_item column;
102 
103  column["label"] = units_.back()->race()->plural_name();
104  row_data.emplace("race", column);
105 
106  column["label"] = units_.back()->type_name();
107  if(units_.back()->type_name().str() != units_.back()->id()) {
108  column["label"] += " (" + units_.back()->id() + ")";
109  }
110  row_data.emplace("unit_type", column);
111 
112  list.add_row(row_data);
113 
114  // Select the previous choice, if any.
115  if(!choice_.empty() && choice_ == i.first) {
116  list.select_last_row();
117  }
118  }
119 
120  if(units_.empty()) {
121  ERR_GUI_G << "no unit types found for unit create dialog; not good"
122  << std::endl;
123  }
124 
125  list.register_translatable_sorting_option(0, [this](const int i) { return (*units_[i]).race()->plural_name().str(); });
126  list.register_translatable_sorting_option(1, [this](const int i) { return (*units_[i]).type_name().str(); });
127 
128  // Select the first entry on sort if no previous selection was provided.
129  list.set_active_sorting_option({0, sort_order::type::ascending}, choice_.empty());
130 
132 }
133 
135 {
136  listbox& list = find_widget<listbox>(&window, "unit_type_list", false);
137 
138  choice_ = "";
139 
140  if(get_retval() != retval::OK) {
141  return;
142  }
143 
144  const int selected_row = list.get_selected_row();
145  if(selected_row < 0) {
146  return;
147  } else if(static_cast<std::size_t>(selected_row) >= units_.size()) {
148  // FIXME: maybe assert?
149  ERR_GUI_G << "unit create dialog has more list items than known unit "
150  "types; not good";
151  return;
152  }
153 
154  last_chosen_type_id = choice_ = units_[selected_row]->id();
157 }
158 
160 {
161  const int selected_row
162  = find_widget<listbox>(this, "unit_type_list", false).get_selected_row();
163 
164  if(selected_row == -1) {
165  return;
166  }
167 
168  const unit_type* ut = &units_[selected_row]->get_gender_unit_type(gender_);
169 
170  if(!variation_.empty()) {
171  // This effectively translates to `ut = ut` if somehow variation_ does
172  // not refer to a variation that the unit type supports.
173  ut = &ut->get_variation(variation_);
174  }
175 
176  find_widget<unit_preview_pane>(this, "unit_details", false).set_displayed_type(*ut);
177 }
178 
180 {
181  const int selected_row
182  = find_widget<listbox>(this, "unit_type_list", false).get_selected_row();
183 
184  if(selected_row == -1) {
185  return;
186  }
187 
189 
191  return units_[selected_row]->has_gender_variation(gender);
192  });
193 
194  menu_button& var_box = find_widget<menu_button>(this, "variation_box", false);
195  std::vector<config> var_box_values;
196  var_box_values.emplace_back("label", _("unit_variation^Default Variation"), "variation_id", "");
197 
198  const auto& ut = *units_[selected_row];
199  const auto& uvars = ut.variation_types();
200 
201  var_box.set_active(!uvars.empty());
202 
203  unsigned n = 0, selection = 0;
204 
205  for(const auto& pair : uvars) {
206  ++n;
207 
208  const std::string& uv_id = pair.first;
209  const unit_type& uv = pair.second;
210 
211  std::string uv_label;
212  if(!uv.variation_name().empty()) {
213  uv_label = uv.variation_name() + " (" + uv_id + ")";
214  } else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
215  uv_label = uv.type_name() + " (" + uv_id + ")";
216  } else {
217  uv_label = uv_id;
218  }
219 
220  var_box_values.emplace_back("label", uv_label, "variation_id", uv_id);
221 
222  if(uv_id == variation_) {
223  selection = n;
224  }
225  }
226 
227  // If we didn't find the variation selection again then the new selected
228  // unit type doesn't have that variation id.
229  if(!selection) {
230  variation_.clear();
231  }
232 
233  var_box.set_values(var_box_values, selection);
234 }
235 
236 void unit_create::filter_text_changed(const std::string& text)
237 {
238  listbox& list = find_widget<listbox>(this, "unit_type_list", false);
239 
240  const std::vector<std::string> words = utils::split(text, ' ');
241 
242  if(words == last_words_)
243  return;
244  last_words_ = words;
245 
246  boost::dynamic_bitset<> show_items;
247  show_items.resize(list.get_item_count(), true);
248 
249  if(!text.empty()) {
250  for(unsigned int i = 0; i < list.get_item_count(); i++) {
251  grid* row = list.get_row_grid(i);
252 
253  grid::iterator it = row->begin();
254  label& type_label
255  = find_widget<label>(*it, "unit_type", false);
256  label& race_label
257  = find_widget<label>(*it, "race", false);
258 
259  assert(i < units_.size());
260  const std::string& unit_type_id = units_[i] ? units_[i]->id() : "";
261 
262  bool found = false;
263  for(const auto & word : words)
264  {
265  found = translation::ci_search(type_label.get_label().str(), word) ||
266  translation::ci_search(race_label.get_label().str(), word) ||
267  translation::ci_search(unit_type_id, word);
268 
269  if(!found) {
270  // one word doesn't match, we don't reach words.end()
271  break;
272  }
273  }
274 
275  show_items[i] = found;
276  }
277  }
278 
279  list.set_row_shown(show_items);
280 }
281 
283 {
284  gender_ = val;
285 
287 }
288 
290 {
291  menu_button& var_box = find_widget<menu_button>(this, "variation_box", false);
292  variation_ = var_box.get_value_config()["variation_id"].str();
293 
295 }
296 
297 } // namespace dialogs
Abstract base class for all modal dialogs.
int get_retval() const
Returns the cached window exit code.
This shows the debug-mode dialog to create new units on the map.
Definition: unit_create.hpp:50
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
unit_race::GENDER gender_
Definition: unit_create.hpp:81
group< unit_race::GENDER > gender_toggle
unit_race::GENDER gender()
Gender choice from the user.
Definition: unit_create.hpp:67
std::vector< std::string > last_words_
Definition: unit_create.hpp:87
void list_item_clicked()
Callbacks.
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: unit_create.cpp:59
void filter_text_changed(const std::string &text)
void gender_toggle_callback(const unit_race::GENDER val)
std::vector< const unit_type * > units_
Definition: unit_create.hpp:79
Iterator for the child items.
Definition: grid.hpp:438
Base container class.
Definition: grid.hpp:32
iterator begin()
Definition: grid.hpp:479
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
A label displays text that can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
The listbox class.
Definition: listbox.hpp:46
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition: listbox.hpp:192
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:139
void set_active_sorting_option(const order_pair &sort_by, const bool select_first=false)
Sorts the listbox by a pre-set sorting option.
Definition: listbox.cpp:624
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:62
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:233
void register_translatable_sorting_option(const int col, translatable_sorter_func_t f)
Registers a special sorting function specifically for translatable values.
Definition: listbox.cpp:616
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:121
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:271
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:127
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:62
const ::config & get_value_config() const
Returns the entire config object for the selected row.
Definition: menu_button.hpp:99
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:77
const t_string & get_label() const
void set_text_changed_callback(std::function< void(text_box_base *textbox, const std::string text)> cb)
Set the text_changed callback.
Class for a single line text area.
Definition: text_box.hpp:142
Class for a toggle button.
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:67
void keyboard_capture(widget *widget)
Definition: window.cpp:1224
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1230
bool empty() const
Definition: tstring.hpp:187
const std::string & str() const
Definition: tstring.hpp:191
@ FEMALE
Definition: race.hpp:27
@ MALE
Definition: race.hpp:27
void build_unit_type(const unit_type &ut, unit_type::BUILD_STATUS status) const
Makes sure the provided unit_type is built to the specified level.
Definition: types.cpp:1238
const unit_type_map & types() const
Definition: types.hpp:396
A single unit type that the player may recruit.
Definition: types.hpp:46
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:474
@ FULL
Definition: types.hpp:77
const t_string & variation_name() const
Definition: types.hpp:177
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:141
std::size_t i
Definition: function.cpp:968
static std::string _(const char *str)
Definition: gettext.hpp:93
Define the common log macros for the gui toolkit.
#define ERR_GUI_G
Definition: log.hpp:44
This file contains the window object, this object is a top level container which has the event manage...
#define REGISTER_DIALOG(window_id)
Wrapper for REGISTER_DIALOG2.
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:205
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:35
std::map< std::string, t_string > widget_item
Definition: widget.hpp:32
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
bool ci_search(const std::string &s1, const std::string &s2)
Definition: gettext.cpp:567
std::vector< std::string > split(const config_attribute_value &val)
This file contains the settings handling of the widget library.
static map_location::DIRECTION n
unit_type_data unit_types
Definition: types.cpp:1465
static unit_race::GENDER last_gender
Definition: unit_create.cpp:43
static std::string last_chosen_type_id
Definition: unit_create.cpp:41
static std::string last_variation
Definition: unit_create.cpp:42