← Back to team overview

widelands-dev team mailing list archive

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

 

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

Commit message:
The Options window now relayouts itself for fullscreen switch.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1398733 in widelands: "Fullscreen Menus cannot relayout themselves"
  https://bugs.launchpad.net/widelands/+bug/1398733

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/fsmenu_fullscreen_4_options/+merge/312966
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/fsmenu_fullscreen_4_options into lp:widelands.
=== modified file 'src/ui_basic/checkbox.cc'
--- src/ui_basic/checkbox.cc	2016-10-25 08:11:31 +0000
+++ src/ui_basic/checkbox.cc	2016-12-10 10:30:36 +0000
@@ -38,6 +38,8 @@
                    const std::string& tooltip_text)
    : Panel(parent, p.x, p.y, kStateboxSize, kStateboxSize, tooltip_text),
      flags_(Is_Enabled),
+     pic_graphics_(pic),
+     label_text_(""),
      rendered_text_(nullptr) {
 	uint16_t w = pic->width();
 	uint16_t h = pic->height();
@@ -45,33 +47,47 @@
 	set_size(w, h);
 	set_can_focus(true);
 	set_flags(Has_Custom_Picture, true);
-	pic_graphics_ = pic;
 }
 
 Statebox::Statebox(Panel* const parent,
                    Vector2i const p,
                    const std::string& label_text,
                    const std::string& tooltip_text,
-                   uint32_t width)
-   : Panel(parent, p.x, p.y, kStateboxSize, kStateboxSize, tooltip_text),
+                   int width)
+   : Panel(parent, p.x, p.y, std::max(width, kStateboxSize), kStateboxSize, tooltip_text),
      flags_(Is_Enabled),
-     rendered_text_(label_text.empty() ? nullptr :
-                                         UI::g_fh1->render(as_uifont(label_text),
-                                                           width > (kStateboxSize + kPadding) ?
-                                                              width - kStateboxSize - kPadding :
-                                                              0)) {
-	pic_graphics_ = g_gr->images().get("images/ui_basic/checkbox_light.png");
-	if (rendered_text_) {
-		int w = rendered_text_->width() + kPadding + pic_graphics_->width() / 2;
-		int h = std::max(rendered_text_->height(), pic_graphics_->height());
+     pic_graphics_(g_gr->images().get("images/ui_basic/checkbox_light.png")),
+     label_text_(label_text),
+     rendered_text_(nullptr) {
+	set_flags(Has_Text, !label_text_.empty());
+	layout();
+}
+
+void Statebox::layout() {
+	// We only need to relayout if we have text
+	if (flags_ & Has_Text) {
+		int w = get_w();
+		int h = kStateboxSize;
+		int pic_width = kStateboxSize;
+		if (pic_graphics_) {
+			w = std::max(pic_graphics_->width(), w);
+			h = pic_graphics_->height();
+			pic_width = pic_graphics_->width();
+		}
+		rendered_text_ = label_text_.empty() ?
+		                    nullptr :
+		                    UI::g_fh1->render(
+		                       as_uifont(label_text_),
+		                       get_w() > (pic_width + kPadding) ? get_w() - pic_width - kPadding : 0);
+		if (rendered_text_) {
+			w = std::max(rendered_text_->width() + kPadding + pic_width, w);
+			h = std::max(rendered_text_->height(), h);
+		}
 		set_desired_size(w, h);
 		set_size(w, h);
 	}
 }
 
-Statebox::~Statebox() {
-}
-
 /**
  * Set the enabled state of the checkbox. A disabled checkbox cannot be clicked
  * and is somewhat darker to tell it apart from enabled ones.

=== modified file 'src/ui_basic/checkbox.h'
--- src/ui_basic/checkbox.h	2016-10-16 09:31:42 +0000
+++ src/ui_basic/checkbox.h	2016-12-10 10:30:36 +0000
@@ -52,8 +52,7 @@
 	         Vector2i,
 	         const std::string& label_text,
 	         const std::string& tooltip_text = std::string(),
-	         uint32_t width = 0);
-	~Statebox();
+	         int width = 0);
 
 	boost::signals2::signal<void()> changed;
 	boost::signals2::signal<void(bool)> changedto;
@@ -66,11 +65,6 @@
 	}
 	void set_state(bool on);
 
-	void set_owns_custopicture_() {
-		assert(flags_ & Has_Custom_Picture);
-		set_flags(Owns_Custom_Picture, true);
-	}
-
 	// Drawing and event handlers
 	void draw(RenderTarget&) override;
 
@@ -80,6 +74,7 @@
 	bool handle_mousemove(uint8_t, int32_t, int32_t, int32_t, int32_t) override;
 
 private:
+	void layout() override;
 	virtual void clicked() = 0;
 
 	enum Flags {
@@ -87,7 +82,7 @@
 		Is_Enabled = 0x02,
 		Is_Checked = 0x04,
 		Has_Custom_Picture = 0x08,
-		Owns_Custom_Picture = 0x10
+		Has_Text = 0x10
 	};
 	uint8_t flags_;
 	void set_flags(uint8_t const flags, bool const enable) {
@@ -96,6 +91,7 @@
 			flags_ |= flags;
 	}
 	const Image* pic_graphics_;
+	const std::string label_text_;
 	const Image* rendered_text_;
 };
 

=== modified file 'src/ui_basic/dropdown.cc'
--- src/ui_basic/dropdown.cc	2016-10-30 05:19:15 +0000
+++ src/ui_basic/dropdown.cc	2016-12-10 10:30:36 +0000
@@ -29,6 +29,16 @@
 #include "graphic/rendertarget.h"
 #include "ui_basic/mouse_constants.h"
 
+namespace {
+
+int base_height() {
+	return std::max(
+	   24,
+	   UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 2);
+}
+
+}  // namespace
+
 namespace UI {
 
 BaseDropdown::BaseDropdown(UI::Panel* parent,
@@ -39,15 +49,11 @@
                            const std::string& label,
                            const Image* background,
                            const Image* button_background)
-   : UI::Panel(
-        parent,
-        x,
-        y,
-        w,
-        std::max(24,
-                 UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))
-                       ->height() +
-                    2)),  // Height only to fit the button, so we can use this in Box layout.
+   : UI::Panel(parent,
+               x,
+               y,
+               w,
+               base_height()),  // Height only to fit the button, so we can use this in Box layout.
      max_list_height_(h - 2 * get_h()),
      mouse_tolerance_(50),
      button_box_(this, 0, 0, UI::Box::Horizontal, w, h),
@@ -76,23 +82,39 @@
 	list_.clicked.connect(boost::bind(&BaseDropdown::set_value, this));
 	list_.clicked.connect(boost::bind(&BaseDropdown::toggle_list, this));
 	set_can_focus(true);
+	layout();
 }
 
 BaseDropdown::~BaseDropdown() {
 	clear();
 }
 
+void BaseDropdown::set_height(int height) {
+	max_list_height_ = height - base_height();
+	layout();
+}
+
+void BaseDropdown::layout() {
+	const int base_h = base_height();
+	const int w = get_w();
+	button_box_.set_size(w, base_h);
+	display_button_.set_desired_size(w - 24, base_h);
+	int new_list_height =
+	   std::min(static_cast<int>(list_.size()) * list_.get_lineheight(), max_list_height_);
+	list_.set_size(w, new_list_height);
+	set_desired_size(w, base_h);
+}
+
 void BaseDropdown::add(const std::string& name,
                        const uint32_t value,
                        const Image* pic,
                        const bool select_this,
                        const std::string& tooltip_text) {
-	list_.set_size(
-	   list_.get_w(), std::min(list_.get_h() + list_.get_lineheight(), max_list_height_));
 	list_.add(name, value, pic, select_this, tooltip_text);
 	if (select_this) {
 		set_value();
 	}
+	layout();
 }
 
 bool BaseDropdown::has_selection() const {

=== modified file 'src/ui_basic/dropdown.h'
--- src/ui_basic/dropdown.h	2016-10-30 06:51:15 +0000
+++ src/ui_basic/dropdown.h	2016-12-10 10:30:36 +0000
@@ -81,6 +81,8 @@
 	/// Handle keypresses
 	bool handle_key(bool down, SDL_Keysym code) override;
 
+	void set_height(int height);
+
 protected:
 	/// Add an element to the list
 	/// \param name         the display name of the entry
@@ -105,6 +107,8 @@
 	void think() override;
 
 private:
+	void layout() override;
+
 	/// Updates the title and tooltip of the display button and triggers a 'selected' signal.
 	void set_value();
 	/// Toggles the dropdown list on and off.

=== modified file 'src/ui_basic/listselect.cc'
--- src/ui_basic/listselect.cc	2016-10-30 07:59:27 +0000
+++ src/ui_basic/listselect.cc	2016-12-10 10:30:36 +0000
@@ -316,6 +316,7 @@
 
 void BaseListselect::layout() {
 	scrollbar_.set_size(scrollbar_.get_w(), get_h());
+	scrollbar_.set_pos(Vector2i(get_w() - Scrollbar::kSize, 0));
 	scrollbar_.set_pagesize(get_h() - 2 * get_lineheight());
 	const int steps = entry_records_.size() * get_lineheight() - get_h();
 	scrollbar_.set_steps(steps);

=== modified file 'src/ui_basic/spinbox.cc'
--- src/ui_basic/spinbox.cc	2016-12-03 13:32:28 +0000
+++ src/ui_basic/spinbox.cc	2016-12-10 10:30:36 +0000
@@ -59,6 +59,7 @@
 
 	/// The UI parts
 	Textarea* text;
+	UI::MultilineTextarea* label;
 	Button* button_plus;
 	Button* button_minus;
 	Button* button_ten_plus;
@@ -88,7 +89,13 @@
                  SpinBox::Type type,
                  int32_t step_size,
                  int32_t big_step_size)
-   : Panel(parent, x, y, std::max(w, unit_w), 0), type_(type), sbi_(new SpinBoxImpl) {
+   : Panel(parent, x, y, std::max(w, unit_w), 0),
+     type_(type),
+     sbi_(new SpinBoxImpl),
+     unit_width_(unit_w),
+     button_height_(20),
+     padding_(2),
+     number_of_paddings_(type_ == SpinBox::Type::kBig ? 6 : 4) {
 	if (type_ == SpinBox::Type::kValueList) {
 		sbi_->min = 0;
 		sbi_->max = 0;
@@ -100,57 +107,35 @@
 	sbi_->unit = unit;
 	sbi_->background = button_background;
 
+	box_ = new UI::Box(this, 0, 0, UI::Box::Horizontal, 0, 0, padding_);
+
+	sbi_->label =
+	   new UI::MultilineTextarea(box_, 0, 0, 0, 0, label_text, UI::Align::kLeft, button_background,
+	                             UI::MultilineTextarea::ScrollMode::kNoScrolling);
+	box_->add(sbi_->label, UI::Align::kHCenter);
+
+	sbi_->text = new UI::Textarea(box_, "", UI::Align::kCenter);
+
 	bool is_big = type_ == SpinBox::Type::kBig;
 
-	uint32_t padding = 2;
-	uint32_t actual_w = std::max(w, unit_w);
-	uint32_t no_padding = (is_big ? 6 : 4);
-	// Give some height margin = 2 to keep the label from generating a scrollbar.
-	uint32_t texth =
-	   UI::g_fh1->render(as_uifont(UI::g_fh1->fontset()->representative_character()))->height() + 2;
-	uint32_t buttonh = 20;
-
-	// 40 is an ad hoc width estimate for the MultilineTextarea scrollbar + a bit of text.
-	if (!label_text.empty() && (w + padding) <= unit_w - 40) {
-		throw wexception(
-		   "SpinBox: Overall width %d must be bigger than unit width %d + %d * %d + 40 for padding",
-		   w, unit_w, no_padding, padding);
-	}
-
-	if (unit_w < (is_big ? 7 * buttonh : 3 * buttonh)) {
-		log("Not enough space to draw spinbox \"%s\".\n"
-		    "Width %d is smaller than required width %d."
-		    "Please report as a bug.\n",
-		    label_text.c_str(), unit_w, (is_big ? 7 * buttonh : 3 * buttonh));
-	}
-
-	box_ = new UI::Box(this, 0, 0, UI::Box::Horizontal, actual_w, texth, padding);
-
-	UI::MultilineTextarea* label = new UI::MultilineTextarea(
-	   box_, 0, 0, w - unit_w - no_padding * padding, texth, label_text, UI::Align::kLeft,
-	   button_background, UI::MultilineTextarea::ScrollMode::kNoScrolling);
-	box_->add(label, UI::Align::kHCenter);
-
-	sbi_->text = new UI::Textarea(box_, "", UI::Align::kCenter);
-
 	sbi_->button_minus =
-	   new Button(box_, "-", 0, 0, buttonh, buttonh, sbi_->background,
+	   new Button(box_, "-", 0, 0, button_height_, button_height_, sbi_->background,
 	              g_gr->images().get(is_big ? "images/ui_basic/scrollbar_left.png" :
 	                                          "images/ui_basic/scrollbar_down.png"),
 	              _("Decrease the value"));
 	sbi_->button_plus =
-	   new Button(box_, "+", 0, 0, buttonh, buttonh, sbi_->background,
+	   new Button(box_, "+", 0, 0, button_height_, button_height_, sbi_->background,
 	              g_gr->images().get(is_big ? "images/ui_basic/scrollbar_right.png" :
 	                                          "images/ui_basic/scrollbar_up.png"),
 	              _("Increase the value"));
 
 	if (is_big) {
 		sbi_->button_ten_minus =
-		   new Button(box_, "--", 0, 0, 2 * buttonh, buttonh, sbi_->background,
+		   new Button(box_, "--", 0, 0, 2 * button_height_, button_height_, sbi_->background,
 		              g_gr->images().get("images/ui_basic/scrollbar_left_fast.png"),
 		              _("Decrease the value by 10"));
 		sbi_->button_ten_plus =
-		   new Button(box_, "++", 0, 0, 2 * buttonh, buttonh, sbi_->background,
+		   new Button(box_, "++", 0, 0, 2 * button_height_, button_height_, sbi_->background,
 		              g_gr->images().get("images/ui_basic/scrollbar_right_fast.png"),
 		              _("Increase the value by 10"));
 
@@ -163,17 +148,12 @@
 		buttons_.push_back(sbi_->button_ten_minus);
 		buttons_.push_back(sbi_->button_ten_plus);
 
-		sbi_->text->set_fixed_width(unit_w - 2 * sbi_->button_ten_plus->get_w() -
-		                            2 * sbi_->button_minus->get_w() - 2 * padding);
-
 		box_->add(sbi_->button_ten_minus, UI::Align::kTop);
 		box_->add(sbi_->button_minus, UI::Align::kTop);
 		box_->add(sbi_->text, UI::Align::kTop);
 		box_->add(sbi_->button_plus, UI::Align::kTop);
 		box_->add(sbi_->button_ten_plus, UI::Align::kTop);
 	} else {
-		sbi_->text->set_fixed_width(unit_w - 2 * sbi_->button_minus->get_w());
-
 		box_->add(sbi_->button_minus, UI::Align::kHCenter);
 		box_->add(sbi_->text, UI::Align::kHCenter);
 		box_->add(sbi_->button_plus, UI::Align::kHCenter);
@@ -188,11 +168,7 @@
 	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);
-
+	layout();
 	update();
 }
 
@@ -201,6 +177,41 @@
 	sbi_ = nullptr;
 }
 
+void SpinBox::layout() {
+	// Only do the checks if we have a unit width, so we can have 0 in the constructor
+	// and set the dimensions later.
+	if (unit_width_ > 0) {
+		// 40 is an ad hoc width estimate for the MultilineTextarea scrollbar + a bit of text.
+		if (!sbi_->label->get_text().empty() && (get_w() + padding_) <= unit_width_ - 40) {
+			throw wexception("SpinBox: Overall width %d must be bigger than unit width %d + %d * %d + "
+			                 "40 for padding",
+			                 get_w(), unit_width_, number_of_paddings_, padding_);
+		}
+
+		if (unit_width_ < (type_ == SpinBox::Type::kBig ? 7 * button_height_ : 3 * button_height_)) {
+			log("Not enough space to draw spinbox \"%s\".\n"
+			    "Width %d is smaller than required width %d."
+			    "Please report as a bug.\n",
+			    sbi_->label->get_text().c_str(), unit_width_,
+			    (type_ == SpinBox::Type::kBig ? 7 * button_height_ : 3 * button_height_));
+		}
+	}
+
+	// 10 is arbitrary, the actual height will be set by the Multilinetextarea itself
+	sbi_->label->set_size(get_w() - unit_width_ - number_of_paddings_ * padding_, 10);
+	if (type_ == SpinBox::Type::kBig) {
+		sbi_->text->set_fixed_width(unit_width_ - 2 * sbi_->button_ten_plus->get_w() -
+		                            2 * sbi_->button_minus->get_w() - 2 * padding_);
+	} else {
+		sbi_->text->set_fixed_width(unit_width_ - 2 * sbi_->button_minus->get_w());
+	}
+
+	uint32_t box_height = std::max(sbi_->label->get_h(), static_cast<int32_t>(button_height_));
+	box_->set_size(get_w(), box_height);
+	set_desired_size(get_w(), box_height);
+	set_size(get_w(), box_height);
+}
+
 /**
  * private function - takes care about all updates in the UI elements
  */
@@ -229,6 +240,14 @@
 }
 
 /**
+ * Display width for the spinbox unit
+ * \note This does not relayout the spinbox.
+ */
+void SpinBox::set_unit_width(uint32_t width) {
+	unit_width_ = width;
+}
+
+/**
  * private function called by spinbox buttons to in-/decrease the value
  */
 void SpinBox::change_value(int32_t const value) {

=== modified file 'src/ui_basic/spinbox.h'
--- src/ui_basic/spinbox.h	2016-10-06 14:32:33 +0000
+++ src/ui_basic/spinbox.h	2016-12-10 10:30:36 +0000
@@ -78,8 +78,10 @@
 	const std::vector<UI::Button*>& get_buttons() {
 		return buttons_;
 	}
+	void set_unit_width(uint32_t width);
 
 private:
+	void layout() override;
 	void update();
 	void change_value(int32_t);
 	const std::string unit_text(int32_t value) const;
@@ -88,6 +90,10 @@
 	SpinBoxImpl* sbi_;
 	std::vector<UI::Button*> buttons_;
 	UI::Box* box_;
+	uint32_t unit_width_;
+	uint32_t button_height_;
+	uint32_t padding_;
+	uint32_t number_of_paddings_;
 };
 }
 

=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc	2016-11-01 12:15:30 +0000
+++ src/ui_fsmenu/options.cc	2016-12-10 10:30:36 +0000
@@ -88,54 +88,41 @@
 
 }  // namespace
 
-// TODO(GunChleoc): Arabic: This doesn't fit the window in Arabic.
 FullscreenMenuOptions::FullscreenMenuOptions(OptionsCtrl::OptionsStruct opt)
    : FullscreenMenuBase(),
 
      // Values for alignment and size
-     butw_(get_w() / 5),
-     buth_(get_h() * 9 / 200),
-     hmargin_(get_w() * 19 / 200),
      padding_(10),
-     tab_panel_width_(get_inner_w() - 2 * hmargin_),
-     column_width_(tab_panel_width_ - padding_),
-     tab_panel_y_(get_h() * 14 / 100),
 
      // Title
-     title_(this, get_w() / 2, buth_, _("Options"), UI::Align::kHCenter),
+     title_(this, 0, 0, _("Options"), UI::Align::kHCenter),
 
      // Buttons
-     cancel_(
-        this,
-        "cancel",
-        UI::g_fh1->fontset()->is_rtl() ? get_w() * 3 / 4 - butw_ / 2 : get_w() * 1 / 4 - butw_ / 2,
-        get_inner_h() - hmargin_,
-        butw_,
-        buth_,
-        g_gr->images().get("images/ui_basic/but0.png"),
-        _("Cancel")),
-     apply_(this,
+     button_box_(this, 0, 0, UI::Box::Horizontal),
+     cancel_(&button_box_,
+             "cancel",
+             0,
+             0,
+             0,
+             0,
+             g_gr->images().get("images/ui_basic/but0.png"),
+             _("Cancel")),
+     apply_(&button_box_,
             "apply",
-            get_w() * 2 / 4 - butw_ / 2,
-            get_inner_h() - hmargin_,
-            butw_,
-            buth_,
+            0,
+            0,
+            0,
+            0,
             g_gr->images().get("images/ui_basic/but0.png"),
             _("Apply")),
-     ok_(this,
-         "ok",
-         UI::g_fh1->fontset()->is_rtl() ? get_w() * 1 / 4 - butw_ / 2 : get_w() * 3 / 4 - butw_ / 2,
-         get_inner_h() - hmargin_,
-         butw_,
-         buth_,
-         g_gr->images().get("images/ui_basic/but2.png"),
-         _("OK")),
+     ok_(&button_box_, "ok", 0, 0, 0, 0, g_gr->images().get("images/ui_basic/but2.png"), _("OK")),
 
+     // Tabs
      tabs_(this,
-           hmargin_,
-           0,
-           tab_panel_width_,
-           get_inner_h() - tab_panel_y_ - buth_ - hmargin_,
+           0,
+           0,
+           100,  // 100 is arbitrary, will be resized in layout().
+           100,
            g_gr->images().get("images/ui_basic/but1.png"),
            UI::TabPanel::Type::kBorder),
 
@@ -150,39 +137,30 @@
                         0,
                         0,
                         column_width_ / 2,
-                        get_inner_h() - tab_panel_y_ - buth_ - hmargin_ - 4 * padding_,
+                        100,  // 100 is arbitrary, will be resized in layout().
                         _("Language")),
      resolution_dropdown_(&box_interface_,
                           0,
                           0,
                           column_width_ / 2,
-                          get_inner_h() - tab_panel_y_ - 2 * buth_ - hmargin_ - 5 * padding_,
+                          100,  // 100 is arbitrary, will be resized in layout().
                           _("In-game resolution")),
 
-     fullscreen_(&box_interface_, Vector2i(0, 0), _("Fullscreen"), "", column_width_),
-     inputgrab_(&box_interface_, Vector2i(0, 0), _("Grab Input"), "", column_width_),
+     fullscreen_(&box_interface_, Vector2i(0, 0), _("Fullscreen"), "", 0),
+     inputgrab_(&box_interface_, Vector2i(0, 0), _("Grab Input"), "", 0),
 
-     sb_maxfps_(&box_interface_,
-                0,
-                0,
-                column_width_ / 2,
-                column_width_ / 4,
-                opt.maxfps,
-                0,
-                99,
-                _("Maximum FPS:")),
+     sb_maxfps_(&box_interface_, 0, 0, 0, 0, opt.maxfps, 0, 99, _("Maximum FPS:")),
 
      // Windows options
      snap_win_overlap_only_(
-        &box_windows_, Vector2i(0, 0), _("Snap windows only when overlapping"), "", column_width_),
-     dock_windows_to_edges_(
-        &box_windows_, Vector2i(0, 0), _("Dock windows to edges"), "", column_width_),
+        &box_windows_, Vector2i(0, 0), _("Snap windows only when overlapping"), "", 0),
+     dock_windows_to_edges_(&box_windows_, Vector2i(0, 0), _("Dock windows to edges"), "", 0),
 
      sb_dis_panel_(&box_windows_,
                    0,
                    0,
-                   column_width_,
-                   200,
+                   0,
+                   0,
                    opt.panel_snap_distance,
                    0,
                    99,
@@ -192,8 +170,8 @@
      sb_dis_border_(&box_windows_,
                     0,
                     0,
-                    column_width_,
-                    200,
+                    0,
+                    0,
                     opt.border_snap_distance,
                     0,
                     99,
@@ -201,17 +179,16 @@
                     UI::SpinBox::Units::kPixels),
 
      // Sound options
-     music_(&box_sound_, Vector2i(0, 0), _("Enable Music"), "", column_width_),
-     fx_(&box_sound_, Vector2i(0, 0), _("Enable Sound Effects"), "", column_width_),
-     message_sound_(
-        &box_sound_, Vector2i(0, 0), _("Play a sound at message arrival"), "", column_width_),
+     music_(&box_sound_, Vector2i(0, 0), _("Enable Music"), "", 0),
+     fx_(&box_sound_, Vector2i(0, 0), _("Enable Sound Effects"), "", 0),
+     message_sound_(&box_sound_, Vector2i(0, 0), _("Play a sound at message arrival"), "", 0),
 
      // Saving options
      sb_autosave_(&box_saving_,
                   0,
                   0,
-                  column_width_,
-                  250,
+                  0,
+                  0,
                   opt.autosave / 60,
                   0,
                   100,
@@ -223,8 +200,8 @@
      sb_rolling_autosave_(&box_saving_,
                           0,
                           0,
-                          column_width_,
-                          250,
+                          0,
+                          0,
                           opt.rolling_autosave,
                           1,
                           20,
@@ -237,22 +214,19 @@
           Vector2i(0, 0),
           _("Compress widelands data files (maps, replays and savegames)"),
           "",
-          column_width_),
+          0),
      write_syncstreams_(&box_saving_,
                         Vector2i(0, 0),
                         _("Write syncstreams in network games to debug desyncs"),
                         "",
-                        column_width_),
+                        0),
 
      // Game options
      auto_roadbuild_mode_(
         &box_game_, Vector2i(0, 0), _("Start building road after placing a flag")),
      show_workarea_preview_(&box_game_, Vector2i(0, 0), _("Show buildings area preview")),
-     transparent_chat_(&box_game_,
-                       Vector2i(0, 0),
-                       _("Show in-game chat with transparent background"),
-                       "",
-                       column_width_),
+     transparent_chat_(
+        &box_game_, Vector2i(0, 0), _("Show in-game chat with transparent background"), "", 0),
 
      /** TRANSLATORS: A watchwindow is a window where you keep watching an object or a map region,*/
      /** TRANSLATORS: and it also lets you jump to it on the map. */
@@ -261,6 +235,14 @@
 	// Set up UI Elements
 	title_.set_fontsize(UI_FONT_SIZE_BIG);
 
+	// Buttons
+	button_box_.add(UI::g_fh1->fontset()->is_rtl() ? &ok_ : &cancel_, UI::Align::kHCenter);
+	button_box_.add_inf_space();
+	button_box_.add(&apply_, UI::Align::kHCenter);
+	button_box_.add_inf_space();
+	button_box_.add(UI::g_fh1->fontset()->is_rtl() ? &cancel_ : &ok_, UI::Align::kHCenter);
+
+	// Tabs
 	tabs_.add("options_interface", _("Interface"), &box_interface_, "");
 	tabs_.add("options_windows", _("Windows"), &box_windows_, "");
 	tabs_.add("options_sound", _("Sound"), &box_sound_, "");
@@ -272,8 +254,6 @@
 		tabs_.activate(os_.active_tab);
 	}
 
-	tabs_.set_pos(Vector2i(hmargin_, tab_panel_y_));
-
 	box_interface_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
 	box_windows_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
 	box_sound_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
@@ -380,6 +360,69 @@
 
 	// Language options
 	add_languages_to_list(opt.language);
+	layout();
+}
+
+void FullscreenMenuOptions::layout() {
+
+	// Values for alignment and size
+	butw_ = get_w() / 5;
+	buth_ = get_h() * 9 / 200;
+	hmargin_ = get_w() * 19 / 200;
+	tab_panel_width_ = get_inner_w() - 2 * hmargin_;
+	column_width_ = tab_panel_width_ - padding_;
+	tab_panel_y_ = get_h() * 14 / 100;
+
+	// Title
+	title_.set_size(get_w(), title_.get_h());
+	title_.set_pos(Vector2i(0, buth_));
+
+	// Buttons
+	cancel_.set_desired_size(butw_, buth_);
+	apply_.set_desired_size(butw_, buth_);
+	ok_.set_desired_size(butw_, buth_);
+	button_box_.set_pos(Vector2i(hmargin_ + butw_ / 3, get_inner_h() - hmargin_));
+	button_box_.set_size(tab_panel_width_ - 2 * butw_ / 3, buth_);
+
+	// Tabs
+	tabs_.set_pos(Vector2i(hmargin_, tab_panel_y_));
+	tabs_.set_size(tab_panel_width_, get_inner_h() - tab_panel_y_ - buth_ - hmargin_);
+
+	// Interface
+	language_dropdown_.set_desired_size(column_width_ / 2, language_dropdown_.get_h());
+	language_dropdown_.set_height(tabs_.get_h() - language_dropdown_.get_y() - buth_ - 3 * padding_);
+	resolution_dropdown_.set_desired_size(column_width_ / 2, resolution_dropdown_.get_h());
+	resolution_dropdown_.set_height(tabs_.get_h() - resolution_dropdown_.get_y() - buth_ -
+	                                3 * padding_);
+
+	fullscreen_.set_desired_size(column_width_, fullscreen_.get_h());
+	inputgrab_.set_desired_size(column_width_, inputgrab_.get_h());
+	sb_maxfps_.set_unit_width(column_width_ / 4);
+	sb_maxfps_.set_desired_size(column_width_ / 2, sb_maxfps_.get_h());
+
+	// Windows options
+	snap_win_overlap_only_.set_desired_size(column_width_, snap_win_overlap_only_.get_h());
+	dock_windows_to_edges_.set_desired_size(column_width_, dock_windows_to_edges_.get_h());
+	sb_dis_panel_.set_unit_width(200);
+	sb_dis_panel_.set_desired_size(column_width_, sb_dis_panel_.get_h());
+	sb_dis_border_.set_unit_width(200);
+	sb_dis_border_.set_desired_size(column_width_, sb_dis_border_.get_h());
+
+	// Sound options
+	music_.set_desired_size(column_width_, music_.get_h());
+	fx_.set_desired_size(column_width_, fx_.get_h());
+	message_sound_.set_desired_size(column_width_, message_sound_.get_h());
+
+	// Saving options
+	sb_autosave_.set_unit_width(250);
+	sb_autosave_.set_desired_size(column_width_, sb_autosave_.get_h());
+	sb_rolling_autosave_.set_unit_width(250);
+	sb_rolling_autosave_.set_desired_size(column_width_, sb_rolling_autosave_.get_h());
+	zip_.set_desired_size(column_width_, zip_.get_h());
+	write_syncstreams_.set_desired_size(column_width_, write_syncstreams_.get_h());
+
+	// Game options
+	transparent_chat_.set_desired_size(column_width_, transparent_chat_.get_h());
 }
 
 void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {

=== modified file 'src/ui_fsmenu/options.h'
--- src/ui_fsmenu/options.h	2016-09-07 10:09:35 +0000
+++ src/ui_fsmenu/options.h	2016-12-10 10:30:36 +0000
@@ -25,6 +25,7 @@
 #include <string>
 #include <vector>
 
+#include "ui_basic/box.h"
 #include "ui_basic/button.h"
 #include "ui_basic/checkbox.h"
 #include "ui_basic/dropdown.h"
@@ -98,21 +99,24 @@
 	OptionsCtrl::OptionsStruct get_values();
 
 private:
+	void layout() override;
+
 	// Fills the language selection list
 	void add_languages_to_list(const std::string& current_locale);
 
 	// Saves the options and reloads the active tab
 	void clicked_apply();
 
-	uint32_t const butw_;
-	uint32_t const buth_;
-	uint32_t const hmargin_;
-	uint32_t const padding_;
-	uint32_t const tab_panel_width_;
-	uint32_t const column_width_;
-	uint32_t const tab_panel_y_;
+	const uint32_t padding_;
+	uint32_t butw_;
+	uint32_t buth_;
+	uint32_t hmargin_;
+	uint32_t tab_panel_width_;
+	uint32_t column_width_;
+	uint32_t tab_panel_y_;
 
 	UI::Textarea title_;
+	UI::Box button_box_;
 	UI::Button cancel_, apply_, ok_;
 
 	// UI elements


Follow ups