← Back to team overview

widelands-dev team mailing list archive

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

 

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

Commit message:
- Fixed crash with richtext font renderer for filenames that contain <some_text> in Table, MultilineTextarea and EditBox.
- EditBox now uses the new font renderer.
- Fixed a crash in SpinBox.

Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1530124 in widelands: "No escaping of special characters when reading files (maps, save games) leads to crash"
  https://bugs.launchpad.net/widelands/+bug/1530124

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

There was a crash with savegame/map filenames that contained <some_text>, because the font renderers interpreted them as illegal tags. This crash happened in Table, MultilineTextarea and EditBox.

I also shifted EditBox to the new font renderer in order to fix this. Note that caret rendering / text scrolling is broken for RTL languages, but it was broken before, so this is not a regression. I don't want to deal with that in this branch.

Also fixed a crash in Spinbox for Arabic in the Options window.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/bug-1530124 into lp:widelands.
=== modified file 'src/editor/ui_menus/editor_main_menu_map_options.cc'
--- src/editor/ui_menus/editor_main_menu_map_options.cc	2015-11-01 10:11:56 +0000
+++ src/editor/ui_menus/editor_main_menu_map_options.cc	2016-01-28 22:15:18 +0000
@@ -74,8 +74,8 @@
 	tags_box_(&tabs_, padding_, padding_, UI::Box::Vertical, max_w_, get_inner_h(), 0),
 	teams_box_(&tabs_, padding_, padding_, UI::Box::Vertical, max_w_, get_inner_h(), 0),
 
-	name_(&main_box_, 0, 0, max_w_, labelh_, g_gr->images().get("pics/but1.png")),
-	author_(&main_box_, 0, 0, max_w_, labelh_, g_gr->images().get("pics/but1.png")),
+	name_(&main_box_, 0, 0, max_w_, g_gr->images().get("pics/but1.png")),
+	author_(&main_box_, 0, 0, max_w_, g_gr->images().get("pics/but1.png")),
 	size_(&main_box_, 0, 0, max_w_ - indent_, labelh_, ""),
 
 	teams_list_(&teams_box_, 0, 0, max_w_, 60, UI::Align_Left, true),

=== modified file 'src/editor/ui_menus/editor_main_menu_random_map.cc'
--- src/editor/ui_menus/editor_main_menu_random_map.cc	2016-01-18 19:35:25 +0000
+++ src/editor/ui_menus/editor_main_menu_random_map.cc	2016-01-28 22:15:18 +0000
@@ -120,12 +120,12 @@
 	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_,
+						  box_width_ - 2 * margin_ - map_number_label_.get_w(),
 						  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_,
+					 box_width_ - 2 * margin_ - map_id_label_.get_w(),
 					 g_gr->images().get("pics/but1.png")),
 	// Buttons
 	button_box_(&box_, 0, 0, UI::Box::Horizontal, 0, 0, margin_),

=== modified file 'src/editor/ui_menus/editor_main_menu_save_map.cc'
--- src/editor/ui_menus/editor_main_menu_save_map.cc	2015-11-18 08:35:44 +0000
+++ src/editor/ui_menus/editor_main_menu_save_map.cc	2016-01-28 22:15:18 +0000
@@ -86,9 +86,7 @@
 	                           editbox_label_.get_x() + editbox_label_.get_w() + padding_,
 	                           editbox_label_.get_y(),
 	                           tablew_ - editbox_label_.get_w() - padding_ + 1,
-	                           buth_,
-	                           g_gr->images().get("pics/but1.png"),
-	                           UI::Align::Align_Left);
+										g_gr->images().get("pics/but1.png"));
 
 	editbox_->set_text(parent.egbase().map().get_name());
 	editbox_->changed.connect(boost::bind(&MainMenuSaveMap::edit_box_changed, this));

=== modified file 'src/editor/ui_menus/editor_main_menu_save_map_make_directory.cc'
--- src/editor/ui_menus/editor_main_menu_save_map_make_directory.cc	2015-10-02 07:02:00 +0000
+++ src/editor/ui_menus/editor_main_menu_save_map_make_directory.cc	2016-01-28 22:15:18 +0000
@@ -32,7 +32,7 @@
 	vbox_(this, padding_, padding_, UI::Box::Vertical,
 		  get_inner_w() - 2 * padding_, get_inner_h() - 3 * padding_ - buth_, padding_ / 2),
 	label_(&vbox_, 0, 0, get_inner_w() - 2 * padding_, buth_, _("Enter Directory Name: "), UI::Align_Left),
-	edit_(&vbox_, 0, 0, get_inner_w() - 2 * padding_, buth_, g_gr->images().get("pics/but1.png")),
+	edit_(&vbox_, 0, 0, get_inner_w() - 2 * padding_, g_gr->images().get("pics/but1.png")),
 	ok_button_(
 		this, "ok",
 		padding_, get_inner_h() - padding_ - buth_,

=== modified file 'src/editor/ui_menus/editor_player_menu.cc'
--- src/editor/ui_menus/editor_player_menu.cc	2016-01-16 12:55:14 +0000
+++ src/editor/ui_menus/editor_player_menu.cc	2016-01-28 22:15:18 +0000
@@ -142,7 +142,7 @@
 		if (!m_plr_names[p - 1]) {
 			m_plr_names[p - 1] =
 				new UI::EditBox
-					(this, posx, posy, 140, size,
+					(this, posx, posy, 140,
 					 g_gr->images().get("pics/but0.png"));
 			m_plr_names[p - 1]->changed.connect
 				(boost::bind(&EditorPlayerMenu::name_changed, this, p - 1));

=== modified file 'src/graphic/text_layout.cc'
--- src/graphic/text_layout.cc	2016-01-23 10:29:29 +0000
+++ src/graphic/text_layout.cc	2016-01-28 22:15:18 +0000
@@ -27,10 +27,20 @@
 
 #include "base/utf8.h"
 #include "graphic/font_handler1.h"
+#include "graphic/image.h"
 #include "graphic/text/bidi.h"
 #include "graphic/text/font_set.h"
 #include "graphic/text_constants.h"
 
+uint32_t text_width(const std::string& text, int ptsize) {
+	return UI::g_fh1->render(as_editorfont(text, ptsize - UI::g_fh1->fontset().size_offset()))->width();
+}
+
+uint32_t text_height(const std::string& text, int ptsize) {
+	return UI::g_fh1->render(as_editorfont(text.empty() ? "." : text,
+														ptsize - UI::g_fh1->fontset().size_offset()))->height();
+}
+
 std::string richtext_escape(const std::string& given_text) {
 	std::string text = given_text;
 	boost::replace_all(text, ">", "&gt;");
@@ -59,6 +69,16 @@
 	return as_aligned(txt, UI::Align::Align_Left, size, clr);
 }
 
+std::string as_editorfont(const std::string& text, int ptsize, const RGBColor& clr) {
+	// UI Text is always bold due to historic reasons
+	static boost::format
+			f("<rt keep_spaces=1><p><font face=serif size=%i bold=1 shadow=1 color=%s>%s</font></p></rt>");
+	f % ptsize;
+	f % clr.hex_value();
+	f % richtext_escape(text);
+	return f.str();
+}
+
 std::string as_aligned(const std::string & txt, UI::Align align, int ptsize, const RGBColor& clr) {
 	std::string alignment = "left";
 	if ((align & UI::Align_Horizontal) == UI::Align::Align_Right) {

=== modified file 'src/graphic/text_layout.h'
--- src/graphic/text_layout.h	2015-12-13 21:04:01 +0000
+++ src/graphic/text_layout.h	2016-01-28 22:15:18 +0000
@@ -29,6 +29,19 @@
 #include "graphic/text_constants.h"
 
 /**
+  * Returns the exact width of the text rendered as editorfont for the given font size.
+  * This function is inefficient; only call when we need the exact width.
+  */
+
+uint32_t text_width(const std::string& text, int ptsize);
+
+/**
+  * Returns the exact height of the text rendered as editorfont for the given font size.
+  * This function is inefficient; only call when we need the exact height.
+  */
+uint32_t text_height(const std::string& text, int ptsize);
+
+/**
  * Checks it the given string is RichText or not. Does not do validity checking.
  */
 inline bool is_richtext(const std::string& text) {
@@ -46,6 +59,10 @@
  */
 std::string as_uifont
 	(const std::string&, int ptsize = UI_FONT_SIZE_SMALL, const RGBColor& clr = UI_FONT_CLR_FG);
+
+std::string as_editorfont(const std::string& text, int ptsize = UI_FONT_SIZE_SMALL,
+								  const RGBColor& clr = UI_FONT_CLR_FG);
+
 std::string as_aligned(const std::string & txt, UI::Align align, int ptsize = UI_FONT_SIZE_SMALL,
 							  const RGBColor& clr = UI_FONT_CLR_FG);
 

=== modified file 'src/graphic/texture_atlas.cc'
--- src/graphic/texture_atlas.cc	2016-01-28 06:31:40 +0000
+++ src/graphic/texture_atlas.cc	2016-01-28 22:15:18 +0000
@@ -81,7 +81,9 @@
                                                                 const int texture_atlas_index,
                                                                 std::vector<PackedTexture>* pack_info) {
 	std::unique_ptr<Node> root(
-	   new Node(Rect(0, 0, blocks_.begin()->texture->width() + kPadding, blocks_.begin()->texture->height() + kPadding)));
+		new Node(Rect(0, 0,
+						  blocks_.begin()->texture->width() + kPadding,
+						  blocks_.begin()->texture->height() + kPadding)));
 
 	const auto grow_right = [&root](int delta_w) {
 		std::unique_ptr<Node> new_root(new Node(Rect(0, 0, root->r.w + delta_w, root->r.h)));

=== modified file 'src/graphic/wordwrap.cc'
--- src/graphic/wordwrap.cc	2016-01-24 09:20:30 +0000
+++ src/graphic/wordwrap.cc	2016-01-28 22:15:18 +0000
@@ -33,32 +33,6 @@
 #include "graphic/rendertarget.h"
 #include "graphic/text/bidi.h"
 
-namespace {
-std::string as_editorfont(const std::string& text,
-								  int ptsize = UI_FONT_SIZE_SMALL,
-								  const RGBColor& clr = UI_FONT_CLR_FG) {
-	// UI Text is always bold due to historic reasons
-	static boost::format
-			f("<rt keep_spaces=1><p><font face=serif size=%i bold=1 shadow=1 color=%s>%s</font></p></rt>");
-	f % ptsize;
-	f % clr.hex_value();
-	f % richtext_escape(text);
-	return f.str();
-}
-
-// This is inefficient; only call when we need the exact width.
-uint32_t text_width(const std::string& text, int ptsize) {
-	return UI::g_fh1->render(as_editorfont(text, ptsize - UI::g_fh1->fontset().size_offset()))->width();
-}
-
-// This is inefficient; only call when we need the exact height.
-uint32_t text_height(const std::string& text, int ptsize) {
-	return UI::g_fh1->render(as_editorfont(text.empty() ? "." : text,
-														ptsize - UI::g_fh1->fontset().size_offset()))->height();
-}
-
-} // namespace
-
 namespace UI {
 
 /**

=== modified file 'src/ui_basic/editbox.cc'
--- src/ui_basic/editbox.cc	2015-09-28 06:41:58 +0000
+++ src/ui_basic/editbox.cc	2016-01-28 22:15:18 +0000
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2003, 2006-2008, 2010-2011 by the Widelands Development Team
+ * Copyright (C) 2003-2016 by the Widelands Development Team
  *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
@@ -22,16 +22,24 @@
 #include <limits>
 
 #include <SDL_keycode.h>
+#include <boost/format.hpp>
 
-#include "graphic/font_handler.h"
 #include "graphic/font_handler1.h"
 #include "graphic/rendertarget.h"
+#include "graphic/text/bidi.h"
 #include "graphic/text/font_set.h"
 #include "graphic/text_constants.h"
 #include "ui_basic/mouse_constants.h"
 
 // TODO(GunChleoc): Arabic: Fix positioning for Arabic
 
+namespace {
+
+constexpr int kMargin = 4;
+
+} // namespace
+
+
 namespace UI {
 
 struct EditBoxImpl {
@@ -41,7 +49,6 @@
 	/*@{*/
 	std::string fontname;
 	uint32_t fontsize;
-	RGBColor fontcolor;
 	/*@}*/
 
 	/// Background tile style.
@@ -65,11 +72,11 @@
 
 EditBox::EditBox
 	(Panel * const parent,
-	 const int32_t x, const int32_t y, const uint32_t w, const uint32_t h,
+	 const int32_t x, const int32_t y, const uint32_t w,
 	 const Image* background,
-	 Align _align)
+	 int font_size)
 	:
-	Panel(parent, x, y, w, h),
+	Panel(parent, x, y, w, UI::g_fh1->render(as_uifont("."), font_size)->height() + 2),
 	m(new EditBoxImpl),
 	m_history_active(false),
 	m_history_position(-1)
@@ -78,10 +85,10 @@
 
 	m->background = background;
 	m->fontname = UI::g_fh1->fontset().serif();
-	m->fontsize = UI_FONT_SIZE_SMALL;
-	m->fontcolor = UI_FONT_CLR_FG;
+	m->fontsize = font_size;
 
-	m->align = static_cast<Align>((_align & Align_Horizontal) | Align_VCenter);
+	// Set alignment to the UI language's principal writing direction
+	m->align = UI::g_fh1->fontset().is_rtl() ? UI::Align::Align_CenterRight : UI::Align::Align_CenterLeft;
 	m->caret = 0;
 	m->scrolloffset = 0;
 	// yes, use *signed* max as maximum length; just a small safe-guard.
@@ -110,16 +117,6 @@
 }
 
 /**
- * Set the font used by the edit box.
- */
-void EditBox::set_font(const std::string & name, int32_t size, RGBColor color)
-{
-	m->fontname = name;
-	m->fontsize = size;
-	m->fontcolor = color;
-}
-
-/**
  * Set the current text in the edit box.
  *
  * The text is truncated if it is longer than the maximum length set by
@@ -173,33 +170,6 @@
 
 
 /**
- * \return the text alignment
- */
-Align EditBox::align() const
-{
-	return m->align;
-}
-
-
-/**
- * Set the new alignment.
- *
- * Note that vertical alignment is always centered, independent of what
- * you select here.
- */
-void EditBox::set_align(Align _align)
-{
-	_align = static_cast<Align>((_align & Align_Horizontal) | Align_VCenter);
-	if (_align != m->align) {
-		m->align = _align;
-		m->scrolloffset = 0;
-		check_caret();
-		update();
-	}
-}
-
-
-/**
  * The mouse was clicked on this editbox
 */
 bool EditBox::handle_mousepress(const uint8_t btn, int32_t, int32_t)
@@ -410,7 +380,7 @@
 		 Point(get_x(), get_y()));
 
 	// Draw border.
-	if (get_w() >= 4 && get_h() >= 4) {
+	if (get_w() >= kMargin && get_h() >= kMargin) {
 		static const RGBColor black(0, 0, 0);
 
 		// bottom edge
@@ -429,32 +399,69 @@
 		dst.fill_rect(Rect(Point(1, 0), 1, get_h() - 2), black);
 	}
 
-	if (has_focus())
+	if (has_focus()) {
 		dst.brighten_rect
 			(Rect(Point(0, 0), get_w(), get_h()), MOUSE_OVER_BRIGHT_FACTOR);
-
-	Point pos(4, get_h() >> 1);
-
-	switch (m->align & Align_Horizontal) {
-	case Align_HCenter:
-		pos.x = get_w() >> 1;
-		break;
-	case Align_Right:
-		pos.x = get_w() - 4;
-		break;
-	default:
-		break;
-	}
-
-	pos.x += m->scrolloffset;
-
-	UI::g_fh->draw_text
-		(dst,
-		 TextStyle::makebold(Font::get(m->fontname, m->fontsize), m->fontcolor),
-		 pos,
-		 m->text,
-		 align(),
-		 has_focus() ? static_cast<int32_t>(m->caret) : std::numeric_limits<uint32_t>::max());
+	}
+
+	const int max_width = get_w() - 2 * kMargin;
+
+	const Image* entry_text_im = UI::g_fh1->render(as_editorfont(m->text, m->fontsize));
+
+	int linewidth = entry_text_im->width();
+	int lineheight = entry_text_im->height();
+
+	Point point(kMargin, get_h() / 2);
+
+	if (m->align & Align_Right) {
+		point.x += max_width;
+	}
+
+	UI::correct_for_align(m->align, linewidth, lineheight, &point);
+
+	// Crop to max_width while blitting
+	if (max_width < linewidth) {
+		// Fix positioning for BiDi languages.
+		if (UI::g_fh1->fontset().is_rtl()) {
+			point.x = 0;
+		}
+		// We want this always on, e.g. for mixed language savegame filenames
+		if (i18n::has_rtl_character(m->text.c_str(), 100)) { // Restrict check for efficiency
+			// TODO(GunChleoc): Arabic: Fix scrolloffset
+			dst.blitrect(point,
+							 entry_text_im,
+							 Rect(linewidth - max_width, 0, linewidth, lineheight));
+		}
+		else {
+			if (m->align & Align_Right) {
+				// TODO(GunChleoc): Arabic: Fix scrolloffset
+				dst.blitrect(point,
+								 entry_text_im,
+								 Rect(point.x + m->scrolloffset + kMargin, point.y, max_width, lineheight));
+			} else {
+				dst.blitrect(point,
+								 entry_text_im,
+								 Rect(point.x - m->scrolloffset - kMargin, point.y, max_width, lineheight));
+			}
+		}
+	} else {
+		dst.blitrect(point, entry_text_im, Rect(0, 0, max_width, lineheight));
+	}
+
+	if (has_focus()) {
+		// Draw the caret
+		std::string line_to_caret = m->text.substr(0, m->caret);
+		// TODO(GunChleoc): Arabic: Fix cursor position for BIDI text.
+		int caret_x = text_width(line_to_caret, m->fontsize);
+
+		const uint16_t fontheight = text_height(m->text, m->fontsize);
+
+		const Image* caret_image = g_gr->images().get("pics/caret.png");
+		Point caretpt;
+		caretpt.x = point.x + m->scrolloffset + caret_x - caret_image->width() + LINE_MARGIN;
+		caretpt.y = point.y + (fontheight - caret_image->height()) / 2;
+		dst.blit(caretpt, caret_image);
+	}
 }
 
 /**
@@ -464,32 +471,24 @@
 {
 	std::string leftstr(m->text, 0, m->caret);
 	std::string rightstr(m->text, m->caret, std::string::npos);
-	uint32_t leftw;
-	uint32_t rightw;
-	uint32_t tmp;
-
-	UI::g_fh->get_size(m->fontname, m->fontsize, leftstr, leftw, tmp);
-	UI::g_fh->get_size(m->fontname, m->fontsize, rightstr, rightw, tmp);
+	int32_t leftw = text_width(leftstr, m->fontsize);
+	int32_t rightw = text_width(rightstr, m->fontsize);
 
 	int32_t caretpos;
 
 	switch (m->align & Align_Horizontal) {
-	case Align_HCenter:
-		caretpos  = (get_w() - static_cast<int32_t>(leftw + rightw)) / 2;
-		caretpos += m->scrolloffset + leftw;
-		break;
 	case Align_Right:
-		caretpos = get_w() - 4 + m->scrolloffset - rightw;
+		caretpos = get_w() - kMargin + m->scrolloffset - rightw;
 		break;
 	default:
-		caretpos = 4 + m->scrolloffset + leftw;
+		caretpos = kMargin + m->scrolloffset + leftw;
 		break;
 	}
 
-	if (caretpos < 4)
-		m->scrolloffset += 4 - caretpos + get_w() / 5;
-	else if (caretpos > get_w() - 4)
-		m->scrolloffset -= caretpos - get_w() + 4 + get_w() / 5;
+	if (caretpos < kMargin)
+		m->scrolloffset += kMargin - caretpos + get_w() / 5;
+	else if (caretpos > get_w() - kMargin)
+		m->scrolloffset -= caretpos - get_w() + kMargin + get_w() / 5;
 
 	if ((m->align & Align_Horizontal) == Align_Left) {
 		if (m->scrolloffset > 0)

=== modified file 'src/ui_basic/editbox.h'
--- src/ui_basic/editbox.h	2015-04-18 11:20:53 +0000
+++ src/ui_basic/editbox.h	2016-01-28 22:15:18 +0000
@@ -41,8 +41,9 @@
 struct EditBox : public Panel {
 	EditBox
 		(Panel *,
-		 int32_t x, int32_t y, uint32_t w, uint32_t h,
-		 const Image* background = g_gr->images().get("pics/but2.png"), Align align = Align_Center);
+		 int32_t x, int32_t y, uint32_t w,
+		 const Image* background = g_gr->images().get("pics/but2.png"),
+		 int font_size = UI_FONT_SIZE_SMALL);
 	virtual ~EditBox();
 
 	boost::signals2::signal<void ()> changed;
@@ -53,9 +54,6 @@
 	void set_text(const std::string &);
 	uint32_t max_length() const;
 	void set_max_length(uint32_t);
-	Align align() const;
-	void set_align(Align);
-	void set_font(const std::string & name, int32_t size, RGBColor color);
 
 	void activate_history(bool activate) {m_history_active = activate;}
 

=== modified file 'src/ui_basic/multilinetextarea.cc'
--- src/ui_basic/multilinetextarea.cc	2015-12-14 05:17:55 +0000
+++ src/ui_basic/multilinetextarea.cc	2016-01-28 22:15:18 +0000
@@ -34,7 +34,7 @@
 MultilineTextarea::MultilineTextarea
 	(Panel * const parent,
 	 const int32_t x, const int32_t y, const uint32_t w, const uint32_t h,
-	 const std::string & text,
+	 const std::string& text,
 	 const Align align,
 	 const bool always_show_scrollbar)
 	:
@@ -68,7 +68,7 @@
  * Replace the current text with a new one.
  * Fix up scrolling state if necessary.
  */
-void MultilineTextarea::set_text(const std::string & text)
+void MultilineTextarea::set_text(const std::string& text)
 {
 	m_text = text;
 	recompute();
@@ -87,8 +87,9 @@
 	for (int i = 0; i < 2; ++i) {
 		if (m_text.compare(0, 3, "<rt")) {
 			isrichtext = false;
-			boost::replace_all(m_text, "\n", "<br>");
-			const Image* text_im = UI::g_fh1->render(as_uifont(m_text, m_style.font->size(), m_style.fg),
+			std::string text_to_render = richtext_escape(m_text);
+			boost::replace_all(text_to_render, "\n", "<br>");
+			const Image* text_im = UI::g_fh1->render(as_uifont(text_to_render, m_style.font->size(), m_style.fg),
 																  get_eff_w() - 2 * RICHTEXT_MARGIN);
 			height = text_im->height();
 		} else {
@@ -147,13 +148,16 @@
 /**
  * Redraw the textarea
  */
-void MultilineTextarea::draw(RenderTarget & dst)
+void MultilineTextarea::draw(RenderTarget& dst)
 {
 	if (isrichtext) {
 		rt.draw(dst, Point(RICHTEXT_MARGIN, RICHTEXT_MARGIN - m_scrollbar.get_scrollpos()));
 	} else {
-		const Image* text_im = UI::g_fh1->render(as_aligned(m_text, m_align, m_style.font->size(), m_style.fg),
-															  get_eff_w() - 2 * RICHTEXT_MARGIN);
+		std::string text_to_render = richtext_escape(m_text);
+		boost::replace_all(text_to_render, "\n", "<br>");
+		const Image* text_im =
+				UI::g_fh1->render(as_aligned(text_to_render, m_align, m_style.font->size(), m_style.fg),
+										get_eff_w() - 2 * RICHTEXT_MARGIN);
 
 		uint32_t blit_width = std::min(text_im->width(), static_cast<int>(get_eff_w()));
 		uint32_t blit_height = std::min(text_im->height(), static_cast<int>(get_inner_h()));

=== modified file 'src/ui_basic/multilinetextarea.h'
--- src/ui_basic/multilinetextarea.h	2015-12-13 21:04:01 +0000
+++ src/ui_basic/multilinetextarea.h	2016-01-28 22:15:18 +0000
@@ -45,14 +45,14 @@
 	MultilineTextarea
 		(Panel * const parent,
 		 const int32_t x, const int32_t y, const uint32_t w, const uint32_t h,
-		 const std::string & text         = std::string(),
+		 const std::string& text          = std::string(),
 		 const Align                      = Align_Left,
 		 const bool always_show_scrollbar = false);
 
-	const std::string & get_text() const {return m_text;}
+	const std::string& get_text() const {return m_text;}
 	ScrollMode get_scrollmode() const {return m_scrollmode;}
 
-	void set_text(const std::string &);
+	void set_text(const std::string&);
 	void set_scrollmode(ScrollMode mode);
 
 	uint32_t scrollbar_w() const {return 24;}
@@ -61,7 +61,7 @@
 	void set_color(RGBColor fg) {m_style.fg = fg;}
 
 	// Drawing and event handlers
-	void draw(RenderTarget &) override;
+	void draw(RenderTarget&) override;
 
 	bool handle_mousewheel(uint32_t which, int32_t x, int32_t y) override;
 	void scroll_to_top();

=== modified file 'src/ui_basic/spinbox.cc'
--- src/ui_basic/spinbox.cc	2016-01-24 08:32:56 +0000
+++ src/ui_basic/spinbox.cc	2016-01-28 22:15:18 +0000
@@ -24,6 +24,7 @@
 #include <boost/format.hpp>
 
 #include "base/i18n.h"
+#include "base/log.h"
 #include "base/wexception.h"
 #include "graphic/font_handler1.h"
 #include "graphic/text/font_set.h"
@@ -119,12 +120,12 @@
 					w, unit_w, no_padding, padding);
 	}
 
-#ifndef NDEBUG //  only in debug builds
-	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, (is_big ? 7 * texth : 3 * buttonh));
+	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));
 	}
-#endif
 
 	box_ = new UI::Box(this, 0, 0, UI::Box::Horizontal, actual_w, texth, padding);
 

=== modified file 'src/ui_fsmenu/internet_lobby.cc'
--- src/ui_fsmenu/internet_lobby.cc	2015-11-09 08:15:31 +0000
+++ src/ui_fsmenu/internet_lobby.cc	2016-01-28 22:15:18 +0000
@@ -85,8 +85,8 @@
 
 // Edit boxes
 	servername
-		(this, get_w() * 17 / 25, get_h() * 68 / 100, m_butw, m_buth,
-		 g_gr->images().get("pics/but2.png")),
+		(this, get_w() * 17 / 25, get_h() * 68 / 100, m_butw,
+		 g_gr->images().get("pics/but2.png"), m_fs),
 
 // List
 	clientsonline
@@ -127,7 +127,6 @@
 	servername  .set_text (server);
 	servername  .changed.connect
 		(boost::bind(&FullscreenMenuInternetLobby::change_servername, this));
-	servername  .set_font(m_fn, m_fs, UI_FONT_CLR_FG);
 
 	// prepare the lists
 	std::string t_tip = (boost::format("%s%s%s%s%s%s%s%s%s%s")

=== modified file 'src/ui_fsmenu/netsetup_lan.cc'
--- src/ui_fsmenu/netsetup_lan.cc	2015-10-03 07:48:26 +0000
+++ src/ui_fsmenu/netsetup_lan.cc	2016-01-28 22:15:18 +0000
@@ -79,11 +79,11 @@
 
 // Edit boxes
 	playername
-		(this, get_w() * 16 / 25, get_h() * 3333 / 10000, m_butw,       m_buth,
-		 g_gr->images().get("pics/but2.png")),
+		(this, get_w() * 16 / 25, get_h() * 3333 / 10000, m_butw,
+		 g_gr->images().get("pics/but2.png"), fs_small()),
 	hostname
-		(this, get_w() * 16 / 25, get_h() * 19 / 40,  get_w() * 17 / 80, m_buth,
-		 g_gr->images().get("pics/but2.png")),
+		(this, get_w() * 16 / 25, get_h() * 19 / 40,  get_w() * 17 / 80,
+		 g_gr->images().get("pics/but2.png"), fs_small()),
 
 // List
 	opengames
@@ -108,11 +108,9 @@
 	title       .set_textstyle(UI::TextStyle::ui_big());
 	hostname    .changed.connect
 		(boost::bind(&FullscreenMenuNetSetupLAN::change_hostname, this));
-	hostname    .set_font(ui_fn(), fs_small(), UI_FONT_CLR_FG);
 	playername  .set_text  (s.get_string("nickname", (_("nobody"))));
 	playername  .changed.connect
 		(boost::bind(&FullscreenMenuNetSetupLAN::change_playername, this));
-	playername  .set_font(ui_fn(), fs_small(), UI_FONT_CLR_FG);
 	opengames   .add_column(m_lisw * 2 / 5, _("Host"));
 	opengames   .add_column(m_lisw * 2 / 5, _("Map"));
 	opengames   .add_column(m_lisw     / 5, _("State"));

=== modified file 'src/wui/building_statistics_menu.cc'
--- src/wui/building_statistics_menu.cc	2016-01-17 08:29:59 +0000
+++ src/wui/building_statistics_menu.cc	2016-01-28 22:15:18 +0000
@@ -51,10 +51,6 @@
 void set_label_font(UI::Textarea* label) {
 	label->set_font(UI::g_fh1->fontset().serif(), kLabelFontSize, UI_FONT_CLR_FG);
 }
-void set_editbox_font(UI::EditBox* editbox) {
-	editbox->set_font(UI::g_fh1->fontset().serif(), kLabelFontSize, UI_FONT_CLR_FG);
-}
-
 }  // namespace
 
 inline InteractivePlayer& BuildingStatisticsMenu::iplayer() const {
@@ -95,7 +91,8 @@
 			_("Low Productivity "),
 			UI::Align_BottomLeft),
 		unproductive_percent_(
-			&unproductive_box_, 0, 0, 35, kLabelHeight, g_gr->images().get("pics/but1.png")),
+			&unproductive_box_, 0, 0, 35, g_gr->images().get("pics/but1.png"),
+			kLabelFontSize - UI::g_fh1->fontset().size_offset()), // We need consistent height here
 		unproductive_label2_(
 			&unproductive_box_,
 			/** TRANSLATORS: This is the second part of productivity with input field */
@@ -260,7 +257,6 @@
 	set_label_font(&owned_label_);
 	set_label_font(&construction_label_);
 	set_label_font(&unproductive_label_);
-	set_editbox_font(&unproductive_percent_);
 	set_label_font(&unproductive_label2_);
 	set_label_font(&no_owned_label_);
 	set_label_font(&no_construction_label_);

=== modified file 'src/wui/game_main_menu_save_game.cc'
--- src/wui/game_main_menu_save_game.cc	2016-01-24 20:11:53 +0000
+++ src/wui/game_main_menu_save_game.cc	2016-01-28 22:15:18 +0000
@@ -42,11 +42,10 @@
 #define VMARGIN                                                               5
 #define VSPACING                                                              5
 #define HSPACING                                                              5
-#define EDITBOX_HEIGHT                                                       20
 #define BUTTON_HEIGHT                                                        20
 #define LIST_WIDTH                                                          280
-#define LIST_HEIGHT   (WINDOW_HEIGHT - 2 * VMARGIN - VSPACING - EDITBOX_HEIGHT)
-#define EDITBOX_Y                    (WINDOW_HEIGHT - EDITBOX_HEIGHT - VMARGIN)
+#define LIST_HEIGHT                    (WINDOW_HEIGHT - 2 * VMARGIN - VSPACING)
+#define EDITBOX_Y                                (WINDOW_HEIGHT - 24 - VMARGIN)
 #define DESCRIPTION_X                         (VMARGIN + LIST_WIDTH + VSPACING)
 #define DESCRIPTION_WIDTH              (WINDOW_WIDTH - DESCRIPTION_X - VMARGIN)
 #define CANCEL_Y                      (WINDOW_HEIGHT - BUTTON_HEIGHT - VMARGIN)
@@ -59,7 +58,8 @@
 	UI::UniqueWindow
 		(&parent, "save_game", &registry,
 		 WINDOW_WIDTH, WINDOW_HEIGHT, _("Save Game")),
-	ls_     (this, HSPACING, VSPACING,  LIST_WIDTH, LIST_HEIGHT),
+	editbox_(this, HSPACING, EDITBOX_Y, LIST_WIDTH, g_gr->images().get("pics/but1.png")),
+	ls_     (this, HSPACING, VSPACING,  LIST_WIDTH, LIST_HEIGHT - editbox_.get_h()),
 	name_label_
 		(this, DESCRIPTION_X,  5, 0, 20, _("Map Name:"),  UI::Align_CenterLeft),
 	mapname_
@@ -76,12 +76,8 @@
 		(this, DESCRIPTION_X, 125, 0, 20, " ",             UI::Align_CenterLeft),
 	curdir_(SaveHandler::get_base_dir())
 {
-	editbox_ =
-		new UI::EditBox
-			(this, HSPACING, EDITBOX_Y, LIST_WIDTH, EDITBOX_HEIGHT,
-			 g_gr->images().get("pics/but1.png"));
-	editbox_->changed.connect(boost::bind(&GameMainMenuSaveGame::edit_box_changed, this));
-	editbox_->ok.connect(boost::bind(&GameMainMenuSaveGame::ok, this));
+	editbox_.changed.connect(boost::bind(&GameMainMenuSaveGame::edit_box_changed, this));
+	editbox_.ok.connect(boost::bind(&GameMainMenuSaveGame::ok, this));
 
 	button_ok_ =
 		new UI::Button
@@ -139,7 +135,7 @@
 		}
 	}
 
-	editbox_->focus();
+	editbox_.focus();
 	pause_game(true);
 }
 
@@ -154,7 +150,7 @@
 	Widelands::GamePreloadPacket gpdp;
 	gl.preload_game(gpdp); //  This has worked before, no problem
 	{
-		editbox_->set_text(FileSystem::filename_without_ext(name.c_str()));
+		editbox_.set_text(FileSystem::filename_without_ext(name.c_str()));
 	}
 	button_ok_->set_enabled(true);
 
@@ -228,7 +224,7 @@
  * The editbox was changed. Enable ok button
  */
 void GameMainMenuSaveGame::edit_box_changed() {
-	button_ok_->set_enabled(editbox_->text().size());
+	button_ok_->set_enabled(editbox_.text().size());
 }
 
 static void dosave
@@ -289,12 +285,12 @@
  */
 void GameMainMenuSaveGame::ok()
 {
-	if (editbox_->text().empty())
+	if (editbox_.text().empty())
 		return;
 
 	std::string const complete_filename =
 		igbase().game().save_handler().create_file_name
-			(curdir_, editbox_->text());
+			(curdir_, editbox_.text());
 
 	//  Check if file exists. If it does, show a warning.
 	if (g_fs->file_exists(complete_filename)) {
@@ -351,7 +347,7 @@
 {
 	std::string const complete_filename =
 		igbase().game().save_handler().create_file_name
-			(curdir_, editbox_->text());
+			(curdir_, editbox_.text());
 
 	//  Check if file exists. If it does, let the user confirm the deletion.
 	if (g_fs->file_exists(complete_filename))

=== modified file 'src/wui/game_main_menu_save_game.h'
--- src/wui/game_main_menu_save_game.h	2016-01-24 20:11:53 +0000
+++ src/wui/game_main_menu_save_game.h	2016-01-28 22:15:18 +0000
@@ -51,8 +51,9 @@
 	bool save_game(std::string);
 	void pause_game(bool paused);
 
+	UI::EditBox editbox_;
 	UI::Listselect<std::string> ls_;
-	UI::EditBox * editbox_;
+
 	UI::Textarea name_label_, mapname_, gametime_label_, gametime_, players_label_,
 		win_condition_label_, win_condition_;
 	UI::Button * button_ok_;

=== modified file 'src/wui/gamechatpanel.cc'
--- src/wui/gamechatpanel.cc	2016-01-24 20:11:53 +0000
+++ src/wui/gamechatpanel.cc	2016-01-28 22:15:18 +0000
@@ -35,13 +35,12 @@
 	UI::Panel(parent, x, y, w, h),
 	chat_   (chat),
 	chatbox  (this, 0, 0, w, h - 25, "", UI::Align_Left, 1),
-	editbox  (this, 0, h - 20, w,  20),
+	editbox  (this, 0, h - 20, w),
 	chat_message_counter(std::numeric_limits<uint32_t>::max())
 {
 	chatbox.set_scrollmode(UI::MultilineTextarea::ScrollLog);
 	editbox.ok.connect(boost::bind(&GameChatPanel::key_enter, this));
 	editbox.cancel.connect(boost::bind(&GameChatPanel::key_escape, this));
-	editbox.set_align(UI::Align_Left);
 	editbox.activate_history(true);
 
 	set_handle_mouse(true);

=== modified file 'src/wui/login_box.cc'
--- src/wui/login_box.cc	2015-10-10 11:47:22 +0000
+++ src/wui/login_box.cc	2016-01-28 22:15:18 +0000
@@ -33,16 +33,10 @@
 	int32_t margin = 10;
 
 	ta_nickname = new UI::Textarea(this, margin, margin, _("Nickname:"));
-	eb_nickname =
-		new UI::EditBox
-			(this, 150, margin, 330, 20,
-			 g_gr->images().get("pics/but2.png"), UI::Align_Left);
+	eb_nickname = new UI::EditBox(this, 150, margin, 330, g_gr->images().get("pics/but2.png"));
 
 	ta_password = new UI::Textarea(this, margin, 40, _("Password:"));
-	eb_password =
-		new UI::EditBox
-			(this, 150, 40, 330, 20,
-			 g_gr->images().get("pics/but2.png"), UI::Align_Left);
+	eb_password = new UI::EditBox(this, 150, 40, 330, g_gr->images().get("pics/but2.png"));
 
 	pwd_warning =
 		new UI::MultilineTextarea


Follow ups