GunChleoc has proposed merging 
lp:~widelands-dev/widelands/smaller_building_statistics into lp:widelands.

Commit message:
Building Statistics now only show relevant buildings
- Military sites not belonging to the tribe are omitted, unless the payer 
currently owns one
- Seafaring and allowed buildings are dynamically checked
- Show "under construction" navigation for buildings being enhanced

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1636511 in widelands: "Buildings being upgraded not showing up in 
Building Statistics box."
  https://bugs.launchpad.net/widelands/+bug/1636511

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/smaller_building_statistics/+merge/342828

Fixes the attached bug.

Since the Frisians were added, the building statistics got too big due to all 
the military sites - the navigation became impossible to reach on 800x600 
resolution without moving the window around. So, I have implemented a dynamic 
approach that will only show allowed and currently owned buildings.
-- 
Your team Widelands Developers is requested to review the proposed merge of 
lp:~widelands-dev/widelands/smaller_building_statistics into lp:widelands.
=== modified file 'data/tribes/buildings/warehouses/atlanteans/port/init.lua'
--- data/tribes/buildings/warehouses/atlanteans/port/init.lua	2017-09-02 19:53:03 +0000
+++ data/tribes/buildings/warehouses/atlanteans/port/init.lua	2018-04-07 16:30:59 +0000
@@ -8,6 +8,7 @@
    helptext_script = dirname .. "helptexts.lua",
    icon = dirname .. "menu.png",
    size = "port",
+   needs_seafaring = true,
 
    buildcost = {
       log = 3,

=== modified file 'data/tribes/buildings/warehouses/barbarians/port/init.lua'
--- data/tribes/buildings/warehouses/barbarians/port/init.lua	2017-09-02 19:53:03 +0000
+++ data/tribes/buildings/warehouses/barbarians/port/init.lua	2018-04-07 16:30:59 +0000
@@ -8,6 +8,7 @@
    helptext_script = dirname .. "helptexts.lua",
    icon = dirname .. "menu.png",
    size = "port",
+   needs_seafaring = true,
 
    buildcost = {
       log = 3,

=== modified file 'data/tribes/buildings/warehouses/empire/port/init.lua'
--- data/tribes/buildings/warehouses/empire/port/init.lua	2017-09-02 19:53:03 +0000
+++ data/tribes/buildings/warehouses/empire/port/init.lua	2018-04-07 16:30:59 +0000
@@ -8,6 +8,7 @@
    helptext_script = dirname .. "helptexts.lua",
    icon = dirname .. "menu.png",
    size = "port",
+   needs_seafaring = true,
 
    buildcost = {
       log = 3,

=== modified file 'data/tribes/buildings/warehouses/frisians/port/init.lua'
--- data/tribes/buildings/warehouses/frisians/port/init.lua	2018-02-16 14:53:04 +0000
+++ data/tribes/buildings/warehouses/frisians/port/init.lua	2018-04-07 16:30:59 +0000
@@ -8,6 +8,7 @@
    helptext_script = dirname .. "helptexts.lua",
    icon = dirname .. "menu.png",
    size = "port",
+   needs_seafaring = true,
 
    buildcost = {
       brick = 6,

=== modified file 'src/ui_basic/tabpanel.cc'
--- src/ui_basic/tabpanel.cc	2017-08-08 17:39:40 +0000
+++ src/ui_basic/tabpanel.cc	2018-04-07 16:30:59 +0000
@@ -225,6 +225,9 @@
 }
 
 bool TabPanel::remove_last_tab(const std::string& tabname) {
+	if (tabs_.empty()) {
+		return false;
+	}
 	if (tabs_.back()->get_name() == tabname) {
 		tabs_.pop_back();
 		if (active_ > tabs_.size() - 1) {

=== modified file 'src/wui/building_statistics_menu.cc'
--- src/wui/building_statistics_menu.cc	2017-12-16 10:48:12 +0000
+++ src/wui/building_statistics_menu.cc	2018-04-07 16:30:59 +0000
@@ -120,118 +120,11 @@
      low_production_(33),
      has_selection_(false) {
 
-	for (int i = 0; i < kNoOfBuildingTabs; ++i) {
-		row_counters_[i] = 0;
-		tabs_[i] = new UI::Box(&tab_panel_, 0, 0, UI::Box::Vertical);
-	}
-
-	tab_panel_.add("building_stats_small",
-	               g_gr->images().get("images/wui/fieldaction/menu_tab_buildsmall.png"),
-	               tabs_[BuildingTab::Small], _("Small buildings"));
-	tab_panel_.add("building_stats_medium",
-	               g_gr->images().get("images/wui/fieldaction/menu_tab_buildmedium.png"),
-	               tabs_[BuildingTab::Medium], _("Medium buildings"));
-	tab_panel_.add("building_stats_big",
-	               g_gr->images().get("images/wui/fieldaction/menu_tab_buildbig.png"),
-	               tabs_[BuildingTab::Big], _("Big buildings"));
-	tab_panel_.add("building_stats_mines",
-	               g_gr->images().get("images/wui/fieldaction/menu_tab_buildmine.png"),
-	               tabs_[BuildingTab::Mines], _("Mines"));
-
-	// Only show the ports tab for seafaring maps
-	if (iplayer().game().map().allows_seafaring()) {
-		tab_panel_.add("building_stats_ports",
-		               g_gr->images().get("images/wui/fieldaction/menu_tab_buildport.png"),
-		               tabs_[BuildingTab::Ports], _("Ports"));
-	}
-
 	const DescriptionIndex nr_buildings = parent.egbase().tribes().nrbuildings();
 	building_buttons_ = std::vector<UI::Button*>(nr_buildings);
 	owned_labels_ = std::vector<UI::Textarea*>(nr_buildings);
 	productivity_labels_ = std::vector<UI::Textarea*>(nr_buildings);
 
-	// Column counters
-	int columns[kNoOfBuildingTabs] = {0, 0, 0, 0, 0};
-
-	// Row containers
-	UI::Box* rows[kNoOfBuildingTabs];
-	for (int i = 0; i < kNoOfBuildingTabs; ++i) {
-		rows[i] = new UI::Box(tabs_[i], 0, 0, UI::Box::Horizontal);
-	}
-
-	// We want to add player tribe's buildings in correct order
-	const TribeDescr& tribe = iplayer().player().tribe();
-	std::vector<DescriptionIndex> buildings_to_add;
-	for (DescriptionIndex index : tribe.buildings()) {
-		// Only add headquarter types that are owned by player.
-		const BuildingDescr& descr = *tribe.get_building_descr(index);
-		const Widelands::Player& player = iplayer().player();
-		if (descr.is_buildable() || descr.is_enhanced() ||
-		    !player.get_building_statistics(index).empty()) {
-			buildings_to_add.push_back(index);
-		}
-	}
-
-	// We want to add other tribes' militarysites on the bottom
-	for (DescriptionIndex index = 0; index < nr_buildings; ++index) {
-		const BuildingDescr& descr = *parent.egbase().tribes().get_building_descr(index);
-		if (descr.type() == MapObjectType::MILITARYSITE && !tribe.has_building(index)) {
-			buildings_to_add.push_back(index);
-		}
-	}
-
-	for (DescriptionIndex id : buildings_to_add) {
-		const BuildingDescr& descr = *tribe.get_building_descr(id);
-
-		if (descr.type() != MapObjectType::CONSTRUCTIONSITE &&
-		    descr.type() != MapObjectType::DISMANTLESITE) {
-			if (descr.get_ismine()) {
-				if (add_button(id, descr, BuildingTab::Mines, *rows[BuildingTab::Mines],
-				               &columns[BuildingTab::Mines])) {
-					rows[BuildingTab::Mines] =
-					   new UI::Box(tabs_[BuildingTab::Mines], 0, 0, UI::Box::Horizontal);
-				}
-			} else if (descr.get_isport()) {
-				if (add_button(id, descr, BuildingTab::Ports, *rows[BuildingTab::Ports],
-				               &columns[BuildingTab::Ports])) {
-					rows[BuildingTab::Ports] =
-					   new UI::Box(tabs_[BuildingTab::Ports], 0, 0, UI::Box::Horizontal);
-				}
-			} else {
-				switch (descr.get_size()) {
-				case BaseImmovable::SMALL:
-					if (add_button(id, descr, BuildingTab::Small, *rows[BuildingTab::Small],
-					               &columns[BuildingTab::Small])) {
-						rows[BuildingTab::Small] =
-						   new UI::Box(tabs_[BuildingTab::Small], 0, 0, UI::Box::Horizontal);
-					}
-					break;
-				case BaseImmovable::MEDIUM:
-					if (add_button(id, descr, BuildingTab::Medium, *rows[BuildingTab::Medium],
-					               &columns[BuildingTab::Medium])) {
-						rows[BuildingTab::Medium] =
-						   new UI::Box(tabs_[BuildingTab::Medium], 0, 0, UI::Box::Horizontal);
-					}
-					break;
-				case BaseImmovable::BIG:
-					if (add_button(id, descr, BuildingTab::Big, *rows[BuildingTab::Big],
-					               &columns[BuildingTab::Big])) {
-						rows[BuildingTab::Big] =
-						   new UI::Box(tabs_[BuildingTab::Big], 0, 0, UI::Box::Horizontal);
-					}
-					break;
-				default:
-					throw wexception(
-					   "Building statictics: Found building without a size: %s", descr.name().c_str());
-				}
-			}
-		}
-	}
-
-	for (int i = 0; i < kNoOfBuildingTabs; ++i) {
-		tabs_[i]->add(rows[i]);
-	}
-
 	set_label_font(&owned_label_);
 	set_label_font(&construction_label_);
 	set_label_font(&unproductive_label_);
@@ -306,7 +199,7 @@
 	unproductive_percent_.cancel.connect(
 	   boost::bind(&BuildingStatisticsMenu::low_production_reset_focus, boost::ref(*this)));
 
-	update();
+	init();
 }
 
 BuildingStatisticsMenu::~BuildingStatisticsMenu() {
@@ -315,6 +208,189 @@
 	productivity_labels_.clear();
 }
 
+void BuildingStatisticsMenu::reset() {
+	update();  // In case a building got removed, make sure to deselect it first
+
+	const int last_selected_tab = tab_assignments_[tab_panel_.active()];
+
+	tab_panel_.remove_last_tab("building_stats_ports");
+	tab_panel_.remove_last_tab("building_stats_mines");
+	tab_panel_.remove_last_tab("building_stats_big");
+	tab_panel_.remove_last_tab("building_stats_medium");
+	tab_panel_.remove_last_tab("building_stats_small");
+
+	// Clean state if buildings disappear from list
+	const DescriptionIndex nr_buildings = iplayer().egbase().tribes().nrbuildings();
+	building_buttons_.clear();
+	building_buttons_.resize(nr_buildings);
+	owned_labels_.clear();
+	owned_labels_.resize(nr_buildings);
+	productivity_labels_.clear();
+	productivity_labels_.resize(nr_buildings);
+
+	// Ensure that defunct buttons disappear
+	for (int tab_index = 0; tab_index < kNoOfBuildingTabs; ++tab_index) {
+		if (tabs_[tab_index] != nullptr) {
+			tabs_[tab_index]->die();
+		}
+	}
+
+	init(last_selected_tab);
+
+	// Reset navigator
+	building_name_.set_text("");
+	if (has_selection_) {
+		if (building_buttons_[current_building_type_] != nullptr) {
+			set_current_building_type(current_building_type_);
+		} else {
+			has_selection_ = false;
+		}
+	}
+}
+
+void BuildingStatisticsMenu::init(int last_selected_tab) {
+	// We want to add player tribe's buildings in correct order
+	const Widelands::Player& player = iplayer().player();
+	const TribeDescr& tribe = player.tribe();
+	std::vector<DescriptionIndex> buildings_to_add[kNoOfBuildingTabs];
+	// Add the player's own tribe's buildings.
+	for (DescriptionIndex index : tribe.buildings()) {
+		if (own_building_is_valid(index)) {
+			buildings_to_add[find_tab_for_building(*tribe.get_building_descr(index))].push_back(index);
+		}
+	}
+
+	// We want to add other tribes' buildings on the bottom. Only add the ones that the player owns.
+	for (DescriptionIndex index = 0; index < iplayer().egbase().tribes().nrbuildings(); ++index) {
+		if (foreign_tribe_building_is_valid(index)) {
+			buildings_to_add[find_tab_for_building(*tribe.get_building_descr(index))].push_back(index);
+		}
+	}
+
+	// Now create the tab contents and add the building buttons
+	int row_counters[kNoOfBuildingTabs];
+	for (int tab_index = 0; tab_index < kNoOfBuildingTabs; ++tab_index) {
+		int current_column = 0;
+		tabs_[tab_index] = new UI::Box(&tab_panel_, 0, 0, UI::Box::Vertical);
+		UI::Box* row = new UI::Box(tabs_[tab_index], 0, 0, UI::Box::Horizontal);
+		row_counters[tab_index] = 0;
+
+		for (const Widelands::DescriptionIndex id : buildings_to_add[tab_index]) {
+			const BuildingDescr& descr = *iplayer().egbase().tribes().get_building_descr(id);
+			add_button(id, descr, row);
+			++current_column;
+			if (current_column == 1) {
+				++row_counters[tab_index];
+			} else if (current_column == kColumns) {
+				tabs_[tab_index]->add(row, UI::Box::Resizing::kFullSize);
+				tabs_[tab_index]->add_space(6);
+				row = new UI::Box(tabs_[tab_index], 0, 0, UI::Box::Horizontal);
+				current_column = 0;
+			}
+		}
+		// Add final row
+		if (current_column != 0) {
+			tabs_[tab_index]->add(row, UI::Box::Resizing::kFullSize);
+		}
+	}
+
+	// Show the tabs that have buttons on them
+	int tab_counter = 0;
+	auto add_tab = [this, row_counters, &tab_counter, last_selected_tab](
+	   int tab_index, const std::string& name, const std::string& image, const std::string& descr) {
+		if (row_counters[tab_index] > 0) {
+			tab_panel_.add(name, g_gr->images().get(image), tabs_[tab_index], descr);
+			if (last_selected_tab == tab_index) {
+				tab_panel_.activate(tab_counter);
+			}
+			tab_assignments_[tab_counter] = tab_index;
+			row_counters_[tab_counter] = row_counters[tab_index];
+			++tab_counter;
+		}
+	};
+	add_tab(BuildingTab::Small, "building_stats_small",
+	        "images/wui/fieldaction/menu_tab_buildsmall.png", _("Small buildings"));
+	add_tab(BuildingTab::Medium, "building_stats_medium",
+	        "images/wui/fieldaction/menu_tab_buildmedium.png", _("Medium buildings"));
+	add_tab(BuildingTab::Big, "building_stats_big", "images/wui/fieldaction/menu_tab_buildbig.png",
+	        _("Big buildings"));
+	add_tab(BuildingTab::Mines, "building_stats_mines",
+	        "images/wui/fieldaction/menu_tab_buildmine.png", _("Mines"));
+	add_tab(BuildingTab::Ports, "building_stats_ports",
+	        "images/wui/fieldaction/menu_tab_buildport.png", _("Ports"));
+
+	update();
+}
+
+bool BuildingStatisticsMenu::own_building_is_valid(Widelands::DescriptionIndex index) const {
+	const Widelands::Player& player = iplayer().player();
+	const BuildingDescr& descr = *player.tribe().get_building_descr(index);
+	// Skip seafaring buildings if not needed
+	if (descr.needs_seafaring() && !iplayer().game().map().allows_seafaring() &&
+	    player.get_building_statistics(index).empty()) {
+		return false;
+	}
+	if (descr.type() == MapObjectType::CONSTRUCTIONSITE ||
+	    descr.type() == MapObjectType::DISMANTLESITE) {
+		return false;
+	}
+	// Only add allowed buildings or buildings that are owned by the player.
+	if ((player.is_building_type_allowed(index) && (descr.is_buildable() || descr.is_enhanced())) ||
+	    !player.get_building_statistics(index).empty()) {
+		return true;
+	}
+	return false;
+}
+
+bool BuildingStatisticsMenu::foreign_tribe_building_is_valid(
+   Widelands::DescriptionIndex index) const {
+	const Widelands::Player& player = iplayer().player();
+	if (!player.tribe().has_building(index) && !player.get_building_statistics(index).empty()) {
+		const BuildingDescr& descr = *iplayer().egbase().tribes().get_building_descr(index);
+		if (descr.type() == MapObjectType::CONSTRUCTIONSITE ||
+		    descr.type() == MapObjectType::DISMANTLESITE) {
+			return false;
+		}
+		return true;
+	}
+	return false;
+}
+
+int BuildingStatisticsMenu::find_tab_for_building(const Widelands::BuildingDescr& descr) const {
+	assert(descr.type() != MapObjectType::CONSTRUCTIONSITE);
+	assert(descr.type() != MapObjectType::DISMANTLESITE);
+	if (descr.get_ismine()) {
+		return BuildingTab::Mines;
+	} else if (descr.get_isport()) {
+		return BuildingTab::Ports;
+	} else {
+		switch (descr.get_size()) {
+		case BaseImmovable::SMALL:
+			return BuildingTab::Small;
+		case BaseImmovable::MEDIUM:
+			return BuildingTab::Medium;
+		case BaseImmovable::BIG:
+			return BuildingTab::Big;
+		default:
+			throw wexception(
+			   "Building statictics: Found building without a size: %s", descr.name().c_str());
+		}
+	}
+	NEVER_HERE();
+}
+
+void BuildingStatisticsMenu::update_building_list() {
+	for (DescriptionIndex index = 0; index < iplayer().egbase().tribes().nrbuildings(); ++index) {
+		const bool should_have_this_building =
+		   own_building_is_valid(index) || foreign_tribe_building_is_valid(index);
+		const bool has_this_building = building_buttons_[index] != nullptr;
+		if (should_have_this_building != has_this_building) {
+			reset();
+			return;
+		}
+	}
+}
+
 /**
  * Adds 3 buttons per building type.
  *
@@ -322,10 +398,10 @@
  * - Buildings owned, steps through constructionsites
  * - Productivity, steps though buildings with low productivity and stopped buildings
  */
-bool BuildingStatisticsMenu::add_button(
-   DescriptionIndex id, const BuildingDescr& descr, int tab_index, UI::Box& row, int* column) {
-
-	UI::Box* button_box = new UI::Box(&row, 0, 0, UI::Box::Vertical);
+void BuildingStatisticsMenu::add_button(DescriptionIndex id,
+                                        const BuildingDescr& descr,
+                                        UI::Box* row) {
+	UI::Box* button_box = new UI::Box(row, 0, 0, UI::Box::Vertical);
 	building_buttons_[id] = new UI::Button(
 	   button_box, (boost::format("building_button%s") % id).str(), 0, 0, kBuildGridCellWidth,
 	   kBuildGridCellHeight, g_gr->images().get("images/ui_basic/but1.png"),
@@ -347,25 +423,10 @@
 	productivity_labels_[id]->set_fixed_width(kBuildGridCellWidth);
 	button_box->add(productivity_labels_[id]);
 
-	row.add(button_box);
+	row->add(button_box);
 
 	building_buttons_[id]->sigclicked.connect(
 	   boost::bind(&BuildingStatisticsMenu::set_current_building_type, boost::ref(*this), id));
-
-	// For dynamic window height
-	if (*column == 0) {
-		++row_counters_[tab_index];
-	}
-
-	// Check if the row is full
-	++*column;
-	if (*column == kColumns) {
-		tabs_[tab_index]->add(&row);
-		tabs_[tab_index]->add_space(6);
-		*column = 0;
-		return true;
-	}
-	return false;
 }
 
 void BuildingStatisticsMenu::jump_building(JumpTarget target, bool reverse) {
@@ -496,28 +557,30 @@
  * Update this statistic
  */
 void BuildingStatisticsMenu::think() {
+	// Update statistics
+	const int32_t gametime = iplayer().game().get_gametime();
+
+	if (was_minimized_ || (gametime - lastupdate_) > kUpdateTimeInGametimeMs) {
+		update_building_list();
+		update();
+		lastupdate_ = gametime;
+	}
+	// Make sure we don't have a delay with displaying labels when we restore the window.
+	was_minimized_ = is_minimal();
+
 	// Adjust height to current tab
 	if (is_minimal()) {
 		tab_panel_.set_size(0, 0);
 	} else {
-		int tab_height =
+		const int tab_height =
 		   35 +
-		   row_counters_[tab_panel_.active()] * (kBuildGridCellHeight + kLabelHeight + kLabelHeight);
+		   row_counters_[tab_panel_.active()] * (kBuildGridCellHeight + kLabelHeight + kLabelHeight) +
+		   kMargin;
 		tab_panel_.set_size(kWindowWidth, tab_height);
 		set_size(
-		   get_w(), tab_height + kMargin + 4 * kButtonRowHeight + get_tborder() + get_bborder());
+		   get_w(), tab_height + kMargin + navigation_panel_.get_h() + get_tborder() + get_bborder());
 		navigation_panel_.set_pos(Vector2i(0, tab_height + kMargin));
 	}
-
-	// Update statistics
-	const int32_t gametime = iplayer().game().get_gametime();
-
-	if (was_minimized_ || (gametime - lastupdate_) > kUpdateTimeInGametimeMs) {
-		update();
-		lastupdate_ = gametime;
-	}
-	// Make sure we don't have a delay with displaying labels when we restore the window.
-	was_minimized_ = is_minimal();
 }
 
 /*
@@ -676,7 +739,9 @@
 		}
 
 		std::string owned_text;
-		if (player.tribe().has_building(id) && (building.is_buildable() || building.is_enhanced())) {
+		const bool can_construct_this_building = player.tribe().has_building(id) &&
+				(building.is_buildable() || building.is_enhanced());
+		if (can_construct_this_building) {
 			/** TRANSLATORS: Buildings: owned / under construction */
 			owned_text = (boost::format(_("%1%/%2%")) % nr_owned % nr_build).str();
 		} else {
@@ -694,7 +759,7 @@
 			no_owned_label_.set_visible(true);
 			navigation_buttons_[NavigationButton::NextOwned]->set_visible(true);
 			navigation_buttons_[NavigationButton::PrevOwned]->set_visible(true);
-			if (player.tribe().has_building(id) && building.is_buildable()) {
+			if (can_construct_this_building) {
 				no_construction_label_.set_text(nr_build > 0 ? std::to_string(nr_build) : "");
 				navigation_buttons_[NavigationButton::NextConstruction]->set_enabled(nr_build > 0);
 				navigation_buttons_[NavigationButton::PrevConstruction]->set_enabled(nr_build > 0);

=== modified file 'src/wui/building_statistics_menu.h'
--- src/wui/building_statistics_menu.h	2017-11-26 14:44:23 +0000
+++ src/wui/building_statistics_menu.h	2018-04-07 16:30:59 +0000
@@ -65,13 +65,21 @@
 		NextUnproductive
 	};
 
+	/// Initialize the buttons
+	void reset();
+	void init(int last_selected_tab = 0);
+
+	bool own_building_is_valid(Widelands::DescriptionIndex index) const;
+	bool foreign_tribe_building_is_valid(Widelands::DescriptionIndex index) const;
+	int find_tab_for_building(const Widelands::BuildingDescr& descr) const;
+
+	void update_building_list();
+
 	/// Adds a button for the building type belonging to the id and descr to the tab.
 	/// Returns true when a new row needs to be created.
-	bool add_button(Widelands::DescriptionIndex id,
+	void add_button(Widelands::DescriptionIndex id,
 	                const Widelands::BuildingDescr& descr,
-	                int tab_index,
-	                UI::Box& row,
-	                int* column);
+	                UI::Box* row);
 
 	/// Jumps to the next / previous appropriate building
 	void jump_building(JumpTarget target, bool reverse);
@@ -96,6 +104,7 @@
 	UI::TabPanel tab_panel_;
 	UI::Box* tabs_[kNoOfBuildingTabs];
 	int row_counters_[kNoOfBuildingTabs];
+	int tab_assignments_[kNoOfBuildingTabs];
 
 	/// Button with building icon
 	std::vector<UI::Button*> building_buttons_;

_______________________________________________
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

Reply via email to