The Battle for Wesnoth  1.19.1+dev
unit_create.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2009 - 2024
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/label.hpp"
24 #include "gui/widgets/grid.hpp"
26 #include "gui/widgets/text_box.hpp"
29 #include "gui/widgets/window.hpp"
30 #include "gettext.hpp"
31 #include "units/types.hpp"
32 
33 #include <functional>
34 
35 static std::string last_chosen_type_id = "";
36 static std::string last_variation = "";
38 
39 namespace gui2::dialogs
40 {
41 
42 REGISTER_DIALOG(unit_create)
43 
45  : modal_dialog(window_id())
46  , gender_(last_gender)
47  , choice_(last_chosen_type_id)
48  , variation_(last_variation)
49  , last_words_()
50 {
51 }
52 
54 {
55  toggle_button& male_toggle
56  = find_widget<toggle_button>(&window, "male_toggle", false);
57  toggle_button& female_toggle
58  = find_widget<toggle_button>(&window, "female_toggle", false);
59 
62 
64 
66  std::bind(&unit_create::gender_toggle_callback, this, std::placeholders::_2));
67 
68  menu_button& var_box = find_widget<menu_button>(&window, "variation_box", false);
69 
71 
72  listbox& list = find_widget<listbox>(&window, "unit_type_list", false);
73 
74  text_box* filter
75  = find_widget<text_box>(&window, "filter_box", false, true);
76 
78  std::bind(&unit_create::filter_text_changed, this, std::placeholders::_2));
79 
80  window.keyboard_capture(filter);
82 
84 
85  list.clear();
86 
87  for(const auto & i : unit_types.types())
88  {
89  // Make sure this unit type is built with the data we need.
91 
92  units_.push_back(&i.second);
93 
94  widget_data row_data;
95  widget_item column;
96 
97  column["label"] = units_.back()->race()->plural_name();
98  row_data.emplace("race", column);
99 
100  column["label"] = units_.back()->type_name();
101  if(units_.back()->type_name().str() != units_.back()->id()) {
102  column["label"] += " (" + units_.back()->id() + ")";
103  }
104  row_data.emplace("unit_type", column);
105 
106  list.add_row(row_data);
107 
108  // Select the previous choice, if any.
109  if(!choice_.empty() && choice_ == i.first) {
110  list.select_last_row();
111  }
112  }
113 
114  if(units_.empty()) {
115  ERR_GUI_G << "no unit types found for unit create dialog; not good"
116  << std::endl;
117  }
118 
119  list.register_translatable_sorting_option(0, [this](const int i) { return (*units_[i]).race()->plural_name().str(); });
120  list.register_translatable_sorting_option(1, [this](const int i) { return (*units_[i]).type_name().str(); });
121 
122  // Select the first entry on sort if no previous selection was provided.
123  list.set_active_sorting_option({0, sort_order::type::ascending}, choice_.empty());
124 
126 }
127 
129 {
130  listbox& list = find_widget<listbox>(&window, "unit_type_list", false);
131 
132  choice_ = "";
133 
134  if(get_retval() != retval::OK) {
135  return;
136  }
137 
138  const int selected_row = list.get_selected_row();
139  if(selected_row < 0) {
140  return;
141  } else if(static_cast<std::size_t>(selected_row) >= units_.size()) {
142  // FIXME: maybe assert?
143  ERR_GUI_G << "unit create dialog has more list items than known unit "
144  "types; not good";
145  return;
146  }
147 
148  last_chosen_type_id = choice_ = units_[selected_row]->id();
151 }
152 
154 {
155  const int selected_row
156  = find_widget<listbox>(this, "unit_type_list", false).get_selected_row();
157 
158  if(selected_row == -1) {
159  return;
160  }
161 
162  const unit_type* ut = &units_[selected_row]->get_gender_unit_type(gender_);
163 
164  if(!variation_.empty()) {
165  // This effectively translates to `ut = ut` if somehow variation_ does
166  // not refer to a variation that the unit type supports.
167  ut = &ut->get_variation(variation_);
168  }
169 
170  find_widget<unit_preview_pane>(this, "unit_details", false).set_displayed_type(*ut);
171 }
172 
174 {
175  const int selected_row
176  = find_widget<listbox>(this, "unit_type_list", false).get_selected_row();
177 
178  if(selected_row == -1) {
179  return;
180  }
181 
183 
185  return units_[selected_row]->has_gender_variation(gender);
186  });
187 
188  menu_button& var_box = find_widget<menu_button>(this, "variation_box", false);
189  std::vector<config> var_box_values;
190  var_box_values.emplace_back("label", _("unit_variation^Default Variation"), "variation_id", "");
191 
192  const auto& ut = *units_[selected_row];
193  const auto& uvars = ut.variation_types();
194 
195  var_box.set_active(!uvars.empty());
196 
197  unsigned n = 0, selection = 0;
198 
199  for(const auto& pair : uvars) {
200  ++n;
201 
202  const std::string& uv_id = pair.first;
203  const unit_type& uv = pair.second;
204 
205  std::string uv_label;
206  if(!uv.variation_name().empty()) {
207  uv_label = uv.variation_name() + " (" + uv_id + ")";
208  } else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
209  uv_label = uv.type_name() + " (" + uv_id + ")";
210  } else {
211  uv_label = uv_id;
212  }
213 
214  var_box_values.emplace_back("label", uv_label, "variation_id", uv_id);
215 
216  if(uv_id == variation_) {
217  selection = n;
218  }
219  }
220 
221  // If we didn't find the variation selection again then the new selected
222  // unit type doesn't have that variation id.
223  if(!selection) {
224  variation_.clear();
225  }
226 
227  var_box.set_values(var_box_values, selection);
228 }
229 
230 void unit_create::filter_text_changed(const std::string& text)
231 {
232  listbox& list = find_widget<listbox>(this, "unit_type_list", false);
233 
234  const std::vector<std::string> words = utils::split(text, ' ');
235 
236  if(words == last_words_)
237  return;
238  last_words_ = words;
239 
240  boost::dynamic_bitset<> show_items;
241  show_items.resize(list.get_item_count(), true);
242 
243  if(!text.empty()) {
244  for(unsigned int i = 0; i < list.get_item_count(); i++) {
245  grid* row = list.get_row_grid(i);
246 
247  grid::iterator it = row->begin();
248  label& type_label
249  = find_widget<label>(*it, "unit_type", false);
250  label& race_label
251  = find_widget<label>(*it, "race", false);
252 
253  assert(i < units_.size());
254  const std::string& unit_type_id = units_[i] ? units_[i]->id() : "";
255 
256  bool found = false;
257  for(const auto & word : words)
258  {
259  found = translation::ci_search(type_label.get_label().str(), word) ||
260  translation::ci_search(race_label.get_label().str(), word) ||
261  translation::ci_search(unit_type_id, word);
262 
263  if(!found) {
264  // one word doesn't match, we don't reach words.end()
265  break;
266  }
267  }
268 
269  show_items[i] = found;
270  }
271  }
272 
273  list.set_row_shown(show_items);
274 }
275 
277 {
278  gender_ = val;
279 
281 }
282 
284 {
285  menu_button& var_box = find_widget<menu_button>(this, "variation_box", false);
286  variation_ = var_box.get_value_config()["variation_id"].str();
287 
289 }
290 
291 } // namespace dialogs
Abstract base class for all modal dialogs.
int get_retval() const
Returns the cached window exit code.
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:66
group< unit_race::GENDER > gender_toggle
Definition: unit_create.hpp:88
unit_race::GENDER gender()
Gender choice from the user.
Definition: unit_create.hpp:52
std::vector< std::string > last_words_
Definition: unit_create.hpp:72
void list_item_clicked()
Callbacks.
virtual void pre_show(window &window) override
Actions to be taken before showing the window.
Definition: unit_create.cpp:53
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:64
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
The listbox class.
Definition: listbox.hpp:43
bool select_last_row(const bool select=true)
Does exactly as advertised: selects the list's last row.
Definition: listbox.hpp:189
void set_row_shown(const unsigned row, const bool shown)
Makes a row visible or invisible.
Definition: listbox.cpp:136
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:621
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:59
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:230
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:613
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:118
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:268
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:124
const ::config & get_value_config() const
Returns the entire config object for the selected row.
Definition: menu_button.hpp:70
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
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.
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
base class of top level items, the only item which needs to store the final canvases to draw on.
Definition: window.hpp:61
void keyboard_capture(widget *widget)
Definition: window.cpp:1221
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1227
bool empty() const
Definition: tstring.hpp:186
const std::string & str() const
Definition: tstring.hpp:190
@ 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:1259
const unit_type_map & types() const
Definition: types.hpp:395
A single unit type that the player may recruit.
Definition: types.hpp:43
const unit_type & get_variation(const std::string &id) const
Definition: types.cpp:474
@ FULL
Definition: types.hpp:74
const t_string & variation_name() const
Definition: types.hpp:174
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:138
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...
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:34
std::map< std::string, t_string > widget_item
Definition: widget.hpp:31
@ 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:565
std::vector< std::string > split(const config_attribute_value &val)
static map_location::DIRECTION n
unit_type_data unit_types
Definition: types.cpp:1486
static unit_race::GENDER last_gender
Definition: unit_create.cpp:37
static std::string last_chosen_type_id
Definition: unit_create.cpp:35
static std::string last_variation
Definition: unit_create.cpp:36