The Battle for Wesnoth  1.19.5+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 
20 #include "gui/core/log.hpp"
21 #include "gui/widgets/listbox.hpp"
22 #include "gui/widgets/label.hpp"
23 #include "gui/widgets/grid.hpp"
25 #include "gui/widgets/text_box.hpp"
28 #include "gui/widgets/window.hpp"
29 #include "gettext.hpp"
30 #include "units/types.hpp"
31 
32 #include <functional>
33 
34 static std::string last_chosen_type_id = "";
35 static std::string last_variation = "";
37 
38 namespace gui2::dialogs
39 {
40 
41 REGISTER_DIALOG(unit_create)
42 
44  : modal_dialog(window_id())
45  , gender_(last_gender)
46  , choice_(last_chosen_type_id)
47  , variation_(last_variation)
48  , last_words_()
49 {
50 }
51 
53 {
54  toggle_button& male_toggle
55  = find_widget<toggle_button>("male_toggle");
56  toggle_button& female_toggle
57  = find_widget<toggle_button>("female_toggle");
58 
61 
63 
65  std::bind(&unit_create::gender_toggle_callback, this, std::placeholders::_2));
66 
67  menu_button& var_box = find_widget<menu_button>("variation_box");
68 
70 
71  listbox& list = find_widget<listbox>("unit_type_list");
72 
73  text_box* filter
74  = find_widget<text_box>("filter_box", false, true);
75 
77  std::bind(&unit_create::filter_text_changed, this, std::placeholders::_2));
78 
79  keyboard_capture(filter);
80  add_to_keyboard_chain(&list);
81 
83 
84  list.clear();
85 
86  for(const auto& i : unit_types.types())
87  {
88  // Make sure this unit type is built with the data we need.
90 
91  units_.push_back(&i.second);
92 
93  widget_data row_data;
94  widget_item column;
95 
96  column["label"] = units_.back()->race()->plural_name();
97  row_data.emplace("race", column);
98 
99  column["label"] = units_.back()->type_name();
100  if(units_.back()->type_name().str() != units_.back()->id()) {
101  column["label"] += " (" + units_.back()->id() + ")";
102  }
103  row_data.emplace("unit_type", column);
104 
105  list.add_row(row_data);
106 
107  // Select the previous choice, if any.
108  if(!choice_.empty() && choice_ == i.first) {
109  list.select_last_row();
110  }
111  }
112 
113  if(units_.empty()) {
114  ERR_GUI_G << "no unit types found for unit create dialog; not good"
115  << std::endl;
116  }
117 
118  list.register_translatable_sorting_option(0, [this](const int i) { return (*units_[i]).race()->plural_name().str(); });
119  list.register_translatable_sorting_option(1, [this](const int i) { return (*units_[i]).type_name().str(); });
120 
121  // Select the first entry on sort if no previous selection was provided.
122  list.set_active_sorting_option({0, sort_order::type::ascending}, choice_.empty());
123 
125 }
126 
128 {
129  listbox& list = find_widget<listbox>("unit_type_list");
130 
131  choice_ = "";
132 
133  if(get_retval() != retval::OK) {
134  return;
135  }
136 
137  const int selected_row = list.get_selected_row();
138  if(selected_row < 0) {
139  return;
140  } else if(static_cast<std::size_t>(selected_row) >= units_.size()) {
141  // FIXME: maybe assert?
142  ERR_GUI_G << "unit create dialog has more list items than known unit "
143  "types; not good";
144  return;
145  }
146 
147  last_chosen_type_id = choice_ = units_[selected_row]->id();
150 }
151 
153 {
154  const int selected_row
155  = find_widget<listbox>("unit_type_list").get_selected_row();
156 
157  if(selected_row == -1) {
158  return;
159  }
160 
161  const unit_type* ut = &units_[selected_row]->get_gender_unit_type(gender_);
162 
163  if(!variation_.empty()) {
164  // This effectively translates to `ut = ut` if somehow variation_ does
165  // not refer to a variation that the unit type supports.
166  ut = &ut->get_variation(variation_);
167  }
168 
169  find_widget<unit_preview_pane>("unit_details").set_displayed_type(*ut);
170 }
171 
173 {
174  const int selected_row
175  = find_widget<listbox>("unit_type_list").get_selected_row();
176 
177  if(selected_row == -1) {
178  return;
179  }
180 
182 
184  return units_[selected_row]->has_gender_variation(gender);
185  });
186 
187  menu_button& var_box = find_widget<menu_button>("variation_box");
188  std::vector<config> var_box_values;
189  var_box_values.emplace_back("label", _("unit_variation^Default Variation"), "variation_id", "");
190 
191  const auto& ut = *units_[selected_row];
192  const auto& uvars = ut.variation_types();
193 
194  var_box.set_active(!uvars.empty());
195 
196  unsigned n = 0, selection = 0;
197 
198  for(const auto& pair : uvars) {
199  ++n;
200 
201  const std::string& uv_id = pair.first;
202  const unit_type& uv = pair.second;
203 
204  std::string uv_label;
205  if(!uv.variation_name().empty()) {
206  uv_label = uv.variation_name() + " (" + uv_id + ")";
207  } else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
208  uv_label = uv.type_name() + " (" + uv_id + ")";
209  } else {
210  uv_label = uv_id;
211  }
212 
213  var_box_values.emplace_back("label", uv_label, "variation_id", uv_id);
214 
215  if(uv_id == variation_) {
216  selection = n;
217  }
218  }
219 
220  // If we didn't find the variation selection again then the new selected
221  // unit type doesn't have that variation id.
222  if(!selection) {
223  variation_.clear();
224  }
225 
226  var_box.set_values(var_box_values, selection);
227 }
228 
229 void unit_create::filter_text_changed(const std::string& text)
230 {
231  listbox& list = find_widget<listbox>("unit_type_list");
232 
233  const std::vector<std::string> words = utils::split(text, ' ');
234 
235  if(words == last_words_)
236  return;
237  last_words_ = words;
238 
239  boost::dynamic_bitset<> show_items;
240  show_items.resize(list.get_item_count(), true);
241 
242  if(!text.empty()) {
243  for(unsigned int i = 0; i < list.get_item_count(); i++) {
244  grid* row = list.get_row_grid(i);
245 
246 // grid::iterator it = row->begin();
247  label& type_label = row->find_widget<label>("unit_type");
248  label& race_label = row->find_widget<label>("race");
249 
250  assert(i < units_.size());
251  const std::string& unit_type_id = units_[i] ? units_[i]->id() : "";
252 
253  bool found = false;
254  for(const auto & word : words)
255  {
256  found = translation::ci_search(type_label.get_label().str(), word) ||
257  translation::ci_search(race_label.get_label().str(), word) ||
258  translation::ci_search(unit_type_id, word);
259 
260  if(!found) {
261  // one word doesn't match, we don't reach words.end()
262  break;
263  }
264  }
265 
266  show_items[i] = found;
267  }
268  }
269 
270  list.set_row_shown(show_items);
271 }
272 
274 {
275  gender_ = val;
276 
278 }
279 
281 {
282  menu_button& var_box = find_widget<menu_button>("variation_box");
283  variation_ = var_box.get_value_config()["variation_id"].str();
284 
286 }
287 
288 } // namespace dialogs
Abstract base class for all modal dialogs.
int get_retval() const
Returns the cached window exit code.
virtual void pre_show() override
Actions to be taken before showing the window.
Definition: unit_create.cpp:52
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.
void filter_text_changed(const std::string &text)
virtual void post_show() override
Actions to be taken after the window has been shown.
void gender_toggle_callback(const unit_race::GENDER val)
std::vector< const unit_type * > units_
Definition: unit_create.hpp:64
Base container class.
Definition: grid.hpp:32
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:135
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:620
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:58
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:229
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:612
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:117
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:267
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:123
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
NOT_DANGLING T * find_widget(const std::string &id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:742
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1213
bool empty() const
Definition: tstring.hpp:194
const std::string & str() const
Definition: tstring.hpp:198
@ FEMALE
Definition: race.hpp:28
@ MALE
Definition: race.hpp:28
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:1257
const unit_type_map & types() const
Definition: types.hpp:396
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:476
@ 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:1028
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: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
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:1500
static unit_race::GENDER last_gender
Definition: unit_create.cpp:36
static std::string last_chosen_type_id
Definition: unit_create.cpp:34
static std::string last_variation
Definition: unit_create.cpp:35