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"),
158 find_widget<label>(
"title").set_label(
title_);
159 find_widget<button>(
"ok").set_label(
ok_label_);
162 find_widget<button>(
"rename").set_visible(
show_rename_);
169 find_widget<menu_button>(
"variation_box"), [
this](
auto&&...) {
update_variation(); });
207 row_data.emplace(
id,
widget_item{{
"use_markup",
"true" }, {
"label", std::move(result) }});
218 const auto [sorter_id, order] = sort_last.value_or(
sort_order_);
228 listbox& list = find_widget<listbox>(
"main_list");
237 std::string name = selected_unit.
name();
239 if(gui2::dialogs::edit_text::execute(
_(
"Rename Unit"),
_(
"Name:"), name)) {
240 selected_unit.
rename(name);
254 LOG_DP <<
"Recall list units:"; dump_recall_list_to_console(unit_list);
256 listbox& list = find_widget<listbox>(
"main_list");
273 ?
_(
"Do you really want to dismiss him?")
274 :
_(
"Do you really want to dismiss her?"));
276 }
else if(u.
level() > 1) {
278 ?
_(
"Do you really want to dismiss him?")
279 :
_(
"Do you really want to dismiss her?"));
283 ?
_(
"Do you really want to dismiss him?")
284 :
_(
"Do you really want to dismiss her?"));
307 LOG_DP <<
"Dismissing a unit, side = " << u.
side() <<
", id = '" << u.
id() <<
"'";
308 LOG_DP <<
"That side's recall list:";
313 assert(dismissed_unit);
326 listbox& list = find_widget<listbox>(
"main_list");
352 selected_index_ = find_widget<listbox>(
"main_list").get_selected_row();
363 listbox& list = find_widget<listbox>(
"main_list");
365 sort_last.emplace(sorter->id(), order);
375 const std::size_t shown = find_widget<listbox>(
"main_list")
381 find_widget<button>(
"rename").set_active(shown > 0);
382 find_widget<button>(
"dismiss").set_active(shown > 0);
389 selected_index_ = find_widget<listbox>(
"main_list").get_selected_row();
399 variation_ = find_widget<menu_button>(
"variation_box").get_value_config()[
"variation_id"].str();
401 selected_index_ = find_widget<listbox>(
"main_list").get_selected_row();
412 auto dlg = std::make_unique<units_dialog>();
414 const auto type_gen = [](
const auto&
type) {
415 std::string type_name =
type->type_name();
416 if(type_name !=
type->id()) {
417 type_name +=
" (" +
type->id() +
")";
422 const auto race_gen = [](
const auto&
type) {
423 return type->race()->plural_name();
426 const auto populate_variations = [&dlg](
const unit_type& ut) {
429 std::vector<config> var_box_values;
430 var_box_values.emplace_back(
"label",
_(
"unit_variation^Default Variation"),
"variation_id",
"");
432 const auto& uvars = ut.variation_types();
436 unsigned n = 0, selection = 0;
438 for(
const auto& [uv_id, uv] : uvars) {
441 std::string uv_label;
442 if(!uv.variation_name().empty()) {
443 uv_label = uv.variation_name() +
" (" + uv_id +
")";
444 }
else if(!uv.type_name().empty() && uv.type_name() != ut.type_name()) {
445 uv_label = uv.type_name() +
" (" + uv_id +
")";
450 var_box_values.emplace_back(
"label", uv_label,
"variation_id", uv_id);
452 if(uv_id == dlg->variation()) {
460 dlg->clear_variation();
463 var_box.
set_values(var_box_values, selection);
466 dlg->set_title(
_(
"Create Unit"))
478 dlg->on_modified([populate_variations, &dlg, &types_list](std::size_t
index) ->
const auto& {
481 if (dlg->is_selected() && (
static_cast<int>(
index) == dlg->get_selected_index())) {
482 dlg->get_toggle().set_member_states(dlg->gender());
484 dlg->get_toggle().set_members_enabled(
485 [ut](const unit_race::GENDER& gender) { return ut->has_gender_variation(gender); });
488 populate_variations(*ut);
490 const auto&
g = dlg->gender();
491 if(ut->has_gender_variation(
g)) {
492 ut = &ut->get_gender_unit_type(
g);
495 const auto& var = dlg->variation();
497 ut = &ut->get_variation(var);
507 const std::vector<const unit_type*>& recruit_list,
512 auto dlg = std::make_unique<units_dialog>();
513 auto set_column = dlg->make_column_builder(recruit_list);
515 set_column(
"unit_image", [&](
const auto& recruit) {
516 std::string image_string = recruit->icon();
517 if (image_string.empty()) {
518 image_string = recruit->image();
520 image_string +=
"~RC(" + recruit->flag_rgb() +
">" +
team.
color() +
")";
521 image_string +=
"~SCALE_INTO(72,72)";
523 if (!err_msgs_map[recruit].empty()) {
524 image_string +=
"~GS()";
529 set_column(
"unit_details", [&](
const auto& recruit) {
531 bool recruitable = err_msgs_map[recruit].empty();
537 dlg->set_title(
_(
"Recruit Unit") + get_title_suffix(
team.
side()))
544 return err_msgs_map[recruit_list[
index]];
547 dlg->
on_modified([&recruit_list](std::size_t
index) ->
const auto& {
return *recruit_list[
index]; });
554 auto dlg = std::make_unique<units_dialog>();
555 dlg->set_title(
_(
"Unit List"))
556 .set_ok_label(
_(
"Scroll To"))
557 .set_help_topic(
"..units")
558 .set_row_num(unit_list.size())
559 .set_show_rename(
true)
560 .set_show_favorite(
true);
565 dlg->rename_unit(unit_list);
571 dlg->toggle_favorite(unit_list);
574 auto set_column = dlg->make_column_builder(unit_list);
576 set_column(
"unit_favorite", [](
const auto&
unit) {
580 set_column(
"unit_name",
581 [](
const auto&
unit) {
585 set_column(
"unit_details",
586 [](
const auto&
unit) {
590 set_column(
"unit_level",
591 [](
const auto&
unit) {
594 [](
const auto&
unit) {
598 set_column(
"unit_moves",
599 [](
const auto&
unit) {
602 [](
const auto&
unit) {
606 set_column(
"unit_hp",
607 [](
const auto&
unit) {
610 [](
const auto&
unit) {
614 set_column(
"unit_xp",
615 [](
const auto&
unit) {
616 std::stringstream exp_str;
618 exp_str << unit->experience() <<
"/" << unit->max_experience();
620 exp_str << font::unicode_en_dash;
624 [](
const auto&
unit) {
630 set_column(
"unit_status", [](
const auto&
unit) {
633 return
"misc/petrified.png";
637 return
"misc/poisoned.png";
641 return
"misc/slowed.png";
645 return
"misc/invisible.png";
651 set_column(
"unit_traits", [](
const auto&
unit) {
655 dlg->set_filter_generator([&unit_list](std::size_t
index) {
657 std::vector<std::string> filter_keys;
661 filter_keys.emplace_back(std::to_string(
unit->
level()));
664 if(
const auto* race =
unit->
race()) {
665 filter_keys.emplace_back(race->name(
unit->
gender()));
666 filter_keys.emplace_back(race->plural_name());
670 filter_keys.emplace_back(trait);
676 dlg->on_modified([&unit_list, &rename](std::size_t
index) ->
const auto& {
686 std::vector<unit_const_ptr>& recall_list,
692 const auto recallable = [wb_gold, &
team](
const unit&
unit) {
698 auto dlg = std::make_unique<units_dialog>();
699 dlg->set_title(
_(
"Recall Unit") + get_title_suffix(
team.
side()))
700 .set_ok_label(
_(
"Recall"))
701 .set_help_topic(
"recruit_and_recall")
702 .set_row_num(recall_list.size())
703 .set_show_rename(
true)
704 .set_show_dismiss(
true)
705 .set_show_favorite(
true);
710 dlg->rename_unit(recall_list);
716 dlg->dismiss_unit(recall_list,
team);
722 dlg->toggle_favorite(recall_list);
725 auto set_column = dlg->make_column_builder(recall_list);
727 set_column(
"unit_favorite", [](
const auto&
unit) {
731 set_column(
"unit_image", [recallable](
const auto&
unit) {
733 if(
unit->
can_recruit()) { mods +=
"~BLIT(" + unit::leader_crown() +
")"; }
735 mods +=
"~BLIT(" + overlay +
")";
737 if(!recallable(*
unit)) { mods +=
"~GS()"; }
738 mods +=
"~SCALE_INTO(72,72)";
742 set_column(
"unit_name",
743 [recallable](
const auto&
unit) {
747 [](
const auto&
unit) {
751 set_column(
"unit_details",
752 [recallable, &
team](
const auto&
unit) {
753 std::stringstream details;
756 if (recallable(*
unit)) {
761 return details.str();
763 [](
const auto&
unit) {
767 set_column(
"unit_moves",
768 [recallable](
const auto&
unit) {
772 [](
const auto&
unit) {
776 set_column(
"unit_level",
777 [recallable](
const auto&
unit) {
780 [](
const auto&
unit) {
784 set_column(
"unit_hp",
785 [recallable](
const auto&
unit) {
789 [](
const auto&
unit) {
793 set_column(
"unit_xp",
794 [recallable](
const auto&
unit) {
802 [](
const auto&
unit) {
808 set_column(
"unit_traits",
809 [recallable](
const auto&
unit) {
812 traits += (traits.empty() ?
"" :
"\n") + trait;
816 [](
const auto&
unit) {
821 dlg->set_sort_by(std::pair(
"unit_level", sort_order::type::descending));
823 dlg->set_tooltip_generator([recallable, wb_gold, &recall_list](std::size_t
index) {
824 if(recallable(*recall_list[
index])) {
825 return std::string();
830 return _(
"This unit cannot be recalled because you will not have enough gold at this point in your plan.");
832 return _(
"This unit cannot be recalled because you do not have enough gold.");
836 dlg->set_filter_generator([&recall_list](std::size_t
index) {
838 std::vector<std::string> filter_keys;
842 filter_keys.emplace_back(std::to_string(
unit->
level()));
845 if(
const auto* race =
unit->
race()) {
846 filter_keys.emplace_back(race->name(
unit->
gender()));
847 filter_keys.emplace_back(race->plural_name());
851 filter_keys.emplace_back(trait);
857 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, show topic with id show_topic.
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")