The Battle for Wesnoth  1.19.7+dev
unit_recruit.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2016 - 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 
17 #include "serialization/markup.hpp"
19 #include "gui/widgets/button.hpp"
20 #include "gui/widgets/image.hpp"
21 #include "gui/widgets/listbox.hpp"
23 #include "gui/widgets/text_box.hpp"
24 #include "gui/widgets/window.hpp"
25 #include "gettext.hpp"
26 #include "help/help.hpp"
27 #include "team.hpp"
28 #include "units/types.hpp"
29 #include "utils/ci_searcher.hpp"
30 
31 #include <functional>
32 
33 namespace gui2::dialogs
34 {
35 
36 REGISTER_DIALOG(unit_recruit)
37 
38 unit_recruit::unit_recruit(std::map<const unit_type*, t_string>& recruit_map, team& team)
39  : modal_dialog(window_id())
40  , recruit_list_()
41  , recruit_map_(recruit_map)
42  , team_(team)
43  , selected_index_(0)
44 {
45  for(const auto& pair : recruit_map) {
46  recruit_list_.push_back(pair.first);
47  }
48  // Ensure the recruit list is sorted by name
49  std::sort(recruit_list_.begin(), recruit_list_.end(), [](const unit_type* t1, const unit_type* t2) {
50  return t1->type_name().str() < t2->type_name().str();
51  });
52 
53 }
54 
55 static const color_t inactive_row_color(0x96, 0x96, 0x96);
56 
57 static inline std::string gray_if_unrecruitable(const std::string& text, const bool is_recruitable)
58 {
59  return is_recruitable ? text : markup::span_color(inactive_row_color, text);
60 }
61 
62 // Compare unit_create::filter_text_change
63 void unit_recruit::filter_text_changed(const std::string& text)
64 {
65  find_widget<listbox>("recruit_list")
66  .filter_rows_by([this, match = translation::make_ci_matcher(text)](std::size_t row) {
67  const unit_type* type = recruit_list_[row];
68  if(!type) return true;
69 
70  const auto default_gender = !type->genders().empty() ? type->genders().front() : unit_race::MALE;
71  const auto race = type->race();
72 
73  // List of possible match criteria for this unit type.
74  // Empty values will never match.
75  return match(
76  (game_config::debug ? type->id() : ""),
77  type->type_name(),
78  std::to_string(type->level()),
79  unit_type::alignment_description(type->alignment(), default_gender),
80  (race ? race->name(default_gender) : ""),
81  (race ? race->plural_name() : "")
82  );
83  });
84 }
85 
87 {
88  text_box* filter = find_widget<text_box>("filter_box", false, true);
90  std::bind(&unit_recruit::filter_text_changed, this, std::placeholders::_2));
91 
92  listbox& list = find_widget<listbox>("recruit_list");
93 
95 
96  keyboard_capture(filter);
97  add_to_keyboard_chain(&list);
98 
100  find_widget<button>("show_help"),
101  std::bind(&unit_recruit::show_help, this));
102 
103  for(const auto& recruit : recruit_list_)
104  {
105  const t_string& error = recruit_map_[recruit];
106  widget_data row_data;
107  widget_item column;
108 
109  std::string image_string = recruit->image() + "~RC(" + recruit->flag_rgb() + ">"
110  + team_.color() + ")";
111 
112  const bool is_recruitable = error.empty();
113 
114  const std::string cost_string = std::to_string(recruit->cost());
115 
116  column["use_markup"] = "true";
117  if(!error.empty()) {
118  // Just set the tooltip on every single element in this row.
119  column["tooltip"] = error;
120  }
121 
122  column["label"] = image_string + (is_recruitable ? "" : "~GS()");
123  row_data.emplace("unit_image", column);
124 
125  column["label"] = gray_if_unrecruitable(recruit->type_name(), is_recruitable);
126  row_data.emplace("unit_type", column);
127 
128  column["label"] = gray_if_unrecruitable(cost_string, is_recruitable);
129  row_data.emplace("unit_cost", column);
130 
131  grid& grid = list.add_row(row_data);
132  if(!is_recruitable) {
133  image *gold_icon = dynamic_cast<image*>(grid.find("gold_icon", false));
134  assert(gold_icon);
135  gold_icon->set_image(gold_icon->get_image() + "~GS()");
136  }
137  }
138 
140 }
141 
143 {
144  const int selected_row
145  = find_widget<listbox>("recruit_list").get_selected_row();
146 
147  if(selected_row == -1) {
148  return;
149  }
150 
151  find_widget<unit_preview_pane>("recruit_details")
152  .set_display_data(*recruit_list_[selected_row]);
153 }
154 
156 {
157  help::show_help("recruit_and_recall");
158 }
159 
161 {
162  if(get_retval() == retval::OK) {
163  selected_index_ = find_widget<listbox>("recruit_list")
164  .get_selected_row();
165  }
166 }
167 
168 } // 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.
virtual void post_show() override
Actions to be taken after the window has been shown.
std::map< const unit_type *, t_string > & recruit_map_
void filter_text_changed(const std::string &text)
std::vector< const unit_type * > recruit_list_
A vector of unit types in the order listed in the UI.
Base container class.
Definition: grid.hpp:32
widget * find(const std::string_view id, const bool must_be_active) override
See widget::find.
Definition: grid.cpp:645
t_string get_image() const
Wrapper for label.
Definition: image.hpp:58
void set_image(const t_string &label)
Wrapper for set_label.
Definition: image.hpp:45
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
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
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
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
bool empty() const
Definition: tstring.hpp:194
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:242
@ MALE
Definition: race.hpp:28
A single unit type that the player may recruit.
Definition: types.hpp:43
static std::string alignment_description(unit_alignments::type align, unit_race::GENDER gender=unit_race::MALE)
Implementation detail of unit_type::alignment_description.
Definition: types.cpp:841
This file contains the window object, this object is a top level container which has the event manage...
const bool & debug
Definition: game_config.cpp:94
static std::string gray_if_unrecruitable(const std::string &text, const bool is_recruitable)
static const color_t inactive_row_color(0x96, 0x96, 0x96)
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
@ OK
Dialog was closed with the OK button.
Definition: retval.hpp:35
void show_help(const std::string &show_topic)
Open the help browser, show topic with id show_topic.
Definition: help.cpp:140
Functions to load and save images from/to disk.
std::string span_color(const color_t &color, Args &&... data)
Definition: markup.hpp:68
auto make_ci_matcher(std::string_view filter_text)
Returns a function which performs locale-aware case-insensitive search.
Definition: ci_searcher.hpp:24
The basic class for representing 8-bit RGB or RGBA colour values.
Definition: color.hpp:59