The Battle for Wesnoth  1.17.0-dev
unit_create.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2021
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  : gender_(last_gender)
52  , choice_(last_chosen_type_id)
53  , variation_(last_variation)
54  , last_words_()
55 {
56  set_restore(true);
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));
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);
87  window.add_to_keyboard_chain(&list);
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  std::map<std::string, string_map> row_data;
101  string_map 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, preferences::SORT_ORDER::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\n";
151  return;
152  }
153 
154  last_chosen_type_id = choice_ = units_[selected_row]->id();
157 }
158 
160 {
161  window* w = get_window();
162 
163  const int selected_row
164  = find_widget<listbox>(w, "unit_type_list", false).get_selected_row();
165 
166  if(selected_row == -1) {
167  return;
168  }
169 
170  const unit_type* ut = &units_[selected_row]->get_gender_unit_type(gender_);
171 
172  if(!variation_.empty()) {
173  // This effectively translates to `ut = ut` if somehow variation_ does
174  // not refer to a variation that the unit type supports.
175  ut = &ut->get_variation(variation_);
176  }
177 
178  find_widget<unit_preview_pane>(w, "unit_details", false).set_displayed_type(*ut);
179 }
180 
182 {
183  const int selected_row
184  = find_widget<listbox>(get_window(), "unit_type_list", false).get_selected_row();
185 
186  if(selected_row == -1) {
187  return;
188  }
189 
191 
193  return units_[selected_row]->has_gender_variation(gender);
194  });
195 
196  menu_button& var_box = find_widget<menu_button>(get_window(), "variation_box", false);
197  std::vector<config> var_box_values;
198  var_box_values.emplace_back("label", _("unit_variation^Default Variation"), "variation_id", "");
199 
200  const auto& ut = *units_[selected_row];
201  const auto& uvars = ut.variation_types();
202 
203  var_box.set_active(!uvars.empty());
204 
205  unsigned n = 0, selection = 0;
206 
207  for(const auto& pair : uvars) {
208  ++n;
209 
210  const std::string& uv_id = pair.first;
211  const unit_type& uv = pair.second;
212 
213  std::string uv_label;
214  if(!uv.variation_name().empty()) {
215  uv_label = uv.variation_name() + " (" + uv_id + ")";
216  } else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
217  uv_label = uv.type_name() + " (" + uv_id + ")";
218  } else {
219  uv_label = uv_id;
220  }
221 
222  var_box_values.emplace_back("label", uv_label, "variation_id", uv_id);
223 
224  if(uv_id == variation_) {
225  selection = n;
226  }
227  }
228 
229  // If we didn't find the variation selection again then the new selected
230  // unit type doesn't have that variation id.
231  if(!selection) {
232  variation_.clear();
233  }
234 
235  var_box.set_values(var_box_values, selection);
236 }
237 
238 void unit_create::filter_text_changed(const std::string& text)
239 {
240  listbox& list = find_widget<listbox>(get_window(), "unit_type_list", false);
241 
242  const std::vector<std::string> words = utils::split(text, ' ');
243 
244  if(words == last_words_)
245  return;
246  last_words_ = words;
247 
248  boost::dynamic_bitset<> show_items;
249  show_items.resize(list.get_item_count(), true);
250 
251  if(!text.empty()) {
252  for(unsigned int i = 0; i < list.get_item_count(); i++) {
253  grid* row = list.get_row_grid(i);
254 
255  grid::iterator it = row->begin();
256  label& type_label
257  = find_widget<label>(*it, "unit_type", false);
258  label& race_label
259  = find_widget<label>(*it, "race", false);
260 
261  assert(i < units_.size());
262  const std::string& unit_type_id = units_[i] ? units_[i]->id() : "";
263 
264  bool found = false;
265  for(const auto & word : words)
266  {
267  found = translation::ci_search(type_label.get_label().str(), word) ||
268  translation::ci_search(race_label.get_label().str(), word) ||
269  translation::ci_search(unit_type_id, word);
270 
271  if(!found) {
272  // one word doesn't match, we don't reach words.end()
273  break;
274  }
275  }
276 
277  show_items[i] = found;
278  }
279  }
280 
281  list.set_row_shown(show_items);
282 }
283 
285 {
287 
289 }
290 
292 {
293  window& window = *this->get_window();
294  menu_button& var_box = find_widget<menu_button>(&window, "variation_box", false);
295  variation_ = var_box.get_value_config()["variation_id"].str();
296 
298 }
299 
300 } // namespace dialogs
Define the common log macros for the gui toolkit.
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:645
void set_text_changed_callback(std::function< void(text_box_base *textbox, const std::string text)> cb)
Set the text_changed callback.
const ::config & get_value_config() const
Returns the entire config object for the selected row.
Definition: menu_button.hpp:99
A menu_button is a styled_widget to choose an element from a list of elements.
Definition: menu_button.hpp:61
iterator begin()
Definition: grid.hpp:480
unit_race::GENDER gender()
Gender choice from the user.
Definition: unit_create.hpp:67
T get_active_member_value()
Returns the value paired with the currently actively toggled member of the group. ...
Definition: group.hpp:96
This file contains the window object, this object is a top level container which has the event manage...
void set_members_enabled(std::function< bool(const T &)> predicate)
Wrapper for enabling or disabling member widgets.
Definition: group.hpp:143
#define ERR_GUI_G
Definition: log.hpp:44
unit_type_data unit_types
Definition: types.cpp:1481
virtual void post_show(window &window) override
Actions to be taken after the window has been shown.
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:637
window * get_window() const
Returns a pointer to the dialog&#39;s window.
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list&#39;s last row.
Definition: listbox.hpp:195
A label displays a text, the text can be wrapped but no scrollbars are provided.
Definition: label.hpp:57
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:277
static std::string _(const char *str)
Definition: gettext.hpp:93
const unit_type_map & types() const
Definition: types.hpp:398
group< unit_race::GENDER > gender_toggle
A single unit type that the player may recruit.
Definition: types.hpp:45
Class for a single line text area.
Definition: text_box.hpp:141
void update_displayed_type() const
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:1254
unit_race::GENDER gender_
Definition: unit_create.hpp:81
The listbox class.
Definition: listbox.hpp:43
Base container class.
Definition: grid.hpp:31
void connect_signal_notify_modified(dispatcher &dispatcher, const signal_notification_function &signal)
Connects a signal handler for getting a notification upon modification.
Definition: dispatcher.cpp:187
This file contains the settings handling of the widget library.
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:141
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:127
static std::string last_chosen_type_id
Definition: unit_create.cpp:41
static std::string last_variation
Definition: unit_create.cpp:42
Iterator for the child items.
Definition: grid.hpp:441
void filter_text_changed(const std::string &text)
void set_values(const std::vector<::config > &values, unsigned selected=0)
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:133
static unit_race::GENDER last_gender
Definition: unit_create.cpp:43
std::vector< const unit_type * > units_
Definition: unit_create.hpp:79
bool ci_search(const std::string &s1, const std::string &s2)
Definition: gettext.cpp:534
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
Definition: group.hpp:42
std::vector< std::string > last_words_
Definition: unit_create.hpp:87
std::size_t i
Definition: function.cpp:967
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: unit_create.cpp:59
std::map< std::string, t_string > string_map
Definition: widget.hpp:26
grid & add_row(const string_map &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:68
int w
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:239
int get_retval() const
Returns the cached window exit code.
const t_string & get_label() const
bool empty() const
Definition: tstring.hpp:187
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:477
std::vector< std::string > split(const config_attribute_value &val)
void list_item_clicked()
Callbacks.
const t_string & variation_name() const
Definition: types.hpp:177
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: menu_button.cpp:75
Dialog was closed with the OK button.
Definition: retval.hpp:35
const std::string & str() const
Definition: tstring.hpp:191
static map_location::DIRECTION n
base class of top level items, the only item which needs to store the final canvases to draw on...
Definition: window.hpp:65
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:145
This shows the debug-mode dialog to create new units on the map.
Definition: unit_create.hpp:49
Class for a toggle button.
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 &)> func)
Sets a common callback function for all members.
Definition: group.hpp:121