← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/toolbar_cleanup into lp:widelands

 

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

Commit message:
Replaced repetitive toolbar button hook macros with a common function 'add_toolbar_button'. UniqueWindows can have a button assigned to them that will be permpressed/not permpressed depending on whether the window is open.

Requested reviews:
  Widelands Developers (widelands-dev)

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/toolbar_cleanup/+merge/311115
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/toolbar_cleanup into lp:widelands.
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2016-11-03 07:20:57 +0000
+++ src/editor/editorinteractive.cc	2016-11-17 08:27:19 +0000
@@ -93,56 +93,47 @@
      realtime_(SDL_GetTicks()),
      is_painting_(false),
      tools_(new Tools()),
-     history_(new EditorHistory(undo_, redo_)),
-
-#define INIT_BUTTON(image, name, tooltip)                                                          \
-	TOOLBAR_BUTTON_COMMON_PARAMETERS(name), g_gr->images().get(image), tooltip
-
-     toggle_main_menu_(
-        INIT_BUTTON("images/wui/menus/menu_toggle_menu.png", "menu", _("Main Menu"))),
-     toggle_tool_menu_(
-        INIT_BUTTON("images/wui/editor/editor_menu_toggle_tool_menu.png", "tools", _("Tools"))),
-     toggle_toolsize_menu_(INIT_BUTTON(
-        "images/wui/editor/editor_menu_set_toolsize_menu.png", "toolsize", _("Tool Size"))),
-     toggle_minimap_(
-        INIT_BUTTON("images/wui/menus/menu_toggle_minimap.png", "minimap", _("Minimap"))),
-     toggle_buildhelp_(INIT_BUTTON("images/wui/menus/menu_toggle_buildhelp.png",
-                                   "buildhelp",
-                                   _("Show Building Spaces (on/off)"))),
-     reset_zoom_(
-        INIT_BUTTON("images/wui/menus/menu_reset_zoom.png", "reset_zoom", _("Reset zoom"))),
-     toggle_player_menu_(
-        INIT_BUTTON("images/wui/editor/editor_menu_player_menu.png", "players", _("Players"))),
-     undo_(INIT_BUTTON("images/wui/editor/editor_undo.png", "undo", _("Undo"))),
-     redo_(INIT_BUTTON("images/wui/editor/editor_redo.png", "redo", _("Redo"))),
-     toggle_help_(INIT_BUTTON("images/ui_basic/menu_help.png", "help", _("Help"))) {
-	toggle_main_menu_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_mainmenu, this));
-	toggle_tool_menu_.sigclicked.connect(boost::bind(&EditorInteractive::tool_menu_btn, this));
-	toggle_toolsize_menu_.sigclicked.connect(
-	   boost::bind(&EditorInteractive::toolsize_menu_btn, this));
-	toggle_minimap_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_minimap, this));
-	toggle_buildhelp_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_buildhelp, this));
-	reset_zoom_.sigclicked.connect(
-	   [this] { zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f)); });
-	toggle_player_menu_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_playermenu, this));
-	undo_.sigclicked.connect([this] { history_->undo_action(egbase().world()); });
-	redo_.sigclicked.connect([this] { history_->redo_action(egbase().world()); });
-	toggle_help_.sigclicked.connect(boost::bind(&EditorInteractive::toggle_help, this));
-
-	toolbar_.set_layout_toplevel(true);
-	toolbar_.add(&toggle_main_menu_, UI::Align::kLeft);
-	toolbar_.add(&toggle_tool_menu_, UI::Align::kLeft);
-	toolbar_.add(&toggle_toolsize_menu_, UI::Align::kLeft);
-	toolbar_.add(&toggle_player_menu_, UI::Align::kLeft);
-	toolbar_.add_space(15);
-	toolbar_.add(&toggle_minimap_, UI::Align::kLeft);
-	toolbar_.add(&toggle_buildhelp_, UI::Align::kLeft);
-	toolbar_.add(&reset_zoom_, UI::Align::kLeft);
-	toolbar_.add_space(15);
-	toolbar_.add(&undo_, UI::Align::kLeft);
-	toolbar_.add(&redo_, UI::Align::kLeft);
-	toolbar_.add_space(15);
-	toolbar_.add(&toggle_help_, UI::Align::kLeft);
+     history_(new EditorHistory(*undo_, *redo_)) {
+	add_toolbar_button("wui/menus/menu_toggle_menu", "menu", _("Main Menu"), &mainmenu_, true);
+	mainmenu_.open_window = [this] { new EditorMainMenu(*this, mainmenu_); };
+
+	add_toolbar_button(
+	   "wui/editor/editor_menu_toggle_tool_menu", "tools", _("Tools"), &toolmenu_, true);
+	toolmenu_.open_window = [this] { new EditorToolMenu(*this, toolmenu_); };
+
+	add_toolbar_button(
+	   "wui/editor/editor_menu_set_toolsize_menu", "toolsize", _("Tool Size"), &toolsizemenu_, true);
+	toolsizemenu_.open_window = [this] { new EditorToolsizeMenu(*this, toolsizemenu_); };
+
+	add_toolbar_button(
+	   "wui/menus/menu_toggle_minimap", "minimap", _("Minimap"), &minimap_registry(), true);
+	minimap_registry().open_window = [this] { toggle_minimap(); };
+
+	toggle_buildhelp_ = add_toolbar_button(
+	   "wui/menus/menu_toggle_buildhelp", "buildhelp", _("Show Building Spaces (on/off)"));
+	toggle_buildhelp_->sigclicked.connect(boost::bind(&EditorInteractive::toggle_buildhelp, this));
+
+	reset_zoom_ = add_toolbar_button(
+		"wui/menus/menu_reset_zoom", "reset_zoom", _("Reset zoom"));
+	reset_zoom_->sigclicked.connect(
+		[this] { zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f)); });
+
+	add_toolbar_button(
+	   "wui/editor/editor_menu_player_menu", "players", _("Players"), &playermenu_, true);
+	playermenu_.open_window = [this] {
+		select_tool(tools_->set_starting_pos, EditorTool::First);
+		new EditorPlayerMenu(*this, playermenu_);
+	};
+
+	undo_ = add_toolbar_button("wui/editor/editor_undo", "undo", _("Undo"));
+	undo_->sigclicked.connect([this] { history_->undo_action(egbase().world()); });
+
+	redo_ = add_toolbar_button("wui/editor/editor_redo", "redo", _("Redo"));
+	redo_->sigclicked.connect([this] { history_->redo_action(egbase().world()); });
+
+	add_toolbar_button("ui_basic/menu_help", "help", _("Help"), &helpmenu_, true);
+	helpmenu_.open_window = [this] { new EditorHelp(*this, helpmenu_, &egbase().lua()); };
+
 	adjust_toolbar_position();
 
 #ifndef NDEBUG
@@ -272,13 +263,6 @@
 	end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
 }
 
-void EditorInteractive::toggle_mainmenu() {
-	if (mainmenu_.window)
-		delete mainmenu_.window;
-	else
-		new EditorMainMenu(*this, mainmenu_);
-}
-
 void EditorInteractive::map_clicked(bool should_draw) {
 	history_->do_action(tools_->current(), tools_->use_tool, egbase().map(), egbase().world(),
 	                    get_sel_pos(), *this, should_draw);
@@ -309,29 +293,6 @@
 		map_clicked(true);
 }
 
-void EditorInteractive::tool_menu_btn() {
-	if (toolmenu_.window)
-		delete toolmenu_.window;
-	else
-		new EditorToolMenu(*this, toolmenu_);
-}
-
-void EditorInteractive::toggle_playermenu() {
-	if (playermenu_.window)
-		delete playermenu_.window;
-	else {
-		select_tool(tools_->set_starting_pos, EditorTool::First);
-		new EditorPlayerMenu(*this, playermenu_);
-	}
-}
-
-void EditorInteractive::toolsize_menu_btn() {
-	if (toolsizemenu_.window)
-		delete toolsizemenu_.window;
-	else
-		new EditorToolsizeMenu(*this, toolsizemenu_);
-}
-
 void EditorInteractive::set_sel_radius_and_update_menu(uint32_t const val) {
 	if (tools_->current().has_size_one()) {
 		set_sel_radius(0);
@@ -352,11 +313,8 @@
 	is_painting_ = false;
 }
 
-void EditorInteractive::toggle_help() {
-	if (helpmenu_.window)
-		delete helpmenu_.window;
-	else
-		new EditorHelp(*this, helpmenu_, &egbase().lua());
+void EditorInteractive::on_buildhelp_changed(const bool value) {
+	toggle_buildhelp_->set_perm_pressed(value);
 }
 
 bool EditorInteractive::handle_key(bool const down, SDL_Keysym const code) {
@@ -438,7 +396,7 @@
 			break;
 
 		case SDLK_h:
-			toggle_mainmenu();
+			mainmenu_.toggle();
 			handled = true;
 			break;
 
@@ -448,7 +406,7 @@
 			break;
 
 		case SDLK_m:
-			toggle_minimap();
+			minimap_registry().toggle();
 			handled = true;
 			break;
 
@@ -459,7 +417,7 @@
 			break;
 
 		case SDLK_p:
-			toggle_playermenu();
+			playermenu_.toggle();
 			handled = true;
 			break;
 
@@ -470,7 +428,7 @@
 			break;
 
 		case SDLK_t:
-			tool_menu_btn();
+			toolmenu_.toggle();
 			handled = true;
 			break;
 
@@ -489,7 +447,7 @@
 			break;
 
 		case SDLK_F1:
-			toggle_help();
+			helpmenu_.toggle();
 			handled = true;
 			break;
 
@@ -649,9 +607,9 @@
 void EditorInteractive::map_changed(const MapWas& action) {
 	switch (action) {
 	case MapWas::kReplaced:
-		history_.reset(new EditorHistory(undo_, redo_));
-		undo_.set_enabled(false);
-		redo_.set_enabled(false);
+		history_.reset(new EditorHistory(*undo_, *redo_));
+		undo_->set_enabled(false);
+		redo_->set_enabled(false);
 
 		tools_.reset(new Tools());
 		select_tool(tools_->info, EditorTool::First);

=== modified file 'src/editor/editorinteractive.h'
--- src/editor/editorinteractive.h	2016-10-24 21:23:30 +0000
+++ src/editor/editorinteractive.h	2016-11-17 08:27:19 +0000
@@ -154,11 +154,7 @@
 	// Registers the overlays for player starting positions.
 	void register_overlays();
 
-	void tool_menu_btn();
-	void toolsize_menu_btn();
-	void toggle_mainmenu();
-	void toggle_playermenu();
-	void toggle_help();
+	void on_buildhelp_changed(const bool value) override;
 
 	//  state variables
 	bool need_save_;
@@ -184,16 +180,10 @@
 	UI::UniqueWindow::Registry resourcesmenu_;
 	UI::UniqueWindow::Registry helpmenu_;
 
-	UI::Button toggle_main_menu_;
-	UI::Button toggle_tool_menu_;
-	UI::Button toggle_toolsize_menu_;
-	UI::Button toggle_minimap_;
-	UI::Button toggle_buildhelp_;
-	UI::Button reset_zoom_;
-	UI::Button toggle_player_menu_;
-	UI::Button undo_;
-	UI::Button redo_;
-	UI::Button toggle_help_;
+	UI::Button* toggle_buildhelp_;
+	UI::Button* reset_zoom_;
+	UI::Button* undo_;
+	UI::Button* redo_;
 };
 
 #endif  // end of include guard: WL_EDITOR_EDITORINTERACTIVE_H

=== modified file 'src/ui_basic/unique_window.cc'
--- src/ui_basic/unique_window.cc	2016-10-16 09:31:42 +0000
+++ src/ui_basic/unique_window.cc	2016-11-17 08:27:19 +0000
@@ -19,6 +19,8 @@
 
 #include "ui_basic/unique_window.h"
 
+#include <boost/bind.hpp>
+
 namespace UI {
 /*
 ==============================================================================
@@ -65,6 +67,21 @@
 	delete window;
 }
 
+void UniqueWindow::Registry::assign_toggle_button(UI::Button* button) {
+	assert(!on_create);
+	assert(!on_delete);
+	on_create = boost::bind(&UI::Button::set_style, button, UI::Button::Style::kPermpressed);
+	on_delete = boost::bind(&UI::Button::set_style, button, UI::Button::Style::kRaised);
+	if (window) {
+		button->set_style(UI::Button::Style::kPermpressed);
+	}
+}
+
+void UniqueWindow::Registry::unassign_toggle_button() {
+	on_create = 0;
+	on_delete = 0;
+}
+
 /**
  * Register, position according to the registry information.
 */

=== modified file 'src/ui_basic/unique_window.h'
--- src/ui_basic/unique_window.h	2016-08-04 15:49:05 +0000
+++ src/ui_basic/unique_window.h	2016-11-17 08:27:19 +0000
@@ -60,6 +60,15 @@
 		Registry() : window(nullptr), x(0), y(0), valid_pos(false) {
 		}
 		~Registry();
+
+		/// The 'button' will be permpressed or not depending on whether this window is
+		/// open (on_create/on_delete calback function hooks).
+		/// This can be assigned only once.
+		void assign_toggle_button(UI::Button* button);
+
+		/// Remove the callback as a safeguard in case somewhere else in the
+		/// code someone would overwrite our hooks.
+		void unassign_toggle_button();
 	};
 
 	UniqueWindow(Panel* parent,

=== modified file 'src/wui/CMakeLists.txt'
--- src/wui/CMakeLists.txt	2016-10-25 07:07:14 +0000
+++ src/wui/CMakeLists.txt	2016-11-17 08:27:19 +0000
@@ -132,8 +132,8 @@
     fieldaction.h
     game_debug_ui.cc
     game_debug_ui.h
-    game_main_menu.cc
-    game_main_menu.h
+    game_statistics_menu.cc
+    game_statistics_menu.h
     game_main_menu_save_game.cc
     game_main_menu_save_game.h
     game_message_menu.cc

=== modified file 'src/wui/fieldaction.cc'
--- src/wui/fieldaction.cc	2016-10-16 09:31:42 +0000
+++ src/wui/fieldaction.cc	2016-11-17 08:27:19 +0000
@@ -737,7 +737,7 @@
 	// Force closing of old fieldaction windows. This is necessary because
 	// show_field_action() does not always open a FieldActionWindow (e.g.
 	// connecting the road we are building to an existing flag)
-	delete registry->window;
+	registry->destroy();
 	*registry = UI::UniqueWindow::Registry();
 
 	if (!ibase->is_building_road()) {

=== modified file 'src/wui/game_options_menu.cc'
--- src/wui/game_options_menu.cc	2016-10-10 13:38:59 +0000
+++ src/wui/game_options_menu.cc	2016-11-17 08:27:19 +0000
@@ -104,29 +104,24 @@
 	box_.set_size(width, sound_.get_h() + 2 * save_game_.get_h() + vgap + 3 * vspacing);
 	set_inner_size(get_inner_w(), box_.get_h() + 2 * margin);
 
-	sound_.sigclicked.connect(boost::bind(&GameOptionsMenu::clicked_sound, boost::ref(*this)));
+	windows_.sound_options.open_window = [this] {
+		new GameOptionsSoundMenu(igb_, windows_.sound_options);
+	};
+	sound_.sigclicked.connect(
+	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.sound_options)));
 	save_game_.sigclicked.connect(
 	   boost::bind(&GameOptionsMenu::clicked_save_game, boost::ref(*this)));
 	exit_game_.sigclicked.connect(
 	   boost::bind(&GameOptionsMenu::clicked_exit_game, boost::ref(*this)));
 
-#define INIT_BTN_HOOKS(registry, btn)                                                              \
-	registry.on_create = std::bind(&UI::Button::set_perm_pressed, &btn, true);                      \
-	registry.on_delete = std::bind(&UI::Button::set_perm_pressed, &btn, false);                     \
-	if (registry.window)                                                                            \
-		btn.set_perm_pressed(true);
-
-	INIT_BTN_HOOKS(windows_.sound_options, sound_)
+	windows_.sound_options.assign_toggle_button(&sound_);
 
 	if (get_usedefaultpos())
 		center_to_parent();
 }
 
-void GameOptionsMenu::clicked_sound() {
-	if (windows_.sound_options.window)
-		delete windows_.sound_options.window;
-	else
-		new GameOptionsSoundMenu(igb_, windows_.sound_options);
+GameOptionsMenu::~GameOptionsMenu() {
+	windows_.sound_options.unassign_toggle_button();
 }
 
 void GameOptionsMenu::clicked_save_game() {

=== modified file 'src/wui/game_options_menu.h'
--- src/wui/game_options_menu.h	2016-08-04 15:49:05 +0000
+++ src/wui/game_options_menu.h	2016-11-17 08:27:19 +0000
@@ -32,6 +32,7 @@
 	GameOptionsMenu(InteractiveGameBase&,
 	                UI::UniqueWindow::Registry&,
 	                InteractiveGameBase::GameMainMenuWindows&);
+	~GameOptionsMenu();
 
 private:
 	InteractiveGameBase& igb_;
@@ -41,7 +42,6 @@
 	UI::Button save_game_;
 	UI::Button exit_game_;
 
-	void clicked_sound();
 	void clicked_save_game();
 	void clicked_exit_game();
 };

=== renamed file 'src/wui/game_main_menu.cc' => 'src/wui/game_statistics_menu.cc'
--- src/wui/game_main_menu.cc	2016-10-10 13:38:59 +0000
+++ src/wui/game_statistics_menu.cc	2016-11-17 08:27:19 +0000
@@ -17,7 +17,7 @@
  *
  */
 
-#include "wui/game_main_menu.h"
+#include "wui/game_statistics_menu.h"
 
 #include <boost/bind.hpp>
 
@@ -30,69 +30,23 @@
 #include "wui/stock_menu.h"
 #include "wui/ware_statistics_menu.h"
 
-GameMainMenu::GameMainMenu(InteractivePlayer& plr,
-                           UI::UniqueWindow::Registry& registry,
-                           InteractivePlayer::GameMainMenuWindows& windows)
-   : UI::UniqueWindow(&plr, "main_menu", &registry, 180, 55, _("Statistics Menu")),
+GameStatisticsMenu::GameStatisticsMenu(InteractivePlayer& plr,
+                                       UI::UniqueWindow::Registry& registry,
+                                       InteractivePlayer::GameMainMenuWindows& windows)
+   : UI::UniqueWindow(&plr, "main_menu", &registry, 0, 0, _("Statistics Menu")),
      player_(plr),
      windows_(windows),
-     general_stats(this,
-                   "general_stats",
-                   posx(0, 4),
-                   posy(0, 3),
-                   buttonw(4),
-                   buttonh(1),
-                   g_gr->images().get("images/ui_basic/but4.png"),
-                   g_gr->images().get("images/wui/menus/menu_general_stats.png"),
-                   _("General Statistics")),
-     ware_stats(this,
-                "ware_stats",
-                posx(1, 4),
-                posy(0, 3),
-                buttonw(4),
-                buttonh(1),
-                g_gr->images().get("images/ui_basic/but4.png"),
-                g_gr->images().get("images/wui/menus/menu_ware_stats.png"),
-                _("Ware Statistics")),
-     building_stats(this,
-                    "building_stats",
-                    posx(2, 4),
-                    posy(0, 3),
-                    buttonw(4),
-                    buttonh(1),
-                    g_gr->images().get("images/ui_basic/but4.png"),
-                    g_gr->images().get("images/wui/menus/menu_building_stats.png"),
-                    _("Building Statistics")),
-     stock(this,
-           "stock",
-           posx(3, 4),
-           posy(0, 3),
-           buttonw(4),
-           buttonh(1),
-           g_gr->images().get("images/ui_basic/but4.png"),
-           g_gr->images().get("images/wui/menus/menu_stock.png"),
-           _("Stock")) {
-	general_stats.sigclicked.connect(
-	   boost::bind(&GeneralStatisticsMenu::Registry::toggle, boost::ref(windows_.general_stats)));
-	ware_stats.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.ware_stats)));
-	building_stats.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.building_stats)));
-	stock.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(windows_.stock)));
-
-#define INIT_BTN_HOOKS(registry, btn)                                                              \
-	assert(!registry.on_create);                                                                    \
-	assert(!registry.on_delete);                                                                    \
-	registry.on_create = std::bind(&UI::Button::set_perm_pressed, &btn, true);                      \
-	registry.on_delete = std::bind(&UI::Button::set_perm_pressed, &btn, false);                     \
-	if (registry.window)                                                                            \
-		btn.set_perm_pressed(true);
-
-	INIT_BTN_HOOKS(windows_.general_stats, general_stats)
-	INIT_BTN_HOOKS(windows_.ware_stats, ware_stats)
-	INIT_BTN_HOOKS(windows_.building_stats, building_stats)
-	INIT_BTN_HOOKS(windows_.stock, stock)
+     box_(this, 0, 0, UI::Box::Horizontal, 0, 0, 5) {
+	add_button("wui/menus/menu_general_stats", "general_stats", _("General Statistics"),
+	           &windows_.general_stats);
+	add_button(
+	   "wui/menus/menu_ware_stats", "ware_stats", _("Ware Statistics"), &windows_.ware_stats);
+	add_button("wui/menus/menu_building_stats", "building_stats", _("Building Statistics"),
+	           &windows_.building_stats);
+	add_button("wui/menus/menu_stock", "stock", _("Stock"), &windows_.stock);
+	box_.set_pos(Vector2i(10, 10));
+	box_.set_size((34 + 5) * 4, 34);
+	set_inner_size(box_.get_w() + 20, box_.get_h() + 20);
 
 	windows_.general_stats.open_window = [this] {
 		new GeneralStatisticsMenu(player_, windows_.general_stats);
@@ -103,23 +57,31 @@
 	windows_.building_stats.open_window = [this] {
 		new BuildingStatisticsMenu(player_, windows_.building_stats);
 	};
+	// The stock window is defined in InteractivePlayer because of the keyboard shortcut.
 
 	if (get_usedefaultpos())
 		center_to_parent();
 }
 
-GameMainMenu::~GameMainMenu() {
-// We need to remove these callbacks because the opened window might
-// live longer than 'this' window, and thus the buttons. The assertions
-// are safeguards in case somewhere else in the code someone would
-// overwrite our hooks.
-
-#define DEINIT_BTN_HOOKS(registry, btn)                                                            \
-	registry.on_create = 0;                                                                         \
-	registry.on_delete = 0;
-
-	DEINIT_BTN_HOOKS(windows_.general_stats, general_stats)
-	DEINIT_BTN_HOOKS(windows_.ware_stats, ware_stats)
-	DEINIT_BTN_HOOKS(windows_.building_stats, building_stats)
-	DEINIT_BTN_HOOKS(windows_.stock, stock)
+UI::Button* GameStatisticsMenu::add_button(const std::string& image_basename,
+                                           const std::string& name,
+                                           const std::string& tooltip,
+                                           UI::UniqueWindow::Registry* window) {
+	UI::Button* button =
+	   new UI::Button(&box_, name, 0, 0, 34U, 34U, g_gr->images().get("images/ui_basic/but4.png"),
+	                  g_gr->images().get("images/" + image_basename + ".png"), tooltip);
+	box_.add(button, UI::Align::kLeft);
+	if (window) {
+		window->assign_toggle_button(button);
+		registries_.push_back(*window);
+		button->sigclicked.connect(
+		   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(*window)));
+	}
+	return button;
+}
+
+GameStatisticsMenu::~GameStatisticsMenu() {
+	for (auto& registry : registries_) {
+		registry.unassign_toggle_button();
+	}
 }

=== renamed file 'src/wui/game_main_menu.h' => 'src/wui/game_statistics_menu.h'
--- src/wui/game_main_menu.h	2016-08-04 15:49:05 +0000
+++ src/wui/game_statistics_menu.h	2016-11-17 08:27:19 +0000
@@ -17,65 +17,40 @@
  *
  */
 
-#ifndef WL_WUI_GAME_MAIN_MENU_H
-#define WL_WUI_GAME_MAIN_MENU_H
+#ifndef WL_WUI_GAME_STATISTICS_MENU_H
+#define WL_WUI_GAME_STATISTICS_MENU_H
 
+#include "ui_basic/box.h"
 #include "ui_basic/button.h"
 #include "wui/interactive_player.h"
 
-// The GameMainMenu is a rather dumb window with lots of buttons
-struct GameMainMenu : public UI::UniqueWindow {
-	GameMainMenu(InteractivePlayer&,
-	             UI::UniqueWindow::Registry&,
-	             InteractivePlayer::GameMainMenuWindows&);
+// The GameStatisticsMenu is a row of buttons that will toggle unique windows
+struct GameStatisticsMenu : public UI::UniqueWindow {
+	GameStatisticsMenu(InteractivePlayer&,
+	                   UI::UniqueWindow::Registry&,
+	                   InteractivePlayer::GameMainMenuWindows&);
 
-	~GameMainMenu();
+	~GameStatisticsMenu();
 
 private:
+	/// Adds a button to the menu that will toggle its window
+	/// \param image_basename:      File path for button image starting from 'images' and without
+	///                             file extension
+	/// \param name:                Internal name of the button
+	/// \param tooltip:             The button tooltip
+	/// \param window:              The window that's associated with this button.
+	UI::Button* add_button(const std::string& image_basename,
+	                       const std::string& name,
+	                       const std::string& tooltip,
+	                       UI::UniqueWindow::Registry* window);
+
 	InteractivePlayer& player_;
 	InteractivePlayer::GameMainMenuWindows& windows_;
-	UI::Button general_stats;
-	UI::Button ware_stats;
-	UI::Button building_stats;
-	UI::Button stock;
-
-	/** Returns the horizontal/vertical spacing between buttons. */
-	uint32_t hspacing() const {
-		return 5;
-	}
-	uint32_t vspacing() const {
-		return 5;
-	}
-
-	/** Returns the horizontal/vertical margin between edge and buttons. */
-	uint32_t hmargin() const {
-		return 2 * hspacing();
-	}
-	uint32_t vmargin() const {
-		return 2 * vspacing();
-	}
-
-	/** Returns the width of a button in a row with nr_buttons buttons. */
-	uint32_t buttonw(uint32_t const nr_buttons) const {
-		return (get_inner_w() - (nr_buttons + 3) * hspacing()) / nr_buttons;
-	}
-
-	/** Returns the height of buttons in a window with nr_rows rows. */
-	uint32_t buttonh(uint32_t const nr_rows) const {
-		return (get_inner_h() - (nr_rows + 3) * vspacing()) / nr_rows;
-	}
-
-	/// Returns the x coordinate of the (left edge of) button number nr in a row
-	/// with nr_buttons buttons.
-	uint32_t posx(uint32_t const nr, uint32_t const nr_buttons) const {
-		return hmargin() + nr * (buttonw(nr_buttons) + hspacing());
-	}
-
-	/// Returns the y coordinate of the (top edge of) a button in row number nr
-	/// in a dialog with nr_rows rows.
-	uint32_t posy(uint32_t const nr, uint32_t const nr_rows) const {
-		return vmargin() + nr * (buttonh(nr_rows) + vspacing());
-	}
+	UI::Box box_;
+
+	// These get collected by add_button
+	// so we can call unassign_toggle_button on them in the destructor.
+	std::vector<UI::UniqueWindow::Registry> registries_;
 };
 
-#endif  // end of include guard: WL_WUI_GAME_MAIN_MENU_H
+#endif  // end of include guard: WL_WUI_GAME_STATISTICS_MENU_H

=== modified file 'src/wui/interactive_base.cc'
--- src/wui/interactive_base.cc	2016-10-25 07:07:14 +0000
+++ src/wui/interactive_base.cc	2016-11-17 08:27:19 +0000
@@ -133,8 +133,12 @@
 }
 
 InteractiveBase::~InteractiveBase() {
-	if (buildroad_)
+	if (buildroad_) {
 		abort_build_road();
+	}
+	for (auto& registry : registries_) {
+		registry.unassign_toggle_button();
+	}
 }
 
 UniqueWindowHandler& InteractiveBase::unique_windows() {
@@ -217,6 +221,26 @@
 	show_buildhelp(!field_overlay_manager_->buildhelp());
 }
 
+UI::Button* InteractiveBase::add_toolbar_button(const std::string& image_basename,
+                                                const std::string& name,
+                                                const std::string& tooltip,
+                                                UI::UniqueWindow::Registry* window,
+                                                bool bind_default_toggle) {
+	UI::Button* button = new UI::Button(
+	   &toolbar_, name, 0, 0, 34U, 34U, g_gr->images().get("images/ui_basic/but2.png"),
+	   g_gr->images().get("images/" + image_basename + ".png"), tooltip);
+	toolbar_.add(button, UI::Align::kLeft);
+	if (window) {
+		window->assign_toggle_button(button);
+		registries_.push_back(*window);
+		if (bind_default_toggle) {
+			button->sigclicked.connect(
+			   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(*window)));
+		}
+	}
+	return button;
+}
+
 void InteractiveBase::on_buildhelp_changed(bool /* value */) {
 }
 
@@ -380,7 +404,7 @@
  * Hide the minimap if it is currently shown; otherwise, do nothing.
  */
 void InteractiveBase::hide_minimap() {
-	delete m->minimap.window;
+	m->minimap.destroy();
 }
 
 /**

=== modified file 'src/wui/interactive_base.h'
--- src/wui/interactive_base.h	2016-10-25 07:07:14 +0000
+++ src/wui/interactive_base.h	2016-11-17 08:27:19 +0000
@@ -161,6 +161,7 @@
 	}
 
 	void toggle_minimap();
+	void toggle_buildhelp();
 
 	// Returns the list of landmarks that have been mapped to the keys 0-9
 	const std::vector<QuickNavigation::Landmark>& landmarks();
@@ -169,10 +170,22 @@
 	void set_landmark(size_t key, const QuickNavigation::View& view);
 
 protected:
+	/// Adds a toolbar button to the toolbar
+	/// \param image_basename:      File path for button image starting from 'images' and without
+	///                             file extension
+	/// \param name:                Internal name of the button
+	/// \param tooltip:             The button tooltip
+	/// \param window:              The window that's associated with this button.
+	/// \param bind_default_toggle: If true, the button will toggle with its 'window'.
+	UI::Button* add_toolbar_button(const std::string& image_basename,
+	                               const std::string& name,
+	                               const std::string& tooltip,
+	                               UI::UniqueWindow::Registry* window = nullptr,
+	                               bool bind_default_toggle = false);
+
 	// Will be called whenever the buildhelp is changed with the new 'value'.
 	virtual void on_buildhelp_changed(bool value);
 
-	void toggle_buildhelp();
 	void hide_minimap();
 
 	MiniMap::Registry& minimap_registry();
@@ -190,7 +203,10 @@
 
 	// TODO(sirver): why are these protected?
 	ChatOverlay* chat_overlay_;
-	UI::Box toolbar_;
+
+	// These get collected by add_toolbar_button
+	// so we can call unassign_toggle_button on them in the destructor.
+	std::vector<UI::UniqueWindow::Registry> registries_;
 
 private:
 	void resize_chat_overlay();
@@ -218,6 +234,7 @@
 		FieldOverlayManager::OverlayId jobid;
 	} sel_;
 
+	UI::Box toolbar_;
 	std::unique_ptr<InteractiveBaseInternals> m;
 
 	std::unique_ptr<FieldOverlayManager> field_overlay_manager_;
@@ -241,7 +258,4 @@
 	std::vector<const Image*> workarea_pics_;
 };
 
-#define PIC2 g_gr->images().get("images/ui_basic/but2.png")
-#define TOOLBAR_BUTTON_COMMON_PARAMETERS(name) &toolbar_, name, 0, 0, 34U, 34U, PIC2
-
 #endif  // end of include guard: WL_WUI_INTERACTIVE_BASE_H

=== modified file 'src/wui/interactive_gamebase.cc'
--- src/wui/interactive_gamebase.cc	2016-10-25 19:18:22 +0000
+++ src/wui/interactive_gamebase.cc	2016-11-17 08:27:19 +0000
@@ -50,23 +50,11 @@
 InteractiveGameBase::InteractiveGameBase(Widelands::Game& g,
                                          Section& global_s,
                                          PlayerType pt,
-                                         bool const chatenabled,
                                          bool const multiplayer)
    : InteractiveBase(g, global_s),
      chat_provider_(nullptr),
-     chatenabled_(chatenabled),
      multiplayer_(multiplayer),
-     playertype_(pt),
-
-#define INIT_BTN(picture, name, tooltip)                                                           \
-	TOOLBAR_BUTTON_COMMON_PARAMETERS(name), g_gr->images().get("images/" picture ".png"), tooltip
-
-     toggle_buildhelp_(INIT_BTN(
-        "wui/menus/menu_toggle_buildhelp", "buildhelp", _("Show Building Spaces (on/off)"))),
-     reset_zoom_(INIT_BTN("wui/menus/menu_reset_zoom", "reset_zoom", _("Reset zoom"))) {
-	toggle_buildhelp_.sigclicked.connect(boost::bind(&InteractiveGameBase::toggle_buildhelp, this));
-	reset_zoom_.sigclicked.connect(
-	   [this] { zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f)); });
+     playertype_(pt) {
 }
 
 /// \return a pointer to the running \ref Game instance.
@@ -81,8 +69,6 @@
 void InteractiveGameBase::set_chat_provider(ChatProvider& chat) {
 	chat_provider_ = &chat;
 	chat_overlay_->set_chat_provider(chat);
-
-	chatenabled_ = true;
 }
 
 ChatProvider* InteractiveGameBase::get_chat_provider() {
@@ -125,7 +111,7 @@
 	Widelands::Map& map = egbase().map();
 	auto* overlay_manager = mutable_field_overlay_manager();
 	show_buildhelp(false);
-	toggle_buildhelp_.set_perm_pressed(buildhelp());
+	on_buildhelp_changed(buildhelp());
 
 	overlay_manager->register_overlay_callback_function(
 	   boost::bind(&InteractiveGameBase::calculate_buildcaps, this, _1));
@@ -134,14 +120,12 @@
 	map.recalc_whole_map(egbase().world());
 
 	// Close game-relevant UI windows (but keep main menu open)
-	delete fieldaction_.window;
-	fieldaction_.window = nullptr;
-
+	fieldaction_.destroy();
 	hide_minimap();
 }
 
 void InteractiveGameBase::on_buildhelp_changed(const bool value) {
-	toggle_buildhelp_.set_perm_pressed(value);
+	toggle_buildhelp_->set_perm_pressed(value);
 }
 
 /**

=== modified file 'src/wui/interactive_gamebase.h'
--- src/wui/interactive_gamebase.h	2016-10-24 21:23:30 +0000
+++ src/wui/interactive_gamebase.h	2016-11-17 08:27:19 +0000
@@ -49,7 +49,6 @@
 	InteractiveGameBase(Widelands::Game&,
 	                    Section& global_s,
 	                    PlayerType pt = NONE,
-	                    bool chatenabled = false,
 	                    bool multiplayer = false);
 	Widelands::Game* get_game() const;
 	Widelands::Game& game() const;
@@ -86,14 +85,12 @@
 
 	GameMainMenuWindows main_windows_;
 	ChatProvider* chat_provider_;
-	bool chatenabled_;
 	bool multiplayer_;
 	PlayerType playertype_;
 	UI::UniqueWindow::Registry fieldaction_;
 	UI::UniqueWindow::Registry game_summary_;
-
-	UI::Button toggle_buildhelp_;
-	UI::Button reset_zoom_;
+	UI::Button* toggle_buildhelp_;
+	UI::Button* reset_zoom_;
 
 private:
 	void on_buildhelp_changed(const bool value) override;

=== modified file 'src/wui/interactive_player.cc'
--- src/wui/interactive_player.cc	2016-10-25 20:15:42 +0000
+++ src/wui/interactive_player.cc	2016-11-17 08:27:19 +0000
@@ -45,11 +45,11 @@
 #include "wui/debugconsole.h"
 #include "wui/fieldaction.h"
 #include "wui/game_chat_menu.h"
-#include "wui/game_main_menu.h"
 #include "wui/game_main_menu_save_game.h"
 #include "wui/game_message_menu.h"
 #include "wui/game_objectives_menu.h"
 #include "wui/game_options_menu.h"
+#include "wui/game_statistics_menu.h"
 #include "wui/general_statistics_menu.h"
 #include "wui/stock_menu.h"
 #include "wui/tribal_encyclopedia.h"
@@ -62,91 +62,57 @@
                                      Section& global_s,
                                      Widelands::PlayerNumber const plyn,
                                      bool const multiplayer)
-   : InteractiveGameBase(g, global_s, NONE, multiplayer, multiplayer),
+   : InteractiveGameBase(g, global_s, NONE, multiplayer),
      auto_roadbuild_mode_(global_s.get_bool("auto_roadbuild_mode", true)),
-     flag_to_connect_(Widelands::Coords::null()),
-
-// Chat is different, as chat_provider_ needs to be checked when toggling
-// Minimap is different as it warps and stuff
-
-#define INIT_BTN_this(picture, name, tooltip)                                                      \
-	TOOLBAR_BUTTON_COMMON_PARAMETERS(name), g_gr->images().get("images/" picture ".png"), tooltip
-
-#define INIT_BTN(picture, name, tooltip)                                                           \
-	TOOLBAR_BUTTON_COMMON_PARAMETERS(name), g_gr->images().get("images/" picture ".png"), tooltip
-
-     toggle_chat_(INIT_BTN_this("wui/menus/menu_chat", "chat", _("Chat"))),
-     toggle_options_menu_(INIT_BTN("wui/menus/menu_options_menu", "options_menu", _("Main Menu"))),
-     toggle_statistics_menu_(
-        INIT_BTN("wui/menus/menu_toggle_menu", "statistics_menu", _("Statistics"))),
-     toggle_objectives_(INIT_BTN("wui/menus/menu_objectives", "objectives", _("Objectives"))),
-     toggle_minimap_(INIT_BTN_this("wui/menus/menu_toggle_minimap", "minimap", _("Minimap"))),
-     toggle_message_menu_(
-        INIT_BTN("wui/menus/menu_toggle_oldmessage_menu", "messages", _("Messages"))),
-     toggle_help_(INIT_BTN("ui_basic/menu_help", "help", _("Tribal Encyclopedia")))
-
-{
-	toggle_chat_.sigclicked.connect(boost::bind(&InteractivePlayer::toggle_chat, this));
-	toggle_options_menu_.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(options_)));
-	toggle_statistics_menu_.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(statisticsmenu_)));
-	toggle_objectives_.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(objectives_)));
-	toggle_minimap_.sigclicked.connect(boost::bind(&InteractivePlayer::toggle_minimap, this));
-	toggle_message_menu_.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(message_menu_)));
-	toggle_help_.sigclicked.connect(
-	   boost::bind(&UI::UniqueWindow::Registry::toggle, boost::ref(encyclopedia_)));
-
-	// TODO(unknown): instead of making unneeded buttons invisible after generation,
-	// they should not at all be generated. -> implement more dynamic toolbar UI
-	toolbar_.add(&toggle_options_menu_, UI::Align::kLeft);
-	toolbar_.add(&toggle_statistics_menu_, UI::Align::kLeft);
-	toolbar_.add_space(15);
-	toolbar_.add(&toggle_minimap_, UI::Align::kLeft);
-	toolbar_.add(&toggle_buildhelp_, UI::Align::kLeft);
-	toolbar_.add(&reset_zoom_, UI::Align::kLeft);
-	toolbar_.add_space(15);
-	if (multiplayer) {
-		toolbar_.add(&toggle_chat_, UI::Align::kLeft);
-		toggle_chat_.set_visible(false);
-		toggle_chat_.set_enabled(false);
-		toolbar_.add_space(15);
-	}
-
-	toolbar_.add(&toggle_objectives_, UI::Align::kLeft);
-	toolbar_.add(&toggle_message_menu_, UI::Align::kLeft);
-	toolbar_.add(&toggle_help_, UI::Align::kLeft);
-
-	set_player_number(plyn);
-	fieldclicked.connect(boost::bind(&InteractivePlayer::node_action, this));
-
-	adjust_toolbar_position();
-
-#define INIT_BTN_HOOKS(registry, btn)                                                              \
-	registry.on_create = std::bind(&UI::Button::set_perm_pressed, &btn, true);                      \
-	registry.on_delete = std::bind(&UI::Button::set_perm_pressed, &btn, false);                     \
-	if (registry.window)                                                                            \
-		btn.set_perm_pressed(true);
-
-	INIT_BTN_HOOKS(chat_, toggle_chat_)
-	INIT_BTN_HOOKS(options_, toggle_options_menu_)
-	INIT_BTN_HOOKS(statisticsmenu_, toggle_statistics_menu_)
-	INIT_BTN_HOOKS(minimap_registry(), toggle_minimap_)
-	INIT_BTN_HOOKS(objectives_, toggle_objectives_)
-	INIT_BTN_HOOKS(message_menu_, toggle_message_menu_)
-	INIT_BTN_HOOKS(encyclopedia_, toggle_help_)
-
-	encyclopedia_.open_window = [this] {
-		new TribalEncyclopedia(*this, encyclopedia_, &game().lua());
-	};
+     flag_to_connect_(Widelands::Coords::null()) {
+	add_toolbar_button(
+	   "wui/menus/menu_options_menu", "options_menu", _("Main Menu"), &options_, true);
 	options_.open_window = [this] { new GameOptionsMenu(*this, options_, main_windows_); };
+
+	add_toolbar_button(
+	   "wui/menus/menu_toggle_menu", "statistics_menu", _("Statistics"), &statisticsmenu_, true);
 	statisticsmenu_.open_window = [this] {
-		new GameMainMenu(*this, statisticsmenu_, main_windows_);
+		new GameStatisticsMenu(*this, statisticsmenu_, main_windows_);
 	};
+
+	add_toolbar_button(
+	   "wui/menus/menu_toggle_minimap", "minimap", _("Minimap"), &minimap_registry(), true);
+	minimap_registry().open_window = [this] { toggle_minimap(); };
+
+	toggle_buildhelp_ = add_toolbar_button(
+	   "wui/menus/menu_toggle_buildhelp", "buildhelp", _("Show Building Spaces (on/off)"));
+	toggle_buildhelp_->sigclicked.connect(boost::bind(&InteractiveBase::toggle_buildhelp, this));
+	reset_zoom_ = add_toolbar_button(
+		"wui/menus/menu_reset_zoom", "reset_zoom", _("Reset zoom"));
+	reset_zoom_->sigclicked.connect(
+		[this] { zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f)); });
+		if (multiplayer) {
+		toggle_chat_ = add_toolbar_button("wui/menus/menu_chat", "chat", _("Chat"), &chat_, true);
+		chat_.open_window = [this] {
+			if (chat_provider_) {
+				GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
+			}
+		};
+	}
+
+	add_toolbar_button(
+	   "wui/menus/menu_objectives", "objectives", _("Objectives"), &objectives_, true);
 	objectives_.open_window = [this] { new GameObjectivesMenu(this, objectives_); };
+
+	toggle_message_menu_ = add_toolbar_button(
+	   "wui/menus/menu_toggle_oldmessage_menu", "messages", _("Messages"), &message_menu_, true);
 	message_menu_.open_window = [this] { new GameMessageMenu(*this, message_menu_); };
+
+	add_toolbar_button("ui_basic/menu_help", "help", _("Tribal Encyclopedia"), &encyclopedia_, true);
+	encyclopedia_.open_window = [this] {
+		new TribalEncyclopedia(*this, encyclopedia_, &game().lua());
+	};
+
+	set_player_number(plyn);
+	fieldclicked.connect(boost::bind(&InteractivePlayer::node_action, this));
+
+	adjust_toolbar_position();
+
 	main_windows_.stock.open_window = [this] { new StockMenu(*this, main_windows_.stock); };
 
 #ifndef NDEBUG  //  only in debug builds
@@ -154,20 +120,6 @@
 #endif
 }
 
-InteractivePlayer::~InteractivePlayer() {
-#define DEINIT_BTN_HOOKS(registry, btn)                                                            \
-	registry.on_create = 0;                                                                         \
-	registry.on_delete = 0;
-
-	DEINIT_BTN_HOOKS(chat_, toggle_chat_)
-	DEINIT_BTN_HOOKS(options_, toggle_options_menu_)
-	DEINIT_BTN_HOOKS(statisticsmenu_, toggle_statistics_menu_)
-	DEINIT_BTN_HOOKS(minimap_registry(), toggle_minimap_)
-	DEINIT_BTN_HOOKS(objectives_, toggle_objectives_)
-	DEINIT_BTN_HOOKS(message_menu_, toggle_message_menu_)
-	DEINIT_BTN_HOOKS(encyclopedia_, toggle_help_)
-}
-
 void InteractivePlayer::think() {
 	InteractiveBase::think();
 
@@ -182,8 +134,7 @@
 					//  we are already in roadbuilding mode from the call below.
 					//  That is not allowed. Therefore we must delete the
 					//  fieldaction window before entering roadbuilding mode here.
-					delete fieldaction_.window;
-					fieldaction_.window = nullptr;
+					fieldaction_.destroy();
 					warp_mouse_to_node(flag_to_connect_);
 					set_sel_pos(Widelands::NodeAndTriangle<>(
 					   flag_to_connect_,
@@ -194,8 +145,8 @@
 		}
 	}
 	if (is_multiplayer()) {
-		toggle_chat_.set_visible(chatenabled_);
-		toggle_chat_.set_enabled(chatenabled_);
+		toggle_chat_->set_visible(chat_provider_);
+		toggle_chat_->set_enabled(chat_provider_);
 	}
 	{
 		char const* msg_icon = "images/wui/menus/menu_toggle_oldmessage_menu.png";
@@ -208,8 +159,8 @@
 			    nr_new_messages)
 			      .str();
 		}
-		toggle_message_menu_.set_pic(g_gr->images().get(msg_icon));
-		toggle_message_menu_.set_tooltip(msg_tooltip);
+		toggle_message_menu_->set_pic(g_gr->images().get(msg_icon));
+		toggle_message_menu_->set_tooltip(msg_tooltip);
 	}
 }
 
@@ -219,14 +170,6 @@
 	dynamic_cast<GameMessageMenu&>(*message_menu_.window).show_new_message(id, message);
 }
 
-//  Toolbar button callback functions.
-void InteractivePlayer::toggle_chat() {
-	if (chat_.window)
-		delete chat_.window;
-	else if (chat_provider_)
-		GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
-}
-
 bool InteractivePlayer::can_see(Widelands::PlayerNumber const p) const {
 	return p == player_number() || player().see_all();
 }
@@ -286,7 +229,7 @@
 			return true;
 
 		case SDLK_m:
-			toggle_minimap();
+			minimap_registry().toggle();
 			return true;
 
 		case SDLK_n:
@@ -330,12 +273,12 @@
 
 		case SDLK_KP_ENTER:
 		case SDLK_RETURN:
-			if (!chat_provider_ | !chatenabled_ || !is_multiplayer())
-				break;
-
-			if (!chat_.window)
-				GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
-
+			if (chat_provider_) {
+				if (!chat_.window) {
+					GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
+				}
+				dynamic_cast<GameChatMenu*>(chat_.window)->enter_chat_message();
+			}
 			return true;
 		default:
 			break;

=== modified file 'src/wui/interactive_player.h'
--- src/wui/interactive_player.h	2016-08-04 15:49:05 +0000
+++ src/wui/interactive_player.h	2016-11-17 08:27:19 +0000
@@ -48,10 +48,6 @@
 	                  Widelands::PlayerNumber,
 	                  bool multiplayer);
 
-	~InteractivePlayer();
-
-	void toggle_chat();
-
 	bool can_see(Widelands::PlayerNumber) const override;
 	bool can_act(Widelands::PlayerNumber) const override;
 	Widelands::PlayerNumber player_number() const override;
@@ -89,13 +85,8 @@
 	bool auto_roadbuild_mode_;
 	Widelands::Coords flag_to_connect_;
 
-	UI::Button toggle_chat_;
-	UI::Button toggle_options_menu_;
-	UI::Button toggle_statistics_menu_;
-	UI::Button toggle_objectives_;
-	UI::Button toggle_minimap_;
-	UI::Button toggle_message_menu_;
-	UI::Button toggle_help_;
+	UI::Button* toggle_chat_;
+	UI::Button* toggle_message_menu_;
 
 	UI::UniqueWindow::Registry chat_;
 	UI::UniqueWindow::Registry options_;

=== modified file 'src/wui/interactive_spectator.cc'
--- src/wui/interactive_spectator.cc	2016-10-10 13:38:59 +0000
+++ src/wui/interactive_spectator.cc	2016-11-17 08:27:19 +0000
@@ -42,85 +42,55 @@
 InteractiveSpectator::InteractiveSpectator(Widelands::Game& g,
                                            Section& global_s,
                                            bool const multiplayer)
-   : InteractiveGameBase(g, global_s, OBSERVER, multiplayer, multiplayer),
-
-#define INIT_BTN(picture, name, tooltip)                                                           \
-	TOOLBAR_BUTTON_COMMON_PARAMETERS(name)                                                          \
-	, g_gr->images().get("images/wui/menus/" picture ".png"), tooltip
-
-     toggle_chat_(INIT_BTN("menu_chat", "chat", _("Chat"))),
-     exit_(INIT_BTN("menu_exit_game", "exit_replay", _("Exit Replay"))),
-     save_(INIT_BTN("menu_save_game", "save_game", _("Save Game"))),
-     toggle_options_menu_(INIT_BTN("menu_options_menu", "options_menu", _("Main Menu"))),
-     toggle_statistics_(INIT_BTN("menu_general_stats", "general_stats", _("Statistics"))),
-     toggle_minimap_(INIT_BTN("menu_toggle_minimap", "minimap", _("Minimap"))) {
-	toggle_chat_.sigclicked.connect(boost::bind(&InteractiveSpectator::toggle_chat, this));
-	exit_.sigclicked.connect(boost::bind(&InteractiveSpectator::exit_btn, this));
-	save_.sigclicked.connect(boost::bind(&InteractiveSpectator::save_btn, this));
-	toggle_options_menu_.sigclicked.connect(
-	   boost::bind(&InteractiveSpectator::toggle_options_menu, this));
-	toggle_statistics_.sigclicked.connect(
-	   boost::bind(&InteractiveSpectator::toggle_statistics, this));
-	toggle_minimap_.sigclicked.connect(boost::bind(&InteractiveSpectator::toggle_minimap, this));
-
-	toolbar_.set_layout_toplevel(true);
-	if (!is_multiplayer()) {
-		toolbar_.add(&exit_, UI::Align::kLeft);
-		toolbar_.add(&save_, UI::Align::kLeft);
-	} else
-		toolbar_.add(&toggle_options_menu_, UI::Align::kLeft);
-	toolbar_.add(&toggle_statistics_, UI::Align::kLeft);
-	toolbar_.add(&toggle_minimap_, UI::Align::kLeft);
-	toolbar_.add(&toggle_buildhelp_, UI::Align::kLeft);
-	toolbar_.add(&toggle_chat_, UI::Align::kLeft);
-
-	// TODO(unknown): instead of making unneeded buttons invisible after generation,
-	// they should not at all be generated. -> implement more dynamic toolbar UI
+   : InteractiveGameBase(g, global_s, OBSERVER, multiplayer) {
 	if (is_multiplayer()) {
-		exit_.set_visible(false);
-		exit_.set_enabled(false);
-		save_.set_visible(false);
-		save_.set_enabled(false);
+		add_toolbar_button(
+		   "wui/menus/menu_options_menu", "options_menu", _("Main Menu"), &options_, true);
+		options_.open_window = [this] { new GameOptionsMenu(*this, options_, main_windows_); };
+
 	} else {
-		toggle_chat_.set_visible(false);
-		toggle_chat_.set_enabled(false);
-		toggle_options_menu_.set_visible(false);
-		toggle_options_menu_.set_enabled(false);
+		UI::Button* button =
+		   add_toolbar_button("wui/menus/menu_exit_game", "exit_replay", _("Exit Replay"));
+		button->sigclicked.connect(boost::bind(&InteractiveSpectator::exit_btn, this));
+
+		add_toolbar_button(
+		   "wui/menus/menu_save_game", "save_game", _("Save Game"), &main_windows_.savegame, true);
+		main_windows_.savegame.open_window = [this] {
+			new GameMainMenuSaveGame(*this, main_windows_.savegame);
+		};
+	}
+	add_toolbar_button("wui/menus/menu_general_stats", "general_stats", _("Statistics"),
+	                   &main_windows_.general_stats, true);
+	main_windows_.general_stats.open_window = [this] {
+		new GeneralStatisticsMenu(*this, main_windows_.general_stats);
+	};
+
+	add_toolbar_button(
+	   "wui/menus/menu_toggle_minimap", "minimap", _("Minimap"), &minimap_registry(), true);
+	minimap_registry().open_window = [this] { toggle_minimap(); };
+
+	toggle_buildhelp_ = add_toolbar_button(
+	   "wui/menus/menu_toggle_buildhelp", "buildhelp", _("Show Building Spaces (on/off)"));
+	toggle_buildhelp_->sigclicked.connect(boost::bind(&InteractiveBase::toggle_buildhelp, this));
+
+	reset_zoom_ = add_toolbar_button(
+		"wui/menus/menu_reset_zoom", "reset_zoom", _("Reset zoom"));
+	reset_zoom_->sigclicked.connect(
+		[this] { zoom_around(1.f, Vector2f(get_w() / 2.f, get_h() / 2.f)); });
+
+	if (is_multiplayer()) {
+		add_toolbar_button("wui/menus/menu_chat", "chat", _("Chat"), &chat_, true);
+		chat_.open_window = [this] {
+			if (chat_provider_) {
+				GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
+			}
+		};
 	}
 
 	adjust_toolbar_position();
 
 	// Setup all screen elements
 	fieldclicked.connect(boost::bind(&InteractiveSpectator::node_action, this));
-
-#define INIT_BTN_HOOKS(registry, btn)                                                              \
-	registry.on_create = std::bind(&UI::Button::set_perm_pressed, &btn, true);                      \
-	registry.on_delete = std::bind(&UI::Button::set_perm_pressed, &btn, false);                     \
-	if (registry.window)                                                                            \
-		btn.set_perm_pressed(true);
-
-	INIT_BTN_HOOKS(chat_, toggle_chat_)
-	INIT_BTN_HOOKS(options_, toggle_options_menu_)
-	INIT_BTN_HOOKS(main_windows_.general_stats, toggle_statistics_)
-	INIT_BTN_HOOKS(main_windows_.savegame, save_)
-	INIT_BTN_HOOKS(minimap_registry(), toggle_minimap_)
-}
-
-InteractiveSpectator::~InteractiveSpectator() {
-// We need to remove these callbacks because the opened window might
-// (theoretically) live longer than 'this' window, and thus the
-// buttons. The assertions are safeguards in case somewhere else in the
-// code someone would overwrite our hooks.
-
-#define DEINIT_BTN_HOOKS(registry, btn)                                                            \
-	registry.on_create = 0;                                                                         \
-	registry.on_delete = 0;
-
-	DEINIT_BTN_HOOKS(chat_, toggle_chat_)
-	DEINIT_BTN_HOOKS(options_, toggle_options_menu_)
-	DEINIT_BTN_HOOKS(main_windows_.general_stats, toggle_statistics_)
-	DEINIT_BTN_HOOKS(main_windows_.savegame, save_)
-	DEINIT_BTN_HOOKS(minimap_registry(), toggle_minimap_)
 }
 
 /**
@@ -147,13 +117,6 @@
 }
 
 // Toolbar button callback functions.
-void InteractiveSpectator::toggle_chat() {
-	if (chat_.window)
-		delete chat_.window;
-	else if (chat_provider_)
-		GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
-}
-
 void InteractiveSpectator::exit_btn() {
 	if (is_multiplayer()) {
 		return;
@@ -161,34 +124,6 @@
 	end_modal<UI::Panel::Returncodes>(UI::Panel::Returncodes::kBack);
 }
 
-void InteractiveSpectator::save_btn() {
-	if (is_multiplayer()) {
-		return;
-	}
-	if (main_windows_.savegame.window)
-		delete main_windows_.savegame.window;
-	else {
-		new GameMainMenuSaveGame(*this, main_windows_.savegame);
-	}
-}
-
-void InteractiveSpectator::toggle_options_menu() {
-	if (!is_multiplayer()) {
-		return;
-	}
-	if (options_.window)
-		delete options_.window;
-	else
-		new GameOptionsMenu(*this, options_, main_windows_);
-}
-
-void InteractiveSpectator::toggle_statistics() {
-	if (main_windows_.general_stats.window)
-		delete main_windows_.general_stats.window;
-	else
-		new GeneralStatisticsMenu(*this, main_windows_.general_stats);
-}
-
 bool InteractiveSpectator::can_see(Widelands::PlayerNumber) const {
 	return true;
 }
@@ -225,7 +160,7 @@
 			return true;
 
 		case SDLK_m:
-			toggle_minimap();
+			minimap_registry().toggle();
 			return true;
 
 		case SDLK_c:
@@ -241,15 +176,13 @@
 
 		case SDLK_RETURN:
 		case SDLK_KP_ENTER:
-			if (!chat_provider_ | !chatenabled_)
-				break;
-
-			if (!chat_.window)
-				GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
-
-			dynamic_cast<GameChatMenu*>(chat_.window)->enter_chat_message();
+			if (chat_provider_) {
+				if (!chat_.window) {
+					GameChatMenu::create_chat_console(this, chat_, *chat_provider_);
+				}
+				dynamic_cast<GameChatMenu*>(chat_.window)->enter_chat_message();
+			}
 			return true;
-
 		default:
 			break;
 		}

=== modified file 'src/wui/interactive_spectator.h'
--- src/wui/interactive_spectator.h	2016-08-04 15:49:05 +0000
+++ src/wui/interactive_spectator.h	2016-11-17 08:27:19 +0000
@@ -40,18 +40,12 @@
 struct InteractiveSpectator : public InteractiveGameBase {
 	InteractiveSpectator(Widelands::Game&, Section& global_s, bool multiplayer = false);
 
-	~InteractiveSpectator();
-
 	Widelands::Player* get_player() const override;
 
 	bool handle_key(bool down, SDL_Keysym) override;
 
 private:
-	void toggle_chat();
-	void toggle_options_menu();
-	void toggle_statistics();
 	void exit_btn();
-	void save_btn();
 	int32_t calculate_buildcaps(const Widelands::TCoords<Widelands::FCoords>& c) override;
 	bool can_see(Widelands::PlayerNumber) const override;
 	bool can_act(Widelands::PlayerNumber) const override;
@@ -59,13 +53,6 @@
 	void node_action() override;
 
 private:
-	UI::Button toggle_chat_;
-	UI::Button exit_;
-	UI::Button save_;
-	UI::Button toggle_options_menu_;
-	UI::Button toggle_statistics_;
-	UI::Button toggle_minimap_;
-
 	UI::UniqueWindow::Registry chat_;
 	UI::UniqueWindow::Registry options_;
 };


Follow ups