← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1523096 in widelands: "Options Window redesign"
  https://bugs.launchpad.net/widelands/+bug/1523096

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

The Options window now has a tabbed layout. I added textual tabs to TabPanel and did a couple of fixes there as well - the tab brightening on mouseover was never executed, and tabs now also play click on mouseclick.

We are discussing some changes to the options themselves in the forum, e.g. some rephrasing and tooltips, but those can always be added later.

options.h/cc is probably best viewed with an offline diff tool.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/options_window into lp:widelands.
=== removed file 'pics/optionsmenu.jpg'
Binary files pics/optionsmenu.jpg	2008-07-23 15:48:24 +0000 and pics/optionsmenu.jpg	1970-01-01 00:00:00 +0000 differ
=== modified file 'src/ui_basic/tabpanel.cc'
--- src/ui_basic/tabpanel.cc	2014-12-04 09:00:20 +0000
+++ src/ui_basic/tabpanel.cc	2015-12-11 20:23:29 +0000
@@ -19,41 +19,51 @@
 
 #include "ui_basic/tabpanel.h"
 
+#include "graphic/font_handler1.h"
 #include "graphic/rendertarget.h"
+#include "graphic/text_layout.h"
 #include "ui_basic/mouse_constants.h"
 
 namespace UI {
 
-// Button size of tab buttons in pixels.
-constexpr int kTabPanelButtonSize = 34;
+// Button height of tab buttons in pixels. Is also used for width with pictorial buttons.
+constexpr int kTabPanelButtonHeight = 34;
 
 // Margin around image. The image will be scaled down to fit into this rectangle with preserving size.
 constexpr int kTabPanelImageMargin = 2;
 
+// Left and right margin around text.
+constexpr int kTabPanelTextMargin = 4;
+
 //  height of the bar separating buttons and tab contents
 constexpr int kTabPanelSeparatorHeight = 4;
 
+// Constant to flag up when we're not at a tab.
+constexpr uint32_t kNotFound = std::numeric_limits<uint32_t>::max();
+
 /*
  * =================
  * class Tab
  * =================
  */
 Tab::Tab
-	(TabPanel         * const parent,
-	 uint32_t            const id,
-	 const std::string &       name,
-	 const Image* gpic,
-	 const std::string &       gtooltip,
-	 Panel             * const gpanel)
+	(TabPanel* const tab_parent,
+	 size_t const tab_id,
+	 int32_t x,
+	 int32_t w,
+	 const std::string& name,
+	 const std::string& _title,
+	 const Image* _pic,
+	 const std::string& tooltip_text,
+	 Panel* const contents)
 	:
-	NamedPanel
-		(parent, name, id * kTabPanelButtonSize, 0, kTabPanelButtonSize,
-		 kTabPanelButtonSize, gtooltip),
-	m_parent(parent),
-	m_id(id),
-	pic(gpic),
-	tooltip(gtooltip),
-	panel(gpanel)
+	NamedPanel(tab_parent, name, x, 0, w, kTabPanelButtonHeight, tooltip_text),
+	parent(tab_parent),
+	id(tab_id),
+	pic(_pic),
+	title(_title),
+	tooltip(tooltip_text),
+	panel(contents)
 {
 }
 
@@ -61,10 +71,15 @@
  * Currently active tab
  */
 bool Tab::active() {
-	return m_parent->m_active == m_id;
+	return parent->active_ == id;
 }
 void Tab::activate() {
-	return m_parent->activate(m_id);
+	return parent->activate(id);
+}
+
+bool Tab::handle_mousepress(uint8_t, int32_t, int32_t) {
+	play_click();
+	return false;
 }
 
 /*
@@ -78,22 +93,26 @@
 TabPanel::TabPanel
 	(Panel * const parent,
 	 int32_t const x, int32_t const y,
-	 const Image* background)
+	 const Image* background,
+	 TabPanel::Type border_type)
 	:
-	Panel            (parent, x, y, 0, 0),
-	m_active         (0),
-	m_highlight      (-1),
-	m_pic_background (background)
+	Panel           (parent, x, y, 0, 0),
+	active_         (0),
+	highlight_      (kNotFound),
+	pic_background_ (background),
+	border_type_    (border_type)
 {}
 TabPanel::TabPanel
 	(Panel * const parent,
 	 int32_t const x, int32_t const y, int32_t const w, int32_t const h,
-	 const Image* background)
+	 const Image* background,
+	 TabPanel::Type border_type)
 	:
-	Panel            (parent, x, y, w, h),
-	m_active         (0),
-	m_highlight      (-1),
-	m_pic_background (background)
+	Panel           (parent, x, y, w, h),
+	active_         (0),
+	highlight_      (kNotFound),
+	pic_background_ (background),
+	border_type_    (border_type)
 {}
 
 /**
@@ -101,12 +120,16 @@
  */
 void TabPanel::layout()
 {
-	if (m_active < m_tabs.size()) {
-		Panel * const panel = m_tabs[m_active]->panel;
+	if (active_ < tabs_.size()) {
+		Panel * const panel = tabs_[active_]->panel;
 		uint32_t h = get_h();
 
 		// avoid excessive craziness in case there is a wraparound
-		h = std::min(h, h - (kTabPanelButtonSize + kTabPanelSeparatorHeight));
+		h = std::min(h, h - (kTabPanelButtonHeight + kTabPanelSeparatorHeight));
+		// If we have a border, we will also want some margin to the bottom
+		if (border_type_ == TabPanel::Type::kBorder) {
+			h = h - kTabPanelSeparatorHeight;
+		}
 		panel->set_size(get_w(), h);
 	}
 }
@@ -120,12 +143,12 @@
 	uint32_t h;
 
 	// size of button row
-	w = kTabPanelButtonSize * m_tabs.size();
-	h = kTabPanelButtonSize + kTabPanelSeparatorHeight;
+	w = kTabPanelButtonHeight * tabs_.size();
+	h = kTabPanelButtonHeight + kTabPanelSeparatorHeight;
 
 	// size of contents
-	if (m_active < m_tabs.size()) {
-		Panel * const panel = m_tabs[m_active]->panel;
+	if (active_ < tabs_.size()) {
+		Panel * const panel = tabs_[active_]->panel;
 		uint32_t panelw, panelh;
 
 		panel->get_desired_size(panelw, panelh);
@@ -146,7 +169,25 @@
 }
 
 /**
- * Add a new tab
+ * Add a new textual tab
+*/
+uint32_t TabPanel::add
+	(const std::string & name,
+	 const std::string & title,
+	 Panel             * const panel,
+	 const std::string &       tooltip_text)
+{
+	const Image* pic = UI::g_fh1->render(as_uifont(title));
+	return add_tab(std::max(kTabPanelButtonHeight, pic->width() + 2 * kTabPanelTextMargin),
+						name,
+						title,
+						pic,
+						tooltip_text,
+						panel);
+}
+
+/**
+ * Add a new pictorial tab
 */
 uint32_t TabPanel::add
 	(const std::string & name,
@@ -154,14 +195,38 @@
 	 Panel             * const panel,
 	 const std::string &       tooltip_text)
 {
+	return add_tab(kTabPanelButtonHeight,
+						name,
+						"",
+						pic,
+						tooltip_text,
+						panel);
+}
+
+/** Common adding function for textual and pictorial tabs. */
+uint32_t TabPanel::add_tab(int32_t width,
+									const std::string& name,
+									const std::string& title,
+									const Image* pic,
+									const std::string& tooltip_text,
+									Panel* panel) {
 	assert(panel);
 	assert(panel->get_parent() == this);
 
-	uint32_t id = m_tabs.size();
-	m_tabs.push_back(new Tab(this, id, name, pic, tooltip_text, panel));
-
-	panel->set_pos(Point(0, kTabPanelButtonSize + kTabPanelSeparatorHeight));
-	panel->set_visible(id == m_active);
+	size_t id = tabs_.size();
+	int32_t x = id > 0 ? tabs_[id - 1]->get_x() + tabs_[id - 1]->get_w() : 0;
+	tabs_.push_back(new Tab(this, id, x, width, name, title, pic, tooltip_text, panel));
+
+	// Add a margin if there is a border
+	if (border_type_ == TabPanel::Type::kBorder) {
+		panel->set_border(kTabPanelSeparatorHeight + 1, kTabPanelSeparatorHeight + 1,
+								kTabPanelSeparatorHeight, kTabPanelSeparatorHeight);
+		panel->set_pos(Point(0, kTabPanelButtonHeight));
+	} else {
+		panel->set_pos(Point(0, kTabPanelButtonHeight + kTabPanelSeparatorHeight));
+	}
+
+	panel->set_visible(id == active_);
 	update_desired_size();
 
 	return id;
@@ -173,20 +238,20 @@
 */
 void TabPanel::activate(uint32_t idx)
 {
-	if (m_active < m_tabs.size())
-		m_tabs[m_active]->panel->set_visible(false);
-	if (idx < m_tabs.size())
-		m_tabs[idx]->panel->set_visible(true);
+	if (active_ < tabs_.size())
+		tabs_[active_]->panel->set_visible(false);
+	if (idx < tabs_.size())
+		tabs_[idx]->panel->set_visible(true);
 
-	m_active = idx;
+	active_ = idx;
 
 	update_desired_size();
 }
 
 void TabPanel::activate(const std::string & name)
 {
-	for (uint32_t t = 0; t < m_tabs.size(); ++t)
-		if (m_tabs[t]->get_name() == name)
+	for (uint32_t t = 0; t < tabs_.size(); ++t)
+		if (tabs_[t]->get_name() == name)
 			activate(t);
 }
 
@@ -194,7 +259,7 @@
  * Return the tab names in order
  */
 const TabPanel::TabList & TabPanel::tabs() {
-	return m_tabs;
+	return tabs_;
 }
 
 /**
@@ -202,96 +267,118 @@
 */
 void TabPanel::draw(RenderTarget & dst)
 {
-	uint32_t idx;
-	uint32_t x;
-
 	// draw the background
-	static_assert(2 < kTabPanelButtonSize, "assert(2 < kTabPanelButtonSize) failed.");
-	static_assert(4 < kTabPanelButtonSize, "assert(4 < kTabPanelButtonSize) failed.");
+	static_assert(2 < kTabPanelButtonHeight, "assert(2 < kTabPanelButtonSize) failed.");
+	static_assert(4 < kTabPanelButtonHeight, "assert(4 < kTabPanelButtonSize) failed.");
 
-	if (m_pic_background) {
-		dst.tile
-			(Rect(Point(0, 0), m_tabs.size() * kTabPanelButtonSize, kTabPanelButtonSize - 2),
-			 m_pic_background, Point(get_x(), get_y()));
-		assert(kTabPanelButtonSize - 2 <= get_h());
-		dst.tile
-			(Rect
-			 (Point(0, kTabPanelButtonSize - 2),
-			  get_w(), get_h() - kTabPanelButtonSize + 2),
-			 m_pic_background,
-			 Point(get_x(), get_y() + kTabPanelButtonSize - 2));
+	if (pic_background_) {
+		if (!tabs_.empty()) {
+			dst.tile
+				(Rect(Point(0, 0), tabs_.back()->get_x() + tabs_.back()->get_w(), kTabPanelButtonHeight - 2),
+				 pic_background_,
+				 Point(get_x(), get_y()));
+		}
+		assert(kTabPanelButtonHeight - 2 <= get_h());
+		dst.tile
+			(Rect(Point(0, kTabPanelButtonHeight - 2), get_w(), get_h() - kTabPanelButtonHeight + 2),
+			 pic_background_,
+			 Point(get_x(), get_y() + kTabPanelButtonHeight - 2));
 	}
 
+	RGBColor black(0, 0, 0);
+
 	// draw the buttons
-	for (idx = 0, x = 0; idx < m_tabs.size(); idx++, x += kTabPanelButtonSize) {
-		if (m_highlight == static_cast<int32_t>(idx))
-			dst.brighten_rect
-				(Rect(Point(x, 0), kTabPanelButtonSize, kTabPanelButtonSize),
-				 MOUSE_OVER_BRIGHT_FACTOR);
-
-		// Draw the icon
-		assert(m_tabs[idx]->pic);
-
-		// Scale the image down if needed, but keep the ratio.
-		constexpr int kMaxImageSize = kTabPanelButtonSize - 2 * kTabPanelImageMargin;
-		double image_scale =
-		   std::min(1.,
-		            std::min(static_cast<double>(kMaxImageSize) / m_tabs[idx]->pic->width(),
-		                     static_cast<double>(kMaxImageSize) / m_tabs[idx]->pic->height()));
-
-		uint16_t picture_width = image_scale * m_tabs[idx]->pic->width();
-		uint16_t picture_height = image_scale * m_tabs[idx]->pic->height();
-		dst.blitrect_scale(Rect(x + (kTabPanelButtonSize - picture_width) / 2,
-		                        (kTabPanelButtonSize - picture_height) / 2,
-		                        picture_width,
-		                        picture_height),
-		                   m_tabs[idx]->pic,
-		                   Rect(0, 0, m_tabs[idx]->pic->width(), m_tabs[idx]->pic->height()),
-		                   1.,
-		                   BlendMode::UseAlpha);
+	int32_t x;
+	int tab_width;
+	for (size_t idx = 0; idx < tabs_.size(); ++idx) {
+		x = tabs_[idx]->get_x();
+		tab_width = tabs_[idx]->get_w();
+
+		if (highlight_ == idx) {
+			dst.brighten_rect(Rect(Point(x, 0), tab_width, kTabPanelButtonHeight), MOUSE_OVER_BRIGHT_FACTOR);
+		}
+
+		assert(tabs_[idx]->pic);
+
+		// If the title is empty, we will assume a pictorial tab
+		if (tabs_[idx]->title.empty()) {
+			// Scale the image down if needed, but keep the ratio.
+			constexpr int kMaxImageSize = kTabPanelButtonHeight - 2 * kTabPanelImageMargin;
+			double image_scale =
+				std::min(1.,
+							std::min(static_cast<double>(kMaxImageSize) / tabs_[idx]->pic->width(),
+										static_cast<double>(kMaxImageSize) / tabs_[idx]->pic->height()));
+
+			uint16_t picture_width = image_scale * tabs_[idx]->pic->width();
+			uint16_t picture_height = image_scale * tabs_[idx]->pic->height();
+			dst.blitrect_scale(Rect(x + (kTabPanelButtonHeight - picture_width) / 2,
+											(kTabPanelButtonHeight - picture_height) / 2,
+											picture_width,
+											picture_height),
+									 tabs_[idx]->pic,
+									 Rect(0, 0, tabs_[idx]->pic->width(), tabs_[idx]->pic->height()),
+									 1.,
+									 BlendMode::UseAlpha);
+		} else {
+			dst.blit(Point(x + kTabPanelTextMargin, (kTabPanelButtonHeight - tabs_[idx]->pic->height()) / 2),
+						tabs_[idx]->pic,
+						BlendMode::UseAlpha,
+						UI::Align_Left);
+		}
 
 		// Draw top part of border
-		RGBColor black(0, 0, 0);
-
-		dst.brighten_rect
-			(Rect(Point(x, 0), kTabPanelButtonSize, 2), BUTTON_EDGE_BRIGHT_FACTOR);
-		dst.brighten_rect
-			(Rect(Point(x, 2), 2, kTabPanelButtonSize - 4),
+		dst.brighten_rect
+			(Rect(Point(x, 0), tab_width, 2), BUTTON_EDGE_BRIGHT_FACTOR);
+		dst.brighten_rect
+			(Rect(Point(x, 2), 2, kTabPanelButtonHeight - 4),
 			 BUTTON_EDGE_BRIGHT_FACTOR);
 		dst.fill_rect
-			(Rect(Point(x + kTabPanelButtonSize - 2, 2), 1, kTabPanelButtonSize - 4),
+			(Rect(Point(x + tab_width - 2, 2), 1, kTabPanelButtonHeight - 4),
 			 black);
 		dst.fill_rect
-			(Rect(Point(x + kTabPanelButtonSize - 1, 1), 1, kTabPanelButtonSize - 3),
+			(Rect(Point(x + tab_width - 1, 1), 1, kTabPanelButtonHeight - 3),
 			 black);
 
 		// Draw bottom part
-		if (m_active != idx)
+		if (active_ != idx)
 			dst.brighten_rect
-				(Rect(Point(x, kTabPanelButtonSize - 2), kTabPanelButtonSize, 2),
+				(Rect(Point(x, kTabPanelButtonHeight - 2), tab_width, 2),
 				 2 * BUTTON_EDGE_BRIGHT_FACTOR);
 		else {
 			dst.brighten_rect
-				(Rect(Point(x, kTabPanelButtonSize - 2), 2, 2),
+				(Rect(Point(x, kTabPanelButtonHeight - 2), 2, 2),
 				 BUTTON_EDGE_BRIGHT_FACTOR);
 
 			dst.brighten_rect
-				(Rect(Point(x + kTabPanelButtonSize - 2, kTabPanelButtonSize - 2), 2, 2),
+				(Rect(Point(x + tab_width - 2, kTabPanelButtonHeight - 2), 2, 2),
 				 2 * BUTTON_EDGE_BRIGHT_FACTOR);
 			dst.fill_rect
-				(Rect(Point(x + kTabPanelButtonSize - 2, kTabPanelButtonSize - 1), 1, 1),
+				(Rect(Point(x + tab_width - 2, kTabPanelButtonHeight - 1), 1, 1),
 				 black);
 			dst.fill_rect
-				(Rect(Point(x + kTabPanelButtonSize - 2, kTabPanelButtonSize - 2), 2, 1),
+				(Rect(Point(x + tab_width - 2, kTabPanelButtonHeight - 2), 2, 1),
 				 black);
 		}
 	}
 
 	// draw the remaining separator
-	assert(x <= static_cast<uint32_t>(get_w()));
+	assert(x <= get_w());
 	dst.brighten_rect
-		(Rect(Point(x, kTabPanelButtonSize - 2), get_w() - x, 2),
+		(Rect(Point(x + tab_width, kTabPanelButtonHeight - 2), get_w() - x, 2),
 		 2 * BUTTON_EDGE_BRIGHT_FACTOR);
+
+	// Draw border around the main panel
+	if (border_type_ == TabPanel::Type::kBorder) {
+		//  left edge
+		dst.brighten_rect
+			(Rect(Point(0, kTabPanelButtonHeight), 2, get_h() - 2), BUTTON_EDGE_BRIGHT_FACTOR);
+		//  bottom edge
+		dst.fill_rect(Rect(Point(2, get_h() - 2), get_w() - 2, 1), black);
+		dst.fill_rect(Rect(Point(1, get_h() - 1), get_w() - 1, 1), black);
+		//  right edge
+		dst.fill_rect(Rect(Point(get_w() - 2, kTabPanelButtonHeight - 1), 1, get_h() - 2), black);
+		dst.fill_rect(Rect(Point(get_w() - 1, kTabPanelButtonHeight - 2), 1, get_h() - 1), black);
+	}
 }
 
 
@@ -300,11 +387,9 @@
 */
 void TabPanel::handle_mousein(bool inside)
 {
-	if (!inside && m_highlight >= 0) {
-		update
-			(m_highlight * kTabPanelButtonSize, 0, kTabPanelButtonSize, kTabPanelButtonSize);
-
-		m_highlight = -1;
+	if (!inside && highlight_ != kNotFound) {
+		update(tabs_[highlight_]->get_x(), 0, tabs_[highlight_]->get_w(), kTabPanelButtonHeight);
+		highlight_ = kNotFound;
 	}
 }
 
@@ -315,31 +400,17 @@
 bool TabPanel::handle_mousemove
 	(uint8_t, int32_t const x, int32_t const y, int32_t, int32_t)
 {
-	int32_t hl;
-
-	if (y < 0 || y >= kTabPanelButtonSize)
-		hl = -1;
-	else {
-		hl = x / kTabPanelButtonSize;
-
-		if (m_tabs.size() <= static_cast<size_t>(hl))
-			hl = -1;
-	}
-
-	if (hl != m_highlight) {
-		{
-			if (hl >= 0)
-				set_tooltip(m_tabs[hl]->tooltip);
-		}
-		if (m_highlight >= 0)
-			update
-				(m_highlight * kTabPanelButtonSize, 0,
-				 kTabPanelButtonSize, kTabPanelButtonSize);
-		if (hl >= 0)
-			update
-				(hl * kTabPanelButtonSize, 0, kTabPanelButtonSize, kTabPanelButtonSize);
-
-		m_highlight = hl;
+	size_t hl = find_tab(x, y);
+
+	if (hl != highlight_) {
+		if (hl != kNotFound) {
+			update(tabs_[hl]->get_x(), 0, tabs_[hl]->get_w(), kTabPanelButtonHeight);
+		}
+		if (highlight_ != kNotFound) {
+			update(tabs_[highlight_]->get_x(), 0, tabs_[highlight_]->get_w(), kTabPanelButtonHeight);
+		}
+		highlight_ = hl;
+		set_tooltip(highlight_ != kNotFound ? tabs_[highlight_]->tooltip : "");
 	}
 	return true;
 }
@@ -350,25 +421,39 @@
 */
 bool TabPanel::handle_mousepress(const uint8_t btn, int32_t x, int32_t y) {
 	if (btn == SDL_BUTTON_LEFT) {
-		int32_t id;
-
-		if (y >= kTabPanelButtonSize)
-			return false;
-
-		id = x / kTabPanelButtonSize;
-
-		if (static_cast<size_t>(id) < m_tabs.size()) {
+		size_t id = find_tab(x, y);
+		if (id != kNotFound) {
 			activate(id);
-
 			return true;
 		}
 	}
-
 	return false;
 }
+
 bool TabPanel::handle_mouserelease(uint8_t, int32_t, int32_t)
 {
 	return false;
 }
 
+
+/**
+ * Find the tab at the coordinates x, y
+ * Returns kNotFound if no tab was found
+ */
+size_t TabPanel::find_tab(int32_t x, int32_t y) const {
+	if (y < 0 || y >= kTabPanelButtonHeight) {
+		return kNotFound;
+	}
+
+	int32_t width = 0;
+	for (size_t id = 0; id < tabs_.size(); ++id) {
+		width += tabs_[id]->get_w();
+		if (width > x) {
+			return id;
+		}
+	}
+	return kNotFound;
+}
+
+
 }

=== modified file 'src/ui_basic/tabpanel.h'
--- src/ui_basic/tabpanel.h	2014-09-14 11:31:58 +0000
+++ src/ui_basic/tabpanel.h	2015-12-11 20:23:29 +0000
@@ -35,11 +35,16 @@
 struct Tab : public NamedPanel {
 	friend struct TabPanel;
 
+	/** If title is not empty, this will be a textual tab.
+	 *  In that case, pic will need to be the rendered title */
 	Tab
 		(TabPanel * parent,
-		 uint32_t,
+		 size_t id,
+		 int32_t x,
+		 int32_t w,
 		 const std::string & name,
-		 const Image*,
+		 const std::string & title,
+		 const Image* pic,
 		 const std::string & gtooltip,
 		 Panel             * gpanel);
 
@@ -47,12 +52,18 @@
 	void activate();
 
 private:
-	TabPanel * m_parent;
-	uint32_t    m_id;
+	// Leave handling the mouse move to the TabPanel.
+	bool handle_mousemove(uint8_t, int32_t, int32_t, int32_t, int32_t) override {return false;}
+	// Play click
+	bool handle_mousepress(uint8_t, int32_t, int32_t) override;
+
+	TabPanel* parent;
+	uint32_t id;
 
 	const Image* pic;
+	std::string title;
 	std::string tooltip;
-	Panel     * panel;
+	Panel* panel;
 };
 
 /**
@@ -66,15 +77,30 @@
  *
  */
 struct TabPanel : public Panel {
+	enum class Type {
+		kNoBorder,
+		kBorder
+	};
+
 	friend struct Tab;
 
-	TabPanel(Panel * parent, int32_t x, int32_t y, const Image* background);
+	TabPanel(Panel * parent, int32_t x, int32_t y, const Image* background,
+				TabPanel::Type border_type = TabPanel::Type::kNoBorder);
 	// For Fullscreen menus
 	TabPanel
 		(Panel * parent,
 		 int32_t x, int32_t y, int32_t w, int32_t h,
-		 const Image* background);
-
+		 const Image* background,
+		 TabPanel::Type border_type = TabPanel::Type::kNoBorder);
+
+	/** Add textual tab */
+	uint32_t add
+		(const std::string & name,
+		 const std::string & title,
+		 Panel             * panel,
+		 const std::string & tooltip = std::string());
+
+	/** Add pictorial tab */
 	uint32_t add
 		(const std::string & name,
 		 const Image* pic,
@@ -86,13 +112,21 @@
 	const TabList & tabs();
 	void activate(uint32_t idx);
 	void activate(const std::string &);
-	uint32_t active() {return m_active;}
+	uint32_t active() {return active_;}
 
 protected:
 	void layout() override;
 	void update_desired_size() override;
 
 private:
+	// Common adding function for textual and pictorial tabs
+	uint32_t add_tab(int32_t width,
+						  const std::string& name,
+						  const std::string& title,
+						  const Image* pic,
+						  const std::string& tooltip,
+						  Panel* contents);
+
 	// Drawing and event handlers
 	void draw(RenderTarget &) override;
 
@@ -102,12 +136,14 @@
 		(uint8_t state, int32_t x, int32_t y, int32_t xdiff, int32_t ydiff) override;
 	void handle_mousein(bool inside) override;
 
-
-	TabList          m_tabs;
-	uint32_t         m_active;         ///< index of the currently active tab
-	int32_t          m_highlight;      ///< index of the highlighted button
-
-	const Image* m_pic_background; ///< picture used to draw background
+	size_t find_tab(int32_t x, int32_t y) const;
+
+	TabList          tabs_;
+	size_t           active_;         ///< index of the currently active tab
+	size_t           highlight_;      ///< index of the highlighted button
+
+	const Image*     pic_background_; ///< picture used to draw background
+	TabPanel::Type   border_type_;    ///< whether there will be a border around the panels.
 };
 }
 

=== modified file 'src/ui_fsmenu/base.h'
--- src/ui_fsmenu/base.h	2015-08-10 19:55:41 +0000
+++ src/ui_fsmenu/base.h	2015-12-11 20:23:29 +0000
@@ -45,7 +45,7 @@
 		kOk = static_cast<int>(UI::Panel::Returncodes::kOk),
 
 		// Options
-		kRestart,
+		kApplyOptions,
 
 		// Main menu
 		kTutorial,

=== 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:23:29 +0000
@@ -96,183 +96,245 @@
 FullscreenMenuOptions::FullscreenMenuOptions
 		(OptionsCtrl::OptionsStruct opt)
 	:
-	FullscreenMenuBase("optionsmenu.jpg"),
+	FullscreenMenuBase("ui_fsmenu.jpg"),
 
 // Values for alignment and size
-	m_vbutw   (get_h() * 333 / 10000),
-	m_butw    (get_w() / 4),
-	m_buth    (get_h() * 9 / 200),
-	m_hmargin (get_w() * 19 / 200),
-	m_padding (10),
-	m_space   (25),
-	m_offset_first_group (get_h() * 1417 / 10000),
-	m_offset_second_group(get_h() * 5833 / 10000),
-
-// Buttons
-	m_advanced_options
-		(this, "advanced_options",
-		 get_w() * 9 / 80, get_h() * 19 / 20, m_butw, m_buth,
-		 g_gr->images().get("pics/but2.png"),
-		 _("Advanced Options"), std::string(), true, false),
-	m_cancel
-		(this, "cancel",
-		 get_w() * 51 / 80, get_h() * 19 / 20, m_butw, m_buth,
-		 g_gr->images().get("pics/but0.png"),
-		 _("Cancel"), std::string(), true, false),
-	m_apply
-		(this, "apply",
-		 get_w() * 3 / 8, get_h() * 19 / 20, m_butw, m_buth,
-		 g_gr->images().get("pics/but2.png"),
-		 _("Apply"), std::string(), true, false),
+	butw_    (get_w() / 5),
+	buth_    (get_h() * 9 / 200),
+	hmargin_ (get_w() * 19 / 200),
+	padding_ (10),
+	space_   (25),
+	tab_panel_width_(get_inner_w() - 2 * hmargin_),
+	column_width_(tab_panel_width_ - padding_),
+	tab_panel_y_(get_h() * 14 / 100),
 
 	// Title
-		m_title
-			(this,
-			 get_w() / 2, get_h() / 40,
-			 _("General Options"), UI::Align_HCenter),
-
-	// First options block 'general options', first column
-	m_label_resolution
-		(this,
-		 m_hmargin, m_offset_first_group,
-		 _("In-game resolution"), UI::Align_VCenter),
-	m_reslist
-		(this,
-		 m_hmargin, m_label_resolution.get_y() + m_label_resolution.get_h(),
-		 (get_w() - 2 * m_hmargin - m_space) / 2, 95,
-		 UI::Align_Left, true),
-
-	m_fullscreen (this, Point(m_hmargin,
-									  m_reslist.get_y() +
-									  m_reslist.get_h() + m_padding),
-					  _("Fullscreen")),
-	m_inputgrab (this, Point(m_hmargin,
-									 m_fullscreen.get_y() +
-									 m_fullscreen.get_h() + m_padding),
-					 _("Grab Input")),
-	m_sb_maxfps
-		(this,
-		 m_hmargin,
-		 m_inputgrab.get_y() + m_inputgrab.get_h() + m_padding,
-		 m_reslist.get_w(), 105,
-		 opt.maxfps, 0, 99, _("Maximum FPS:"), ""),
-
-
-	// First options block 'general options', second column
-	m_label_language
-		(this,
-		 get_w() - m_hmargin - (get_w() - 2 * m_hmargin - m_space) / 2, m_offset_first_group,
-		 _("Language"), UI::Align_VCenter),
-	// same height as m_reslist
-	m_language_list
-		(this,
-		 m_label_language.get_x(), m_label_language.get_y() + m_label_language.get_h(),
-		 (get_w() - 2 * m_hmargin - m_space) / 2, m_reslist.get_h(),
-		 UI::Align_Left, true),
-
-	m_music (this, Point(m_label_language.get_x(),
-								m_language_list.get_y() +
-								m_language_list.get_h() + m_padding),
-				_("Enable Music")),
-	m_fx (this, Point(m_label_language.get_x(),
-							m_music.get_y() +
-							m_music.get_h() + m_padding),
-			_("Enable Sound Effects")),
-
-	// Second options block 'In-game options'
-	// Title 2
-	m_label_game_options
-		(this,
-		 get_w() / 2, get_h() / 2,
-		 _("In-game Options"), UI::Align_HCenter),
-
-	/** 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. */
-	m_single_watchwin (this, Point(m_hmargin, m_offset_second_group), _("Use single watchwindow mode")),
-
-	m_auto_roadbuild_mode (this, Point(m_single_watchwin.get_x(),
-												  m_single_watchwin.get_y() +
-												  m_single_watchwin.get_h() + m_padding),
-								  _("Start building road after placing a flag")),
-	m_show_workarea_preview
-		(this, Point(m_auto_roadbuild_mode.get_x(),
-						 m_auto_roadbuild_mode.get_y() +
-						 m_auto_roadbuild_mode.get_h() + m_padding),
-		 _("Show buildings area preview")),
-
-	m_snap_win_overlap_only
-		(this, Point(m_show_workarea_preview.get_x(),
-						 m_show_workarea_preview.get_y() +
-						 m_show_workarea_preview.get_h() + m_padding),
-		 _("Snap windows only when overlapping")),
-
-	m_dock_windows_to_edges (this, Point(m_snap_win_overlap_only.get_x(),
-													 m_snap_win_overlap_only.get_y() +
-													 m_snap_win_overlap_only.get_h() + m_padding),
-									 _("Dock windows to edges")),
-	m_sb_autosave
-		(this,
-		 m_hmargin,
-		 m_dock_windows_to_edges.get_y() + m_dock_windows_to_edges.get_h() + m_padding,
-		 get_w() - 2 * m_hmargin,
-		 240,
-		 opt.autosave / 60, 0, 100,
-		 _("Save game automatically every"),
+	title_
+	(this,
+	 get_w() / 2, buth_,
+	 _("Options"), UI::Align_HCenter),
+
+	// Buttons
+	cancel_
+		(this, "cancel",
+		 get_w() * 1 / 4 - butw_ / 2,
+		 get_inner_h() - hmargin_,
+		 butw_, buth_,
+		 g_gr->images().get("pics/but0.png"),
+		 _("Cancel"), std::string(), true, false),
+	apply_
+		(this, "apply",
+		 get_w() * 2 / 4 - butw_ / 2,
+		 get_inner_h() - hmargin_,
+		 butw_, buth_,
+		 g_gr->images().get("pics/but0.png"),
+		 _("Apply"), std::string(), true, false),
+	ok_
+		(this, "ok",
+		 get_w() * 3 / 4 - butw_ / 2,
+		 get_inner_h() - hmargin_,
+		 butw_, buth_,
+		 g_gr->images().get("pics/but2.png"),
+		 _("OK"), std::string(), true, false),
+
+	tabs_(this, hmargin_, 0,
+			tab_panel_width_, get_inner_h() - tab_panel_y_ - buth_ - hmargin_,
+			g_gr->images().get("pics/but1.png"),
+			UI::TabPanel::Type::kBorder),
+
+	box_interface_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
+	box_windows_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
+	box_sound_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
+	box_saving_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
+	box_game_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
+	box_language_(&tabs_, 0, 0, UI::Box::Vertical, 0, 0, padding_),
+
+	// Interface options
+	label_resolution_(&box_interface_, _("In-game resolution"), UI::Align_Left),
+	resolution_list_(&box_interface_, 0, 0, column_width_ / 2, 80, UI::Align_Left, true),
+
+	fullscreen_ (&box_interface_, Point(0, 0), _("Fullscreen"), "", column_width_),
+	inputgrab_ (&box_interface_, Point(0, 0), _("Grab Input"), "", column_width_),
+
+	sb_maxfps_(&box_interface_, 0, 0, column_width_ / 2, column_width_ / 4,
+				  opt.maxfps, 0, 99,
+				  _("Maximum FPS:"), ""),
+
+
+	// Windows options
+	snap_win_overlap_only_(&box_windows_, Point(0, 0), _("Snap windows only when overlapping"),
+								  "", column_width_),
+	dock_windows_to_edges_(&box_windows_, Point(0, 0), _("Dock windows to edges"),
+								  "", column_width_),
+
+	sb_dis_panel_
+			(&box_windows_, 0, 0, column_width_, 200,
+			 opt.panel_snap_distance, 0, 99, _("Distance for windows to snap to other panels:"),
+			 /** TRANSLATORS: Options: Distance for windows to snap to  other panels: */
+			 /** TRANSLATORS: This will have a number added in front of it */
+			 ngettext("pixel", "pixels", opt.panel_snap_distance)),
+
+	sb_dis_border_
+			(&box_windows_, 0, 0, column_width_, 200,
+			 opt.border_snap_distance, 0, 99,
+			 _("Distance for windows to snap to borders:"),
+			 /** TRANSLATORS: Options: Distance for windows to snap to borders: */
+			 /** TRANSLATORS: This will have a number added in front of it */
+			 ngettext("pixel", "pixels", opt.border_snap_distance)),
+
+	// Sound options
+	music_ (&box_sound_, Point(0, 0), _("Enable Music"), "", column_width_),
+	fx_ (&box_sound_, Point(0, 0), _("Enable Sound Effects"), "", column_width_),
+	message_sound_(&box_sound_, Point(0, 0), _("Play a sound at message arrival"),
+						"", column_width_),
+
+	// Saving options
+	sb_autosave_
+		(&box_saving_, 0, 0, column_width_, 240,
+		 opt.autosave / 60, 0, 100, _("Save game automatically every"),
 		 /** 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),
 
-	m_sb_remove_replays
-		(this,
-		 m_hmargin,
-		 m_sb_autosave.get_y() + m_sb_autosave.get_h() + m_padding,
-		 get_w() - 2 * m_hmargin,
-		 240,
-		 opt.remove_replays, 0, 365,
-		 _("Remove replays older than:"),
+	sb_remove_replays_
+		(&box_saving_, 0, 0, column_width_, 240,
+		 opt.remove_replays, 0, 365, _("Remove replays older than:"),
 		 /** 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),
-	os(opt)
+
+	nozip_(&box_saving_, Point(0, 0), _("Do not zip widelands data files (maps, replays and savegames)"),
+			 "", column_width_),
+	remove_syncstreams_(&box_saving_, Point(0, 0), _("Remove Syncstream dumps on startup"),
+							  "", column_width_),
+
+	// Game options
+	auto_roadbuild_mode_(&box_game_, Point(0, 0), _("Start building road after placing a flag")),
+	show_workarea_preview_(&box_game_, Point(0, 0), _("Show buildings area preview")),
+	transparent_chat_(&box_game_, Point(0, 0), _("Show in-game chat with transparent background"),
+							"", column_width_),
+
+	/** 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. */
+	single_watchwin_(&box_game_, Point(0, 0), _("Use single watchwindow mode")),
+
+	// Language options
+	label_language_(&box_language_, _("Language"), UI::Align_Left),
+	language_list_(&box_language_, 0, 0, column_width_ / 2,
+						get_inner_h() - tab_panel_y_ - buth_ - hmargin_ - 5 * padding_,
+						UI::Align_Left, true),
+
+	os_(opt)
 {
-	m_advanced_options.sigclicked.connect
-		(boost::bind(&FullscreenMenuOptions::advanced_options, boost::ref(*this)));
-	m_cancel.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this));
-	m_apply.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_ok, this));
+	// Set up UI Elements
+	title_           .set_textstyle(UI::TextStyle::ui_big());
+
+	tabs_.add("options_interface", _("Interface"), &box_interface_, "");
+	tabs_.add("options_windows", _("Windows"), &box_windows_, "");
+	tabs_.add("options_sound", _("Sound"), &box_sound_, "");
+	tabs_.add("options_saving", _("Saving"), &box_saving_, "");
+	tabs_.add("options_game", _("Game"), &box_game_, "");
+	tabs_.add("options_language", _("Language"), &box_language_, "");
+
+	// We want the last active tab when "Apply" was clicked.
+	if (os_.active_tab < tabs_.tabs().size()) {
+		tabs_.activate(os_.active_tab);
+	}
+
+	tabs_.set_pos(Point(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());
+	box_saving_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
+	box_game_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
+	box_language_.set_size(tabs_.get_inner_w(), tabs_.get_inner_h());
+
+	// Interface
+	box_interface_.add(&label_resolution_, UI::Align_Left);
+	box_interface_.add(&resolution_list_, UI::Align_Left);
+	box_interface_.add(&fullscreen_, UI::Align_Left);
+	box_interface_.add(&inputgrab_, UI::Align_Left);
+	box_interface_.add(&sb_maxfps_, UI::Align_Left);
+
+	// Windows
+	box_windows_.add(&snap_win_overlap_only_, UI::Align_Left);
+	box_windows_.add(&dock_windows_to_edges_, UI::Align_Left);
+	box_windows_.add(&sb_dis_panel_, UI::Align_Left);
+	box_windows_.add(&sb_dis_border_, UI::Align_Left);
+
+	// Sound
+	box_sound_.add(&music_, UI::Align_Left);
+	box_sound_.add(&fx_, UI::Align_Left);
+	box_sound_.add(&message_sound_, UI::Align_Left);
+
+	// Saving
+	box_saving_.add(&sb_autosave_, UI::Align_Left);
+	box_saving_.add(&sb_remove_replays_, UI::Align_Left);
+	box_saving_.add(&nozip_, UI::Align_Left);
+	box_saving_.add(&remove_syncstreams_, UI::Align_Left);
+
+	// Game
+	box_game_.add(&auto_roadbuild_mode_, UI::Align_Left);
+	box_game_.add(&show_workarea_preview_, UI::Align_Left);
+	box_game_.add(&transparent_chat_, UI::Align_Left);
+	box_game_.add(&single_watchwin_, UI::Align_Left);
+
+	// Language
+	box_language_.add(&label_language_, UI::Align_Left);
+	box_language_.add(&language_list_, UI::Align_Left);
+
+
+	// Bind actions
+	cancel_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_back, this));
+	apply_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_apply, this));
+	ok_.sigclicked.connect(boost::bind(&FullscreenMenuOptions::clicked_ok, this));
 
 	/** TRANSLATORS Options: Save game automatically every: */
-	m_sb_autosave     .add_replacement(0, _("Off"));
-	for (UI::Button* temp_button : m_sb_autosave.get_buttons()) {
+	sb_autosave_     .add_replacement(0, _("Off"));
+	for (UI::Button* temp_button : sb_autosave_.get_buttons()) {
 		temp_button->sigclicked.connect
 				(boost::bind
 					(&FullscreenMenuOptions::update_sb_autosave_unit,
 					 boost::ref(*this)));
 	}
 	/** TRANSLATORS Options: Remove Replays older than: */
-	m_sb_remove_replays.add_replacement(0, _("Never"));
-	for (UI::Button* temp_button : m_sb_remove_replays.get_buttons()) {
+	sb_remove_replays_.add_replacement(0, _("Never"));
+	for (UI::Button* temp_button : sb_remove_replays_.get_buttons()) {
 		temp_button->sigclicked.connect
 				(boost::bind
 					(&FullscreenMenuOptions::update_sb_remove_replays_unit,
 					 boost::ref(*this)));
 	}
-
-	m_title           .set_textstyle(UI::TextStyle::ui_big());
-	m_fullscreen      .set_state(opt.fullscreen);
-	m_inputgrab       .set_state(opt.inputgrab);
-	m_music           .set_state(opt.music);
-	m_music           .set_enabled(!g_sound_handler.lock_audio_disabling_);
-	m_fx              .set_state(opt.fx);
-	m_fx              .set_enabled(!g_sound_handler.lock_audio_disabling_);
-
-	m_label_game_options             .set_textstyle(UI::TextStyle::ui_big());
-	m_single_watchwin                .set_state(opt.single_watchwin);
-	m_auto_roadbuild_mode            .set_state(opt.auto_roadbuild_mode);
-	m_show_workarea_preview          .set_state(opt.show_warea);
-	m_snap_win_overlap_only          .set_state(opt.snap_win_overlap_only);
-	m_dock_windows_to_edges          .set_state(opt.dock_windows_to_edges);
+	for (UI::Button* temp_button : sb_dis_panel_.get_buttons()) {
+		temp_button->sigclicked.connect
+				(boost::bind
+					(&FullscreenMenuOptions::update_sb_dis_panel_unit,
+					 boost::ref(*this)));
+	}
+
+	for (UI::Button* temp_button : sb_dis_border_.get_buttons()) {
+		temp_button->sigclicked.connect
+				(boost::bind
+					(&FullscreenMenuOptions::update_sb_dis_border_unit,
+					 boost::ref(*this)));
+	}
+
+	// Fill in data
+	fullscreen_      .set_state(opt.fullscreen);
+	inputgrab_       .set_state(opt.inputgrab);
+	music_           .set_state(opt.music);
+	music_           .set_enabled(!g_sound_handler.lock_audio_disabling_);
+	fx_              .set_state(opt.fx);
+	fx_              .set_enabled(!g_sound_handler.lock_audio_disabling_);
+
+	single_watchwin_                .set_state(opt.single_watchwin);
+	auto_roadbuild_mode_            .set_state(opt.auto_roadbuild_mode);
+	show_workarea_preview_          .set_state(opt.show_warea);
+	snap_win_overlap_only_          .set_state(opt.snap_win_overlap_only);
+	dock_windows_to_edges_          .set_state(opt.dock_windows_to_edges);
 
 	for (int modes = 0; modes < SDL_GetNumDisplayModes(0); ++modes) {
 		SDL_DisplayMode  mode;
@@ -283,62 +345,67 @@
 			ScreenResolution this_res = {
 			   mode.w, mode.h, static_cast<int32_t>(SDL_BITSPERPIXEL(mode.format))};
 			if (this_res.depth == 24) this_res.depth = 32;
-			if (m_resolutions.empty()
-				 || this_res.xres != m_resolutions.rbegin()->xres
-				 || this_res.yres != m_resolutions.rbegin()->yres) {
-				m_resolutions.push_back(this_res);
+			if (resolutions_.empty()
+				 || this_res.xres != resolutions_.rbegin()->xres
+				 || this_res.yres != resolutions_.rbegin()->yres) {
+				resolutions_.push_back(this_res);
 			}
 		}
 	}
 
 	bool did_select_a_res = false;
-	for (uint32_t i = 0; i < m_resolutions.size(); ++i) {
+	for (uint32_t i = 0; i < resolutions_.size(); ++i) {
 		const bool selected =
-			m_resolutions[i].xres  == opt.xres &&
-			m_resolutions[i].yres  == opt.yres;
+			resolutions_[i].xres  == opt.xres &&
+			resolutions_[i].yres  == opt.yres;
 		did_select_a_res |= selected;
 		/** TRANSLATORS: Screen resolution, e.g. 800 x 600*/
-		m_reslist.add((boost::format(_("%1% x %2%"))
-							% m_resolutions[i].xres
-							% m_resolutions[i].yres).str(),
+		resolution_list_.add((boost::format(_("%1% x %2%"))
+							% resolutions_[i].xres
+							% resolutions_[i].yres).str(),
 						  nullptr, nullptr, selected);
 	}
 	if (!did_select_a_res) {
-		m_reslist.add((boost::format(_("%1% x %2%"))
+		resolution_list_.add((boost::format(_("%1% x %2%"))
 							% opt.xres
 							% opt.yres).str(),
 						  nullptr, nullptr, true);
-		uint32_t entry = m_resolutions.size();
-		m_resolutions.resize(entry + 1);
-		m_resolutions[entry].xres  = opt.xres;
-		m_resolutions[entry].yres  = opt.yres;
+		uint32_t entry = resolutions_.size();
+		resolutions_.resize(entry + 1);
+		resolutions_[entry].xres  = opt.xres;
+		resolutions_[entry].yres  = opt.yres;
 	}
 
 	add_languages_to_list(opt.language);
-	m_language_list.focus();
+	language_list_.focus();
+
+	message_sound_        .set_state(opt.message_sound);
+	nozip_                .set_state(opt.nozip);
+	remove_syncstreams_   .set_state(opt.remove_syncstreams);
+	transparent_chat_     .set_state(opt.transparent_chat);
 }
 
 void FullscreenMenuOptions::update_sb_autosave_unit() {
-	m_sb_autosave.set_unit(ngettext("minute", "minutes", m_sb_autosave.get_value()));
+	sb_autosave_.set_unit(ngettext("minute", "minutes", sb_autosave_.get_value()));
 }
 
 void FullscreenMenuOptions::update_sb_remove_replays_unit() {
-	m_sb_remove_replays.set_unit(ngettext("day", "days", m_sb_remove_replays.get_value()));
-}
-
-void FullscreenMenuOptions::advanced_options() {
-	FullscreenMenuAdvancedOptions aom(os);
-	if (aom.run<FullscreenMenuBase::MenuTarget>() == FullscreenMenuBase::MenuTarget::kOk) {
-		os = aom.get_values();
-		end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kRestart);
-	}
+	sb_remove_replays_.set_unit(ngettext("day", "days", sb_remove_replays_.get_value()));
+}
+
+void FullscreenMenuOptions::update_sb_dis_panel_unit() {
+	sb_dis_panel_.set_unit(ngettext("pixel", "pixels", sb_dis_panel_.get_value()));
+}
+
+void FullscreenMenuOptions::update_sb_dis_border_unit() {
+	sb_dis_border_.set_unit(ngettext("pixel", "pixels", sb_dis_border_.get_value()));
 }
 
 void FullscreenMenuOptions::add_languages_to_list(const std::string& current_locale) {
 
 	// We want these two entries on top - the most likely user's choice and the default.
-	m_language_list.add(_("Try system language"), "", nullptr, current_locale == "");
-	m_language_list.add("English", "en", nullptr, current_locale == "en");
+	language_list_.add(_("Try system language"), "", nullptr, current_locale == "");
+	language_list_.add("English", "en", nullptr, current_locale == "en");
 
 	// We start with the locale directory so we can pick up locales
 	// that don't have a configuration file yet.
@@ -391,241 +458,138 @@
 
 	std::sort(entries.begin(), entries.end());
 	for (const LanguageEntry& entry : entries) {
-		m_language_list.add(entry.descname.c_str(), entry.localename, nullptr,
+		language_list_.add(entry.descname.c_str(), entry.localename, nullptr,
 									entry.localename == selected_locale, "", entry.fontname);
 	}
 }
 
 
+void FullscreenMenuOptions::clicked_apply() {
+	end_modal<FullscreenMenuBase::MenuTarget>(FullscreenMenuBase::MenuTarget::kApplyOptions);
+}
+
+
 OptionsCtrl::OptionsStruct FullscreenMenuOptions::get_values() {
-	const uint32_t res_index = m_reslist.selection_index();
+	const uint32_t res_index = resolution_list_.selection_index();
 
 	// Write all data from UI elements
-	os.xres                  = m_resolutions[res_index].xres;
-	os.yres                  = m_resolutions[res_index].yres;
-	os.inputgrab             = m_inputgrab.get_state();
-	os.fullscreen            = m_fullscreen.get_state();
-	os.single_watchwin       = m_single_watchwin.get_state();
-	os.auto_roadbuild_mode   = m_auto_roadbuild_mode.get_state();
-	os.show_warea            = m_show_workarea_preview.get_state();
-	os.snap_win_overlap_only = m_snap_win_overlap_only.get_state();
-	os.dock_windows_to_edges = m_dock_windows_to_edges.get_state();
-	os.music                 = m_music.get_state();
-	os.fx                    = m_fx.get_state();
-	if (m_language_list.has_selection())
-		os.language           = m_language_list.get_selected();
-	os.autosave              = m_sb_autosave.get_value();
-	os.maxfps                = m_sb_maxfps.get_value();
-	os.remove_replays        = m_sb_remove_replays.get_value();
-
-	return os;
-}
-
-
-/**
- * The advanced option menu
- */
-FullscreenMenuAdvancedOptions::FullscreenMenuAdvancedOptions
-	(OptionsCtrl::OptionsStruct const opt)
-	:
-	FullscreenMenuBase("ui_fsmenu.jpg"),
-
-// Values for alignment and size
-	m_vbutw   (get_h() * 333 / 10000),
-	m_butw    (get_w() / 4),
-	m_buth    (get_h() * 9 / 200),
-	m_hmargin (get_w() * 19 / 200),
-	m_padding (10),
-	m_space   (25),
-
-// Buttons
-	m_cancel
-		(this, "cancel",
-		 get_w() * 41 / 80, get_h() * 19 / 20, m_butw, m_buth,
-		 g_gr->images().get("pics/but0.png"),
-		 _("Cancel"), std::string(), true, false),
-	m_apply
-		(this, "apply",
-		 get_w() / 4,   get_h() * 19 / 20, m_butw, m_buth,
-		 g_gr->images().get("pics/but2.png"),
-		 _("Apply"), std::string(), true, false),
-
-// Title
-	m_title
-		(this,
-		 get_w() / 2, get_h() * 17 / 150,
-		 _("Advanced Options"), UI::Align_HCenter),
-
-	// Spinboxes
-	m_sb_dis_panel
-			(this,
-			 m_hmargin, get_h() * 9 / 30,
-			 get_w() - 2 * m_hmargin, get_w() / 5,
-			 opt.panel_snap_distance, 0, 99,
-			 _("Distance for windows to snap to other panels:"),
-			 ngettext("pixel", "pixels", opt.panel_snap_distance)),
-
-	m_sb_dis_border
-			(this,
-			 m_hmargin, m_sb_dis_panel.get_y() + m_sb_dis_panel.get_h() + 2 * m_padding,
-			 get_w() - 2 * m_hmargin, get_w() / 5,
-			 opt.border_snap_distance, 0, 99,
-			 _("Distance for windows to snap to borders:"),
-			 ngettext("pixel", "pixels", opt.border_snap_distance)),
-
-	m_transparent_chat (this, Point(m_hmargin,
-											  m_sb_dis_border.get_y() +
-											  m_sb_dis_border.get_h() + m_space),
-							  _("Show in-game chat with transparent background"),
-							  "", get_w() - 2 * m_hmargin),
-
-	m_message_sound
-		(this, Point(m_hmargin,
-						 m_transparent_chat.get_y() +
-						 m_transparent_chat.get_h() + m_padding),
-		 _("Play a sound at message arrival"),
-		 "", get_w() - 2 * m_hmargin),
-
-	m_nozip (this, Point(m_hmargin,
-								m_message_sound.get_y() +
-								m_message_sound.get_h() + m_padding),
-				_("Do not zip widelands data files (maps, replays and savegames)"),
-				"", get_w() - 2 * m_hmargin),
-
-	m_remove_syncstreams (this, Point(m_hmargin,
-												 m_nozip.get_y() +
-												 m_nozip.get_h() + m_padding),
-								 _("Remove Syncstream dumps on startup"),
-								 "", get_w() - 2 * m_hmargin),
-
-	os(opt)
-{
-	for (UI::Button* temp_button : m_sb_dis_panel.get_buttons()) {
-		temp_button->sigclicked.connect
-				(boost::bind
-					(&FullscreenMenuAdvancedOptions::update_sb_dis_panel_unit,
-					 boost::ref(*this)));
-	}
-
-	for (UI::Button* temp_button : m_sb_dis_border.get_buttons()) {
-		temp_button->sigclicked.connect
-				(boost::bind
-					(&FullscreenMenuAdvancedOptions::update_sb_dis_border_unit,
-					 boost::ref(*this)));
-	}
-
-	m_cancel.sigclicked.connect(boost::bind(&FullscreenMenuAdvancedOptions::clicked_back, boost::ref(*this)));
-	m_apply.sigclicked.connect(boost::bind(&FullscreenMenuAdvancedOptions::clicked_ok, boost::ref(*this)));
-
-	m_title                .set_textstyle(UI::TextStyle::ui_big());
-	m_message_sound        .set_state(opt.message_sound);
-	m_nozip                .set_state(opt.nozip);
-	m_remove_syncstreams   .set_state(opt.remove_syncstreams);
-	m_transparent_chat     .set_state(opt.transparent_chat);
-}
-
-void FullscreenMenuAdvancedOptions::update_sb_dis_panel_unit() {
-	m_sb_dis_panel.set_unit(ngettext("pixel", "pixels", m_sb_dis_panel.get_value()));
-}
-
-void FullscreenMenuAdvancedOptions::update_sb_dis_border_unit() {
-	m_sb_dis_border.set_unit(ngettext("pixel", "pixels", m_sb_dis_border.get_value()));
-}
-
-OptionsCtrl::OptionsStruct FullscreenMenuAdvancedOptions::get_values() {
-	// Write all remaining data from UI elements
-	os.message_sound        = m_message_sound.get_state();
-	os.nozip                = m_nozip.get_state();
-	os.panel_snap_distance  = m_sb_dis_panel.get_value();
-	os.border_snap_distance = m_sb_dis_border.get_value();
-	os.remove_syncstreams   = m_remove_syncstreams.get_state();
-	os.transparent_chat     = m_transparent_chat.get_state();
-	return os;
-}
-
+	os_.xres                  = resolutions_[res_index].xres;
+	os_.yres                  = resolutions_[res_index].yres;
+	os_.inputgrab             = inputgrab_.get_state();
+	os_.fullscreen            = fullscreen_.get_state();
+	os_.single_watchwin       = single_watchwin_.get_state();
+	os_.auto_roadbuild_mode   = auto_roadbuild_mode_.get_state();
+	os_.show_warea            = show_workarea_preview_.get_state();
+	os_.snap_win_overlap_only = snap_win_overlap_only_.get_state();
+	os_.dock_windows_to_edges = dock_windows_to_edges_.get_state();
+	os_.music                 = music_.get_state();
+	os_.fx                    = fx_.get_state();
+	if (language_list_.has_selection())
+		os_.language           = language_list_.get_selected();
+	os_.autosave              = sb_autosave_.get_value();
+	os_.maxfps                = sb_maxfps_.get_value();
+	os_.remove_replays        = sb_remove_replays_.get_value();
+		os_.message_sound        = message_sound_.get_state();
+	os_.nozip                = nozip_.get_state();
+	os_.panel_snap_distance  = sb_dis_panel_.get_value();
+	os_.border_snap_distance = sb_dis_border_.get_value();
+	os_.remove_syncstreams   = remove_syncstreams_.get_state();
+	os_.transparent_chat     = transparent_chat_.get_state();
+
+	// Last tab for reloading the options menu
+	os_.active_tab = tabs_.active();
+
+	return os_;
+}
 
 /**
  * Handles communication between window class and options
  */
 OptionsCtrl::OptionsCtrl(Section & s)
-: m_opt_section(s), m_opt_dialog(new FullscreenMenuOptions(options_struct()))
+: opt_section_(s), opt_dialog_(new FullscreenMenuOptions(options_struct(0)))
 {
 	handle_menu();
 }
 
 OptionsCtrl::~OptionsCtrl() {
-	delete m_opt_dialog;
+	delete opt_dialog_; // NOCOM unique_ptr
 }
 
 void OptionsCtrl::handle_menu()
 {
-	FullscreenMenuBase::MenuTarget i = m_opt_dialog->run<FullscreenMenuBase::MenuTarget>();
+	FullscreenMenuBase::MenuTarget i = opt_dialog_->run<FullscreenMenuBase::MenuTarget>();
 	if (i != FullscreenMenuBase::MenuTarget::kBack)
 		save_options();
-	if (i == FullscreenMenuBase::MenuTarget::kRestart) {
-		delete m_opt_dialog;
-		m_opt_dialog = new FullscreenMenuOptions(options_struct());
+	if (i == FullscreenMenuBase::MenuTarget::kApplyOptions) {
+		uint32_t active_tab = opt_dialog_->get_values().active_tab;
+		g_gr->change_resolution(opt_dialog_->get_values().xres, opt_dialog_->get_values().yres);
+		g_gr->set_fullscreen(opt_dialog_->get_values().fullscreen);
+
+		delete opt_dialog_;
+		opt_dialog_ = new FullscreenMenuOptions(options_struct(active_tab));
 		handle_menu(); // Restart general options menu
 	}
 }
 
-OptionsCtrl::OptionsStruct OptionsCtrl::options_struct() {
+OptionsCtrl::OptionsStruct OptionsCtrl::options_struct(uint32_t active_tab) {
 	OptionsStruct opt;
-	opt.xres = m_opt_section.get_int("xres", DEFAULT_RESOLUTION_W);
-	opt.yres = m_opt_section.get_int("yres", DEFAULT_RESOLUTION_H);
-	opt.inputgrab = m_opt_section.get_bool("inputgrab", false);
-	opt.fullscreen = m_opt_section.get_bool("fullscreen", false);
-	opt.single_watchwin = m_opt_section.get_bool("single_watchwin", false);
-	opt.auto_roadbuild_mode = m_opt_section.get_bool("auto_roadbuild_mode", true);
-	opt.show_warea = m_opt_section.get_bool("workareapreview", true);
+	opt.xres = opt_section_.get_int("xres", DEFAULT_RESOLUTION_W);
+	opt.yres = opt_section_.get_int("yres", DEFAULT_RESOLUTION_H);
+	opt.inputgrab = opt_section_.get_bool("inputgrab", false);
+	opt.fullscreen = opt_section_.get_bool("fullscreen", false);
+	opt.single_watchwin = opt_section_.get_bool("single_watchwin", false);
+	opt.auto_roadbuild_mode = opt_section_.get_bool("auto_roadbuild_mode", true);
+	opt.show_warea = opt_section_.get_bool("workareapreview", true);
 	opt.snap_win_overlap_only =
-	   m_opt_section.get_bool("snap_windows_only_when_overlapping", false);
-	opt.dock_windows_to_edges = m_opt_section.get_bool("dock_windows_to_edges", false);
-	opt.language = m_opt_section.get_string("language", "");
-	opt.music = !m_opt_section.get_bool("disable_music", false);
-	opt.fx = !m_opt_section.get_bool("disable_fx", false);
-	opt.autosave = m_opt_section.get_int("autosave", DEFAULT_AUTOSAVE_INTERVAL * 60);
-	opt.rolling_autosave = m_opt_section.get_int("rolling_autosave", 5);
-	opt.maxfps = m_opt_section.get_int("maxfps", 25);
-
-	opt.message_sound = m_opt_section.get_bool("sound_at_message", true);
-	opt.nozip = m_opt_section.get_bool("nozip", false);
-	opt.border_snap_distance = m_opt_section.get_int("border_snap_distance", 0);
-	opt.panel_snap_distance = m_opt_section.get_int("panel_snap_distance", 0);
-	opt.remove_replays = m_opt_section.get_int("remove_replays", 0);
-	opt.remove_syncstreams = m_opt_section.get_bool("remove_syncstreams", true);
-	opt.transparent_chat = m_opt_section.get_bool("transparent_chat", true);
+		opt_section_.get_bool("snap_windows_only_when_overlapping", false);
+	opt.dock_windows_to_edges = opt_section_.get_bool("dock_windows_to_edges", false);
+	opt.language = opt_section_.get_string("language", "");
+	opt.music = !opt_section_.get_bool("disable_music", false);
+	opt.fx = !opt_section_.get_bool("disable_fx", false);
+	opt.autosave = opt_section_.get_int("autosave", DEFAULT_AUTOSAVE_INTERVAL * 60);
+	opt.rolling_autosave = opt_section_.get_int("rolling_autosave", 5);
+	opt.maxfps = opt_section_.get_int("maxfps", 25);
+
+	opt.message_sound = opt_section_.get_bool("sound_at_message", true);
+	opt.nozip = opt_section_.get_bool("nozip", false);
+	opt.border_snap_distance = opt_section_.get_int("border_snap_distance", 0);
+	opt.panel_snap_distance = opt_section_.get_int("panel_snap_distance", 0);
+	opt.remove_replays = opt_section_.get_int("remove_replays", 0);
+	opt.remove_syncstreams = opt_section_.get_bool("remove_syncstreams", true);
+	opt.transparent_chat = opt_section_.get_bool("transparent_chat", true);
+
+	opt.active_tab = active_tab;
 	return opt;
 }
 
 void OptionsCtrl::save_options() {
-	OptionsCtrl::OptionsStruct opt = m_opt_dialog->get_values();
-	m_opt_section.set_int ("xres",                  opt.xres);
-	m_opt_section.set_int ("yres",                  opt.yres);
-	m_opt_section.set_bool("fullscreen",            opt.fullscreen);
-	m_opt_section.set_bool("inputgrab",             opt.inputgrab);
-	m_opt_section.set_bool("single_watchwin",       opt.single_watchwin);
-	m_opt_section.set_bool("auto_roadbuild_mode",   opt.auto_roadbuild_mode);
-	m_opt_section.set_bool("workareapreview",       opt.show_warea);
-	m_opt_section.set_bool
+	OptionsCtrl::OptionsStruct opt = opt_dialog_->get_values();
+	opt_section_.set_int ("xres",                  opt.xres);
+	opt_section_.set_int ("yres",                  opt.yres);
+	opt_section_.set_bool("fullscreen",            opt.fullscreen);
+	opt_section_.set_bool("inputgrab",             opt.inputgrab);
+	opt_section_.set_bool("single_watchwin",       opt.single_watchwin);
+	opt_section_.set_bool("auto_roadbuild_mode",   opt.auto_roadbuild_mode);
+	opt_section_.set_bool("workareapreview",       opt.show_warea);
+	opt_section_.set_bool
 		("snap_windows_only_when_overlapping",
 		 opt.snap_win_overlap_only);
-	m_opt_section.set_bool("dock_windows_to_edges", opt.dock_windows_to_edges);
-	m_opt_section.set_bool("disable_music",        !opt.music);
-	m_opt_section.set_bool("disable_fx",           !opt.fx);
-	m_opt_section.set_string("language",            opt.language);
-	m_opt_section.set_int("autosave",               opt.autosave * 60);
-	m_opt_section.set_int("rolling_autosave",       opt.rolling_autosave);
-	m_opt_section.set_int("maxfps",                 opt.maxfps);
-
-	m_opt_section.set_bool("sound_at_message",      opt.message_sound);
-	m_opt_section.set_bool("nozip",                 opt.nozip);
-	m_opt_section.set_int("border_snap_distance",   opt.border_snap_distance);
-	m_opt_section.set_int("panel_snap_distance",    opt.panel_snap_distance);
-
-	m_opt_section.set_int("remove_replays",         opt.remove_replays);
-	m_opt_section.set_bool("remove_syncstreams",    opt.remove_syncstreams);
-	m_opt_section.set_bool("transparent_chat",      opt.transparent_chat);
+	opt_section_.set_bool("dock_windows_to_edges", opt.dock_windows_to_edges);
+	opt_section_.set_bool("disable_music",        !opt.music);
+	opt_section_.set_bool("disable_fx",           !opt.fx);
+	opt_section_.set_string("language",            opt.language);
+	opt_section_.set_int("autosave",               opt.autosave * 60);
+	opt_section_.set_int("rolling_autosave",       opt.rolling_autosave);
+	opt_section_.set_int("maxfps",                 opt.maxfps);
+
+	opt_section_.set_bool("sound_at_message",      opt.message_sound);
+	opt_section_.set_bool("nozip",                 opt.nozip);
+	opt_section_.set_int("border_snap_distance",   opt.border_snap_distance);
+	opt_section_.set_int("panel_snap_distance",    opt.panel_snap_distance);
+
+	opt_section_.set_int("remove_replays",         opt.remove_replays);
+	opt_section_.set_bool("remove_syncstreams",    opt.remove_syncstreams);
+	opt_section_.set_bool("transparent_chat",      opt.transparent_chat);
 
 	WLApplication::get()->set_input_grab(opt.inputgrab);
 	i18n::set_locale(opt.language);

=== modified file 'src/ui_fsmenu/options.h'
--- src/ui_fsmenu/options.h	2015-12-01 16:58:54 +0000
+++ src/ui_fsmenu/options.h	2015-12-11 20:23:29 +0000
@@ -31,6 +31,7 @@
 #include "ui_basic/multilinetextarea.h"
 #include "ui_basic/spinbox.h"
 #include "ui_basic/textarea.h"
+#include "ui_basic/tabpanel.h"
 
 class FullscreenMenuOptions;
 class Section;
@@ -39,6 +40,7 @@
 class OptionsCtrl {
 public:
 	struct OptionsStruct {
+		// NOCOM reorder these once we have decided on where everything goes.
 		int32_t xres;
 		int32_t yres;
 		bool inputgrab;
@@ -64,16 +66,19 @@
 		std::string ui_font;
 		int32_t border_snap_distance;
 		int32_t panel_snap_distance;
+
+		// Last tab for reloading the options menu
+		uint32_t active_tab;
 	};
 
 	OptionsCtrl(Section &);
 	~OptionsCtrl();
 	void handle_menu();
-	OptionsCtrl::OptionsStruct options_struct();
+	OptionsCtrl::OptionsStruct options_struct(uint32_t active_tab);
 	void save_options();
 private:
-	Section & m_opt_section;
-	FullscreenMenuOptions * m_opt_dialog;
+	Section & opt_section_;
+	FullscreenMenuOptions * opt_dialog_;
 };
 
 /**
@@ -86,47 +91,74 @@
 	OptionsCtrl::OptionsStruct get_values();
 
 private:
-	uint32_t const              m_vbutw;
-	uint32_t const              m_butw;
-	uint32_t const              m_buth;
-	uint32_t const              m_hmargin;
-	uint32_t const              m_padding;
-	uint32_t const              m_space;
-	uint32_t const              m_offset_first_group;
-	uint32_t const              m_offset_second_group;
-
-	UI::Button                  m_advanced_options, m_cancel, m_apply;
-
-	UI::Textarea                m_title;
-	UI::Textarea                m_label_resolution;
-	UI::Listselect<void *>      m_reslist;
-	UI::Checkbox                m_fullscreen;
-	UI::Checkbox                m_inputgrab;
-	UI::SpinBox                 m_sb_maxfps;
-
-	UI::Textarea                m_label_language;
-	UI::Listselect<std::string> m_language_list;
-	UI::Checkbox                m_music;
-	UI::Checkbox                m_fx;
-
-	UI::Textarea                m_label_game_options;
-	UI::Checkbox                m_single_watchwin;
-	UI::Checkbox                m_auto_roadbuild_mode;
-	UI::Checkbox                m_show_workarea_preview;
-	UI::Checkbox                m_snap_win_overlap_only;
-	UI::Checkbox                m_dock_windows_to_edges;
-	UI::SpinBox                 m_sb_autosave;
-	UI::SpinBox                 m_sb_remove_replays;
-
-	OptionsCtrl::OptionsStruct  os;
-
 	void update_sb_autosave_unit();
 	void update_sb_remove_replays_unit();
-	void advanced_options();
+	void update_sb_dis_panel_unit();
+	void update_sb_dis_border_unit();
 
 	// 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              space_;
+	uint32_t const              tab_panel_width_;
+	uint32_t const              column_width_;
+	uint32_t const              tab_panel_y_;
+
+	UI::Textarea                title_;
+	UI::Button                  cancel_, apply_, ok_;
+
+	// UI elements
+	UI::TabPanel tabs_;
+	UI::Box box_interface_;
+	UI::Box box_windows_;
+	UI::Box box_sound_;
+	UI::Box box_saving_;
+	UI::Box box_game_;
+	UI::Box box_language_;
+
+	// Interface options
+	UI::Textarea                label_resolution_;
+	UI::Listselect<void *>      resolution_list_;
+	UI::Checkbox                fullscreen_;
+	UI::Checkbox                inputgrab_;
+	UI::SpinBox                 sb_maxfps_;
+
+	// Windows options
+	UI::Checkbox                snap_win_overlap_only_;
+	UI::Checkbox                dock_windows_to_edges_;
+	UI::SpinBox                 sb_dis_panel_;
+	UI::SpinBox                 sb_dis_border_;
+
+	// Sound options
+	UI::Checkbox                music_;
+	UI::Checkbox                fx_;
+	UI::Checkbox                message_sound_;
+
+	// Saving options
+	UI::SpinBox                 sb_autosave_;
+	UI::SpinBox                 sb_remove_replays_;
+	UI::Checkbox                nozip_;
+	UI::Checkbox                remove_syncstreams_;
+
+	// Game options
+	UI::Checkbox                auto_roadbuild_mode_;
+	UI::Checkbox                show_workarea_preview_;
+	UI::Checkbox                transparent_chat_;
+	UI::Checkbox                single_watchwin_;
+
+	// Language options
+	UI::Textarea                label_language_;
+	UI::Listselect<std::string> language_list_;
+
+	OptionsCtrl::OptionsStruct  os_;
+
 	class ScreenResolution {
 	public:
 		int32_t xres;
@@ -135,39 +167,7 @@
 	};
 
 	/// All supported screen resolutions.
-	std::vector<ScreenResolution> m_resolutions;
-};
-
-/**
- * Fullscreen Optionsmenu. A modal optionsmenu
- */
-
-class FullscreenMenuAdvancedOptions : public FullscreenMenuBase {
-public:
-	FullscreenMenuAdvancedOptions(OptionsCtrl::OptionsStruct opt);
-	OptionsCtrl::OptionsStruct get_values();
-
-private:
-	void update_sb_dis_panel_unit();
-	void update_sb_dis_border_unit();
-
-	uint32_t const              m_vbutw;
-	uint32_t const              m_butw;
-	uint32_t const              m_buth;
-	uint32_t const              m_hmargin;
-	uint32_t const              m_padding;
-	uint32_t const              m_space;
-
-	UI::Button                  m_cancel, m_apply;
-	UI::Textarea                m_title;
-
-	UI::SpinBox                 m_sb_dis_panel, m_sb_dis_border;
-	UI::Checkbox                m_transparent_chat;
-	UI::Checkbox                m_message_sound;
-	UI::Checkbox                m_nozip;
-	UI::Checkbox                m_remove_syncstreams;
-
-	OptionsCtrl::OptionsStruct  os;
+	std::vector<ScreenResolution> resolutions_;
 };
 
 #endif  // end of include guard: WL_UI_FSMENU_OPTIONS_H


Follow ups