The Battle for Wesnoth  1.19.8+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 std::pair sort_default{ std::string{"unit_name"}, sort_order::type::ascending };
62 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  , num_rows_(0)
71  , ok_label_(_("OK"))
72  , cancel_label_(_("Cancel"))
73  , show_rename_(false)
74  , show_dismiss_(false)
75  , show_variations_(false)
76  , gender_(unit_race::GENDER::MALE)
77  , variation_()
78  , filter_options_()
79  , gender_toggle_()
80 {
81  for(widget* sort_toggle : find_widget<grid>("_header_grid")) {
82  // FIXME: only proper (non-spacer) header options are currently given an ID.
83  // We want the spacers to remain for sizing, but this is pretty fragile.
84  if(!sort_toggle->id().empty()) {
86  }
87  }
88 }
89 
90 namespace {
91 
92 template<typename T>
93 void dump_recall_list_to_console(const T& units)
94 {
95  log_scope2(log_display, "dump_recall_list_to_console()")
96 
97  LOG_DP << "size: " << units.size();
98 
99  std::size_t idx = 0;
100  for(const auto& u_ptr : units) {
101  LOG_DP << "\tunit[" << (idx++) << "]: " << u_ptr->id() << " name = '" << u_ptr->name() << "'";
102  }
103 }
104 
105 std::string get_title_suffix(int side_num)
106 {
107  if(!resources::gameboard) {
108  return "";
109  }
110 
111  unit_map& units = resources::gameboard->units();
112 
113  int controlled_recruiters = 0;
114  for(const auto& team : resources::gameboard->teams()) {
115  if(team.is_local_human() && !team.recruits().empty() && units.find_leader(team.side()) !=units.end()) {
116  ++controlled_recruiters;
117  }
118  }
119 
120  std::stringstream msg;
121  if(controlled_recruiters >= 2) {
123  if(leader != resources::gameboard->units().end() && !leader->name().empty()) {
124  msg << " (" << leader->name() << ")";
125  }
126  }
127 
128  return msg.str();
129 }
130 }
131 
133 {
134  text_box& filter = find_widget<text_box>("filter_box");
135  filter.on_modified([this](const auto& box) { filter_text_changed(box.text()); });
136 
137  listbox& list = find_widget<listbox>("main_list");
139 
141  find_widget<button>("show_help"),
142  std::bind(&units_dialog::show_help, this));
143 
145  add_to_keyboard_chain(&list);
146 
147  show_list(list);
148 
149  find_widget<label>("title").set_label(title_);
150  find_widget<button>("ok").set_label(ok_label_);
151  find_widget<button>("cancel").set_label(cancel_label_);
152  find_widget<button>("dismiss").set_visible(show_dismiss_);
153  find_widget<button>("rename").set_visible(show_rename_);
154  find_widget<grid>("variation_gender_grid").set_visible(show_variations_);
155 
156  // Gender and variation selectors
157  if(show_variations_) {
159  find_widget<menu_button>("variation_box"), [this](auto&&...) { update_variation(); });
160 
161  auto& group = get_toggle();
162  group.add_member(find_widget<toggle_button>("male_toggle", true, true), unit_race::MALE);
163  group.add_member(find_widget<toggle_button>("female_toggle", true, true), unit_race::FEMALE);
164 
167  [this](widget&, const unit_race::GENDER& gender) { update_gender(gender); });
168  }
169 
171 }
172 
174 {
175  if (num_rows_ == 0) {
176  return;
177  }
178 
179  for(std::size_t i = 0; i < num_rows_; i++) {
180  widget_data row_data;
181  widget_item column;
182  formatter filter_fmt;
183  // generate tooltip for ith row
184  if (tooltip_gen_) {
185  column["tooltip"] = tooltip_gen_(i);
186  }
187 
188  for (const auto& [id, gen] : column_generators_) {
189  column["use_markup"] = "true";
190  // generate label for ith row and column with 'id'
191  column["label"] = gen(i);
192  if (id != "unit_image") {
193  filter_fmt << column["label"];
194  }
195  row_data.emplace(id, column);
196  }
197 
198  filter_options_.push_back(filter_fmt.str());
199  list.add_row(row_data);
200  }
201 
202  const auto [sorter_id, order] = sort_last.value_or(sort_default);
203  list.set_active_sorter(sorter_id, order, true);
204 }
205 
206 void units_dialog::rename_unit(std::vector<unit_const_ptr>& unit_list)
207 {
208  listbox& list = find_widget<listbox>("main_list");
209 
211  if (selected_index_ == -1) {
212  return;
213  }
214 
215  unit& selected_unit = const_cast<unit&>(*unit_list[selected_index_]);
216 
217  std::string name = selected_unit.name();
218 
219  if(gui2::dialogs::edit_text::execute(_("Rename Unit"), _("Name:"), name)) {
220  selected_unit.rename(name);
221 
222  list.get_row_grid(selected_index_)->find_widget<label>("unit_name").set_label(name);
223 
225  std::ostringstream filter_text;
226  filter_text << selected_unit.type_name() << " " << name << " " << std::to_string(selected_unit.level());
227  for(const std::string& trait : selected_unit.trait_names()) {
228  filter_text << " " << trait;
229  }
230  filter_options_.insert(filter_options_.begin() + selected_index_, filter_text.str());
231 
234  }
235 }
236 
237 void units_dialog::dismiss_unit(std::vector<unit_const_ptr>& unit_list, const team& team)
238 {
239  LOG_DP << "Recall list units:"; dump_recall_list_to_console(unit_list);
240 
241  listbox& list = find_widget<listbox>("main_list");
243  if (selected_index_ == -1) {
244  return;
245  }
246 
247  const unit& u = *unit_list[selected_index_].get();
248 
249  // If the unit is of level > 1, or is close to advancing, we warn the player about it
250  std::stringstream message;
251  if(u.loyal()) {
252  message << _("This unit is loyal and requires no upkeep.") << " " << (u.gender() == unit_race::MALE
253  ? _("Do you really want to dismiss him?")
254  : _("Do you really want to dismiss her?"));
255 
256  } else if(u.level() > 1) {
257  message << _("This unit is an experienced one, having advanced levels.") << " " << (u.gender() == unit_race::MALE
258  ? _("Do you really want to dismiss him?")
259  : _("Do you really want to dismiss her?"));
260 
261  } else if(u.experience() > u.max_experience()/2) {
262  message << _("This unit is close to advancing a level.") << " " << (u.gender() == unit_race::MALE
263  ? _("Do you really want to dismiss him?")
264  : _("Do you really want to dismiss her?"));
265  }
266 
267  if(!message.str().empty()) {
268  const int res = gui2::show_message(_("Dismiss Unit"), message.str(), message::yes_no_buttons);
269  if(res != gui2::retval::OK) {
270  return;
271  }
272  }
273 
274  unit_list.erase(unit_list.begin() + selected_index_);
275 
276  // Remove the entry from the dialog list
279 
280  // Remove the entry from the filter list
282  assert(filter_options_.size() == list.get_item_count());
283 
284  LOG_DP << "Dismissing a unit, side = " << u.side() << ", id = '" << u.id() << "'";
285  LOG_DP << "That side's recall list:";
286  dump_recall_list_to_console(team.recall_list());
287 
288  // Find the unit in the recall list.
289  unit_const_ptr dismissed_unit = team.recall_list().find_if_matches_id(u.id());
290  assert(dismissed_unit);
291 
292  // Record the dismissal, then delete the unit.
293  synced_context::run_and_throw("disband", replay_helper::get_disband(dismissed_unit->id()));
294 
295  // Close the dialog if all units are dismissed
296  if(list.get_item_count() == 0) {
298  }
299 }
300 
302 {
303  if (!topic_id_.empty()) {
305  }
306 }
307 
309 {
310  selected_index_ = find_widget<listbox>("main_list").get_selected_row();
311 
312  if (selected_index_ == -1) {
313  return;
314  }
315 
316  fire(event::NOTIFY_MODIFIED, *this, nullptr);
317 }
318 
320 {
321  listbox& list = find_widget<listbox>("main_list");
322  if(const auto [sorter, order] = list.get_active_sorter(); sorter) {
323  sort_last.emplace(sorter->id(), order);
324  } else {
325  sort_last.reset();
326  }
327 
328  if(get_retval() == retval::OK) {
330  }
331 }
332 
333 void units_dialog::filter_text_changed(const std::string& text)
334 {
335  auto& list = find_widget<listbox>("main_list");
336  const std::size_t shown = list.filter_rows_by(
337  [this, match = translation::make_ci_matcher(text)](std::size_t row) { return match(filter_options_[row]); });
338 
339  // Disable rename and dismiss buttons if no units are shown
340  find_widget<button>("rename").set_active(shown > 0);
341  find_widget<button>("dismiss").set_active(shown > 0);
342 }
343 
345 {
346  gender_ = val;
347 
348  selected_index_ = find_widget<listbox>("main_list").get_selected_row();
349  if(selected_index_ == -1) {
350  return;
351  }
352 
353  fire(event::NOTIFY_MODIFIED, *this, nullptr);
354 }
355 
357 {
358  variation_ = find_widget<menu_button>("variation_box").get_value_config()["variation_id"].str();
359 
360  selected_index_ = find_widget<listbox>("main_list").get_selected_row();
361  if(selected_index_ == -1) {
362  return;
363  }
364 
365  fire(event::NOTIFY_MODIFIED, *this, nullptr);
366 }
367 
368 // } -------------------- BUILDERS -------------------- {
369 std::unique_ptr<units_dialog> units_dialog::build_create_dialog(const std::vector<const unit_type*>& types_list)
370 {
371  auto dlg = std::make_unique<units_dialog>();
372 
373  const auto type_gen = [](const auto& type) {
374  std::string type_name = type->type_name();
375  if(type_name != type->id()) {
376  type_name += " (" + type->id() + ")";
377  }
378  return type_name;
379  };
380 
381  const auto race_gen = [](const auto& type) {
382  return type->race()->plural_name();
383  };
384 
385  const auto populate_variations = [&dlg](const unit_type& ut) {
386  // Populate variations box
387  menu_button& var_box = dlg->find_widget<menu_button>("variation_box");
388  std::vector<config> var_box_values;
389  var_box_values.emplace_back("label", _("unit_variation^Default Variation"), "variation_id", "");
390 
391  const auto& uvars = ut.variation_types();
392 
393  var_box.set_active(!uvars.empty());
394 
395  unsigned n = 0, selection = 0;
396 
397  for(const auto& [uv_id, uv] : uvars) {
398  ++n;
399 
400  std::string uv_label;
401  if(!uv.variation_name().empty()) {
402  uv_label = uv.variation_name() + " (" + uv_id + ")";
403  } else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
404  uv_label = uv.type_name() + " (" + uv_id + ")";
405  } else {
406  uv_label = uv_id;
407  }
408 
409  var_box_values.emplace_back("label", uv_label, "variation_id", uv_id);
410 
411  if(uv_id == dlg->variation()) {
412  selection = n;
413  }
414  }
415 
416  // If we didn't find the variation selection again then the new selected
417  // unit type doesn't have that variation id.
418  if(!selection) {
419  dlg->clear_variation();
420  }
421 
422  var_box.set_values(var_box_values, selection);
423  };
424 
425  dlg->set_title(_("Create Unit"))
426  .set_ok_label(_("Create"))
427  .set_help_topic("..units")
428  .set_row_num(types_list.size())
429  .set_show_variations(true);
430 
431  // Listbox data
432  auto set_column = dlg->make_column_builder(types_list);
433 
434  set_column("unit_name", type_gen, sort_type::generator);
435  set_column("unit_details", race_gen, sort_type::generator);
436 
437  dlg->on_modified([populate_variations, &dlg, &types_list](std::size_t index) -> const auto& {
438  const unit_type* ut = types_list[index];
439 
440  dlg->get_toggle().set_members_enabled(
441  [ut](const unit_race::GENDER& gender) { return ut->has_gender_variation(gender); });
442 
443  populate_variations(*ut);
444 
445  const auto& g = dlg->gender();
446  if(ut->has_gender_variation(g)) {
447  ut = &ut->get_gender_unit_type(g);
448  }
449 
450  const auto& var = dlg->variation();
451  if(!var.empty()) {
452  ut = &ut->get_variation(var);
453  }
454 
455  return *ut;
456  });
457 
458  return dlg;
459 }
460 
461 std::unique_ptr<units_dialog> units_dialog::build_recruit_dialog(
462  const std::vector<const unit_type*>& recruit_list,
463  const team& team)
464 {
465  auto dlg = std::make_unique<units_dialog>();
466  auto set_column = dlg->make_column_builder(recruit_list);
467 
468  set_column("unit_image", [&team](const auto& recruit) {
469  std::string image_string = recruit->icon();
470  if (image_string.empty()) {
471  image_string = recruit->image();
472  }
473  image_string += "~RC(" + recruit->flag_rgb() + ">" + team.color() + ")";
474  image_string += "~SCALE_INTO(72,72)";
475  return image_string;
476  }, sort_type::none);
477 
478  set_column("unit_details", [](const auto& recruit) {
479  return recruit->type_name() + unit_helper::format_cost_string(recruit->cost());
481 
482  dlg->set_title(_("Recruit Unit") + get_title_suffix(team.side()))
483  .set_ok_label(_("Recruit"))
484  .set_help_topic("recruit_and_recall")
485  .set_row_num(recruit_list.size());
486 
487  dlg->on_modified([&recruit_list](std::size_t index) -> const auto& { return *recruit_list[index]; });
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  .set_show_rename(true);
500 
501  // Rename functionality
502  button& rename = dlg->find_widget<button>("rename");
503  connect_signal_mouse_left_click(rename, std::bind([&]() {
504  dlg->rename_unit(unit_list);
505  }));
506 
507  auto set_column = dlg->make_column_builder(unit_list);
508 
509  set_column("unit_name",
510  [](const auto& unit) {
511  return !unit->name().empty() ? unit->name().str() : font::unicode_en_dash;
513 
514  set_column("unit_details",
515  [](const auto& unit) {
516  return unit->type_name().str();
518 
519  set_column("unit_level",
520  [](const auto& unit) {
522  },
523  [](const auto& unit) {
524  return std::tuple(unit->level(), -static_cast<int>(unit->experience_to_advance()));
525  });
526 
527  set_column("unit_moves",
528  [](const auto& unit) {
530  },
531  [](const auto& unit) {
532  return unit->movement_left();
533  });
534 
535  set_column("unit_hp",
536  [](const auto& unit) {
538  },
539  [](const auto& unit) {
540  return unit->hitpoints();
541  });
542 
543  set_column("unit_xp",
544  [](const auto& unit) {
545  std::stringstream exp_str;
546  if(unit->can_advance()) {
547  exp_str << unit->experience() << "/" << unit->max_experience();
548  } else {
549  exp_str << font::unicode_en_dash;
550  }
551  return markup::span_color(unit->xp_color(), exp_str.str());
552  },
553  [](const auto& unit) {
554  // this allows 0/35, 0/100 etc to be sorted
555  // also sorts 23/35 before 0/35, after which 0/100 comes
556  return unit->experience() + unit->max_experience();
557  });
558 
559  set_column("unit_status", [](const auto& unit) {
560  // Status
561  if(unit->incapacitated()) {
562  return "misc/petrified.png";
563  }
564 
565  if(unit->poisoned()) {
566  return "misc/poisoned.png";
567  }
568 
569  if(unit->slowed()) {
570  return "misc/slowed.png";
571  }
572 
573  if(unit->invisible(unit->get_location(), false)) {
574  return "misc/invisible.png";
575  }
576 
577  return "";
578  }, sort_type::none);
579 
580  set_column("unit_traits", [](const auto& unit) {
581  return utils::join(unit->trait_names(), ", ");
583 
584  dlg->on_modified([&unit_list, &rename](std::size_t index) -> const auto& {
585  auto& unit = unit_list[index];
586  rename.set_active(!unit->unrenamable());
587  return *unit;
588  });
589 
590  return dlg;
591 }
592 
593 std::unique_ptr<units_dialog> units_dialog::build_recall_dialog(
594  std::vector<unit_const_ptr>& recall_list,
595  const team& team)
596 {
597  int wb_gold = 0;
598  if(resources::controller && resources::controller->get_whiteboard()) {
599  wb::future_map future; // So gold takes into account planned spending
600  wb_gold = resources::controller->get_whiteboard()->get_spent_gold_for(team.side());
601  }
602 
603  // Lambda to check if a unit is recallable
604  const auto recallable = [wb_gold, &team](const unit& unit) {
605  // Note: Our callers apply [filter_recall], but leave it to us to apply cost-based filtering.
606  const int recall_cost = unit.recall_cost() > -1 ? unit.recall_cost() : team.recall_cost();
607  return recall_cost <= team.gold() - wb_gold;
608  };
609 
610  auto dlg = std::make_unique<units_dialog>();
611  dlg->set_title(_("Recall Unit") + get_title_suffix(team.side()))
612  .set_ok_label(_("Recall"))
613  .set_help_topic("recruit_and_recall")
614  .set_row_num(recall_list.size())
615  .set_show_rename(true)
616  .set_show_dismiss(true);
617 
618  // Rename functionality
619  button& rename = dlg->find_widget<button>("rename");
620  connect_signal_mouse_left_click(rename, std::bind([&]() {
621  dlg->rename_unit(recall_list);
622  }));
623 
624  // Dismiss functionality
625  button& dismiss = dlg->find_widget<button>("dismiss");
626  connect_signal_mouse_left_click(dismiss, std::bind([&]() {
627  dlg->dismiss_unit(recall_list, team);
628  }));
629 
630  auto set_column = dlg->make_column_builder(recall_list);
631 
632  set_column("unit_image", [recallable](const auto& unit) {
633  std::string mods = unit->image_mods();
634  if(unit->can_recruit()) { mods += "~BLIT(" + unit::leader_crown() + ")"; }
635  for(const std::string& overlay : unit->overlays()) {
636  mods += "~BLIT(" + overlay + ")";
637  }
638  if(!recallable(*unit)) { mods += "~GS()"; }
639  mods += "~SCALE_INTO(72,72)";
640  return unit->absolute_image() + mods;
641  }, sort_type::none);
642 
643  set_column("unit_name",
644  [recallable](const auto& unit) {
645  const std::string& name = !unit->name().empty() ? unit->name().str() : font::unicode_en_dash;
646  return unit_helper::maybe_inactive(name, recallable(*unit));
647  },
648  [](const auto& unit) {
649  return unit->name().str();
650  });
651 
652  set_column("unit_details",
653  [recallable, &team](const auto& unit) {
654  std::stringstream details;
655  details << unit_helper::maybe_inactive(unit->type_name().str(), recallable(*unit));
657  return details.str();
658  },
659  [](const auto& unit) {
660  return unit->type_name().str();
661  });
662 
663  set_column("unit_moves",
664  [](const auto& unit) {
666  },
667  [](const auto& unit) {
668  return unit->movement_left();
669  });
670 
671  set_column("unit_level",
672  [recallable](const auto& unit) {
673  return unit_helper::format_level_string(unit->level(), recallable(*unit));
674  },
675  [](const auto& unit) {
676  return std::tuple(unit->level(), -static_cast<int>(unit->experience_to_advance()));
677  });
678 
679  set_column("unit_hp",
680  [](const auto& unit) {
682  },
683  [](const auto& unit) {
684  return unit->hitpoints();
685  });
686 
687  set_column("unit_xp",
688  [](const auto& unit) {
689  if(unit->can_advance()) {
690  return markup::span_color(unit->xp_color(), unit->experience(), "/", unit->max_experience());
691  } else {
692  return markup::span_color(unit->xp_color(), font::unicode_en_dash);
693  }
694  },
695  [](const auto& unit) {
696  // this allows 0/35, 0/100 etc to be sorted
697  // also sorts 23/35 before 0/35, after which 0/100 comes
698  return unit->experience() + unit->max_experience();
699  });
700 
701  set_column("unit_traits",
702  [recallable](const auto& unit) {
703  std::string traits;
704  for(const std::string& trait : unit->trait_names()) {
705  traits += (traits.empty() ? "" : "\n") + trait;
706  }
707  return unit_helper::maybe_inactive((!traits.empty() ? traits : font::unicode_en_dash), recallable(*unit));
708  },
709  [](const auto& unit) {
710  return !unit->trait_names().empty() ? unit->trait_names().front().str() : "";
711  });
712 
713  dlg->set_tooltip_generator([recallable, wb_gold, &recall_list](std::size_t index) {
714  if(recallable(*recall_list[index])) {
715  return std::string();
716  }
717 
718  // Just set the tooltip on every single element in this row.
719  if(wb_gold > 0) {
720  return _("This unit cannot be recalled because you will not have enough gold at this point in your plan.");
721  } else {
722  return _("This unit cannot be recalled because you do not have enough gold.");
723  }
724  });
725 
726  dlg->on_modified([&recall_list, &rename](std::size_t index) -> const auto& {
727  const auto& unit = recall_list[index];
728  rename.set_active(!unit->unrenamable());
729  return *unit;
730  });
731 
732  return dlg;
733 }
734 
735 
736 
737 } // 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.
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)
auto make_column_builder(const std::vector< Value > &list)
Creates a generator function which registers secondary generator and sorter functions for the list co...
std::function< std::string(std::size_t)> tooltip_gen_
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_show_variations(bool show=true)
group< unit_race::GENDER > & get_toggle()
void filter_text_changed(const std::string &text)
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_
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)
bool fire(const ui_event event, widget &target)
Fires an event which has no extra parameters.
Definition: dispatcher.cpp:74
void on_modified(const Func &func)
Sets a common callback function for all members.
Definition: group.hpp:121
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
Definition: group.hpp:41
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:110
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:626
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:640
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
Definition: listbox.cpp:112
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
@ invisible
The user set the widget invisible, that means:
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:747
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
Definition: window.hpp:395
void keyboard_capture(widget *widget)
Definition: window.cpp:1193
void invalidate_layout()
Updates the size of the window.
Definition: window.cpp:752
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
Definition: window.cpp:1199
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
int get_retval()
Definition: window.hpp:402
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:199
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:2560
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:904
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
bool unrenamable() const
Whether this unit can be renamed.
Definition: unit.hpp:436
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:913
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:895
unsigned int experience_to_advance() const
The number of experience points this unit needs to level up, or 0 if current XP > max XP.
Definition: unit.hpp:541
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:1208
color_t hp_color() const
Color for this unit's current hitpoints.
Definition: unit.cpp:1164
std::string image_mods() const
Gets an IPF string containing all IPF image mods.
Definition: unit.cpp:2718
const std::vector< std::string > & overlays() const
Get the unit's overlay images.
Definition: unit.hpp:1670
std::string absolute_image() const
The name of the file to game_display (used in menus).
Definition: unit.cpp:2537
const map_location & get_location() const
The current map location this unit is at.
Definition: unit.hpp:1397
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1322
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1306
const std::vector< t_string > & trait_names() const
Gets the names of the currently registered traits.
Definition: unit.hpp:1091
bool loyal() const
Gets whether this unit is loyal - ie, it costs no upkeep.
Definition: unit.cpp:1693
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)
@ NOTIFY_MODIFIED
Definition: handler.hpp:158
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)
Applies Pango markup to the input specifying its display color.
Definition: markup.hpp:87
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")