Benedikt Straub has proposed merging lp:~widelands-dev/widelands/economy-target-profiles into lp:widelands.
Commit message: Users can define and save their own profiles of economy target quantities. Redesigned the economy options menu. WaresDisplays and will relayout themselves dynamically on fullscreen switch. Requested reviews: Widelands Developers (widelands-dev) Related bugs: Bug #1827696 in widelands: "Allow users to define their own economy default settings" https://bugs.launchpad.net/widelands/+bug/1827696 For more details, see: https://code.launchpad.net/~widelands-dev/widelands/economy-target-profiles/+merge/366987 For each tribe, I added two profiles: "Efficiency" is equal to the changes proposed previously, and "Stockpile" is for people who, well, like to stockpile stuff. Additionally there is an unchangeable "Default" pseudo-profile that resets items to the default settings. To apply a profile, select the items you wish to change and choose the profile from the dropdown. Use "Save" to save your current settings as a profile. The save window also allows you to delete profiles. -- Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/economy-target-profiles into lp:widelands.
=== added file 'data/images/ui_basic/scrollbar_down_fast.png' Binary files data/images/ui_basic/scrollbar_down_fast.png 1970-01-01 00:00:00 +0000 and data/images/ui_basic/scrollbar_down_fast.png 2019-05-06 13:53:16 +0000 differ === added file 'data/images/ui_basic/scrollbar_up_fast.png' Binary files data/images/ui_basic/scrollbar_up_fast.png 1970-01-01 00:00:00 +0000 and data/images/ui_basic/scrollbar_up_fast.png 2019-05-06 13:53:16 +0000 differ === added directory 'data/tribes/economy_profiles' === added file 'data/tribes/economy_profiles/atlanteans' --- data/tribes/economy_profiles/atlanteans 1970-01-01 00:00:00 +0000 +++ data/tribes/economy_profiles/atlanteans 2019-05-06 13:53:16 +0000 @@ -0,0 +1,89 @@ +# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug) + +[Default] +0=_"Efficiency" +1=_"Stockpile" + +[0] +blackroot_flour="1" +atlanteans_bread="20" +bread_paddle="0" +buckets="0" +coal="5" +cornmeal="3" +diamond="3" +fire_tongs="1" +fishing_net="2" +gold="1" +gold_ore="1" +gold_thread="0" +granite="10" +hammer="0" +hook_pole="0" +hunting_bow="1" +iron="5" +iron_ore="1" +milking_tongs="0" +pick="1" +planks="1" +quartz="3" +saw="0" +scythe="0" +shield_advanced="0" +shield_steel="0" +shovel="0" +smoked_fish="5" +smoked_meat="3" +spidercloth="5" +spider_silk="5" +tabard="1" +tabard_golden="0" +trident_double="0" +trident_heavy_double="0" +trident_light="1" +trident_long="0" +trident_steel="0" +atlanteans_horse="1" +atlanteans_soldier="10" + +[1] +blackroot_flour="20" +atlanteans_bread="30" +bread_paddle="1" +buckets="2" +coal="25" +cornmeal="20" +diamond="10" +fire_tongs="1" +fishing_net="2" +gold="20" +gold_ore="15" +gold_thread="5" +granite="30" +hammer="2" +hook_pole="1" +hunting_bow="1" +iron="25" +iron_ore="20" +milking_tongs="1" +pick="3" +planks="40" +quartz="10" +saw="2" +scythe="1" +shield_advanced="1" +shield_steel="1" +shovel="2" +smoked_fish="40" +smoked_meat="25" +spidercloth="20" +spider_silk="15" +tabard="30" +tabard_golden="1" +trident_double="1" +trident_heavy_double="1" +trident_light="30" +trident_long="1" +trident_steel="1" +atlanteans_horse="20" +atlanteans_soldier="20" === added file 'data/tribes/economy_profiles/barbarians' --- data/tribes/economy_profiles/barbarians 1970-01-01 00:00:00 +0000 +++ data/tribes/economy_profiles/barbarians 2019-05-06 13:53:16 +0000 @@ -0,0 +1,81 @@ +# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug) + +[Default] +0=_"Efficiency" +1=_"Stockpile" + +[0] +ax="1" +ax_battle="0" +ax_broad="0" +ax_bronze="0" +ax_sharp="0" +ax_warriors="0" +beer="0" +beer_strong="1" +blackwood="40" +barbarians_bread="5" +bread_paddle="0" +cloth="10" +coal="20" +felling_ax="0" +fire_tongs="1" +fishing_rod="0" +gold="1" +gold_ore="1" +granite="10" +grout="1" +hammer="1" +helmet="0" +helmet_mask="0" +helmet_warhelm="0" +hunting_spear="0" +iron="5" +iron_ore="5" +kitchen_tools="0" +meal="5" +pick="1" +ration="20" +scythe="0" +shovel="0" +snack="0" +barbarians_ox="1" +barbarians_soldier="10" + +[1] +ax="30" +ax_battle="1" +ax_broad="1" +ax_bronze="1" +ax_sharp="1" +ax_warriors="1" +beer="15" +beer_strong="20" +blackwood="45" +barbarians_bread="25" +bread_paddle="1" +cloth="10" +coal="25" +felling_ax="5" +fire_tongs="1" +fishing_rod="1" +gold="20" +gold_ore="15" +granite="30" +grout="20" +hammer="2" +helmet="1" +helmet_mask="1" +helmet_warhelm="1" +hunting_spear="1" +iron="25" +iron_ore="20" +kitchen_tools="1" +meal="15" +pick="2" +ration="30" +scythe="1" +shovel="1" +snack="20" +barbarians_ox="20" +barbarians_soldier="20" === added file 'data/tribes/economy_profiles/empire' --- data/tribes/economy_profiles/empire 1970-01-01 00:00:00 +0000 +++ data/tribes/economy_profiles/empire 2019-05-06 13:53:16 +0000 @@ -0,0 +1,89 @@ +# Automatically created by Widelands bzr9094[economy-target-profiles] (Debug) + +[Default] +0=_"Efficiency" +1=_"Stockpile" + +[0] +armor="1" +armor_chain="1" +armor_gilded="1" +armor_helmet="30" +basket="1" +beer="1" +empire_bread="20" +bread_paddle="0" +cloth="15" +coal="5" +felling_ax="0" +fire_tongs="1" +fishing_rod="0" +flour="20" +gold="1" +gold_ore="1" +granite="10" +hammer="0" +hunting_spear="0" +iron="5" +iron_ore="3" +kitchen_tools="0" +marble="30" +marble_column="10" +meal="5" +meat="20" +pick="1" +planks="1" +ration="20" +saw="0" +scythe="0" +shovel="0" +spear="1" +spear_advanced="1" +spear_heavy="1" +spear_war="1" +spear_wooden="30" +wool="10" +empire_donkey="1" +empire_soldier="10" + +[1] +armor="1" +armor_chain="1" +armor_gilded="1" +armor_helmet="30" +basket="1" +beer="20" +empire_bread="30" +bread_paddle="1" +cloth="15" +coal="25" +felling_ax="3" +fire_tongs="1" +fishing_rod="1" +flour="25" +gold="20" +gold_ore="15" +granite="30" +hammer="2" +hunting_spear="1" +iron="25" +iron_ore="20" +kitchen_tools="1" +marble="35" +marble_column="15" +meal="20" +meat="30" +pick="2" +planks="40" +ration="25" +saw="1" +scythe="1" +shovel="1" +spear="1" +spear_advanced="1" +spear_heavy="1" +spear_war="1" +spear_wooden="30" +wool="15" +empire_donkey="20" +empire_soldier="20" === added file 'data/tribes/economy_profiles/frisians' --- data/tribes/economy_profiles/frisians 1970-01-01 00:00:00 +0000 +++ data/tribes/economy_profiles/frisians 2019-05-06 13:53:16 +0000 @@ -0,0 +1,93 @@ +# Automatically created by Widelands bzr9093[economy-target-profiles] (Debug) + +[Default] +0=_"Efficiency" +1=_"Stockpile" + +[0] +clay="30" +brick="40" +bread_frisians="20" +honey_bread="20" +mead="15" +fur="10" +fur_garment="30" +fur_garment_studded="2" +fur_garment_golden="2" +helmet_golden="2" +sword_short="30" +sword_long="2" +sword_broad="2" +sword_double="2" +needles="1" +basket="1" +beer="1" +bread_paddle="1" +cloth="10" +coal="20" +felling_ax="0" +fire_tongs="1" +fish="20" +fishing_net="2" +gold="1" +gold_ore="1" +granite="10" +hammer="1" +helmet="0" +hunting_spear="0" +iron="5" +iron_ore="3" +kitchen_tools="0" +meal="1" +pick="1" +ration="20" +scythe="0" +shovel="0" +smoked_fish="20" +smoked_meat="10" +frisians_reindeer="1" +frisians_soldier="10" + +[1] +clay="35" +brick="50" +bread_frisians="30" +honey_bread="30" +mead="30" +fur="20" +fur_garment="30" +fur_garment_studded="2" +fur_garment_golden="2" +helmet_golden="2" +sword_short="30" +sword_long="2" +sword_broad="2" +sword_double="2" +needles="1" +basket="1" +beer="30" +bread_paddle="1" +cloth="10" +coal="35" +felling_ax="3" +fire_tongs="2" +fish="40" +fishing_net="2" +gold="20" +gold_ore="15" +granite="35" +hammer="3" +helmet="2" +hunting_spear="1" +iron="25" +iron_ore="20" +kitchen_tools="2" +meal="10" +pick="3" +ration="30" +scythe="2" +shovel="5" +smoked_fish="30" +smoked_meat="20" +frisians_reindeer="20" +frisians_soldier="20" === modified file 'src/logic/filesystem_constants.h' --- src/logic/filesystem_constants.h 2019-04-18 16:50:35 +0000 +++ src/logic/filesystem_constants.h 2019-05-06 13:53:16 +0000 @@ -78,4 +78,6 @@ /// Filesystem names for config const std::string kConfigFile = "config"; +const std::string kEconomyProfilesDir = "tribes/economy_profiles"; + #endif // end of include guard: WL_LOGIC_FILESYSTEM_CONSTANTS_H === modified file 'src/logic/map_objects/tribes/tribe_descr.cc' --- src/logic/map_objects/tribes/tribe_descr.cc 2019-05-04 10:47:44 +0000 +++ src/logic/map_objects/tribes/tribe_descr.cc 2019-05-06 13:53:16 +0000 @@ -89,8 +89,6 @@ load_roads("busy", &busy_road_paths_); items_table = table.get_table("wares_order"); - wares_order_coords_.resize(tribes_.nrwares()); - int columnindex = 0; for (const int key : items_table->keys<int>()) { std::vector<DescriptionIndex> column; std::vector<std::string> warenames = @@ -104,7 +102,6 @@ } wares_.insert(wareindex); column.push_back(wareindex); - wares_order_coords_[wareindex] = std::make_pair(columnindex, rowindex); } catch (const WException& e) { throw GameDataError( "Failed adding ware '%s: %s", warenames[rowindex].c_str(), e.what()); @@ -112,13 +109,10 @@ } if (!column.empty()) { wares_order_.push_back(column); - ++columnindex; } } items_table = table.get_table("workers_order"); - workers_order_coords_.resize(tribes_.nrworkers()); - columnindex = 0; for (const int key : items_table->keys<int>()) { std::vector<DescriptionIndex> column; std::vector<std::string> workernames = @@ -132,7 +126,6 @@ } workers_.insert(workerindex); column.push_back(workerindex); - workers_order_coords_[workerindex] = std::make_pair(columnindex, rowindex); const WorkerDescr& worker_descr = *tribes_.get_worker_descr(workerindex); if (worker_descr.is_buildable() && worker_descr.buildcost().empty()) { @@ -145,7 +138,6 @@ } if (!column.empty()) { workers_order_.push_back(column); - ++columnindex; } } @@ -423,38 +415,6 @@ return list->second.find(lowest)->second; } -void TribeDescr::resize_ware_orders(size_t maxLength) { - bool need_resize = false; - - // Check if we actually need to resize. - for (WaresOrder::iterator it = wares_order_.begin(); it != wares_order_.end(); ++it) { - if (it->size() > maxLength) { - need_resize = true; - } - } - - // Build new smaller wares_order. - if (need_resize) { - WaresOrder new_wares_order; - for (WaresOrder::iterator it = wares_order_.begin(); it != wares_order_.end(); ++it) { - new_wares_order.push_back(std::vector<Widelands::DescriptionIndex>()); - for (std::vector<Widelands::DescriptionIndex>::iterator it2 = it->begin(); - it2 != it->end(); ++it2) { - if (new_wares_order.rbegin()->size() >= maxLength) { - new_wares_order.push_back(std::vector<Widelands::DescriptionIndex>()); - } - new_wares_order.rbegin()->push_back(*it2); - wares_order_coords_[*it2].first = new_wares_order.size() - 1; - wares_order_coords_[*it2].second = new_wares_order.rbegin()->size() - 1; - } - } - - // Remove old array. - wares_order_.clear(); - wares_order_ = new_wares_order; - } -} - void TribeDescr::add_building(const std::string& buildingname) { try { DescriptionIndex index = tribes_.safe_building_index(buildingname); === modified file 'src/logic/map_objects/tribes/tribe_descr.h' --- src/logic/map_objects/tribes/tribe_descr.h 2019-03-01 04:19:53 +0000 +++ src/logic/map_objects/tribes/tribe_descr.h 2019-05-06 13:53:16 +0000 @@ -149,22 +149,13 @@ } using WaresOrder = std::vector<std::vector<Widelands::DescriptionIndex>>; - using WaresOrderCoords = std::vector<std::pair<uint32_t, uint32_t>>; const WaresOrder& wares_order() const { return wares_order_; } - const WaresOrderCoords& wares_order_coords() const { - return wares_order_coords_; - } const WaresOrder& workers_order() const { return workers_order_; } - const WaresOrderCoords& workers_order_coords() const { - return workers_order_coords_; - } - - void resize_ware_orders(size_t maxLength); const std::vector<std::string>& get_ship_names() const { return ship_names_; @@ -215,9 +206,7 @@ std::vector<DescriptionIndex> trainingsites_; // Order and positioning of wares in the warehouse display WaresOrder wares_order_; - WaresOrderCoords wares_order_coords_; WaresOrder workers_order_; - WaresOrderCoords workers_order_coords_; std::vector<Widelands::TribeBasicInfo::Initialization> initializations_; === modified file 'src/logic/map_objects/tribes/tribes.cc' --- src/logic/map_objects/tribes/tribes.cc 2019-03-01 04:19:53 +0000 +++ src/logic/map_objects/tribes/tribes.cc 2019-05-06 13:53:16 +0000 @@ -340,14 +340,9 @@ // Calculate the trainingsites proportions. postload_calculate_trainingsites_proportions(); - // Resize the configuration of our wares if they won't fit in the current window (12 = info label - // size). - // Also, do some final checks on the gamedata - int number = (g_gr->get_yres() - 290) / (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + 12); + // Some final checks on the gamedata for (DescriptionIndex i = 0; i < tribes_->size(); ++i) { TribeDescr* tribe_descr = tribes_->get_mutable(i); - tribe_descr->resize_ware_orders(number); - // Verify that the preciousness has been set for all of the tribe's wares for (const DescriptionIndex wi : tribe_descr->wares()) { if (tribe_descr->get_ware_descr(wi)->preciousness(tribe_descr->name()) == kInvalidWare) { === modified file 'src/logic/map_objects/tribes/ware_descr.h' --- src/logic/map_objects/tribes/ware_descr.h 2019-02-27 19:00:36 +0000 +++ src/logic/map_objects/tribes/ware_descr.h 2019-05-06 13:53:16 +0000 @@ -33,10 +33,8 @@ class Image; class LuaTable; -#define WARE_MENU_PIC_WIDTH 24 //!< Default width for ware's menu icons -#define WARE_MENU_PIC_HEIGHT 24 //!< Default height for ware's menu icons -#define WARE_MENU_PIC_PAD_X 3 //!< Default padding between menu icons -#define WARE_MENU_PIC_PAD_Y 4 //!< Default padding between menu icons +constexpr int kWareMenuPicWidth = 24; //!< Default width for ware's menu icons +constexpr int kWareMenuPicHeight = 24; //!< Default height for ware's menu icons namespace Widelands { === modified file 'src/logic/playercommand.h' --- src/logic/playercommand.h 2019-02-23 11:00:49 +0000 +++ src/logic/playercommand.h 2019-05-06 13:53:16 +0000 @@ -581,6 +581,7 @@ uint32_t permanent_; }; +// TODO(Nordfriese): CmdResetWareTargetQuantity can be removed when we next break savegame compatibility struct CmdResetWareTargetQuantity : public CmdChangeTargetQuantity { CmdResetWareTargetQuantity() : CmdChangeTargetQuantity() { } @@ -629,6 +630,7 @@ uint32_t permanent_; }; +// TODO(Nordfriese): CmdResetWorkerTargetQuantity can be removed when we next break savegame compatibility struct CmdResetWorkerTargetQuantity : public CmdChangeTargetQuantity { CmdResetWorkerTargetQuantity() : CmdChangeTargetQuantity() { } === modified file 'src/wui/CMakeLists.txt' --- src/wui/CMakeLists.txt 2019-05-04 15:37:33 +0000 +++ src/wui/CMakeLists.txt 2019-05-06 13:53:16 +0000 @@ -44,8 +44,10 @@ graphic logic logic_commands + logic_filesystem_constants logic_map_objects notifications + profile ui_basic wui_waresdisplay ) === modified file 'src/wui/economy_options_window.cc' --- src/wui/economy_options_window.cc 2019-02-23 11:00:49 +0000 +++ src/wui/economy_options_window.cc 2019-05-06 13:53:16 +0000 @@ -19,36 +19,98 @@ #include "wui/economy_options_window.h" +#include <memory> + #include <boost/lexical_cast.hpp> #include "graphic/graphic.h" #include "logic/editor_game_base.h" +#include "logic/filesystem_constants.h" #include "logic/map_objects/tribes/ware_descr.h" #include "logic/map_objects/tribes/worker_descr.h" #include "logic/player.h" #include "logic/playercommand.h" +#include "profile/profile.h" #include "ui_basic/button.h" +#include "ui_basic/editbox.h" +#include "ui_basic/messagebox.h" +#include "ui_basic/table.h" static const char pic_tab_wares[] = "images/wui/buildings/menu_tab_wares.png"; static const char pic_tab_workers[] = "images/wui/buildings/menu_tab_workers.png"; +constexpr int kDesiredWidth = 216; + +static inline int32_t calc_hgap(int32_t columns, int32_t total_w) { + return (total_w - columns * kWareMenuPicWidth) / (columns - 1); +} + EconomyOptionsWindow::EconomyOptionsWindow(UI::Panel* parent, Widelands::Economy* economy, bool can_act) : UI::Window(parent, "economy_options", 0, 0, 0, 0, _("Economy options")), + main_box_(this, 0, 0, UI::Box::Vertical), serial_(economy->serial()), player_(&economy->owner()), tabpanel_(this, UI::TabPanelStyle::kWuiDark), - ware_panel_(new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWARE)), + ware_panel_(new EconomyOptionsPanel(&tabpanel_, this, serial_, player_, can_act, Widelands::wwWARE, kDesiredWidth)), worker_panel_( - new EconomyOptionsPanel(&tabpanel_, serial_, player_, can_act, Widelands::wwWORKER)) { - set_center_panel(&tabpanel_); + new EconomyOptionsPanel(&tabpanel_, this, serial_, player_, can_act, Widelands::wwWORKER, kDesiredWidth)), + dropdown_box_(this, 0, 0, UI::Box::Horizontal), + dropdown_(&dropdown_box_, 0, 0, 174, 200, 34, "", UI::DropdownType::kTextual, UI::PanelStyle::kWui) { + set_center_panel(&main_box_); tabpanel_.add("wares", g_gr->images().get(pic_tab_wares), ware_panel_, _("Wares")); tabpanel_.add("workers", g_gr->images().get(pic_tab_workers), worker_panel_, _("Workers")); + + UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal); + UI::Button* b = new UI::Button(buttons, "decrease_target_fast", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary, + g_gr->images().get("images/ui_basic/scrollbar_down_fast.png"), _("Decrease target by 10")); + b->sigclicked.connect([this] { change_target(-10); }); + buttons->add(b); + b->set_repeating(true); + buttons->add_space(8); + b = new UI::Button(buttons, "decrease_target", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary, + g_gr->images().get("images/ui_basic/scrollbar_down.png"), _("Decrease target")); + b->sigclicked.connect([this] { change_target(-1); }); + buttons->add(b); + b->set_repeating(true); + buttons->add_space(24); + + b = new UI::Button(buttons, "increase_target", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary, + g_gr->images().get("images/ui_basic/scrollbar_up.png"), _("Increase target")); + b->sigclicked.connect([this] { change_target(1); }); + buttons->add(b); + b->set_repeating(true); + buttons->add_space(8); + b = new UI::Button(buttons, "increase_target_fast", 0, 0, 44, 28, UI::ButtonStyle::kWuiSecondary, + g_gr->images().get("images/ui_basic/scrollbar_up_fast.png"), _("Increase target by 10")); + b->sigclicked.connect([this] { change_target(10); }); + buttons->add(b); + b->set_repeating(true); + + dropdown_.set_tooltip(_("Profile to apply to the selected items")); + dropdown_box_.set_size(40, 20); // Prevent assert failures + dropdown_box_.add(&dropdown_, UI::Box::Resizing::kFullSize); + dropdown_.selected.connect([this] { reset_target(); }); + + b = new UI::Button(&dropdown_box_, "save_targets", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, + g_gr->images().get("images/wui/menus/menu_save_game.png"), _("Save target settings")); + b->sigclicked.connect([this] { create_target(); }); + dropdown_box_.add_space(8); + dropdown_box_.add(b); + + main_box_.add(&tabpanel_, UI::Box::Resizing::kAlign, UI::Align::kCenter); + main_box_.add_space(8); + main_box_.add(buttons, UI::Box::Resizing::kAlign, UI::Align::kCenter); + main_box_.add_space(8); + main_box_.add(&dropdown_box_, UI::Box::Resizing::kAlign, UI::Align::kCenter); + economy->set_has_window(true); economynotes_subscriber_ = Notifications::subscribe<Widelands::NoteEconomy>( [this](const Widelands::NoteEconomy& note) { on_economy_note(note); }); + + read_targets(); } EconomyOptionsWindow::~EconomyOptionsWindow() { @@ -81,6 +143,20 @@ } } +void EconomyOptionsWindow::layout() { + int w, h; + tabpanel_.get_desired_size(&w, &h); + main_box_.set_desired_size(w, h + 78); + update_desired_size(); + UI::Window::layout(); +} + +void EconomyOptionsWindow::EconomyOptionsPanel::update_desired_size() { + display_.set_hgap(std::max(3, calc_hgap(display_.get_extent().w, kDesiredWidth))); + Box::update_desired_size(); + get_parent()->layout(); +} + EconomyOptionsWindow::TargetWaresDisplay::TargetWaresDisplay(UI::Panel* const parent, int32_t const x, int32_t const y, @@ -129,42 +205,26 @@ * Wraps the wares/workers display together with some buttons */ EconomyOptionsWindow::EconomyOptionsPanel::EconomyOptionsPanel(UI::Panel* parent, + EconomyOptionsWindow* eco_window, Widelands::Serial serial, Widelands::Player* player, bool can_act, - Widelands::WareWorker type) + Widelands::WareWorker type, + int32_t min_w) : UI::Box(parent, 0, 0, UI::Box::Vertical), serial_(serial), player_(player), type_(type), can_act_(can_act), - display_(this, 0, 0, serial_, player_, type_, can_act_) { + display_(this, 0, 0, serial_, player_, type_, can_act_), + economy_options_window_(eco_window) { add(&display_, UI::Box::Resizing::kFullSize); + display_.set_hgap(std::max(3, calc_hgap(display_.get_extent().w, min_w))); + if (!can_act_) { return; } - UI::Box* buttons = new UI::Box(this, 0, 0, UI::Box::Horizontal); - add(buttons); - - UI::Button* b = new UI::Button(buttons, "decrease_target", 0, 0, 34, 34, - UI::ButtonStyle::kWuiMenu, "-", _("Decrease target")); - b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::change_target, this, -1)); - buttons->add(b); - b->set_repeating(true); - buttons->add_space(8); - - b = new UI::Button(buttons, "increase_target", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, "+", - _("Increase target")); - b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::change_target, this, 1)); - buttons->add(b); - b->set_repeating(true); - buttons->add_space(8); - - b = new UI::Button( - buttons, "reset_target", 0, 0, 34, 34, UI::ButtonStyle::kWuiMenu, "R", _("Reset to default")); - b->sigclicked.connect(boost::bind(&EconomyOptionsPanel::reset_target, this)); - buttons->add(b); } void EconomyOptionsWindow::EconomyOptionsPanel::set_economy(Widelands::Serial serial) { @@ -172,7 +232,26 @@ display_.set_economy(serial); } -void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int amount) { +void EconomyOptionsWindow::change_target(int amount) { + if (tabpanel_.active() == 0) { + ware_panel_->change_target(amount); + } else { + worker_panel_->change_target(amount); + } +} + +void EconomyOptionsWindow::reset_target() { + if (tabpanel_.active() == 0) { + ware_panel_->reset_target(); + } else { + worker_panel_->reset_target(); + } +} + +void EconomyOptionsWindow::EconomyOptionsPanel::change_target(int delta) { + if (delta == 0) { + return; + } Widelands::Economy* economy = player_->get_economy(serial_); if (economy == nullptr) { die(); @@ -186,35 +265,286 @@ const Widelands::Economy::TargetQuantity& tq = is_wares ? economy->ware_target_quantity(index) : economy->worker_target_quantity(index); - // Don't allow negative new amount. - if (amount >= 0 || -amount <= static_cast<int>(tq.permanent)) { - if (is_wares) { - game.send_player_command(*new Widelands::CmdSetWareTargetQuantity( - game.get_gametime(), player_->player_number(), serial_, index, - tq.permanent + amount)); + // Don't allow negative new amount + const int old_amount = static_cast<int>(tq.permanent); + const int new_amount = std::max(0, old_amount + delta); + if (new_amount == old_amount) { + continue; + } + if (is_wares) { + game.send_player_command(*new Widelands::CmdSetWareTargetQuantity( + game.get_gametime(), player_->player_number(), serial_, index, new_amount)); + } else { + game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity( + game.get_gametime(), player_->player_number(), serial_, index, new_amount)); + } + } + } +} + +void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() { + Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase()); + const bool is_wares = type_ == Widelands::wwWARE; + const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers(); + const PredefinedTargets settings = economy_options_window_->get_selected_target(); + for (const Widelands::DescriptionIndex& index : items) { + if (display_.ware_selected(index)) { + if (is_wares) { + game.send_player_command(*new Widelands::CmdSetWareTargetQuantity( + game.get_gametime(), player_->player_number(), serial_, index, settings.wares.at(index))); + } else { + game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity( + game.get_gametime(), player_->player_number(), serial_, index, settings.workers.at(index))); + } + } + } +} + +void EconomyOptionsWindow::update_profiles(const std::string& select) { + dropdown_.clear(); + for (const auto& pair : predefined_targets_) { + dropdown_.add(_(pair.first), pair.first, nullptr, pair.first == select); + } +} + +struct SaveProfileWindow : public UI::Window { + void update_save_enabled() { + const std::string& text = profile_name_.text(); + if (text.empty() || text == kDefaultEconomyProfile) { + save_.set_enabled(false); + save_.set_tooltip(text.empty() ? _("The profile name cannot be empty") : + _("The default profile cannot be overwritten")); + } else { + save_.set_enabled(true); + save_.set_tooltip(_("Save the profile under this name")); + } + } + + void table_selection_changed() { + if (!table_.has_selection()) { + delete_.set_enabled(false); + delete_.set_tooltip(""); + return; + } + const std::string& sel = table_[table_.selection_index()]; + if (sel == kDefaultEconomyProfile) { + delete_.set_tooltip(_("The default profile cannot be deleted")); + delete_.set_enabled(false); + } else { + delete_.set_tooltip(_("Delete the selected profiles")); + delete_.set_enabled(true); + } + profile_name_.set_text(sel); + update_save_enabled(); + } + + void close(bool ok) { + end_modal(ok ? UI::Panel::Returncodes::kOk : UI::Panel::Returncodes::kBack); + die(); + } + + void update_table() { + table_.clear(); + for (const auto& pair : economy_options_->get_predefined_targets()) { + table_.add(pair.first).set_string(0, _(pair.first)); + } + layout(); + } + + void save() { + const std::string name = profile_name_.text(); + assert(!name.empty()); + assert(name != kDefaultEconomyProfile); + for (const auto& pair : economy_options_->get_predefined_targets()) { + if (pair.first == name) { + UI::WLMessageBox m(this, _("Overwrite?"), + _("A profile with this name already exists.\nDo you wish to replace it?"), + UI::WLMessageBox::MBoxType::kOkCancel); + if (m.run<UI::Panel::Returncodes>() != UI::Panel::Returncodes::kOk) { + return; + } + break; + } + } + economy_options_->do_create_target(name); + close(true); + } + + void delete_selected() { + assert(table_.has_selection()); + auto& map = economy_options_->get_predefined_targets(); + const std::string& name = table_[table_.selection_index()]; + assert(name != kDefaultEconomyProfile); + for (auto it = map.begin(); it != map.end(); ++it) { + if (it->first == name) { + map.erase(it); + break; + } + } + economy_options_->save_targets(); + update_table(); + } + + explicit SaveProfileWindow(UI::Panel* parent, EconomyOptionsWindow* eco) + : UI::Window(parent, "save_economy_options_profile", 0, 0, 0, 0, _("Save Profile")), + economy_options_(eco), + main_box_(this, 0, 0, UI::Box::Vertical), + table_box_(&main_box_, 0, 0, UI::Box::Vertical), + table_(&table_box_, 0, 0, 460, 120, UI::PanelStyle::kWui), + buttons_box_(&main_box_, 0, 0, UI::Box::Horizontal), + profile_name_(&buttons_box_, 0, 0, 240, 0, 0, UI::PanelStyle::kWui), + save_(&buttons_box_, "save", 0, 0, 80, 34, UI::ButtonStyle::kWuiPrimary, _("Save")), + cancel_(&buttons_box_, "cancel", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Cancel")), + delete_(&buttons_box_, "delete", 0, 0, 80, 34, UI::ButtonStyle::kWuiSecondary, _("Delete")) { + table_.add_column(200, _("Existing Profiles")); + update_table(); + + table_.selected.connect([this](uint32_t) { table_selection_changed(); }); + profile_name_.changed.connect([this] { update_save_enabled(); }); + profile_name_.ok.connect([this] { save(); }); + profile_name_.cancel.connect([this] { close(false); }); + save_.sigclicked.connect([this] { save(); }); + cancel_.sigclicked.connect([this] { close(false); }); + delete_.sigclicked.connect([this] { delete_selected(); }); + + table_box_.add(&table_, UI::Box::Resizing::kFullSize); + buttons_box_.add(&profile_name_, UI::Box::Resizing::kFullSize); + buttons_box_.add(&save_); + buttons_box_.add(&cancel_); + buttons_box_.add(&delete_); + main_box_.add(&table_box_, UI::Box::Resizing::kFullSize); + main_box_.add(&buttons_box_, UI::Box::Resizing::kFullSize); + set_center_panel(&main_box_); + + table_selection_changed(); + update_save_enabled(); + } + ~SaveProfileWindow() { + } + +private: + EconomyOptionsWindow* economy_options_; + UI::Box main_box_; + UI::Box table_box_; + UI::Table<const std::string&> table_; + UI::Box buttons_box_; + UI::EditBox profile_name_; + UI::Button save_; + UI::Button cancel_; + UI::Button delete_; +}; + +void EconomyOptionsWindow::create_target() { + std::unique_ptr<SaveProfileWindow> s (new SaveProfileWindow(get_parent(), this)); + s->run<UI::Panel::Returncodes>(); +} + +void EconomyOptionsWindow::do_create_target(const std::string& name) { + assert(!name.empty()); + assert(name != kDefaultEconomyProfile); + const Widelands::Tribes& tribes = player_->egbase().tribes(); + const Widelands::TribeDescr& tribe = player_->tribe(); + Widelands::Economy* economy = player_->get_economy(serial_); + PredefinedTargets t; + for (Widelands::DescriptionIndex di : tribe.wares()) { + if (tribes.get_ware_descr(di)->has_demand_check(tribe.name())) { + t.wares[di] = economy->ware_target_quantity(di).permanent; + } + } + for (Widelands::DescriptionIndex di : tribe.workers()) { + if (tribes.get_worker_descr(di)->has_demand_check()) { + t.workers[di] = economy->worker_target_quantity(di).permanent; + } + } + predefined_targets_[name] = t; + + save_targets(); + update_profiles(name); +} + +void EconomyOptionsWindow::save_targets() { + const Widelands::Tribes& tribes = player_->egbase().tribes(); + Profile profile; + + std::map<std::string, uint32_t> serials; + for (const auto& pair : predefined_targets_) { + if (pair.first != kDefaultEconomyProfile) { + serials.emplace(pair.first, serials.size()); + } + } + Section& global_section = profile.create_section(kDefaultEconomyProfile.c_str()); + for (const auto& pair : serials) { + global_section.set_string(std::to_string(pair.second).c_str(), pair.first); + } + + for (const auto& pair : predefined_targets_) { + if (pair.first == kDefaultEconomyProfile) { + continue; + } + Section& section = profile.create_section(std::to_string(serials.at(pair.first)).c_str()); + for (const auto& setting : pair.second.wares) { + section.set_natural(tribes.get_ware_descr(setting.first)->name().c_str(), setting.second); + } + for (const auto& setting : pair.second.workers) { + section.set_natural(tribes.get_worker_descr(setting.first)->name().c_str(), setting.second); + } + } + + g_fs->ensure_directory_exists(kEconomyProfilesDir); + std::string complete_filename = kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name(); + profile.write(complete_filename.c_str(), false); +} + +void EconomyOptionsWindow::read_targets(const std::string& select) { + predefined_targets_.clear(); + const Widelands::Tribes& tribes = player_->egbase().tribes(); + const Widelands::TribeDescr& tribe = player_->tribe(); + + { + PredefinedTargets t; + for (Widelands::DescriptionIndex di : tribe.wares()) { + const Widelands::WareDescr* descr = tribes.get_ware_descr(di); + if (descr->has_demand_check(tribe.name())) { + t.wares.emplace(di, descr->default_target_quantity(tribe.name())); + } + } + for (Widelands::DescriptionIndex di : tribe.workers()) { + const Widelands::WorkerDescr* descr = tribes.get_worker_descr(di); + if (descr->has_demand_check()) { + t.workers.emplace(di, descr->default_target_quantity()); + } + } + predefined_targets_.emplace(kDefaultEconomyProfile, t); + } + + std::string complete_filename = kEconomyProfilesDir + g_fs->file_separator() + player_->tribe().name(); + Profile profile; + profile.read(complete_filename.c_str()); + + Section* global_section = profile.get_section(kDefaultEconomyProfile); + if (global_section) { + std::map<std::string, std::string> serials; + while (Section::Value* v = global_section->get_next_val()) { + serials.emplace(std::string(v->get_name()), v->get_string()); + } + + for (const auto& pair : serials) { + Section* section = profile.get_section(pair.first); + PredefinedTargets t; + while (Section::Value* v = section->get_next_val()) { + const std::string name = std::string(v->get_name()); + Widelands::DescriptionIndex di = tribes.ware_index(name); + if (di == Widelands::INVALID_INDEX) { + di = tribes.worker_index(name); + assert(di != Widelands::INVALID_INDEX); + t.workers.emplace(di, v->get_natural()); } else { - game.send_player_command(*new Widelands::CmdSetWorkerTargetQuantity( - game.get_gametime(), player_->player_number(), serial_, index, - tq.permanent + amount)); + t.wares.emplace(di, v->get_natural()); } } + predefined_targets_.emplace(pair.second, t); } } -} -void EconomyOptionsWindow::EconomyOptionsPanel::reset_target() { - Widelands::Game& game = dynamic_cast<Widelands::Game&>(player_->egbase()); - const bool is_wares = type_ == Widelands::wwWARE; - const auto& items = is_wares ? player_->tribe().wares() : player_->tribe().workers(); - for (const Widelands::DescriptionIndex& index : items) { - if (display_.ware_selected(index)) { - if (is_wares) { - game.send_player_command(*new Widelands::CmdResetWareTargetQuantity( - game.get_gametime(), player_->player_number(), serial_, index)); - } else { - game.send_player_command(*new Widelands::CmdResetWorkerTargetQuantity( - game.get_gametime(), player_->player_number(), serial_, index)); - } - } - } + update_profiles(select); } === modified file 'src/wui/economy_options_window.h' --- src/wui/economy_options_window.h 2019-02-23 11:00:49 +0000 +++ src/wui/economy_options_window.h 2019-05-06 13:53:16 +0000 @@ -20,20 +20,48 @@ #ifndef WL_WUI_ECONOMY_OPTIONS_WINDOW_H #define WL_WUI_ECONOMY_OPTIONS_WINDOW_H +#include <map> #include <memory> +#include <string> #include "economy/economy.h" #include "logic/map_objects/tribes/tribe_descr.h" #include "notifications/notifications.h" #include "ui_basic/box.h" +#include "ui_basic/dropdown.h" #include "ui_basic/tabpanel.h" #include "ui_basic/window.h" #include "wui/waresdisplay.h" +const std::string kDefaultEconomyProfile = "Default"; + struct EconomyOptionsWindow : public UI::Window { EconomyOptionsWindow(UI::Panel* parent, Widelands::Economy* economy, bool can_act); ~EconomyOptionsWindow(); + struct PredefinedTargets { + using Targets = std::map<Widelands::DescriptionIndex, uint32_t>; + Targets wares; + Targets workers; + }; + + void create_target(); + void do_create_target(const std::string&); + void save_targets(); + void read_targets(const std::string& = kDefaultEconomyProfile); + void update_profiles(const std::string&); + std::map<std::string, PredefinedTargets>& get_predefined_targets() { + return predefined_targets_; + } + const PredefinedTargets& get_selected_target() const { + return predefined_targets_.at(dropdown_.get_selected()); + } + + void change_target(int amount); + void reset_target(); + + void layout() override; + private: struct TargetWaresDisplay : public AbstractWaresDisplay { TargetWaresDisplay(UI::Panel* const parent, @@ -59,14 +87,17 @@ */ struct EconomyOptionsPanel : UI::Box { EconomyOptionsPanel(UI::Panel* parent, + EconomyOptionsWindow* eco_window, Widelands::Serial serial, Widelands::Player* player, bool can_act, - Widelands::WareWorker type); + Widelands::WareWorker type, + int32_t min_w); void set_economy(Widelands::Serial serial); void change_target(int amount); void reset_target(); + void update_desired_size() override; private: Widelands::Serial serial_; @@ -74,17 +105,23 @@ Widelands::WareWorker type_; bool can_act_; TargetWaresDisplay display_; + EconomyOptionsWindow* economy_options_window_; }; /// Actions performed when a NoteEconomyWindow is received. void on_economy_note(const Widelands::NoteEconomy& note); + UI::Box main_box_; Widelands::Serial serial_; Widelands::Player* player_; UI::TabPanel tabpanel_; EconomyOptionsPanel* ware_panel_; EconomyOptionsPanel* worker_panel_; std::unique_ptr<Notifications::Subscriber<Widelands::NoteEconomy>> economynotes_subscriber_; + + std::map<std::string, PredefinedTargets> predefined_targets_; + UI::Box dropdown_box_; + UI::Dropdown<std::string> dropdown_; }; #endif // end of include guard: WL_WUI_ECONOMY_OPTIONS_WINDOW_H === modified file 'src/wui/inputqueuedisplay.cc' --- src/wui/inputqueuedisplay.cc 2019-04-25 06:40:24 +0000 +++ src/wui/inputqueuedisplay.cc 2019-05-06 13:53:16 +0000 @@ -71,7 +71,7 @@ uint32_t priority_button_height = show_only ? 0 : 3 * PriorityButtonSize; uint32_t image_height = - show_only ? WARE_MENU_PIC_HEIGHT : std::max<int32_t>(WARE_MENU_PIC_HEIGHT, ph); + show_only ? kWareMenuPicHeight : std::max<int32_t>(kWareMenuPicHeight, ph); total_height_ = std::max(priority_button_height, image_height) + 2 * Border; @@ -91,7 +91,7 @@ */ void InputQueueDisplay::max_size_changed() { uint32_t pbs = show_only_ ? 0 : PriorityButtonSize; - uint32_t ctrl_b_size = show_only_ ? 0 : 2 * WARE_MENU_PIC_WIDTH; + uint32_t ctrl_b_size = show_only_ ? 0 : 2 * kWareMenuPicWidth; cache_size_ = queue_.get_max_size(); @@ -140,7 +140,7 @@ Vector2i point = Vector2i::zero(); point.x = Border + (show_only_ ? 0 : CellWidth + CellSpacing); - point.y = Border + (total_height_ - 2 * Border - WARE_MENU_PIC_HEIGHT) / 2; + point.y = Border + (total_height_ - 2 * Border - kWareMenuPicHeight) / 2; for (; nr_inputs_to_draw; --nr_inputs_to_draw, point.x += CellWidth + CellSpacing) { dst.blitrect(Vector2i(point.x, point.y), icon_, Recti(0, 0, icon_->width(), icon_->height()), @@ -240,12 +240,12 @@ return; uint32_t x = Border; - uint32_t y = Border + (total_height_ - 2 * Border - WARE_MENU_PIC_WIDTH) / 2; + uint32_t y = Border + (total_height_ - 2 * Border - kWareMenuPicWidth) / 2; boost::format tooltip_format("%s<br><p><font size=%d bold=0>%s<br>%s</font></p>"); decrease_max_fill_ = new UI::Button( - this, "decrease_max_fill", x, y, WARE_MENU_PIC_WIDTH, WARE_MENU_PIC_HEIGHT, + this, "decrease_max_fill", x, y, kWareMenuPicWidth, kWareMenuPicHeight, UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_left.png"), (tooltip_format /** TRANSLATORS: Button tooltip in in a building's wares input queue */ @@ -262,7 +262,7 @@ x = Border + (cache_size_ + 1) * (CellWidth + CellSpacing); increase_max_fill_ = new UI::Button( - this, "increase_max_fill", x, y, WARE_MENU_PIC_WIDTH, WARE_MENU_PIC_HEIGHT, + this, "increase_max_fill", x, y, kWareMenuPicWidth, kWareMenuPicHeight, UI::ButtonStyle::kWuiMenu, g_gr->images().get("images/ui_basic/scrollbar_right.png"), (tooltip_format /** TRANSLATORS: Button tooltip in a building's wares input queue */ === modified file 'src/wui/inputqueuedisplay.h' --- src/wui/inputqueuedisplay.h 2019-04-23 14:53:35 +0000 +++ src/wui/inputqueuedisplay.h 2019-05-06 13:53:16 +0000 @@ -49,7 +49,7 @@ */ class InputQueueDisplay : public UI::Panel { public: - enum { CellWidth = WARE_MENU_PIC_WIDTH, CellSpacing = 2, Border = 4, PriorityButtonSize = 10 }; + enum { CellWidth = kWareMenuPicWidth, CellSpacing = 2, Border = 4, PriorityButtonSize = 10 }; InputQueueDisplay(UI::Panel* parent, int32_t x, === modified file 'src/wui/waresdisplay.cc' --- src/wui/waresdisplay.cc 2019-02-23 11:00:49 +0000 +++ src/wui/waresdisplay.cc 2019-05-06 13:53:16 +0000 @@ -35,8 +35,9 @@ #include "logic/map_objects/tribes/ware_descr.h" #include "logic/map_objects/tribes/worker.h" #include "logic/player.h" +#include "ui_basic/window.h" -const int WARE_MENU_INFO_SIZE = 12; +constexpr int kWareMenuInfoSize = 12; AbstractWaresDisplay::AbstractWaresDisplay( UI::Panel* const parent, @@ -46,7 +47,9 @@ Widelands::WareWorker type, bool selectable, boost::function<void(Widelands::DescriptionIndex, bool)> callback_function, - bool horizontal) + bool horizontal, + int32_t hgap, + int32_t vgap) : // Size is set when add_warelist is called, as it depends on the type_. UI::Panel(parent, x, y, 0, 0), tribe_(tribe), @@ -57,6 +60,8 @@ selectable_(selectable), horizontal_(horizontal), + hgap_(hgap), + vgap_(vgap), selection_anchor_(Widelands::INVALID_INDEX), callback_function_(callback_function) { for (const Widelands::DescriptionIndex& index : indices_) { @@ -67,22 +72,65 @@ curware_.set_text(_("Stock")); - // Find out geometry from icons_order - unsigned int columns = icons_order().size(); - unsigned int rows = 0; - for (unsigned int i = 0; i < icons_order().size(); i++) - if (icons_order()[i].size() > rows) - rows = icons_order()[i].size(); + graphic_resolution_changed_subscriber_ = Notifications::subscribe<GraphicResolutionChanged>( + [this](const GraphicResolutionChanged&) { + recalc_desired_size(true); + }); + + recalc_desired_size(false); +} + +Widelands::Extent AbstractWaresDisplay::get_extent() const { + int16_t columns = 0; + int16_t rows = 0; + for (const auto& pair : icons_order_coords()) { + columns = std::max(columns, pair.second.x); + rows = std::max(rows, pair.second.y); + } + // We cound from 0 up + ++columns; + ++rows; + if (horizontal_) { - unsigned int s = columns; + const int16_t s = columns; columns = rows; rows = s; } + return Widelands::Extent(columns, rows); +} + +void AbstractWaresDisplay::set_hgap(int32_t gap) { + hgap_ = gap; + recalc_desired_size(true); +} + +void AbstractWaresDisplay::set_vgap(int32_t gap) { + vgap_ = gap; + recalc_desired_size(true); +} + +void AbstractWaresDisplay::recalc_desired_size(bool relayout) { + relayout_icons_order_coords(); + + // Find out geometry from icons_order + const Widelands::Extent size = get_extent(); // 25 is height of curware_ text set_desired_size( - columns * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X) + 1, - rows * (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y) + 1 + 25); + size.w * (kWareMenuPicWidth + hgap_) + 1, + size.h * (kWareMenuPicHeight + kWareMenuInfoSize + vgap_) + 1 + 25); + + if (relayout) { + // Since we are usually stacked deep within other panels, we need to tell our highest parent window to relayout + UI::Panel* p = this; + while (p->get_parent()) { + p = p->get_parent(); + if (dynamic_cast<UI::Window*>(p)) { + p->layout(); + return; + } + } + } } bool AbstractWaresDisplay::handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t, int32_t) { @@ -162,21 +210,33 @@ * DescriptionIndex::null() if the given point is outside the range. */ Widelands::DescriptionIndex AbstractWaresDisplay::ware_at_point(int32_t x, int32_t y) const { - if (x < 0 || y < 0) - return Widelands::INVALID_INDEX; - - unsigned int i = x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X); - unsigned int j = y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y); + // Graphical offset + x -= 2; + y -= 2; + + if (x < 0 || y < 0) { + return Widelands::INVALID_INDEX; + } + + int i = x / (kWareMenuPicWidth + hgap_); + int j = y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_); + if (kWareMenuPicWidth * (i + 1) + hgap_ * i < x || + (kWareMenuPicHeight + kWareMenuInfoSize) * (j + 1) + vgap_ * j < y) { + // Not on the ware, but on the space between + return Widelands::INVALID_INDEX; + } if (horizontal_) { - unsigned int s = i; + int s = i; i = j; j = s; } - if (i < icons_order().size() && j < icons_order()[i].size()) { - const Widelands::DescriptionIndex& ware = icons_order()[i][j]; - assert(hidden_.count(ware) == 1); - if (!(hidden_.find(ware)->second)) { - return ware; + for (const auto& pair : icons_order_coords()) { + if (pair.second.x == i && pair.second.y == j) { + assert(hidden_.count(pair.first) == 1); + if (!(hidden_.find(pair.first)->second)) { + return pair.first; + } + break; } } @@ -199,15 +259,13 @@ Vector2i anchor_pos = ware_position(selection_anchor_); // Add an offset to make sure the anchor line and column will be // selected when selecting in topleft direction - int32_t anchor_x = anchor_pos.x + WARE_MENU_PIC_WIDTH / 2; - int32_t anchor_y = anchor_pos.y + WARE_MENU_PIC_HEIGHT / 2; + int32_t anchor_x = anchor_pos.x + kWareMenuPicWidth / 2; + int32_t anchor_y = anchor_pos.y + kWareMenuPicHeight / 2; - unsigned int left_ware_idx = anchor_x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X); - unsigned int top_ware_idx = - anchor_y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y); - unsigned int right_ware_idx = x / (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X); - unsigned int bottoware_idx_ = - y / (WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + WARE_MENU_PIC_PAD_Y); + unsigned int left_ware_idx = anchor_x / (kWareMenuPicWidth + hgap_); + unsigned int top_ware_idx = anchor_y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_); + unsigned int right_ware_idx = x / (kWareMenuPicWidth + hgap_); + unsigned int bottoware_idx_ = y / (kWareMenuPicHeight + kWareMenuInfoSize + vgap_); unsigned int tmp; // Reverse col/row and anchor/endpoint if needed @@ -271,26 +329,43 @@ NEVER_HERE(); } -const Widelands::TribeDescr::WaresOrderCoords& AbstractWaresDisplay::icons_order_coords() const { - switch (type_) { - case Widelands::wwWARE: - return tribe_.wares_order_coords(); - case Widelands::wwWORKER: - return tribe_.workers_order_coords(); +const WaresOrderCoords& AbstractWaresDisplay::icons_order_coords() const { + assert(!order_coords_.empty()); + return order_coords_; +} + +void AbstractWaresDisplay::relayout_icons_order_coords() { + order_coords_.clear(); + const int column_number = icons_order().size(); + const int column_max_size = (g_gr->get_yres() - 290) / (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize); + + int16_t column_index_to_apply = 0; + for (int16_t column_index = 0; column_index < column_number; ++column_index) { + const std::vector<Widelands::DescriptionIndex>& column = icons_order().at(column_index); + const int row_number = column.size(); + int16_t row_index_to_apply = 0; + for (int16_t row_index = 0; row_index < row_number; ++row_index) { + order_coords_.emplace(column.at(row_index), Widelands::Coords(column_index_to_apply, row_index_to_apply)); + ++row_index_to_apply; + if (row_index_to_apply > column_max_size) { + row_index_to_apply = 0; + ++column_index_to_apply; + } + } + if (row_index_to_apply > 0) { + ++column_index_to_apply; + } } - NEVER_HERE(); } Vector2i AbstractWaresDisplay::ware_position(Widelands::DescriptionIndex id) const { Vector2i p(2, 2); if (horizontal_) { - p.x += icons_order_coords()[id].second * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X); - p.y += icons_order_coords()[id].first * - (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + WARE_MENU_INFO_SIZE); + p.x += icons_order_coords().at(id).y * (kWareMenuPicWidth + hgap_); + p.y += icons_order_coords().at(id).x * (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize); } else { - p.x += icons_order_coords()[id].first * (WARE_MENU_PIC_WIDTH + WARE_MENU_PIC_PAD_X); - p.y += icons_order_coords()[id].second * - (WARE_MENU_PIC_HEIGHT + WARE_MENU_PIC_PAD_Y + WARE_MENU_INFO_SIZE); + p.x += icons_order_coords().at(id).x * (kWareMenuPicWidth + hgap_); + p.y += icons_order_coords().at(id).y * (kWareMenuPicHeight + vgap_ + kWareMenuInfoSize); } return p; } @@ -326,15 +401,15 @@ const Image* icon = type_ == Widelands::wwWORKER ? tribe_.get_worker_descr(id)->icon() : tribe_.get_ware_descr(id)->icon(); - dst.blit(p + Vector2i((w - WARE_MENU_PIC_WIDTH) / 2, 1), icon); + dst.blit(p + Vector2i((w - kWareMenuPicWidth) / 2, 1), icon); - dst.fill_rect(Recti(p + Vector2i(0, WARE_MENU_PIC_HEIGHT), w, WARE_MENU_INFO_SIZE), + dst.fill_rect(Recti(p + Vector2i(0, kWareMenuPicHeight), w, kWareMenuInfoSize), info_color_for_ware(id)); std::shared_ptr<const UI::RenderedText> rendered_text = UI::g_fh->render(as_waresinfo(info_for_ware(id))); rendered_text->draw(dst, Vector2i(p.x + w - rendered_text->width() - 1, - p.y + WARE_MENU_PIC_HEIGHT + WARE_MENU_INFO_SIZE + 1 - + p.y + kWareMenuPicHeight + kWareMenuInfoSize + 1 - rendered_text->height())); } === modified file 'src/wui/waresdisplay.h' --- src/wui/waresdisplay.h 2019-02-23 11:00:49 +0000 +++ src/wui/waresdisplay.h 2019-05-06 13:53:16 +0000 @@ -20,6 +20,7 @@ #ifndef WL_WUI_WARESDISPLAY_H #define WL_WUI_WARESDISPLAY_H +#include <memory> #include <vector> #include "logic/map_objects/tribes/tribe_descr.h" @@ -36,6 +37,8 @@ struct WareList; } // namespace Widelands +using WaresOrderCoords = std::map<Widelands::DescriptionIndex, Widelands::Coords>; + /** * Display wares or workers together with some string (typically a number) * in the style of the @ref WarehouseWindow. @@ -54,7 +57,9 @@ CLANG_DIAG_OFF("-Wunknown-pragmas") CLANG_DIAG_OFF("-Wzero-as-null-pointer-constant") boost::function<void(Widelands::DescriptionIndex, bool)> callback_function = 0, CLANG_DIAG_ON("-Wzero-as-null-pointer-constant") - CLANG_DIAG_ON("-Wunknown-pragmas") bool horizontal = false); + CLANG_DIAG_ON("-Wunknown-pragmas") bool horizontal = false, + int32_t hgap = 3, + int32_t vgap = 4); bool handle_mousemove(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff) override; @@ -74,6 +79,19 @@ return type_; } + int32_t get_hgap() { + return hgap_; + } + int32_t get_vgap() { + return vgap_; + } + void set_hgap(int32_t); + void set_vgap(int32_t); + + Widelands::Extent get_extent() const; + + const WaresOrderCoords& icons_order_coords() const; + protected: void layout() override; @@ -82,7 +100,6 @@ virtual RGBColor info_color_for_ware(Widelands::DescriptionIndex); const Widelands::TribeDescr::WaresOrder& icons_order() const; - const Widelands::TribeDescr::WaresOrderCoords& icons_order_coords() const; virtual Vector2i ware_position(Widelands::DescriptionIndex) const; void draw(RenderTarget&) override; virtual void draw_ware(RenderTarget&, Widelands::DescriptionIndex); @@ -110,6 +127,13 @@ WareListSelectionType in_selection_; // Wares in temporary anchored selection bool selectable_; bool horizontal_; + int32_t hgap_; + int32_t vgap_; + + WaresOrderCoords order_coords_; + + void relayout_icons_order_coords(); + void recalc_desired_size(bool); /** * The ware on which the mouse press has been performed. @@ -117,6 +141,8 @@ */ Widelands::DescriptionIndex selection_anchor_; boost::function<void(Widelands::DescriptionIndex, bool)> callback_function_; + + std::unique_ptr<Notifications::Subscriber<GraphicResolutionChanged>> graphic_resolution_changed_subscriber_; }; /*
_______________________________________________ Mailing list: https://launchpad.net/~widelands-dev Post to : widelands-dev@lists.launchpad.net Unsubscribe : https://launchpad.net/~widelands-dev More help : https://help.launchpad.net/ListHelp