The Battle for Wesnoth  1.19.7+dev
units_dialog.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 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 "game_board.hpp"
21 #include "gettext.hpp"
23 #include "gui/dialogs/message.hpp"
24 #include "gui/widgets/listbox.hpp"
25 #include "gui/widgets/button.hpp"
26 #include "gui/widgets/image.hpp"
27 #include "gui/widgets/label.hpp"
30 #include "gui/widgets/text_box.hpp"
33 #include "gui/widgets/window.hpp"
34 #include "help/help.hpp"
35 #include "log.hpp"
36 #include "replay_helper.hpp"
37 #include "play_controller.hpp"
38 #include "resources.hpp"
39 #include "serialization/markup.hpp"
40 #include "synced_context.hpp"
41 #include "team.hpp"
42 #include "units/helper.hpp"
43 #include "units/unit.hpp"
44 #include "units/ptr.hpp"
45 #include "units/types.hpp"
46 #include "utils/ci_searcher.hpp"
47 #include "whiteboard/manager.hpp"
48 
49 #include <functional>
50 #include <string>
51 
52 static lg::log_domain log_display("display");
53 #define LOG_DP LOG_STREAM(info, log_display)
54 
55 namespace gui2::dialogs
56 {
57 
58 namespace
59 {
60 // Index 2 is by-level
61 static std::pair sort_default{ std::string{"unit_name"}, sort_order::type::ascending };
62 static utils::optional<decltype(sort_default)> sort_last;
63 }
64 
65 REGISTER_DIALOG(units_dialog)
66 
68  : modal_dialog(window_id())
69  , selected_index_(-1)
70  , row_num_(0)
71  , ok_label_(_("OK"))
72  , cancel_label_(_("Cancel"))
73  , show_header_(true)
74  , gender_(unit_race::GENDER::MALE)
75  , variation_()
76  , filter_options_()
77  , last_words_()
78  , gender_toggle_()
79 {
80 }
81 
82 namespace {
83 
84 template<typename T>
85 void dump_recall_list_to_console(const T& units)
86 {
87  log_scope2(log_display, "dump_recall_list_to_console()")
88 
89  LOG_DP << "size: " << units.size();
90 
91  std::size_t idx = 0;
92  for(const auto& u_ptr : units) {
93  LOG_DP << "\tunit[" << (idx++) << "]: " << u_ptr->id() << " name = '" << u_ptr->name() << "'";
94  }
95 }
96 
97 std::string get_title_suffix(int side_num)
98 {
100  return "";
101  }
102 
103  unit_map& units = resources::gameboard->units();
104 
105  int controlled_recruiters = 0;
106  for(const auto& team : resources::gameboard->teams()) {
107  if(team.is_local_human() && !team.recruits().empty() && units.find_leader(team.side()) !=units.end()) {
108  ++controlled_recruiters;
109  }
110  }
111 
112  std::stringstream msg;
113  if(controlled_recruiters >= 2) {
115  if(leader != resources::gameboard->units().end() && !leader->name().empty()) {
116  msg << " (" << leader->name() << ")";
117  }
118  }
119 
120  return msg.str();
121 }
122 }
123 
125 {
126  text_box& filter = find_widget<text_box>("filter_box");
128 
129  listbox& list = find_widget<listbox>("main_list");
131 
133  find_widget<button>("show_help"),
134  std::bind(&units_dialog::show_help, this));
135 
136  list.clear();
137 
139  add_to_keyboard_chain(&list);
140 
141  show_list(list);
142 
143  find_widget<label>("title").set_label(title_);
144  find_widget<button>("ok").set_label(ok_label_);
145  find_widget<button>("cancel").set_label(cancel_label_);
146  find_widget<button>("dismiss").set_visible(false);
147  find_widget<button>("rename").set_visible(false);
148  find_widget<grid>("variation_gender_grid").set_visible(false);
149  find_widget<grid>("_header_grid").set_visible(show_header_ );
150 
152 }
153 
155 {
156  if (row_num_ == 0) {
157  return;
158  }
159 
160  for(std::size_t i = 0; i < row_num_; i++) {
161  widget_data row_data;
162  widget_item column;
163  formatter filter_fmt;
164  // generate tooltip for ith row
165  if (tooltip_gen_) {
166  column["tooltip"] = tooltip_gen_(i);
167  }
168 
169  for (const auto& [id, gen] : column_generators_) {
170  column["use_markup"] = "true";
171  // generate label for ith row and column with 'id'
172  column["label"] = gen(i);
173  if (id != "unit_image") {
174  filter_fmt << column["label"];
175  }
176  row_data.emplace(id, column);
177  }
178 
179  filter_options_.push_back(filter_fmt.str());
180  list.add_row(row_data);
181  }
182 
183  const auto [sorter_id, order] = sort_last.value_or(sort_default);
184  list.set_active_sorter(sorter_id, order, true);
185 }
186 
187 void units_dialog::rename_unit(std::vector<unit_const_ptr>& unit_list)
188 {
189  listbox& list = find_widget<listbox>("main_list");
190 
192  if (selected_index_ == -1) {
193  return;
194  }
195 
196  unit& selected_unit = const_cast<unit&>(*unit_list[selected_index_]);
197 
198  std::string name = selected_unit.name();
199 
200  if(gui2::dialogs::edit_text::execute(_("Rename Unit"), _("Name:"), name)) {
201  selected_unit.rename(name);
202 
203  list.get_row_grid(selected_index_)->find_widget<label>("unit_name").set_label(name);
204 
206  std::ostringstream filter_text;
207  filter_text << selected_unit.type_name() << " " << name << " " << std::to_string(selected_unit.level());
208  for(const std::string& trait : selected_unit.trait_names()) {
209  filter_text << " " << trait;
210  }
211  filter_options_.insert(filter_options_.begin() + selected_index_, filter_text.str());
212 
215  }
216 }
217 
218 void units_dialog::dismiss_unit(std::vector<unit_const_ptr>& unit_list, const team& team)
219 {
220  LOG_DP << "Recall list units:"; dump_recall_list_to_console(unit_list);
221 
222  listbox& list = find_widget<listbox>("main_list");
224  if (selected_index_ == -1) {
225  return;
226  }
227 
228  const unit& u = *unit_list[selected_index_].get();
229 
230  // If the unit is of level > 1, or is close to advancing, we warn the player about it
231  std::stringstream message;
232  if(u.loyal()) {
233  message << _("This unit is loyal and requires no upkeep.") << " " << (u.gender() == unit_race::MALE
234  ? _("Do you really want to dismiss him?")
235  : _("Do you really want to dismiss her?"));
236 
237  } else if(u.level() > 1) {
238  message << _("This unit is an experienced one, having advanced levels.") << " " << (u.gender() == unit_race::MALE
239  ? _("Do you really want to dismiss him?")
240  : _("Do you really want to dismiss her?"));
241 
242  } else if(u.experience() > u.max_experience()/2) {
243  message << _("This unit is close to advancing a level.") << " " << (u.gender() == unit_race::MALE
244  ? _("Do you really want to dismiss him?")
245  : _("Do you really want to dismiss her?"));
246  }
247 
248  if(!message.str().empty()) {
249  const int res = gui2::show_message(_("Dismiss Unit"), message.str(), message::yes_no_buttons);
250  if(res != gui2::retval::OK) {
251  return;
252  }
253  }
254 
255  unit_list.erase(unit_list.begin() + selected_index_);
256 
257  // Remove the entry from the dialog list
260 
261  // Remove the entry from the filter list
263  assert(filter_options_.size() == list.get_item_count());
264 
265  LOG_DP << "Dismissing a unit, side = " << u.side() << ", id = '" << u.id() << "'";
266  LOG_DP << "That side's recall list:";
267  dump_recall_list_to_console(team.recall_list());
268 
269  // Find the unit in the recall list.
270  unit_const_ptr dismissed_unit = team.recall_list().find_if_matches_id(u.id());
271  assert(dismissed_unit);
272 
273  // Record the dismissal, then delete the unit.
274  synced_context::run_and_throw("disband", replay_helper::get_disband(dismissed_unit->id()));
275 
276  // Close the dialog if all units are dismissed
277  if(list.get_item_count() == 0) {
279  }
280 }
281 
283 {
284  if (!topic_id_.empty()) {
286  }
287 }
288 
290 {
291  selected_index_ = find_widget<listbox>("main_list").get_selected_row();
292 
293  if (selected_index_ == -1) {
294  return;
295  }
296 
297  if (update_view_ != nullptr) {
299  }
300 }
301 
303 {
304  listbox& list = find_widget<listbox>("main_list");
305  if(const auto [sorter, order] = list.get_active_sorter(); sorter) {
306  sort_last.emplace(sorter->id(), order);
307  } else {
308  sort_last.reset();
309  }
310 
311  if(get_retval() == retval::OK) {
313  }
314 }
315 
317 {
318  const std::string& text = find_widget<text_box>("filter_box").get_value();
319  auto& list = find_widget<listbox>("main_list");
320  const std::size_t shown = list.filter_rows_by([this, match = translation::make_ci_matcher(text)](std::size_t row) {
321  return match(filter_options_[row]);
322  });
323 
324  // Disable rename and dismiss buttons if no units are shown
325  find_widget<button>("rename").set_active(shown > 0);
326  find_widget<button>("dismiss").set_active(shown > 0);
327 }
328 
330 {
331  gender_ = val;
332 
333  selected_index_ = find_widget<listbox>("main_list").get_selected_row();
334  if(selected_index_ == -1) {
335  return;
336  }
337 
338  if (update_view_ != nullptr) {
340  }
341 }
342 
344 {
345  variation_ = find_widget<menu_button>("variation_box").get_value_config()["variation_id"].str();
346 
347  selected_index_ = find_widget<listbox>("main_list").get_selected_row();
348  if(selected_index_ == -1) {
349  return;
350  }
351 
352  if (update_view_ != nullptr) {
354  }
355 }
356 
357 // } -------------------- BUILDERS -------------------- {
358 std::unique_ptr<units_dialog> units_dialog::build_create_dialog(const std::vector<const unit_type*>& types_list)
359 {
360  auto dlg = std::make_unique<units_dialog>();
361 
362  const auto type_gen = [](const auto& type) {
363  std::string type_name = type->type_name();
364  if(type_name != type->id()) {
365  type_name += " (" + type->id() + ")";
366  }
367  return type_name;
368  };
369 
370  const auto race_gen = [](const auto& type) {
371  return type->race()->plural_name();
372  };
373 
374  const auto populate_variations = [&dlg](const unit_type* ut) {
375  // Populate variations box
376  menu_button& var_box = dlg->find_widget<menu_button>("variation_box");
377  std::vector<config> var_box_values;
378  var_box_values.emplace_back("label", _("unit_variation^Default Variation"), "variation_id", "");
379 
380  const auto& uvars = ut->variation_types();
381 
382  var_box.set_active(!uvars.empty());
383 
384  unsigned n = 0, selection = 0;
385 
386  for(const auto& [uv_id, uv] : uvars) {
387  ++n;
388 
389  std::string uv_label;
390  if(!uv.variation_name().empty()) {
391  uv_label = uv.variation_name() + " (" + uv_id + ")";
392  } else if(!uv.type_name().empty() && uv.type_name() != ut->type_name()) {
393  uv_label = uv.type_name() + " (" + uv_id + ")";
394  } else {
395  uv_label = uv_id;
396  }
397 
398  var_box_values.emplace_back("label", uv_label, "variation_id", uv_id);
399 
400  if(uv_id == dlg->variation()) {
401  selection = n;
402  }
403  }
404 
405  // If we didn't find the variation selection again then the new selected
406  // unit type doesn't have that variation id.
407  if(!selection) {
408  dlg->clear_variation();
409  }
410 
411  var_box.set_values(var_box_values, selection);
412  };
413 
414  dlg->set_title(_("Create Unit"))
415  .set_ok_label(_("Create"))
416  .set_help_topic("..units")
417  .set_row_num(types_list.size())
418  .show_all_headers(false);
419 
420  // Gender and variation selectors
421  toggle_button& male_toggle = dlg->find_widget<toggle_button>("male_toggle");
422  toggle_button& female_toggle = dlg->find_widget<toggle_button>("female_toggle");
423  auto& group = dlg->get_toggle();
424  group.add_member(&male_toggle, unit_race::MALE);
425  group.add_member(&female_toggle, unit_race::FEMALE);
427  group.set_callback_on_value_change([&dlg](widget& /*w*/, const auto& gender){
428  dlg->update_gender(gender);
429  });
430  menu_button& var_box = dlg->find_widget<menu_button>("variation_box");
431  connect_signal_notify_modified(var_box, std::bind([&dlg]() {
432  dlg->update_variation();
433  }));
434 
435  // Listbox data
436  dlg->set_column("unit_name", types_list, type_gen, true)
437  .set_column("unit_details", types_list, race_gen, true)
438  .set_update_function([&](const std::size_t index) {
439  dlg->find_widget<grid>("variation_gender_grid").set_visible(true);
440 
441  const unit_type* ut = types_list[index];
442 
444  return ut->has_gender_variation(gender);
445  });
446 
447  populate_variations(ut);
448 
449  const auto& g = dlg->gender();
450  if (ut->has_gender_variation(g)) {
451  ut = &ut->get_gender_unit_type(g);
452  }
453 
454  const auto& var = dlg->variation();
455  if (!var.empty()) {
456  ut = &ut->get_variation(var);
457  }
458 
459  // Update preview
460  dlg->find_widget<unit_preview_pane>("unit_details").set_display_data(*ut);
461  });
462 
463  return dlg;
464 }
465 
466 std::unique_ptr<units_dialog> units_dialog::build_recruit_dialog(
467  const std::vector<const unit_type*>& recruit_list,
468  const team& team)
469 {
470  auto dlg = std::make_unique<units_dialog>();
471  dlg->set_title(_("Recruit Unit") + get_title_suffix(team.side()))
472  .set_ok_label(_("Recruit"))
473  .set_help_topic("recruit_and_recall")
474  .set_row_num(recruit_list.size())
475  .show_all_headers(false)
476  .set_column("unit_image", recruit_list, [&team](const auto& recruit) {
477  std::string image_string = recruit->image();
478  image_string += "~RC(" + recruit->flag_rgb() + ">" + team.color() + ")";
479  image_string += "~SCALE_INTO(72,72)";
480  return image_string;
481  })
482  .set_column("unit_details", recruit_list, [](const auto& recruit) {
483  return recruit->type_name() + unit_helper::format_cost_string(recruit->cost());
484  }, true)
485  .set_update_function([&](const std::size_t index) {
486  dlg->find_widget<unit_preview_pane>("unit_details").set_display_data(*recruit_list[index]);
487  });
488 
489  return dlg;
490 }
491 
492 std::unique_ptr<units_dialog> units_dialog::build_unit_list_dialog(std::vector<unit_const_ptr>& unit_list)
493 {
494  auto dlg = std::make_unique<units_dialog>();
495  dlg->set_title(_("Unit List"))
496  .set_ok_label(_("Scroll To"))
497  .set_help_topic("..units")
498  .set_row_num(unit_list.size());
499 
500  // Rename functionality
501  button& rename = dlg->find_widget<button>("rename");
502  connect_signal_mouse_left_click(rename, std::bind([&]() {
503  dlg->rename_unit(unit_list);
504  }));
505 
506  dlg->set_column("unit_name", unit_list, [](const auto& unit) {
507  return !unit->name().empty() ? unit->name().str() : font::unicode_en_dash;
508  }, true);
509 
510  dlg->set_column("unit_details", unit_list, [](const auto& unit) {
511  return unit->type_name().str();
512  }, true);
513 
514  dlg->set_column("unit_level", unit_list,
515  [](const auto& unit) {
517  },
518  [](const auto& u) {
519  return std::tuple(u->level(), -static_cast<int>(u->experience_to_advance()));
520  });
521 
522  dlg->set_column("unit_moves", unit_list,
523  [](const auto& unit) {
525  },
526  [](const auto& u) {
527  return u->movement_left();
528  });
529 
530  dlg->set_column("unit_hp", unit_list,
531  [](const auto& unit) {
533  },
534  [](const auto& u) {
535  return u->hitpoints();
536  });
537 
538  dlg->set_column("unit_xp", unit_list,
539  [](const auto& unit) {
540  std::stringstream exp_str;
541  if(unit->can_advance()) {
542  exp_str << unit->experience() << "/" << unit->max_experience();
543  } else {
544  exp_str << font::unicode_en_dash;
545  }
546  return markup::span_color(unit->xp_color(), exp_str.str());
547  },
548  [](const auto& u) {
549  // this allows 0/35, 0/100 etc to be sorted
550  // also sorts 23/35 before 0/35, after which 0/100 comes
551  return u->experience() + u->max_experience();
552  });
553  dlg->set_column("unit_status", unit_list, [](const auto& unit) {
554  // Status
555  if(unit->incapacitated()) {
556  return "misc/petrified.png";
557  }
558 
559  if(unit->poisoned()) {
560  return "misc/poisoned.png";
561  }
562 
563  if(unit->slowed()) {
564  return "misc/slowed.png";
565  }
566 
567  if(unit->invisible(unit->get_location(), false)) {
568  return "misc/invisible.png";
569  }
570 
571  return "";
572  });
573  dlg->set_column("unit_traits", unit_list, [](const auto& unit) {
574  return utils::join(unit->trait_names(), ", ");
575  }, true);
576  dlg->set_update_function([&](const std::size_t index) {
577  rename.set_visible(true);
578  rename.set_active(!unit_list[index]->unrenamable());
579  dlg->find_widget<unit_preview_pane>("unit_details").set_display_data(*unit_list[index]);
580  });
581 
582  return dlg;
583 }
584 
585 std::unique_ptr<units_dialog> units_dialog::build_recall_dialog(
586  std::vector<unit_const_ptr>& recall_list,
587  const team& team)
588 {
589  int wb_gold = 0;
590  if(resources::controller && resources::controller->get_whiteboard()) {
591  wb::future_map future; // So gold takes into account planned spending
592  wb_gold = resources::controller->get_whiteboard()->get_spent_gold_for(team.side());
593  }
594 
595  // Lambda to check if a unit is recallable
596  const auto& recallable = [wb_gold, team](const unit_const_ptr& unit) {
597  // Note: Our callers apply [filter_recall], but leave it to us
598  // to apply cost-based filtering.
599  const int recall_cost =
600  (unit->recall_cost() > -1 ? unit->recall_cost() : team.recall_cost());
601  return (recall_cost <= team.gold() - wb_gold);
602  };
603 
604  auto dlg = std::make_unique<units_dialog>();
605  dlg->set_title(_("Recall Unit") + get_title_suffix(team.side()))
606  .set_ok_label(_("Recall"))
607  .set_help_topic("recruit_and_recall")
608  .set_row_num(recall_list.size());
609 
610  // Rename functionality
611  button& rename = dlg->find_widget<button>("rename");
612  connect_signal_mouse_left_click(rename, std::bind([&]() {
613  dlg->rename_unit(recall_list);
614  }));
615 
616  // Dismiss functionality
617  button& dismiss = dlg->find_widget<button>("dismiss");
618  connect_signal_mouse_left_click(dlg->find_widget<button>("dismiss"), std::bind([&]() {
619  dlg->dismiss_unit(recall_list, team);
620  }));
621 
622  dlg->set_column("unit_image", recall_list, [recallable](const auto& unit) {
623  std::string mods = unit->image_mods();
624  if(unit->can_recruit()) { mods += "~BLIT(" + unit::leader_crown() + ")"; }
625  for(const std::string& overlay : unit->overlays()) {
626  mods += "~BLIT(" + overlay + ")";
627  }
628  if(!recallable(unit)) { mods += "~GS()"; }
629  mods += "~SCALE_INTO(72,72)";
630  return unit->absolute_image() + mods;
631  });
632 
633  dlg->set_column("unit_name", recall_list,
634  [recallable](const auto& unit) {
635  const std::string& name = !unit->name().empty() ? unit->name().str() : font::unicode_en_dash;
636  return unit_helper::maybe_inactive(name, recallable(unit));
637  },
638  [](const auto& unit) { return unit->name().str(); });
639 
640  dlg->set_column("unit_details", recall_list,
641  [recallable, &team](const auto& unit) {
642  std::stringstream details;
643  details << unit_helper::maybe_inactive(unit->type_name().str(), recallable(unit));
645  return details.str();
646  },
647  [](const auto& unit) { return unit->type_name().str(); });
648 
649  dlg->set_column("unit_moves", recall_list,
650  [](const auto& unit) {
652  },
653  [](const auto& recall) { return recall->movement_left(); });
654 
655  dlg->set_column("unit_level", recall_list,
656  [recallable](const auto& unit) {
657  return unit_helper::format_level_string(unit->level(), recallable(unit));
658  },
659  [](const auto& recall) {
660  return std::tuple(recall->level(), -static_cast<int>(recall->experience_to_advance()));
661  });
662 
663  dlg->set_column("unit_hp", recall_list,
664  [](const auto& unit) {
666  },
667  [](const auto& recall) { return recall->hitpoints(); });
668 
669  dlg->set_column("unit_xp", recall_list,
670  [](const auto& unit) {
671  std::stringstream exp_str;
672  if(unit->can_advance()) {
673  exp_str << unit->experience() << "/" << unit->max_experience();
674  } else {
675  exp_str << font::unicode_en_dash;
676  }
677  return markup::span_color(unit->xp_color(), exp_str.str());
678  },
679  [](const auto& recall) {
680  // this allows 0/35, 0/100 etc to be sorted
681  // also sorts 23/35 before 0/35, after which 0/100 comes
682  return recall->experience() + recall->max_experience();
683  });
684 
685  dlg->set_column("unit_traits", recall_list,
686  [recallable](const auto& unit) {
687  std::string traits;
688  for(const std::string& trait : unit->trait_names()) {
689  traits += (traits.empty() ? "" : "\n") + trait;
690  }
691  return unit_helper::maybe_inactive((!traits.empty() ? traits : font::unicode_en_dash), recallable(unit));
692  },
693  [](const auto& recall) {
694  return !recall->trait_names().empty() ? recall->trait_names().front().str() : "";
695  });
696 
697  dlg->show_header("unit_status", false);
698 
699  dlg->set_tooltip_generator(recall_list, [&](const auto& recall) {
700  if (!recallable(recall)) {
701  // Just set the tooltip on every single element in this row.
702  if(wb_gold > 0) {
703  return _("This unit cannot be recalled because you will not have enough gold at this point in your plan.");
704  } else {
705  return _("This unit cannot be recalled because you do not have enough gold.");
706  }
707  } else {
708  return std::string();
709  }
710  });
711 
712  dlg->set_update_function([&](const std::size_t index) {
713  rename.set_visible(true);
714  rename.set_active(!recall_list[index]->unrenamable());
715  dismiss.set_visible(true);
716  dlg->find_widget<unit_preview_pane>("unit_details").set_display_data(*recall_list[index]);
717  });
718 
719  return dlg;
720 }
721 
722 
723 
724 } // namespace dialogs
double g
Definition: astarsearch.cpp:63
std::ostringstream wrapper.
Definition: formatter.hpp:40
std::string str() const
Definition: formatter.hpp:61
virtual const unit_map & units() const override
Definition: game_board.hpp:107
Simple push button.
Definition: button.hpp:36
virtual void set_active(const bool active) override
See styled_widget::set_active.
Definition: button.cpp:64
Main class to show messages to the user.
Definition: message.hpp:36
@ yes_no_buttons
Shows a yes and no button.
Definition: message.hpp:81
Abstract base class for all modal dialogs.
int get_retval() const
Returns the cached window exit code.
void show_list(listbox &list)
static std::unique_ptr< units_dialog > build_recruit_dialog(const std::vector< const unit_type * > &recruit_list, const team &team)
std::vector< std::string > filter_options_
units_dialog & set_ok_label(const std::string &ok_label)
units_dialog & show_all_headers(const bool show=true)
std::function< std::string(std::size_t)> tooltip_gen_
std::function< void(const std::size_t)> update_view_
virtual void post_show() override
Actions to be taken after the window has been shown.
void rename_unit(std::vector< unit_const_ptr > &unit_list)
units_dialog & set_help_topic(const std::string &topic_id)
units_dialog & set_update_function(const std::function< void(const std::size_t)> &update_func)
Called to update the UI components, such as the preview pane, gender toggles and variation box.
static std::unique_ptr< units_dialog > build_unit_list_dialog(std::vector< unit_const_ptr > &units_list)
units_dialog & set_row_num(const std::size_t row_num)
static std::unique_ptr< units_dialog > build_create_dialog(const std::vector< const unit_type * > &types_list)
void dismiss_unit(std::vector< unit_const_ptr > &unit_list, const team &team)
std::map< std::string_view, std::function< std::string(std::size_t)> > column_generators_
units_dialog & set_column(std::string_view id, const std::vector< Value > &container, const Generator &generator, const bool use_as_sorter=false)
Corresponding to each widget in the row with id 'id', there is a lambda that generates the correspond...
virtual void pre_show() override
Actions to be taken before showing the window.
void list_item_clicked()
Callbacks.
unit_race::GENDER gender() const
Gender choice from the user.
static std::unique_ptr< units_dialog > build_recall_dialog(std::vector< unit_const_ptr > &recall_list, const team &team)
void update_gender(const unit_race::GENDER val)
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: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
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
Definition: listbox.cpp:267
void set_active_sorter(std::string_view id, sort_order::type order, bool select_first=false)
Sorts the listbox by a pre-set sorting option.
Definition: listbox.cpp:621
std::pair< widget *, sort_order::type > get_active_sorter() const
Returns a widget pointer to the active sorter, along with its corresponding order.
Definition: listbox.cpp:635
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:112
void clear()
Removes all the rows in the listbox, clearing it.
Definition: listbox.cpp:153
int get_selected_row() const
Returns the first selected row.
Definition: listbox.cpp:305
unsigned get_item_count() const
Returns the number of items in the listbox.
Definition: listbox.cpp:159
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
virtual void set_label(const t_string &text)
A widget that allows the user to input text in single line.
Definition: text_box.hpp:125
Base class for all widgets.
Definition: widget.hpp:55
void set_visible(const visibility visible)
Definition: widget.cpp:479
window * get_window()
Get the parent window.
Definition: widget.cpp:117
T * find_widget(const std::string_view id, const bool must_be_active, const bool must_exist)
Gets a widget with the wanted id.
Definition: widget.hpp:753
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:394
void keyboard_capture(widget *widget)
Definition: window.cpp:1207
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:761
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1213
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
std::shared_ptr< wb::manager > get_whiteboard() const
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id.
static config get_disband(const std::string &unit_id)
static bool run_and_throw(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
const std::string & str() const
Definition: tstring.hpp:198
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
const std::string & color() const
Definition: team.hpp:247
int side() const
Definition: team.hpp:180
int recall_cost() const
Definition: team.hpp:185
bool is_local_human() const
Definition: team.hpp:258
int gold() const
Definition: team.hpp:181
recall_list_manager & recall_list()
Definition: team.hpp:206
const std::set< std::string > & recruits() const
Definition: team.hpp:214
Container associating units to locations.
Definition: map.hpp:98
unit_iterator end()
Definition: map.hpp:428
unit_iterator find_leader(int side)
Definition: map.cpp:320
@ FEMALE
Definition: race.hpp:28
@ MALE
Definition: race.hpp:28
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 unit_type & get_variation(const std::string &id) const
Definition: types.cpp:476
bool has_gender_variation(const unit_race::GENDER gender) const
Definition: types.hpp:250
This class represents a single unit of a specific type.
Definition: unit.hpp:133
std::size_t i
Definition: function.cpp:1029
static std::string _(const char *str)
Definition: gettext.hpp:93
bool invisible(const map_location &loc, bool see_all=true) const
Definition: unit.cpp:2580
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:505
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:911
int level() const
The current level of this unit.
Definition: unit.hpp:559
const t_string & type_name() const
Gets the translatable name of this unit's type.
Definition: unit.hpp:369
int recall_cost() const
How much gold it costs to recall this unit, or -1 if the side's default recall cost is used.
Definition: unit.hpp:640
void rename(const std::string &name)
Attempts to rename this unit's translatable display name, taking the 'unrenamable' flag into account.
Definition: unit.hpp:424
int hitpoints() const
The current number of hitpoints this unit has.
Definition: unit.hpp:499
bool slowed() const
Check if the unit has been slowed.
Definition: unit.hpp:920
int experience() const
The current number of experience points this unit has.
Definition: unit.hpp:523
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition: unit.hpp:612
const std::string & id() const
Gets this unit's id.
Definition: unit.hpp:380
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
bool poisoned() const
Check if the unit has been poisoned.
Definition: unit.hpp:902
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:529
unit_race::GENDER gender() const
The gender of this unit.
Definition: unit.hpp:465
const t_string & name() const
Gets this unit's translatable display name.
Definition: unit.hpp:403
bool can_advance() const
Checks whether this unit has any options to advance to.
Definition: unit.hpp:272
color_t xp_color() const
Color for this unit's XP.
Definition: unit.cpp:1216
color_t hp_color() const
Color for this unit's current hitpoints.
Definition: unit.cpp:1172
std::string image_mods() const
Gets an IPF string containing all IPF image mods.
Definition: unit.cpp:2738
const std::vector< std::string > & overlays() const
Get the unit's overlay images.
Definition: unit.hpp:1677
std::string absolute_image() const
The name of the file to game_display (used in menus).
Definition: unit.cpp:2557
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1404
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1329
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1313
const std::vector< t_string > & trait_names() const
Gets the names of the currently registered traits.
Definition: unit.hpp:1098
bool loyal() const
Gets whether this unit is loyal - ie, it costs no upkeep.
Definition: unit.cpp:1701
This file contains the window object, this object is a top level container which has the event manage...
T end(const std::pair< T, T > &p)
Standard logging facilities (interface).
#define log_scope2(domain, description)
Definition: log.hpp:277
const std::string unicode_en_dash
Definition: constants.cpp:43
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
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
Definition: dispatcher.cpp:177
std::map< std::string, widget_item > widget_data
Definition: widget.hpp:36
std::map< std::string, t_string > widget_item
Definition: widget.hpp:33
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:148
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
@ CANCEL
Dialog was closed with the CANCEL button.
Definition: retval.hpp:38
void show_help(const std::string &show_topic)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:140
std::string span_color(const color_t &color, Args &&... data)
Definition: markup.hpp:68
game_board * gameboard
Definition: resources.cpp:20
play_controller * controller
Definition: resources.cpp:21
auto make_ci_matcher(std::string_view filter_text)
Returns a function which performs locale-aware case-insensitive search.
Definition: ci_searcher.hpp:24
std::string format_level_string(const int level, bool recallable)
Definition: helper.cpp:120
std::string format_cost_string(int unit_recall_cost, const int team_recall_cost)
Definition: helper.cpp:96
std::string maybe_inactive(const std::string &str, bool active)
Definition: helper.cpp:91
std::string format_movement_string(const int moves_left, const int moves_max)
Definition: helper.cpp:139
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::size_t index(std::string_view str, const std::size_t index)
Codepoint index corresponding to the nth character in a UTF-8 string.
Definition: unicode.cpp:70
constexpr auto filter
Definition: ranges.hpp:38
std::string join(const T &v, const std::string &s=",")
Generates a new string joining container items in a list.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
std::shared_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:27
Applies the planned unit map for the duration of the struct's life.
Definition: manager.hpp:253
static map_location::direction n
#define LOG_DP
static lg::log_domain log_display("display")