← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/bug-536409 into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/bug-536409 into lp:widelands with lp:~widelands-dev/widelands/editor_new_map as a prerequisite.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #536409 in widelands: "create new map with different terrain"
  https://bugs.launchpad.net/widelands/+bug/536409

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/bug-536409/+merge/280352

Allow user to pick default terrain for new maps in the editor.

We are talking in the forum about giving this a tabbed design and remembering the user choice, but this is already functional.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-536409 into lp:widelands.
=== modified file 'src/editor/editorinteractive.cc'
--- src/editor/editorinteractive.cc	2015-11-08 17:31:06 +0000
+++ src/editor/editorinteractive.cc	2015-12-11 20:16:39 +0000
@@ -593,6 +593,7 @@
 				   editor.world(),
 				   64,
 				   64,
+					0,
 					/** TRANSLATORS: Default name for new map */
 				   _("No Name"),
 					g_options.pull_section("global").get_string("realname", pgettext("map_name", "Unknown")));

=== modified file 'src/editor/ui_menus/editor_main_menu_new_map.cc'
--- src/editor/ui_menus/editor_main_menu_new_map.cc	2015-11-03 18:52:00 +0000
+++ src/editor/ui_menus/editor_main_menu_new_map.cc	2015-12-11 20:16:39 +0000
@@ -19,122 +19,95 @@
 
 #include "editor/ui_menus/editor_main_menu_new_map.h"
 
-#include <cstdio>
-#include <cstring>
+#include <memory>
 #include <string>
 #include <vector>
 
 #include <boost/format.hpp>
 
 #include "base/i18n.h"
+#include "base/macros.h"
 #include "editor/editorinteractive.h"
+#include "graphic/image.h"
 #include "graphic/graphic.h"
+#include "graphic/texture.h"
 #include "logic/editor_game_base.h"
 #include "logic/map.h"
 #include "logic/world/world.h"
-#include "ui_basic/button.h"
+#include "logic/world/terrain_description.h"
 #include "ui_basic/progresswindow.h"
-#include "ui_basic/textarea.h"
-#include "ui_basic/window.h"
 
-using Widelands::NUMBER_OF_MAP_DIMENSIONS;
+inline EditorInteractive& MainMenuNewMap::eia() {
+	return dynamic_cast<EditorInteractive&>(*get_parent());
+}
 
 MainMenuNewMap::MainMenuNewMap(EditorInteractive & parent)
 	:
-	UI::Window
-		(&parent, "new_map_menu",
-		 (parent.get_w() - 180) / 2, (parent.get_h() - 150) / 2, 180, 150,
-		 _("New Map"))
+	UI::Window(&parent, "new_map_menu", 0, 0, 360, 150, _("New Map")),
+	margin_(4),
+	box_width_(get_inner_w() -  2 * margin_),
+	box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),
+	width_(&box_, 0, 0, box_width_, box_width_ / 3,
+			 0, 0, 0,
+			 _("Width:"), "", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kValueList),
+	height_(&box_, 0, 0, box_width_, box_width_ / 3,
+			  0, 0, 0,
+			  _("Height:"), "", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kValueList),
+	list_(&box_, 0, 0, box_width_, 330),
+	// Buttons
+	button_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	ok_button_(&button_box_, "create_map", 0, 0, box_width_ / 2 - margin_, 0,
+		 g_gr->images().get("pics/but5.png"),
+		 _("Create Map")),
+	cancel_button_(&button_box_, "generate_map", 0, 0, box_width_ / 2 - margin_, 0,
+		 g_gr->images().get("pics/but1.png"),
+		 _("Cancel"))
 {
-	int32_t const offsx   =  5;
-	int32_t const offsy   = 30;
-	int32_t const spacing =  5;
-	int32_t const width   = get_inner_w() - offsx * 2;
-	int32_t const height  = 20;
-	int32_t       posx    = offsx;
-	int32_t       posy    = offsy;
-	const Widelands::Map & map = parent.egbase().map();
+	width_.set_value_list(Widelands::kMapDimensions);
+	height_.set_value_list(Widelands::kMapDimensions);
+
 	{
-		Widelands::Extent const map_extent = map.extent();
-		for (m_w = 0; Widelands::MAP_DIMENSIONS[m_w] < map_extent.w; ++m_w) {}
-		for (m_h = 0; Widelands::MAP_DIMENSIONS[m_h] < map_extent.h; ++m_h) {}
-	}
-
-	m_width = new UI::Textarea(this, posx + spacing + 20, posy,
-										(boost::format(_("Width: %u")) % Widelands::MAP_DIMENSIONS[m_w]).str());
-
-	UI::Button * widthupbtn = new UI::Button
-		(this, "width_up",
-		 get_inner_w() - spacing - 20, posy, 20, 20,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	widthupbtn->sigclicked.connect(boost::bind(&MainMenuNewMap::button_clicked, this, 0));
-
-	UI::Button * widthdownbtn = new UI::Button
-		(this, "width_down",
-		 posx, posy, 20, 20,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	widthdownbtn->sigclicked.connect(boost::bind(&MainMenuNewMap::button_clicked, this, 1));
-
-	posy += 20 + spacing + spacing;
-
-	m_height = new UI::Textarea(this, posx + spacing + 20, posy,
-										 (boost::format(_("Height: %u"))
-										  % Widelands::MAP_DIMENSIONS[m_h]).str());
-
-	UI::Button * heightupbtn = new UI::Button
-		(this, "height_up",
-		 get_inner_w() - spacing - 20, posy, 20, 20,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	heightupbtn->sigclicked.connect(boost::bind(&MainMenuNewMap::button_clicked, this, 2));
-
-	UI::Button * heightdownbtn = new UI::Button
-		(this, "height_down",
-		 posx, posy, 20, 20,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	heightdownbtn->sigclicked.connect(boost::bind(&MainMenuNewMap::button_clicked, this, 3));
-
-	posy += 20 + spacing + spacing;
-
-	posy += height + spacing + spacing + spacing;
-
-	UI::Button * createbtn = new UI::Button
-		(this, "create_map",
-		 posx, posy, width, height,
-		 g_gr->images().get("pics/but5.png"),
-		 _("Create Map"));
-	createbtn->sigclicked.connect(boost::bind(&MainMenuNewMap::clicked_create_map, this));
-}
-
-
-/**
- * Called, when button get clicked
-*/
-void MainMenuNewMap::button_clicked(int32_t n) {
-	switch (n) {
-	case 0: ++m_w; break;
-	case 1: --m_w; break;
-	case 2: ++m_h; break;
-	case 3: --m_h; break;
-	default:
-		assert(false);
-	}
-
-	if (m_w <  0)                        m_w = 0;
-	if (m_w >= NUMBER_OF_MAP_DIMENSIONS) m_w = NUMBER_OF_MAP_DIMENSIONS - 1;
-	if (m_h <  0)                        m_h = 0;
-	if (m_h >= NUMBER_OF_MAP_DIMENSIONS) m_h = NUMBER_OF_MAP_DIMENSIONS - 1;
-	m_width ->set_text((boost::format(_("Width: %u")) % Widelands::MAP_DIMENSIONS[m_w]).str());
-	m_height->set_text((boost::format(_("Height: %u")) % Widelands::MAP_DIMENSIONS[m_h]).str());
-}
+		size_t width_index, height_index;
+		Widelands::Extent const map_extent = parent.egbase().map().extent();
+		for (width_index = 0;
+			  width_index < Widelands::kMapDimensions.size() &&
+			  Widelands::kMapDimensions[width_index] < map_extent.w;
+			  ++width_index) {}
+		width_.set_value(width_index);
+
+		for (height_index = 0;
+			  height_index < Widelands::kMapDimensions.size() &&
+			  Widelands::kMapDimensions[height_index] < map_extent.h;
+			  ++height_index) {}
+		height_.set_value(height_index);
+	}
+
+	box_.add(&width_, UI::Box::AlignLeft);
+	box_.add(&height_, UI::Box::AlignLeft);
+	box_.add_space(margin_);
+	UI::Textarea* terrain_label = new UI::Textarea(&box_, _("Terrain:"));
+	box_.add(terrain_label, UI::Box::AlignLeft);
+	box_.add(&list_, UI::Box::AlignLeft);
+	box_.add_space(2 * margin_);
+
+	cancel_button_.sigclicked.connect(boost::bind(&MainMenuNewMap::clicked_cancel, this));
+	ok_button_.sigclicked.connect(boost::bind(&MainMenuNewMap::clicked_create_map, this));
+	button_box_.add(&cancel_button_, UI::Box::AlignLeft);
+	button_box_.add(&ok_button_, UI::Box::AlignLeft);
+	box_.add(&button_box_, UI::Box::AlignLeft);
+
+	box_.set_size(box_width_,
+					  width_.get_h() + height_.get_h() + terrain_label->get_h() + list_.get_h()
+					  + button_box_.get_h() + 9 * margin_);
+	set_size(get_w(), box_.get_h() + 2 * margin_ + get_h() - get_inner_h());
+	fill_list();
+	center_to_parent();
+}
+
 
 void MainMenuNewMap::clicked_create_map() {
-	EditorInteractive & eia =
-		dynamic_cast<EditorInteractive&>(*get_parent());
-	Widelands::EditorGameBase & egbase = eia.egbase();
+	EditorInteractive& parent = eia();
+	Widelands::EditorGameBase & egbase = parent.egbase();
 	Widelands::Map              & map    = egbase.map();
 	UI::ProgressWindow loader;
 
@@ -142,8 +115,9 @@
 
 	map.create_empty_map(
 				egbase.world(),
-				Widelands::MAP_DIMENSIONS[m_w],
-				Widelands::MAP_DIMENSIONS[m_h],
+				width_.get_value() > 0 ? width_.get_value() : Widelands::kMapDimensions[0],
+				height_.get_value() > 0 ? height_.get_value() : Widelands::kMapDimensions[0],
+				list_.get_selected(),
 				_("No Name"),
 				g_options.pull_section("global").get_string("realname", pgettext("map_name", "Unknown")));
 
@@ -152,9 +126,28 @@
 
 	map.recalc_whole_map(egbase.world());
 
-	eia.set_need_save(true);
-	eia.toggle_minimap();
-	eia.toggle_minimap();
-
-	die();
+	parent.set_need_save(true);
+	parent.toggle_minimap();
+	parent.toggle_minimap();
+
+	die();
+}
+
+void MainMenuNewMap::clicked_cancel() {
+	die();
+}
+
+/*
+ * fill the terrain list
+ */
+void MainMenuNewMap::fill_list() {
+	list_.clear();
+	const DescriptionMaintainer<Widelands::TerrainDescription>& terrains = eia().egbase().world().terrains();
+
+	for (Widelands::DescriptionIndex index = 0; index < terrains.size(); ++index) {
+		const Widelands::TerrainDescription& terrain = terrains.get(index);
+		upcast(Image const, image, &terrain.get_texture(0));
+		list_.add(terrain.descname(), index, image);
+	}
+	list_.select(0);
 }

=== modified file 'src/editor/ui_menus/editor_main_menu_new_map.h'
--- src/editor/ui_menus/editor_main_menu_new_map.h	2014-09-10 14:08:25 +0000
+++ src/editor/ui_menus/editor_main_menu_new_map.h	2015-12-11 20:16:39 +0000
@@ -20,15 +20,14 @@
 #ifndef WL_EDITOR_UI_MENUS_EDITOR_MAIN_MENU_NEW_MAP_H
 #define WL_EDITOR_UI_MENUS_EDITOR_MAIN_MENU_NEW_MAP_H
 
-#include <vector>
-
+#include "logic/description_maintainer.h"
+#include "ui_basic/box.h"
+#include "ui_basic/button.h"
+#include "ui_basic/listselect.h"
+#include "ui_basic/spinbox.h"
 #include "ui_basic/window.h"
 
 struct EditorInteractive;
-namespace UI {
-struct Button;
-struct Textarea;
-}
 
 /**
  * This is the new map selection menu. It offers
@@ -39,11 +38,23 @@
 	MainMenuNewMap(EditorInteractive &);
 
 private:
-	UI::Textarea * m_width, * m_height;
-	int32_t m_w, m_h;
-
-	void button_clicked(int32_t);
+	EditorInteractive& eia();
 	void clicked_create_map();
+	void clicked_cancel();
+	void fill_list();
+
+	int32_t margin_;
+	int32_t box_width_;
+	UI::Box box_;
+	UI::SpinBox width_;
+	UI::SpinBox height_;
+
+	// Terrains list
+	UI::Listselect<Widelands::DescriptionIndex> list_;
+
+	// Buttons
+	UI::Box button_box_;
+	UI::Button ok_button_, cancel_button_;
 };
 
 #endif  // end of include guard: WL_EDITOR_UI_MENUS_EDITOR_MAIN_MENU_NEW_MAP_H

=== modified file 'src/editor/ui_menus/editor_main_menu_random_map.cc'
--- src/editor/ui_menus/editor_main_menu_random_map.cc	2015-11-03 18:52:00 +0000
+++ src/editor/ui_menus/editor_main_menu_random_map.cc	2015-12-11 20:16:39 +0000
@@ -29,310 +29,279 @@
 #include "base/i18n.h"
 #include "editor/editorinteractive.h"
 #include "editor/map_generator.h"
+#include "graphic/font_handler1.h"
 #include "graphic/graphic.h"
 #include "logic/constants.h"
 #include "logic/editor_game_base.h"
 #include "logic/map.h"
 #include "logic/world/world.h"
 #include "random/random.h"
-#include "ui_basic/button.h"
 #include "ui_basic/progresswindow.h"
-#include "ui_basic/textarea.h"
-#include "ui_basic/window.h"
 
 using namespace Widelands;
-// TODO(GunChleoc): Arabic: buttons need more height for Arabic.
+
 MainMenuNewRandomMap::MainMenuNewRandomMap(EditorInteractive& parent) :
-	UI::Window(&parent,
-				  "random_map_menu",
-				  (parent.get_w() - 260) / 2,
-				  (parent.get_h() - 450) / 2,
-				  305,
-				  500,
-				  _("New Random Map")),
-   // TRANSLATORS: The next are world names for the random map generator.
-	m_world_descriptions(
+	UI::Window(&parent, "random_map_menu", 0, 0, 400, 500, _("New Random Map")),
+	// UI elements
+	margin_(4),
+	box_width_(get_inner_w() -  2 * margin_),
+	label_height_(UI::g_fh1->render(as_uifont("."))->height() + 2),
+	box_(this, margin_, margin_, UI::Box::Vertical, 0, 0, margin_),
+	// Size
+	width_(&box_, 0, 0, box_width_, box_width_ / 3,
+			 0, 0, 0,
+			 _("Width:"), "", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kValueList),
+	height_(&box_, 0, 0, box_width_, box_width_ / 3,
+			  0, 0, 0,
+			  _("Height:"), "", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kValueList),
+	max_players_(2),
+	players_(&box_, 0, 0, box_width_, box_width_ / 3,
+			  2, 1, max_players_,
+			  _("Players:"), "", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kSmall),
+	// World + Resources
+	world_descriptions_(
 	{
+		/** TRANSLATORS: A world name for the random map generator in the editor */
 		{"greenland", _("Summer")},
+		/** TRANSLATORS: A world name for the random map generator in the editor */
 		{"winterland", _("Winter")},
+		/** TRANSLATORS: A world name for the random map generator in the editor */
 		{"desert", _("Desert")},
+		/** TRANSLATORS: A world name for the random map generator in the editor */
 		{"blackland", _("Wasteland")},
 	}),
-	m_current_world(0) {
-	int32_t const offsx   =  5;
-	int32_t const offsy   =  5;
-	int32_t const spacing =  5;
-	int32_t const width   = get_inner_w() - offsx * 2;
-	int32_t const height  = 20;
-	int32_t       posx    = offsx;
-	int32_t       posy    = offsy;
-	m_waterval     = 20;
-	m_landval      = 60;
-	m_wastelandval = 0;
-	m_mountainsval = 100 - m_waterval - m_landval - m_wastelandval;
-	m_pn = 1;
-
-	// ---------- Random map number edit ----------
-
-	new UI::Textarea(this, posx, posy, _("Random Number:"));
-	posy += height + spacing;
-
-	m_nrEditbox =
-		new UI::EditBox
-			(this,
-			 posx, posy,
-			 width, height,
-			 g_gr->images().get("pics/but1.png"));
-	m_nrEditbox->changed.connect
-		(boost::bind(&MainMenuNewRandomMap::nr_edit_box_changed, this));
-	RNG rng;
-	rng.seed(clock());
-	rng.rand();
-	m_mapNumber = rng.rand();
-	m_nrEditbox->set_text(std::to_string(static_cast<unsigned int>(m_mapNumber)));
-	posy += height + 3 * spacing;
-
-
-	// ---------- Width  ----------
-
-
-	const Widelands::Map & map = parent.egbase().map();
-	{
+	current_world_(0),
+	resource_amounts_(
+	{
+		/** TRANSLATORS: Amount of resources in the random map generator in the editor */
+		_("Low"),
+		/** TRANSLATORS: Amount of resources in the random map generator in the editor */
+		_("Medium"),
+		/** TRANSLATORS: Amount of resources in the random map generator in the editor */
+		_("High"),
+	}),
+	resource_amount_(2),
+	world_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	resources_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	world_label_(&world_box_, 0, 0, _("Climate:")),
+	resources_label_(&resources_box_, 0, 0, _("Resources:")),
+	world_
+		(&world_box_, "world", 0, 0,
+		 box_width_ - 2 * margin_ - std::max(world_label_.get_w(), resources_label_.get_w()), label_height_,
+		 g_gr->images().get("pics/but1.png"),
+		 world_descriptions_[current_world_].descname),
+	resources_(&resources_box_, "resources", 0, 0,
+				  box_width_ - 2 * margin_ - std::max(world_label_.get_w(), resources_label_.get_w()),
+				  label_height_,
+				  g_gr->images().get("pics/but1.png"),
+				  resource_amounts_[resource_amount_].c_str()),
+	// Terrain
+	waterval_(20),
+	landval_(60),
+	wastelandval_(0),
+	mountainsval_(100 - waterval_ - landval_ - wastelandval_),
+	water_(&box_, 0, 0, box_width_, box_width_ / 3,
+			  waterval_, 0, 60,
+			  _("Water:"), "%", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kSmall, 5),
+	land_(&box_, 0, 0, box_width_, box_width_ / 3,
+			  landval_, 0, 100,
+			  _("Land:"), "%", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kSmall, 5),
+	wasteland_(&box_, 0, 0, box_width_, box_width_ / 3,
+			  wastelandval_, 0, 70,
+			  _("Wasteland:"), "%", g_gr->images().get("pics/but1.png"), UI::SpinBox::Type::kSmall, 5),
+	mountains_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	mountains_label_(&mountains_box_, 0, 0, _("Mountains:")),
+	mountains_(&mountains_box_, 0, 0, box_width_ / 3, resources_label_.get_h(),
+				  (boost::format(_("%i %%")) % mountainsval_).str(),
+				  UI::Align::Align_HCenter),
+	island_mode_(&box_, Point(0, 0), _("Island mode")),
+	// Geeky stuff
+	map_number_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	map_number_label_(&map_number_box_, 0, 0, _("Random Number:")),
+	map_number_edit_(&map_number_box_, 0, 0,
+						  box_width_ - 2 * margin_ - map_number_label_.get_w(), label_height_,
+						  g_gr->images().get("pics/but1.png")),
+	map_id_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	map_id_label_(&map_id_box_, 0, 0, _("Map ID:")),
+	map_id_edit_(&map_id_box_, 0, 0,
+					 box_width_ - 2 * margin_ - map_id_label_.get_w(), label_height_,
+					 g_gr->images().get("pics/but1.png")),
+	// Buttons
+	button_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),
+	ok_button_(&button_box_, "generate_map", 0, 0, box_width_ / 2 - margin_, 0,
+		 g_gr->images().get("pics/but5.png"),
+		 _("Generate Map")),
+	cancel_button_(&button_box_, "generate_map", 0, 0, box_width_ / 2 - margin_, 0,
+		 g_gr->images().get("pics/but1.png"),
+		 _("Cancel"))
+{
+	int32_t box_height = 0;
+
+	// ---------- Width + Height ----------
+
+	width_.set_value_list(Widelands::kMapDimensions);
+	height_.set_value_list(Widelands::kMapDimensions);
+	{
+		const Widelands::Map & map = parent.egbase().map();
 		Widelands::Extent const map_extent = map.extent();
-
-		for (m_w = 0; Widelands::MAP_DIMENSIONS[m_w] < map_extent.w; ++m_w) {}
-		for (m_h = 0; Widelands::MAP_DIMENSIONS[m_h] < map_extent.h; ++m_h) {}
-	}
-
-	UI::Button * widthupbtn = new UI::Button
-		(this, "width_up",
-		 get_inner_w() - spacing - height, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	widthupbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::MAP_W_PLUS));
-
-	UI::Button * widthdownbtn = new UI::Button
-		(this, "width_down",
-		 posx, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	widthdownbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::MAP_W_MINUS));
-
-	m_width = new UI::Textarea(this, posx + spacing + height, posy,
-										(boost::format(_("Width: %u"))
-										 % Widelands::MAP_DIMENSIONS[m_w]).str());
-
-	posy += height + 2 * spacing;
-
-	// ---------- Height  ----------
-
-	m_height = new UI::Textarea(this, posx + spacing + height, posy,
-										 (boost::format(_("Height: %u"))
-										  % Widelands::MAP_DIMENSIONS[m_h]).str());
-
-	UI::Button * heightupbtn = new UI::Button
-		(this, "height_up",
-		 get_inner_w() - spacing - height, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	heightupbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::MAP_H_PLUS));
-
-	UI::Button * heightdownbtn = new UI::Button
-		(this, "height_down",
-		 posx, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	heightdownbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::MAP_H_MINUS));
-
-	posy += height + 4 * spacing;
-
+		width_.set_value(find_dimension_index(map_extent.w));
+		height_.set_value(find_dimension_index(map_extent.h));
+	}
+
+	width_.get_buttons()[0]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kMapSize));
+	width_.get_buttons()[1]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kMapSize));
+	height_.get_buttons()[0]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kMapSize));
+	height_.get_buttons()[1]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kMapSize));
+
+	box_.add(&width_, UI::Box::AlignLeft);
+	box_.add(&height_, UI::Box::AlignLeft);
+	box_height += margin_ + width_.get_h();
+	box_height += margin_ + height_.get_h();
+
+
+	// ---------- Players -----------
+
+	box_.add(&players_, UI::Box::AlignLeft);
+	box_height += margin_ + players_.get_h();
+
+	box_.add_space(margin_);
+	box_height += margin_;
+
+	// ---------- Worlds ----------
+
+	world_box_.add(&world_label_, UI::Box::AlignLeft);
+	if (world_label_.get_w() < resources_label_.get_w()) {
+		world_box_.add_space(resources_label_.get_w() - world_label_.get_w() - margin_);
+	}
+
+	world_.sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kWorld));
+	world_box_.add(&world_, UI::Box::AlignLeft);
+	box_.add(&world_box_, UI::Box::AlignLeft);
+	box_height += margin_ + world_box_.get_h();
+	box_.add_space(margin_);
+	box_height += margin_;
+
+	// ---------- Amount of Resources (Low/Medium/High) ----------
+
+	resources_box_.add(&resources_label_, UI::Box::AlignLeft);
+	if (resources_label_.get_w() < world_label_.get_w()) {
+		resources_box_.add_space(world_label_.get_w() - resources_label_.get_w() - margin_);
+	}
+
+	resources_.sigclicked.connect(boost::bind(&MainMenuNewRandomMap::button_clicked,
+															this,
+															ButtonId::kResources));
+	resources_box_.add(&resources_, UI::Box::AlignLeft);
+	box_.add(&resources_box_, UI::Box::AlignLeft);
+	box_height += margin_ + resources_box_.get_h();
+	box_.add_space(margin_);
+	box_height += margin_;
 
 	// ---------- Water -----------
-
-	UI::Button * waterupbtn = new UI::Button
-		(this, "water_up",
-		 get_inner_w() - spacing - height, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	waterupbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::WATER_PLUS));
-
-	UI::Button * waterdownbtn = new UI::Button
-		(this, "water_down",
-		 posx, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	waterdownbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::WATER_MINUS));
-
-	m_water = new UI::Textarea(this, posx + spacing + height, posy,
-										(boost::format(_("Water: %i %%")) % m_waterval).str());
-
-	posy += height + 2 * spacing;
-
-
+	water_.get_buttons()[0]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kWater));
+	water_.get_buttons()[1]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kWater));
+
+	box_.add(&water_, UI::Box::AlignLeft);
+	box_height += margin_ + water_.get_h();
 
 	// ---------- Land -----------
 
-	UI::Button * landupbtn = new UI::Button
-		(this, "land_up",
-		 get_inner_w() - spacing - height, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	landupbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::LAND_PLUS));
-
-	UI::Button * landdownbtn = new UI::Button
-		(this, "land_down",
-		 posx, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	landdownbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::LAND_MINUS));
-
-	m_land = new UI::Textarea(this, posx + spacing + height, posy,
-									  (boost::format(_("Land: %i %%")) % m_landval).str());
-
-	posy += height + 2 * spacing;
-
+	land_.get_buttons()[0]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kLand));
+	land_.get_buttons()[1]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kLand));
+
+	box_.add(&land_, UI::Box::AlignLeft);
+	box_height += margin_ + land_.get_h();
 
 
 	// ---------- Wasteland -----------
 
-	UI::Button * wastelandupbtn = new UI::Button
-		(this, "wasteland_up",
-		 get_inner_w() - spacing - height, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	wastelandupbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::WASTE_PLUS));
-
-	UI::Button * wastelanddownbtn = new UI::Button
-		(this, "wasteland_down",
-		 posx, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	wastelanddownbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::WASTE_MINUS));
-
-	m_wasteland = new UI::Textarea(this, posx + spacing + height, posy,
-											 (boost::format(_("Wasteland: %i %%")) % m_wastelandval).str());
-
-	posy += height + 2 * spacing;
-
-
+	wasteland_.get_buttons()[0]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kWasteland));
+	wasteland_.get_buttons()[1]->sigclicked.connect
+		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::kWasteland));
+
+	box_.add(&wasteland_, UI::Box::AlignLeft);
+	box_height += margin_ + wasteland_.get_h();
 
 	// ---------- Mountains -----------
 
-	m_mountains = new UI::Textarea(this, posx + spacing + height, posy,
-											 (boost::format(_("Mountains: %i %%")) % m_mountainsval).str());
-
-	posy += height + 2 * spacing;
+	mountains_box_.add(&mountains_label_, UI::Box::AlignLeft);
+
+	// Convince the value label to align with the spinbox labels above
+	mountains_box_.add_space(box_width_ - box_width_ / 6
+									 - mountains_label_.get_w() - mountains_.get_w() + margin_ + 3);
+	mountains_.set_fixed_width(box_width_ / 3 - margin_);
+	mountains_box_.add(&mountains_, UI::Box::AlignLeft);
+	mountains_box_.set_size(box_width_, mountains_label_.get_h());
+
+	box_.add(&mountains_box_, UI::Box::AlignLeft);
+	box_height += margin_ + mountains_box_.get_h();
+	box_.add_space(margin_);
+	box_height += margin_;
 
 
 	// ---------- Island mode ----------
 
-	m_island_mode = new UI::Checkbox(this, Point(posx, posy), _("Island mode"));
-	m_island_mode->set_state(true);
-	m_island_mode->changed.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::SWITCH_ISLAND_MODE));
-	posy += height + spacing;
-
-
-
-	// ---------- Amount of Resources (Low/Medium/High) ----------
-
-	new UI::Textarea(this, posx, posy, _("Resources:"));
-	posy += height + spacing;
-
-	m_res_amounts.push_back(_("Low"));
-	m_res_amounts.push_back(_("Medium"));
-	m_res_amounts.push_back(_("High"));
-
-	m_res_amount = 2;
-
-	m_res = new UI::Button
-		(this, "resources",
-		 posx, posy, width, height,
-		 g_gr->images().get("pics/but1.png"),
-		 m_res_amounts[m_res_amount].c_str());
-	m_res->sigclicked.connect(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::SWITCH_RES));
-
-	posy += height + 3 * spacing;
-
-	// ---------- Worlds ----------
-	m_world = new UI::Button
-		(this, "world",
-		 posx, posy, width, height,
-		 g_gr->images().get("pics/but1.png"),
-		 m_world_descriptions[m_current_world].descrname);
-	m_world->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::SWITCH_WORLD));
-
-	posy += height + 3 * spacing;
+	island_mode_.set_state(true);
+	box_.add(&island_mode_, UI::Box::AlignLeft);
+	box_height += margin_ + island_mode_.get_h();
+	box_.add_space(margin_);
+	box_height += margin_;
+
+	// ---------- Random map number edit ----------
+
+	map_number_box_.add(&map_number_label_, UI::Box::AlignLeft);
+
+	map_number_edit_.changed.connect(boost::bind(&MainMenuNewRandomMap::nr_edit_box_changed, this));
+	RNG rng;
+	rng.seed(clock());
+	rng.rand();
+	map_number_ = rng.rand();
+	map_number_edit_.set_text(std::to_string(static_cast<unsigned int>(map_number_)));
+	map_number_box_.add(&map_number_edit_, UI::Box::AlignLeft);
+	box_.add(&map_number_box_, UI::Box::AlignLeft);
+
+	box_height += margin_ + map_number_box_.get_h();
+	box_.add_space(margin_);
+	box_height += margin_;
 
 	// ---------- Map ID String edit ----------
 
-	new UI::Textarea(this, posx, posy, _("Map ID:"));
-	posy += height + spacing;
-
-	m_idEditbox =
-		new UI::EditBox
-			(this,
-			 posx, posy,
-			 width, height,
-			 g_gr->images().get("pics/but1.png"));
-	m_idEditbox->set_text("abcd-efgh-ijkl-mnop");
-	m_idEditbox->changed.connect
-		(boost::bind(&MainMenuNewRandomMap::id_edit_box_changed, this));
-	posy += height + 3 * spacing;
-
-
-
-	// ---------- Players -----------
-
-	UI::Button * playerupbtn = new UI::Button
-		(this, "player_up",
-		 get_inner_w() - spacing - height, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_up.png"));
-	playerupbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::PLAYER_PLUS));
-
-	UI::Button * playerdownbtn = new UI::Button
-		(this, "player_down",
-		 posx, posy, height, height,
-		 g_gr->images().get("pics/but1.png"),
-		 g_gr->images().get("pics/scrollbar_down.png"));
-	playerdownbtn->sigclicked.connect
-		(boost::bind(&MainMenuNewRandomMap::button_clicked, this, ButtonId::PLAYER_MINUS));
-
-	m_players = new UI::Textarea(this, posx + spacing + height, posy,
-										  (boost::format(_("Players: %u"))
-											% static_cast<unsigned int>(m_pn)).str());
-
-	posy += height + 2 * spacing;
-
+	map_id_box_.add(&map_id_label_, UI::Box::AlignLeft);
+
+	map_id_edit_.set_text("abcd-efgh-ijkl-mnop");
+	map_id_edit_.changed.connect(boost::bind(&MainMenuNewRandomMap::id_edit_box_changed, this));
+	map_id_box_.add(&map_id_edit_, UI::Box::AlignLeft);
+	box_.add(&map_id_box_, UI::Box::AlignLeft);
+	box_height += margin_ + map_id_edit_.get_h();
+	box_.add_space(margin_);
+	box_height += margin_;
 
 
 	// ---------- "Generate Map" button ----------
-
-	m_goButton = new UI::Button
-		(this, "generate_map",
-		 posx, posy, width, height,
-		 g_gr->images().get("pics/but5.png"),
-		 _("Generate Map"));
-	m_goButton->sigclicked.connect(boost::bind(&MainMenuNewRandomMap::clicked_create_map, this));
-	posy += height + spacing;
-
-	set_inner_size(get_inner_w(), posy);
-
+	cancel_button_.sigclicked.connect(boost::bind(&MainMenuNewRandomMap::clicked_cancel, this));
+	ok_button_.sigclicked.connect(boost::bind(&MainMenuNewRandomMap::clicked_create_map, this));
+	button_box_.add(&cancel_button_, UI::Box::AlignLeft);
+	button_box_.add(&ok_button_, UI::Box::AlignLeft);
+	box_.add(&button_box_, UI::Box::AlignLeft);
+	box_height += margin_ + button_box_.get_h();
+	box_height += 6 * margin_;
+
+	box_.set_size(box_width_, box_height);
+
+	set_inner_size(box_.get_w() + 2 * margin_, box_.get_h() + 2 * margin_);
 
 	nr_edit_box_changed();
+	center_to_parent();
 }
 
 
@@ -341,94 +310,54 @@
 */
 void MainMenuNewRandomMap::button_clicked(MainMenuNewRandomMap::ButtonId n) {
 	switch (n) {
-	case ButtonId::MAP_W_PLUS: ++m_w; break;
-	case ButtonId::MAP_W_MINUS:
-		--m_w;
-		if (m_w >= 0 && m_pn > m_w + 2)
-			--m_pn;
-		break;
-	case ButtonId::MAP_H_PLUS: ++m_h; break;
-	case ButtonId::MAP_H_MINUS:
-		--m_h;
-		if (m_h >= 0 && m_pn > m_h + 2)
-			--m_pn;
-		break;
-	case ButtonId::PLAYER_PLUS:
-		// Only higher the player number, if there is enough space
-		if (m_pn < MAX_PLAYERS && m_pn < m_w + 2 && m_pn < m_h + 2)
-			++m_pn;
-		break;
-	case ButtonId::PLAYER_MINUS:
-		if (m_pn > 1)
-			--m_pn;
-		break;
-	case ButtonId::WATER_PLUS:
-		if (m_waterval < 60)
-			m_waterval += 5;
-		normalize_landmass(n);
-		break;
-	case ButtonId::WATER_MINUS:
-		if (m_waterval >= 5)
-			m_waterval -= 5;
-		else
-			m_waterval = 0;
-		normalize_landmass(n);
-		break;
-	case ButtonId::LAND_PLUS:
-		if (m_landval < 100)
-			m_landval += 5;
-		normalize_landmass(n);
-		break;
-	case ButtonId::LAND_MINUS:
-		if (m_landval >= 5)
-			m_landval -= 5;
-		else
-			m_landval = 0;
-		normalize_landmass(n);
-		break;
-	case ButtonId::SWITCH_WORLD:
-		++ m_current_world;
-		m_current_world %= m_world_descriptions.size();
-		m_world->set_title(m_world_descriptions[m_current_world].descrname);
-		break;
-	case ButtonId::SWITCH_ISLAND_MODE:
-		break;
-	case ButtonId::WASTE_PLUS:
-		if (m_wastelandval < 70)
-			m_wastelandval += 5;
-		normalize_landmass(n);
-		break;
-	case ButtonId::WASTE_MINUS:
-		if (m_wastelandval >= 5)
-			m_wastelandval -= 5;
-		else
-			m_wastelandval = 0;
-		normalize_landmass(n);
-		break;
-	case ButtonId::SWITCH_RES:
-		++ m_res_amount;
-		if (m_res_amount == m_res_amounts.size())
-			m_res_amount = 0;
-		m_res->set_title(m_res_amounts[m_res_amount].c_str());
+	case ButtonId::kMapSize:
+		// Restrict maximum players according to map size, but allow at least 2 players.
+		max_players_ =
+				std::min(static_cast<size_t>(MAX_PLAYERS),
+							(find_dimension_index(width_.get_value())
+							 + find_dimension_index(height_.get_value())) / 2 + 2);
+		players_.set_interval(1, max_players_);
+		if (players_.get_value() > max_players_) {
+			players_.set_value(max_players_);
+		}
+		break;
+	case ButtonId::kWater:
+		waterval_ = water_.get_value();
+		normalize_landmass(n);
+		break;
+	case ButtonId::kLand:
+		landval_ = land_.get_value();
+		normalize_landmass(n);
+		break;
+	case ButtonId::kWasteland:
+		wastelandval_ = wasteland_.get_value();
+		normalize_landmass(n);
+		break;
+	case ButtonId::kResources:
+		++ resource_amount_;
+		resource_amount_ %= resource_amounts_.size();
+		resources_.set_title(resource_amounts_[resource_amount_].c_str());
+		break;
+	case ButtonId::kWorld:
+		++ current_world_;
+		current_world_ %= world_descriptions_.size();
+		world_.set_title(world_descriptions_[current_world_].descname);
+		break;
+	case ButtonId::kNone:
+		// Make sure that all conditions are met
+		max_players_ =
+				std::min(static_cast<size_t>(MAX_PLAYERS),
+							(find_dimension_index(width_.get_value()) +
+							 find_dimension_index(height_.get_value())) / 2 + 2);
+		players_.set_interval(1, max_players_);
+		if (players_.get_value() > max_players_) {
+			players_.set_value(max_players_);
+		}
+		normalize_landmass(n);
 		break;
 	default:
 		assert(false);
 	}
-
-	if (m_w <  0)                        m_w = 0;
-	if (m_w >= NUMBER_OF_MAP_DIMENSIONS) m_w = NUMBER_OF_MAP_DIMENSIONS - 1;
-	if (m_h <  0)                        m_h = 0;
-	if (m_h >= NUMBER_OF_MAP_DIMENSIONS) m_h = NUMBER_OF_MAP_DIMENSIONS - 1;
-
-	m_width ->set_text((boost::format(_("Width: %u")) % Widelands::MAP_DIMENSIONS[m_w]).str());
-	m_height->set_text((boost::format(_("Height: %u")) % Widelands::MAP_DIMENSIONS[m_h]).str());
-	m_water->set_text((boost::format(_("Water: %i %%")) % m_waterval).str());
-	m_land->set_text((boost::format(_("Land: %i %%")) % m_landval).str());
-	m_wasteland->set_text((boost::format(_("Wasteland: %i %%")) % m_wastelandval).str());
-	m_mountains->set_text((boost::format(_("Mountains: %i %%")) % m_mountainsval).str());
-	m_players->set_text((boost::format(_("Players: %u"))
-								% static_cast<unsigned int>(m_pn)).str());
-
 	nr_edit_box_changed();  // Update ID String
 }
 
@@ -436,46 +365,51 @@
 // If the the sum of our landmass is < 0% or > 100% change the mountain value.
 // If the mountain value gets out of range, change the other values.
 void MainMenuNewRandomMap::normalize_landmass(ButtonId clicked_button) {
-	int32_t sum_without_mountainsval = m_waterval + m_landval + m_wastelandval;
+	int32_t sum_without_mountainsval = waterval_ + landval_ + wastelandval_;
 
 	// Prefer changing mountainsval to keep consistency with old behaviour
-	while (sum_without_mountainsval + m_mountainsval > 100) {
-			m_mountainsval -= 1;
+	while (sum_without_mountainsval + mountainsval_ > 100) {
+			mountainsval_ -= 1;
 	}
-	while (sum_without_mountainsval + m_mountainsval < 100) {
-			m_mountainsval += 1;
+	while (sum_without_mountainsval + mountainsval_ < 100) {
+			mountainsval_ += 1;
 	}
 
 	// Compensate if mountainsval got above 100% / below 0%
-	while (m_mountainsval < 0) {
-		if (clicked_button != ButtonId::WASTE_PLUS && m_wastelandval > 0) {
-			m_wastelandval -= 5;
-			m_mountainsval += 5;
-		}
-		if (m_mountainsval < 0 && clicked_button != ButtonId::LAND_PLUS && m_landval > 0) {
-			m_landval -= 5;
-			m_mountainsval += 5;
-		}
-		if (m_mountainsval < 0 && clicked_button != ButtonId::WATER_PLUS && m_waterval > 0) {
-			m_waterval -= 5;
-			m_mountainsval += 5;
-		}
-	}
-
-	while (m_mountainsval > 100) {
-		if (clicked_button != ButtonId::WASTE_MINUS && m_wastelandval < 100) {
-			m_wastelandval += 5;
-			m_mountainsval -= 5;
-		}
-		if (m_mountainsval > 100 && clicked_button != ButtonId::LAND_MINUS && m_landval < 100) {
-			m_landval += 5;
-			m_mountainsval -= 5;
-		}
-		if (m_mountainsval > 100 && clicked_button != ButtonId::WATER_MINUS && m_waterval < 100) {
-			m_waterval += 5;
-			m_mountainsval -= 5;
-		}
-	}
+	while (mountainsval_ < 0) {
+		if (clicked_button != ButtonId::kWasteland && wastelandval_ > 0) {
+			wastelandval_ -= 5;
+			mountainsval_ += 5;
+		}
+		if (mountainsval_ < 0 && clicked_button != ButtonId::kLand && landval_ > 0) {
+			landval_ -= 5;
+			mountainsval_ += 5;
+		}
+		if (mountainsval_ < 0 && clicked_button != ButtonId::kWater && waterval_ > 0) {
+			waterval_ -= 5;
+			mountainsval_ += 5;
+		}
+	}
+
+	while (mountainsval_ > 100) {
+		if (clicked_button != ButtonId::kWasteland && wastelandval_ < 100) {
+			wastelandval_ += 5;
+			mountainsval_ -= 5;
+		}
+		if (mountainsval_ > 100 && clicked_button != ButtonId::kLand && landval_ < 100) {
+			landval_ += 5;
+			mountainsval_ -= 5;
+		}
+		if (mountainsval_ > 100 && clicked_button != ButtonId::kWater && waterval_ < 100) {
+			waterval_ += 5;
+			mountainsval_ -= 5;
+		}
+	}
+
+	water_.set_value(waterval_);
+	land_.set_value(landval_);
+	wasteland_.set_value(wastelandval_);
+	mountains_.set_text((boost::format(_("%i %%")) % mountainsval_).str());
 }
 
 void MainMenuNewRandomMap::clicked_create_map() {
@@ -487,23 +421,24 @@
 
 	egbase.cleanup_for_load();
 
-	UniqueRandomMapInfo mapInfo;
-	set_map_info(mapInfo);
+	UniqueRandomMapInfo map_info;
+	set_map_info(map_info);
 
 	std::stringstream sstrm;
 	sstrm << "Random generated map\nRandom number = "
-		<< mapInfo.mapNumber << "\n"
-		<< "Water = " << m_waterval << " %\n"
-		<< "Land = " << m_landval << " %\n"
-		<< "Wasteland = " << m_wastelandval << " %\n"
-		<< "Resources = " << m_res->get_title() << "\n"
-		<< "ID = " << m_idEditbox->text() << "\n";
+		<< map_info.mapNumber << "\n"
+		<< "Water = " << waterval_ << " %\n"
+		<< "Land = " << landval_ << " %\n"
+		<< "Wasteland = " << wastelandval_ << " %\n"
+		<< "Resources = " << resources_.get_title() << "\n"
+		<< "ID = " << map_id_edit_.text() << "\n";
 
-	MapGenerator gen(map, mapInfo, egbase);
+	MapGenerator gen(map, map_info, egbase);
 	map.create_empty_map(
 		egbase.world(),
-		mapInfo.w,
-		mapInfo.h,
+		map_info.w,
+		map_info.h,
+		0,
 		_("No Name"),
 		g_options.pull_section("global").get_string("realname", pgettext("map_name", "Unknown")),
 		sstrm.str().c_str());
@@ -523,39 +458,38 @@
 	die();
 }
 
+void MainMenuNewRandomMap::clicked_cancel() {
+	die();
+}
+
+
 void MainMenuNewRandomMap::id_edit_box_changed()
 {
-	UniqueRandomMapInfo mapInfo;
-
-	std::string str = m_idEditbox->text();
-
-	if (!UniqueRandomMapInfo::set_from_id_string(mapInfo, str))
-		m_goButton->set_enabled(false);
+	UniqueRandomMapInfo map_info;
+
+	std::string str = map_id_edit_.text();
+
+	if (!UniqueRandomMapInfo::set_from_id_string(map_info, str))
+		ok_button_.set_enabled(false);
 	else {
 		std::stringstream sstrm;
-		sstrm << mapInfo.mapNumber;
-		m_nrEditbox->set_text(sstrm.str());
-
-		m_h = 0;
-		for (uint32_t ix = 0; ix < NUMBER_OF_MAP_DIMENSIONS; ++ix)
-			if (MAP_DIMENSIONS[ix] == mapInfo.h)
-				m_h = ix;
-
-		m_w = 0;
-		for (uint32_t ix = 0; ix < NUMBER_OF_MAP_DIMENSIONS; ++ix)
-			if (MAP_DIMENSIONS[ix] == mapInfo.w)
-				m_w = ix;
-
-		m_landval  = mapInfo.landRatio  * 100.0 + 0.49;
-		m_waterval = mapInfo.waterRatio * 100.0 + 0.49;
-		m_res_amount = mapInfo.resource_amount;
-
-		m_res->set_title(m_res_amounts[m_res_amount].c_str());
+		sstrm << map_info.mapNumber;
+		map_number_edit_.set_text(sstrm.str());
+
+		width_.set_value(find_dimension_index(map_info.w));
+		height_.set_value(find_dimension_index(map_info.h));
+
+		landval_  = map_info.landRatio  * 100.0 + 0.49;
+		waterval_ = map_info.waterRatio * 100.0 + 0.49;
+
+		resource_amount_ = map_info.resource_amount;
+
+		resources_.set_title(resource_amounts_[resource_amount_].c_str());
 
 		// Update other values in UI as well
-		button_clicked(ButtonId::NO_BUTTON);
+		button_clicked(ButtonId::kNone);
 
-		m_goButton->set_enabled(true);
+		ok_button_.set_enabled(true);
 	}
 }
 
@@ -563,43 +497,49 @@
 {
 
 	try {
-		std::string const text = m_nrEditbox->text();
+		std::string const text = map_number_edit_.text();
 		std::stringstream sstrm(text);
 		unsigned int number;
 		sstrm >> number;
 
 		if (!sstrm.fail()) {
-			m_mapNumber = number;
-
-			Widelands::UniqueRandomMapInfo mapInfo;
-			set_map_info(mapInfo);
-
-			std::string idStr;
-			Widelands::UniqueRandomMapInfo::generate_id_string(idStr, mapInfo);
-
-			m_idEditbox->set_text(idStr);
-
-			m_goButton->set_enabled(true);
+			map_number_ = number;
+
+			Widelands::UniqueRandomMapInfo map_info;
+			set_map_info(map_info);
+
+			std::string id_string;
+			Widelands::UniqueRandomMapInfo::generate_id_string(id_string, map_info);
+
+			map_id_edit_.set_text(id_string);
+
+			ok_button_.set_enabled(true);
 		} else
-			m_goButton->set_enabled(false);
+			ok_button_.set_enabled(false);
 	} catch (...) {
-		m_goButton->set_enabled(false);
+		ok_button_.set_enabled(false);
 	}
 }
 
 void MainMenuNewRandomMap::set_map_info
-	(Widelands::UniqueRandomMapInfo & mapInfo) const
+	(Widelands::UniqueRandomMapInfo& map_info) const
 {
-	mapInfo.h = Widelands::MAP_DIMENSIONS[m_h];
-	mapInfo.w = Widelands::MAP_DIMENSIONS[m_w];
-	mapInfo.waterRatio = static_cast<double>(m_waterval) / 100.0;
-	mapInfo.landRatio  = static_cast<double>(m_landval) / 100.0;
-	mapInfo.wastelandRatio = static_cast<double>(m_wastelandval) / 100.0;
-	mapInfo.mapNumber = m_mapNumber;
-	mapInfo.islandMode = m_island_mode->get_state();
-	mapInfo.numPlayers = m_pn;
-	mapInfo.resource_amount = static_cast
+	map_info.w = width_.get_value() > 0 ? width_.get_value() : Widelands::kMapDimensions[0];
+	map_info.h = height_.get_value() > 0 ? height_.get_value() : Widelands::kMapDimensions[0];
+	map_info.waterRatio = static_cast<double>(waterval_) / 100.0;
+	map_info.landRatio  = static_cast<double>(landval_) / 100.0;
+	map_info.wastelandRatio = static_cast<double>(wastelandval_) / 100.0;
+	map_info.mapNumber = map_number_;
+	map_info.islandMode = island_mode_.get_state();
+	map_info.numPlayers = players_.get_value();
+	map_info.resource_amount = static_cast
 		<Widelands::UniqueRandomMapInfo::ResourceAmount>
-			(m_res_amount);
-	mapInfo.world_name = m_world_descriptions[m_current_world].name;
+			(resource_amount_);
+	map_info.world_name = world_descriptions_[current_world_].name;
+}
+
+size_t MainMenuNewRandomMap::find_dimension_index(int32_t value) {
+	size_t result = 0;
+	for (; result < Widelands::kMapDimensions.size() && Widelands::kMapDimensions[result] < value; ++result) {}
+	return result;
 }

=== modified file 'src/editor/ui_menus/editor_main_menu_random_map.h'
--- src/editor/ui_menus/editor_main_menu_random_map.h	2015-10-22 10:49:14 +0000
+++ src/editor/ui_menus/editor_main_menu_random_map.h	2015-12-11 20:16:39 +0000
@@ -23,8 +23,11 @@
 #include <vector>
 
 #include "base/macros.h"
+#include "ui_basic/box.h"
 #include "ui_basic/checkbox.h"
 #include "ui_basic/editbox.h"
+#include "ui_basic/spinbox.h"
+#include "ui_basic/textarea.h"
 #include "ui_basic/window.h"
 
 namespace Widelands {
@@ -34,7 +37,6 @@
 struct EditorInteractive;
 namespace UI {
 template <typename T, typename ID> struct IDButton;
-struct Textarea;
 }
 
 /**
@@ -46,32 +48,24 @@
 	MainMenuNewRandomMap(EditorInteractive &);
 
 	enum class ButtonId: uint8_t {
-		NO_BUTTON,
-		MAP_W_PLUS,
-		MAP_W_MINUS,
-		MAP_H_PLUS,
-		MAP_H_MINUS,
-		WATER_PLUS,
-		WATER_MINUS,
-		LAND_PLUS,
-		LAND_MINUS,
-		WASTE_PLUS,
-		WASTE_MINUS,
-		PLAYER_PLUS,
-		PLAYER_MINUS,
-		SWITCH_ISLAND_MODE,
-		SWITCH_RES,
-		SWITCH_WORLD
+		kNone,
+		kMapSize,
+		kWater,
+		kLand,
+		kWasteland,
+		kResources,
+		kWorld
 	};
 
 private:
 	struct WorldDescription {
 		std::string name;
-		std::string descrname;
+		std::string descname;
 	};
 
 	void button_clicked(ButtonId);
 	void clicked_create_map();
+	void clicked_cancel();
 	void id_edit_box_changed();
 	void nr_edit_box_changed();
 
@@ -83,25 +77,54 @@
 	//                        as requested by the user.
 	void normalize_landmass(MainMenuNewRandomMap::ButtonId clicked_button);
 
-	void set_map_info(Widelands::UniqueRandomMapInfo & mapInfo) const;
-
-	const std::vector<WorldDescription> m_world_descriptions;
-	int m_current_world;
-	UI::Textarea * m_width, * m_height, * m_land;
-	UI::Textarea * m_water, * m_mountains, * m_wasteland, * m_players;
-	UI::Button * m_res;
-	UI::Button * m_world;
-	UI::Checkbox * m_island_mode;
-	UI::Button * m_goButton;
-	int32_t m_w, m_h;
-	int32_t m_landval, m_waterval, m_wastelandval, m_mountainsval;
-	uint8_t m_pn;
-	uint32_t m_mapNumber;
-	uint32_t m_res_amount;
-	std::vector<std::string> m_res_amounts;
-
-	UI::EditBox * m_nrEditbox;
-	UI::EditBox * m_idEditbox;
+	void set_map_info(Widelands::UniqueRandomMapInfo& map_info) const;
+
+	// Helper function to find a map dimension in the global list of available dimensions.
+	size_t find_dimension_index(int32_t value);
+
+	// UI elements
+	int32_t margin_;
+	int32_t box_width_;
+	int32_t label_height_;
+	UI::Box box_;
+
+	// Size
+	UI::SpinBox width_;
+	UI::SpinBox height_;
+
+	uint8_t max_players_;
+	UI::SpinBox players_;
+
+	// World + Resources
+	const std::vector<WorldDescription> world_descriptions_;
+	int current_world_;
+	std::vector<std::string> resource_amounts_;
+	uint32_t resource_amount_;
+	UI::Box world_box_, resources_box_;
+	UI::Textarea world_label_, resources_label_;
+	UI::Button world_, resources_;
+
+	// Land
+	int32_t waterval_, landval_, wastelandval_, mountainsval_;
+	UI::SpinBox water_, land_, wasteland_;
+	UI::Box mountains_box_;
+	UI::Textarea mountains_label_, mountains_;
+
+	UI::Checkbox island_mode_;
+
+	// Geeky stuff
+	UI::Box map_number_box_;
+	uint32_t map_number_;
+	UI::Textarea map_number_label_;
+	UI::EditBox map_number_edit_;
+
+	UI::Box map_id_box_;
+	UI::Textarea map_id_label_;
+	UI::EditBox map_id_edit_;
+
+	// Buttons
+	UI::Box button_box_;
+	UI::Button ok_button_, cancel_button_;
 
 	DISALLOW_COPY_AND_ASSIGN(MainMenuNewRandomMap);
 };

=== modified file 'src/logic/map.cc'
--- src/logic/map.cc	2015-12-05 10:59:02 +0000
+++ src/logic/map.cc	2015-12-11 20:16:39 +0000
@@ -323,6 +323,7 @@
 */
 void Map::create_empty_map
 	(const World& world, uint32_t const w, uint32_t const h,
+	 const Widelands::DescriptionIndex default_terrain,
 	 const std::string& name,
 	 const std::string& author,
 	 const std::string& description)
@@ -342,8 +343,8 @@
 
 	{
 		Field::Terrains default_terrains;
-		default_terrains.d = 0;
-		default_terrains.r = 0;
+		default_terrains.d = default_terrain;
+		default_terrains.r = default_terrain;
 		Field * field = m_fields.get();
 		const Field * const fields_end = field + max_index();
 		for (; field < fields_end; ++field) {

=== modified file 'src/logic/map.h'
--- src/logic/map.h	2015-11-18 08:41:27 +0000
+++ src/logic/map.h	2015-12-11 20:16:39 +0000
@@ -30,6 +30,7 @@
 #include "base/i18n.h"
 #include "economy/itransport_cost_calculator.h"
 #include "logic/field.h"
+#include "logic/description_maintainer.h"
 #include "logic/map_revision.h"
 #include "logic/objective.h"
 #include "logic/walkingdir.h"
@@ -58,9 +59,8 @@
 
 #define S2MF_MAGIC  "WORLD_V1.0"
 
-
-uint16_t const NUMBER_OF_MAP_DIMENSIONS = 29;
-const uint16_t MAP_DIMENSIONS[] = {
+// Global list of available map dimensions.
+const std::vector<int32_t> kMapDimensions = {
 	64, 80, 96, 112, 128, 144, 160, 176, 192, 208, 224, 240, 256, 272, 288, 304,
 	320, 336, 352, 368, 384, 400, 416, 432, 448, 464, 480, 496, 512
 };
@@ -177,6 +177,7 @@
 	   (const World& world,
 	    uint32_t w = 64,
 	    uint32_t h = 64,
+		 const Widelands::DescriptionIndex default_terrain = 0,
 		 const std::string& name = _("No Name"),
 		 const std::string& author = pgettext("author_name", "Unknown"),
 		 const std::string& description = _("No description defined"));

=== modified file 'src/ui_basic/spinbox.cc'
--- src/ui_basic/spinbox.cc	2015-12-05 17:18:30 +0000
+++ src/ui_basic/spinbox.cc	2015-12-11 20:16:39 +0000
@@ -50,6 +50,9 @@
 	int32_t min;
 	int32_t max;
 
+	/// List of possible values for type kValueList
+	std::vector<int32_t> values;
+
 	/// The unit of the value
 	std::string unit;
 
@@ -82,21 +85,29 @@
 	 const std::string& label_text,
 	 const std::string& unit,
 	 const Image* background,
-	 bool const big)
+	 SpinBox::Type type,
+	 int32_t step_size, int32_t big_step_size)
 	:
 	Panel(parent, x, y, std::max(w, unit_w), 0),
-	big_(big),
+	type_(type),
 	sbi_(new SpinBoxImpl)
 {
+	if (type_ == SpinBox::Type::kValueList) {
+		sbi_->min   = 0;
+		sbi_->max   = 0;
+	} else {
+		sbi_->min   = minval;
+		sbi_->max   = maxval;
+	}
 	sbi_->value = startval;
-	sbi_->min   = minval;
-	sbi_->max   = maxval;
 	sbi_->unit  = unit;
 	sbi_->background = background;
 
+	bool is_big = type_ == SpinBox::Type::kBig;
+
 	uint32_t padding = 2;
 	uint32_t actual_w = std::max(w, unit_w);
-	uint32_t no_padding = (big_ ? 6 : 4);
+	uint32_t no_padding = (is_big ? 6 : 4);
 	uint32_t texth = UI::g_fh1->render(as_uifont("."))->height();
 	uint32_t buttonh = 20;
 
@@ -108,9 +119,9 @@
 	}
 
 #ifndef NDEBUG //  only in debug builds
-	if (unit_w < (big_ ? 7 * buttonh : 3 * buttonh)) {
+	if (unit_w < (is_big ? 7 * texth : 3 * buttonh)) {
 		throw wexception("Not enough space to draw spinbox. Width %d is smaller than required width %d",
-							  unit_w, (big_ ? 7 * buttonh : 3 * buttonh));
+							  unit_w, (is_big ? 7 * texth : 3 * buttonh));
 	}
 #endif
 
@@ -135,17 +146,17 @@
 			(box_, "-",
 			 0, 0, buttonh, buttonh,
 			 sbi_->background,
-			 g_gr->images().get(big_? "pics/scrollbar_left.png" : "pics/scrollbar_down.png"),
+			 g_gr->images().get(is_big ? "pics/scrollbar_left.png" : "pics/scrollbar_down.png"),
 			 _("Decrease the value"));
 	sbi_->button_plus =
 		new Button
 			(box_, "+",
 			 0, 0, buttonh, buttonh,
 			 sbi_->background,
-			 g_gr->images().get(big_? "pics/scrollbar_right.png" : "pics/scrollbar_up.png"),
+			 g_gr->images().get(is_big ? "pics/scrollbar_right.png" : "pics/scrollbar_up.png"),
 			 _("Increase the value"));
 
-	if (big_) {
+	if (is_big) {
 		sbi_->button_ten_minus =
 			new Button
 				(box_, "--",
@@ -161,8 +172,12 @@
 				 g_gr->images().get("pics/scrollbar_right_fast.png"),
 				 _("Increase the value by 10"));
 
-		sbi_->button_ten_plus->sigclicked.connect(boost::bind(&SpinBox::change_value, boost::ref(*this), 10));
-		sbi_->button_ten_minus->sigclicked.connect(boost::bind(&SpinBox::change_value, boost::ref(*this), -10));
+		sbi_->button_ten_plus->sigclicked.connect(boost::bind(&SpinBox::change_value,
+																				boost::ref(*this),
+																				big_step_size));
+		sbi_->button_ten_minus->sigclicked.connect(boost::bind(&SpinBox::change_value,
+																				 boost::ref(*this),
+																				 -1 * big_step_size));
 		sbi_->button_ten_plus->set_repeating(true);
 		sbi_->button_ten_minus->set_repeating(true);
 		buttons_.push_back(sbi_->button_ten_minus);
@@ -173,11 +188,11 @@
 											 - 2 * sbi_->button_minus->get_w()
 											 - 4 * padding);
 
-		box_->add(sbi_->button_ten_minus, UI::Box::AlignCenter);
-		box_->add(sbi_->button_minus, UI::Box::AlignCenter);
-		box_->add(sbi_->text, UI::Box::AlignCenter);
-		box_->add(sbi_->button_plus, UI::Box::AlignCenter);
-		box_->add(sbi_->button_ten_plus, UI::Box::AlignCenter);
+		box_->add(sbi_->button_ten_minus, UI::Box::AlignTop);
+		box_->add(sbi_->button_minus, UI::Box::AlignTop);
+		box_->add(sbi_->text, UI::Box::AlignTop);
+		box_->add(sbi_->button_plus, UI::Box::AlignTop);
+		box_->add(sbi_->button_ten_plus, UI::Box::AlignTop);
 	} else {
 		sbi_->text->set_fixed_width(unit_w - 2 * sbi_->button_minus->get_w() - 2 * padding);
 
@@ -186,16 +201,20 @@
 		box_->add(sbi_->button_plus, UI::Box::AlignCenter);
 	}
 
-	sbi_->button_plus->sigclicked.connect(boost::bind(&SpinBox::change_value, boost::ref(*this), 1));
-	sbi_->button_minus->sigclicked.connect(boost::bind(&SpinBox::change_value, boost::ref(*this), -1));
+	sbi_->button_plus->sigclicked.connect(boost::bind(&SpinBox::change_value, boost::ref(*this), step_size));
+	sbi_->button_minus->sigclicked.connect(boost::bind(&SpinBox::change_value,
+																		boost::ref(*this),
+																		-1 * step_size));
 	sbi_->button_plus->set_repeating(true);
 	sbi_->button_minus->set_repeating(true);
 	buttons_.push_back(sbi_->button_minus);
 	buttons_.push_back(sbi_->button_plus);
+
 	uint32_t box_height = std::max(label->get_h(), static_cast<int32_t>(buttonh));
 	box_->set_size(actual_w, box_height);
 	set_desired_size(actual_w, box_height);
 	set_size(actual_w, box_height);
+
 	update();
 }
 
@@ -219,13 +238,24 @@
 		}
 	}
 	if (!was_in_list) {
-		/** TRANSLATORS: %i = number, %s = unit, e.g. "5 pixels" in the advanced options */
-		sbi_->text->set_text((boost::format(_("%1$i %2$s")) % sbi_->value % sbi_->unit.c_str()).str());
+		if (type_ == SpinBox::Type::kValueList) {
+			if ((sbi_->value >= 0) && (sbi_->values.size() > static_cast<size_t>(sbi_->value))) {
+				/** TRANSLATORS: %i = number, %s = unit, e.g. "5 pixels" in the advanced options */
+				sbi_->text->set_text((boost::format(_("%1$i %2$s"))
+											 % sbi_->values.at(sbi_->value)
+											 % sbi_->unit.c_str()).str());
+			} else {
+				sbi_->text->set_text("undefined"); // The user should never see this, so we're not localizing
+			}
+		} else {
+			/** TRANSLATORS: %i = number, %s = unit, e.g. "5 pixels" in the advanced options */
+			sbi_->text->set_text((boost::format(_("%1$i %2$s")) % sbi_->value % sbi_->unit.c_str()).str());
+		}
 	}
 
 	sbi_->button_minus->set_enabled(sbi_->min < sbi_->value);
 	sbi_->button_plus ->set_enabled(sbi_->value < sbi_->max);
-	if (big_) {
+	if (type_ == SpinBox::Type::kBig) {
 		sbi_->button_ten_minus->set_enabled(sbi_->min < sbi_->value);
 		sbi_->button_ten_plus ->set_enabled(sbi_->value < sbi_->max);
 	}
@@ -254,6 +284,13 @@
 	update();
 }
 
+void SpinBox::set_value_list(const std::vector<int32_t>& values) {
+	sbi_->values = values;
+	sbi_->min = 0;
+	sbi_->max = values.size() - 1;
+	update();
+}
+
 
 /**
  * sets the interval the value may lay in and fixes the value, if outside.
@@ -283,15 +320,23 @@
 /**
  * \returns the value
  */
-int32_t SpinBox::get_value()
+int32_t SpinBox::get_value() const
 {
-	return sbi_->value;
+	if (type_ == SpinBox::Type::kValueList) {
+		if ((sbi_->value >= 0) && (sbi_->values.size() > static_cast<size_t>(sbi_->value))) {
+			return sbi_->values.at(sbi_->value);
+		} else {
+			return -1;
+		}
+	} else {
+		return sbi_->value;
+	}
 }
 
 /**
  * \returns the unit
  */
-std::string SpinBox::get_unit()
+std::string SpinBox::get_unit() const
 {
 	return sbi_->unit;
 }

=== modified file 'src/ui_basic/spinbox.h'
--- src/ui_basic/spinbox.h	2015-12-01 16:58:54 +0000
+++ src/ui_basic/spinbox.h	2015-12-11 20:16:39 +0000
@@ -40,6 +40,12 @@
 /// label_text is a text that precedes the actual spinbox.
 class SpinBox : public Panel {
 public:
+	enum class Type {
+		kSmall,    // Displays buttons for small steps
+		kBig,      // Displays buttons for small and big steps
+		kValueList // Uses the values that are set by set_value_list().
+	};
+
 	SpinBox
 		(Panel*,
 		 int32_t x, int32_t y, uint32_t w, uint32_t unit_w,
@@ -47,14 +53,19 @@
 		 const std::string& label_text = std::string(),
 		 const std::string& unit = std::string(),
 		 const Image* buttonbackground = g_gr->images().get("pics/but3.png"),
-		 bool big = false);
+		 SpinBox::Type = SpinBox::Type::kSmall,
+		  // The amount by which units are increased/decreased for small and big steps when a button is pressed.
+		 int32_t step_size = 1, int32_t big_step_size = 10);
 	~SpinBox();
 
 	void set_value(int32_t);
+	// For spinboxes of type kValueList. The vector needs to be sorted in ascending order,
+	// otherwise you will confuse the user.
+	void set_value_list(const std::vector<int32_t>&);
 	void set_interval(int32_t min, int32_t max);
 	void set_unit(const std::string&);
-	int32_t get_value();
-	std::string get_unit();
+	int32_t get_value() const;
+	std::string get_unit() const;
 	void add_replacement(int32_t, const std::string&);
 	void remove_replacement(int32_t);
 	bool has_replacement(int32_t) const;
@@ -65,7 +76,7 @@
 	void change_value(int32_t);
 	int32_t find_replacement(int32_t value) const;
 
-	const bool big_;
+	const SpinBox::Type type_;
 	SpinBoxImpl* sbi_;
 	std::vector<UI::Button*> buttons_;
 	UI::Box* box_;

=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc	2015-12-01 16:58:54 +0000
+++ src/ui_fsmenu/options.cc	2015-12-11 20:16:39 +0000
@@ -221,7 +221,7 @@
 		 /** TRANSLATORS: Options: Save game automatically every: */
 		 /** TRANSLATORS: This will have a number added in front of it */
 		 ngettext("minute", "minutes", opt.autosave / 60),
-		 g_gr->images().get("pics/but3.png"), true),
+		 g_gr->images().get("pics/but3.png"), UI::SpinBox::Type::kBig),
 
 	m_sb_remove_replays
 		(this,
@@ -234,7 +234,7 @@
 		 /** TRANSLATORS: Options: Remove Replays older than: */
 		 /** TRANSLATORS: This will have a number added in front of it */
 		 ngettext("day", "days", opt.remove_replays),
-		 g_gr->images().get("pics/but3.png"), true),
+		 g_gr->images().get("pics/but3.png"), UI::SpinBox::Type::kBig),
 	os(opt)
 {
 	m_advanced_options.sigclicked.connect


Follow ups