15 #define GETTEXT_DOMAIN "wesnoth-lib"
53 #define LOG_DP LOG_STREAM(info, log_display)
61 std::pair sort_default{ std::string{
"unit_name"}, sort_order::type::ascending };
62 utils::optional<decltype(sort_default)> sort_last;
64 std::string star(
bool starred)
67 return starred ?
"\u2605" :
"\u2606";
79 , cancel_label_(
_("Cancel"))
81 , show_dismiss_(false)
82 , show_mark_favorite_(false)
83 , show_variations_(false)
84 , sort_order_(sort_default)
90 for(
widget* sort_toggle : find_widget<grid>(
"_header_grid")) {
93 if(!sort_toggle->id().empty()) {
102 void dump_recall_list_to_console(
const T& units)
109 for(const auto& u_ptr : units) {
110 LOG_DP <<
"\tunit[" << (idx++) <<
"]: " << u_ptr->id() <<
" name = '" << u_ptr->name() <<
"'";
114 std::string get_title_suffix(
int side_num)
122 int controlled_recruiters = 0;
125 ++controlled_recruiters;
129 std::stringstream
msg;
130 if(controlled_recruiters >= 2) {
133 msg <<
" (" << leader->name() <<
")";
146 listbox& list = find_widget<listbox>(
"main_list");
150 find_widget<button>(
"show_help"),
164 find_widget<label>(
"title").set_label(
title_);
165 find_widget<button>(
"ok").set_label(
ok_label_);
168 find_widget<button>(
"rename").set_visible(
show_rename_);
175 find_widget<menu_button>(
"variation_box"), [
this](
auto&&...) {
update_variation(); });
213 row_data.emplace(
id,
widget_item{{
"use_markup",
"true" }, {
"label", std::move(result) }});
224 const auto [sorter_id, order] = sort_last.value_or(
sort_order_);
234 listbox& list = find_widget<listbox>(
"main_list");
243 std::string name = selected_unit.
name();
245 if(gui2::dialogs::edit_text::execute(
_(
"Rename Unit"),
_(
"Name:"), name)) {
246 selected_unit.
rename(name);
260 LOG_DP <<
"Recall list units:"; dump_recall_list_to_console(unit_list);
262 listbox& list = find_widget<listbox>(
"main_list");
279 ?
_(
"Do you really want to dismiss him?")
280 :
_(
"Do you really want to dismiss her?"));
282 }
else if(u.
level() > 1) {
284 ?
_(
"Do you really want to dismiss him?")
285 :
_(
"Do you really want to dismiss her?"));
289 ?
_(
"Do you really want to dismiss him?")
290 :
_(
"Do you really want to dismiss her?"));
313 LOG_DP <<
"Dismissing a unit, side = " << u.
side() <<
", id = '" << u.
id() <<
"'";
314 LOG_DP <<
"That side's recall list:";
319 assert(dismissed_unit);
332 listbox& list = find_widget<listbox>(
"main_list");
358 selected_index_ = find_widget<listbox>(
"main_list").get_selected_row();
369 listbox& list = find_widget<listbox>(
"main_list");
371 sort_last.emplace(sorter->id(), order);
381 const std::size_t shown = find_widget<listbox>(
"main_list")
387 find_widget<button>(
"rename").set_active(shown > 0);
388 find_widget<button>(
"dismiss").set_active(shown > 0);
395 selected_index_ = find_widget<listbox>(
"main_list").get_selected_row();
405 variation_ = find_widget<menu_button>(
"variation_box").get_value_config()[
"variation_id"].str();
407 selected_index_ = find_widget<listbox>(
"main_list").get_selected_row();
418 auto dlg = std::make_unique<units_dialog>();
420 const auto type_gen = [](
const auto&
type) {
421 std::string type_name =
type->type_name();
422 if(type_name !=
type->id()) {
423 type_name +=
" (" +
type->id() +
")";
428 const auto race_gen = [](
const auto&
type) {
429 return type->race()->plural_name();
432 const auto populate_variations = [&dlg](
const unit_type& ut) {
435 std::vector<config> var_box_values;
436 var_box_values.emplace_back(
"label",
_(
"unit_variation^Default Variation"),
"variation_id",
"");
438 const auto& uvars = ut.variation_types();
442 unsigned n = 0, selection = 0;
444 for(
const auto& [uv_id, uv] : uvars) {
447 std::string uv_label;
448 if(!uv.variation_name().empty()) {
449 uv_label = uv.variation_name() +
" (" + uv_id +
")";
450 }
else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
451 uv_label = uv.type_name() +
" (" + uv_id +
")";
456 var_box_values.emplace_back(
"label", uv_label,
"variation_id", uv_id);
458 if(uv_id == dlg->variation()) {
466 dlg->clear_variation();
469 var_box.
set_values(var_box_values, selection);
472 dlg->set_title(
_(
"Create Unit"))
484 dlg->on_modified([populate_variations, &dlg, &types_list](std::size_t
index) ->
const auto& {
487 if (dlg->is_selected() && (
static_cast<int>(
index) == dlg->get_selected_index())) {
488 dlg->get_toggle().set_member_states(dlg->gender());
490 dlg->get_toggle().set_members_enabled(
491 [ut](const unit_race::GENDER& gender) { return ut->has_gender_variation(gender); });
494 populate_variations(*ut);
496 const auto&
g = dlg->gender();
497 if(ut->has_gender_variation(
g)) {
498 ut = &ut->get_gender_unit_type(
g);
501 const auto& var = dlg->variation();
503 ut = &ut->get_variation(var);
513 const std::vector<const unit_type*>& recruit_list,
518 auto dlg = std::make_unique<units_dialog>();
519 auto set_column = dlg->make_column_builder(recruit_list);
521 set_column(
"unit_image", [&](
const auto& recruit) {
522 std::string image_string = recruit->icon();
523 if (image_string.empty()) {
524 image_string = recruit->image();
526 image_string +=
"~RC(" + recruit->flag_rgb() +
">" +
team.
color() +
")";
527 image_string +=
"~SCALE_INTO(72,72)";
529 if (!err_msgs_map[recruit].empty()) {
530 image_string +=
"~GS()";
535 set_column(
"unit_details", [&](
const auto& recruit) {
537 bool recruitable = err_msgs_map[recruit].empty();
543 dlg->set_title(
_(
"Recruit Unit") + get_title_suffix(
team.
side()))
550 return err_msgs_map[recruit_list[
index]];
553 dlg->
on_modified([&recruit_list](std::size_t
index) ->
const auto& {
return *recruit_list[
index]; });
560 auto dlg = std::make_unique<units_dialog>();
561 dlg->set_title(
_(
"Unit List"))
562 .set_ok_label(
_(
"Scroll To"))
563 .set_help_topic(
"..units")
564 .set_row_num(unit_list.size())
565 .set_show_rename(
true)
566 .set_show_favorite(
true);
571 dlg->rename_unit(unit_list);
577 dlg->toggle_favorite(unit_list);
580 auto set_column = dlg->make_column_builder(unit_list);
582 set_column(
"unit_favorite", [](
const auto&
unit) {
586 set_column(
"unit_name",
587 [](
const auto&
unit) {
591 set_column(
"unit_details",
592 [](
const auto&
unit) {
596 set_column(
"unit_level",
597 [](
const auto&
unit) {
600 [](
const auto&
unit) {
604 set_column(
"unit_moves",
605 [](
const auto&
unit) {
608 [](
const auto&
unit) {
612 set_column(
"unit_hp",
613 [](
const auto&
unit) {
616 [](
const auto&
unit) {
620 set_column(
"unit_xp",
621 [](
const auto&
unit) {
622 std::stringstream exp_str;
624 exp_str << unit->experience() <<
"/" << unit->max_experience();
626 exp_str << font::unicode_en_dash;
630 [](
const auto&
unit) {
636 set_column(
"unit_status", [](
const auto&
unit) {
639 return
"misc/petrified.png";
643 return
"misc/poisoned.png";
647 return
"misc/slowed.png";
651 return
"misc/invisible.png";
657 set_column(
"unit_traits", [](
const auto&
unit) {
661 dlg->set_filter_generator([&unit_list](std::size_t
index) {
663 std::vector<std::string> filter_keys;
667 filter_keys.emplace_back(std::to_string(
unit->
level()));
670 if(
const auto* race =
unit->
race()) {
671 filter_keys.emplace_back(race->name(
unit->
gender()));
672 filter_keys.emplace_back(race->plural_name());
676 filter_keys.emplace_back(trait);
682 dlg->on_modified([&unit_list, &rename](std::size_t
index) ->
const auto& {
692 std::vector<unit_const_ptr>& recall_list,
698 const auto recallable = [wb_gold, &
team](
const unit&
unit) {
704 auto dlg = std::make_unique<units_dialog>();
705 dlg->set_title(
_(
"Recall Unit") + get_title_suffix(
team.
side()))
706 .set_ok_label(
_(
"Recall"))
707 .set_help_topic(
"recruit_and_recall")
708 .set_row_num(recall_list.size())
709 .set_show_rename(
true)
710 .set_show_dismiss(
true)
711 .set_show_favorite(
true);
716 dlg->rename_unit(recall_list);
722 dlg->dismiss_unit(recall_list,
team);
728 dlg->toggle_favorite(recall_list);
731 auto set_column = dlg->make_column_builder(recall_list);
733 set_column(
"unit_favorite", [](
const auto&
unit) {
737 set_column(
"unit_image", [recallable](
const auto&
unit) {
739 if(
unit->
can_recruit()) { mods +=
"~BLIT(" + unit::leader_crown() +
")"; }
741 mods +=
"~BLIT(" + overlay +
")";
743 if(!recallable(*
unit)) { mods +=
"~GS()"; }
744 mods +=
"~SCALE_INTO(72,72)";
748 set_column(
"unit_name",
749 [recallable](
const auto&
unit) {
753 [](
const auto&
unit) {
757 set_column(
"unit_details",
758 [recallable, &
team](
const auto&
unit) {
759 std::stringstream details;
762 if (recallable(*
unit)) {
767 return details.str();
769 [](
const auto&
unit) {
773 set_column(
"unit_moves",
774 [recallable](
const auto&
unit) {
778 [](
const auto&
unit) {
782 set_column(
"unit_level",
783 [recallable](
const auto&
unit) {
786 [](
const auto&
unit) {
790 set_column(
"unit_hp",
791 [recallable](
const auto&
unit) {
795 [](
const auto&
unit) {
799 set_column(
"unit_xp",
800 [recallable](
const auto&
unit) {
808 [](
const auto&
unit) {
812 set_column(
"unit_traits",
813 [recallable](
const auto&
unit) {
816 traits += (traits.empty() ?
"" :
"\n") + trait;
820 [](
const auto&
unit) {
825 dlg->set_sort_by(std::pair(
"unit_level", sort_order::type::descending));
827 dlg->set_tooltip_generator([recallable, wb_gold, &recall_list](std::size_t
index) {
828 if(recallable(*recall_list[
index])) {
829 return std::string();
834 return _(
"This unit cannot be recalled because you will not have enough gold at this point in your plan.");
836 return _(
"This unit cannot be recalled because you do not have enough gold.");
840 dlg->set_filter_generator([&recall_list](std::size_t
index) {
842 std::vector<std::string> filter_keys;
846 filter_keys.emplace_back(std::to_string(
unit->
level()));
849 if(
const auto* race =
unit->
race()) {
850 filter_keys.emplace_back(race->name(
unit->
gender()));
851 filter_keys.emplace_back(race->plural_name());
855 filter_keys.emplace_back(trait);
861 dlg->on_modified([&recall_list, &rename](std::size_t
index) ->
const auto& {
virtual const unit_map & units() const override
Main class to show messages to the user.
@ yes_no_buttons
Shows a yes and no button.
Abstract base class for all modal dialogs.
units_dialog & set_tooltip_generator(const Generator &generator)
Sets the generator function for the tooltips.
void show_list(listbox &list)
std::pair< std::string, sort_order::type > sort_order_
std::function< std::vector< std::string >std::size_t)> filter_gen_
std::map< const unit_type *, t_string > recruit_msgs_map
units_dialog & set_ok_label(const std::string &ok_label)
std::vector< std::vector< std::string > > filter_options_
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()
unit_race::GENDER gender_
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)
void toggle_favorite(std::vector< unit_const_ptr > &unit_list)
void on_modified(const Func &f)
Registers an function which will fired on NOTIFY_MODIFIED dialog events.
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.
std::string cancel_label_
static std::unique_ptr< units_dialog > build_recruit_dialog(const std::vector< const unit_type * > &recruit_list, recruit_msgs_map &err_msgs_map, const team &team)
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.
Basic template class to generate new items.
void on_modified(const Func &func)
Sets a common callback function for all members.
void add_member(selectable_item *w, const T &value)
Adds a widget/value pair to the group map.
void set_member_states(const T &value)
Sets the toggle values for all widgets besides the one associated with the specified value to false.
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.
const grid * get_row_grid(const unsigned row) const
Returns the grid of the wanted row.
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.
bool select_row(const unsigned row, const bool select=true)
Selects a row.
std::pair< widget *, sort_order::type > get_active_sorter() const
Returns a widget pointer to the active sorter, along with its corresponding order.
void remove_row(const unsigned row, unsigned count=1)
Removes a row in the listbox.
int get_selected_row() const
Returns the first selected row.
unsigned get_item_count() const
Returns the number of items in the listbox.
A widget that allows the user to input text in single line.
void set_retval(const int retval, const bool close_window=true)
Sets there return value of the window.
void keyboard_capture(widget *widget)
void invalidate_layout()
Updates the size of the window.
void add_to_keyboard_chain(widget *widget)
Adds the widget to the keyboard chain.
static const std::string & type()
Static type getter that does not rely on the widget being constructed.
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
This class stores all the data for a single 'side' (in game nomenclature).
const std::string & color() const
bool is_local_human() const
recall_list_manager & recall_list()
const std::set< std::string > & recruits() const
Container associating units to locations.
unit_iterator find_leader(int side)
A single unit type that the player may recruit.
static std::string alignment_description(unit_alignments::type align, unit_race::GENDER gender=unit_race::MALE)
Implementation detail of unit_type::alignment_description.
This class represents a single unit of a specific type.
static std::string _(const char *str)
void set_favorite(bool favorite)
bool invisible(const map_location &loc, bool see_all=true) const
int max_hitpoints() const
The max number of hitpoints this unit can have.
unit_alignments::type alignment() const
The alignment of this unit.
bool incapacitated() const
Check if the unit has been petrified.
int level() const
The current level of this unit.
const t_string & type_name() const
Gets the translatable name of this unit's type.
bool unrenamable() const
Whether this unit can be renamed.
int recall_cost() const
How much gold it costs to recall this unit, or -1 if the side's default recall cost is used.
void rename(const std::string &name)
Attempts to rename this unit's translatable display name, taking the 'unrenamable' flag into account.
int hitpoints() const
The current number of hitpoints this unit has.
bool slowed() const
Check if the unit has been slowed.
const unit_race * race() const
Gets this unit's race.
int experience() const
The current number of experience points this unit has.
t_string block_dismiss_message() const
A message of why this unit cannot be dismissed.
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
const std::string & id() const
Gets this unit's id.
int side() const
The side this unit belongs to.
bool dismissable() const
Whether this unit can be dismissed.
bool poisoned() const
Check if the unit has been poisoned.
unsigned int experience_to_advance() const
The number of experience points this unit needs to level up, or 0 if current XP > max XP.
int max_experience() const
The max number of experience points this unit can have.
unit_race::GENDER gender() const
The gender of this unit.
const t_string & name() const
Gets this unit's translatable display name.
bool can_advance() const
Checks whether this unit has any options to advance to.
color_t xp_color() const
Color for this unit's XP.
color_t hp_color() const
Color for this unit's current hitpoints.
std::string image_mods() const
Gets an IPF string containing all IPF image mods.
const std::vector< std::string > & overlays() const
Get the unit's overlay images.
std::string absolute_image() const
The name of the file to game_display (used in menus).
const map_location & get_location() const
The current map location this unit is at.
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
int total_movement() const
The maximum moves this unit has.
const std::vector< t_string > & trait_names() const
Gets the names of the currently registered traits.
bool loyal() const
Gets whether this unit is loyal - ie, it costs no upkeep.
T end(const std::pair< T, T > &p)
Standard logging facilities (interface).
#define log_scope2(domain, description)
const std::string unicode_en_dash
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.
void connect_signal_mouse_left_click(dispatcher &dispatcher, const signal &signal)
Connects a signal handler for a left mouse button click.
std::map< std::string, widget_item > widget_data
std::map< std::string, t_string > widget_item
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
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.
@ OK
Dialog was closed with the OK button.
@ CANCEL
Dialog was closed with the CANCEL button.
void show_help(const std::string &show_topic)
Open the help browser.
std::string span_color(const color_t &color, Args &&... data)
Applies Pango markup to the input specifying its display color.
auto make_ci_matcher(std::string_view filter_text)
Returns a function which performs locale-aware case-insensitive search.
std::string format_level_string(const int level, bool recallable)
std::string format_movement_string(const int moves_left, const int moves_max, bool active)
int planned_gold_spent(int side_number)
std::string maybe_inactive(const std::string &str, bool active)
std::string format_cost_string(int unit_recall_cost, bool active)
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
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.
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="")
std::shared_ptr< const unit > unit_const_ptr
The basic class for representing 8-bit RGB or RGBA colour values.
static map_location::direction n
static lg::log_domain log_display("display")