← Back to team overview

widelands-dev team mailing list archive

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

 

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

Requested reviews:
  Widelands Developers (widelands-dev)

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

Cleaned up buggy text positioning and width for spinboxes. Also, spinboxes now own their labels, just like checkboxes.

Labels will automatically take up more than 1 line if needed - we don't have a test case for this right now, but it safeguards us against text overflow with translations.

For testing: Spinboxes are only used in the Options window so far.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/spinbox into lp:widelands.
=== modified file 'src/ui_basic/spinbox.cc'
--- src/ui_basic/spinbox.cc	2015-10-23 10:17:13 +0000
+++ src/ui_basic/spinbox.cc	2015-12-01 17:08:30 +0000
@@ -29,6 +29,7 @@
 #include "graphic/text/font_set.h"
 #include "graphic/text_constants.h"
 #include "ui_basic/button.h"
+#include "ui_basic/multilinetextarea.h"
 #include "ui_basic/textarea.h"
 
 namespace UI {
@@ -76,13 +77,14 @@
  */
 SpinBox::SpinBox
 	(Panel * const parent,
-	 const int32_t x, const int32_t y, const uint32_t w,
+	 const int32_t x, const int32_t y, const uint32_t w, const uint32_t unit_w,
 	 int32_t const startval, int32_t const minval, int32_t const maxval,
+	 const std::string& label_text,
 	 const std::string& unit,
 	 const Image* background,
 	 bool const big)
 	:
-	Panel(parent, x, y, w, 0),
+	Panel(parent, x, y, std::max(w, unit_w), 0),
 	big_(big),
 	sbi_(new SpinBoxImpl)
 {
@@ -93,17 +95,40 @@
 	sbi_->background = background;
 
 	uint32_t padding = 2;
-
+	uint32_t actual_w = std::max(w, unit_w);
+	uint32_t no_padding = (big_ ? 6 : 4);
 	uint32_t texth = UI::g_fh1->render(as_uifont("."))->height();
-	box_ = new UI::Box(this, 0, 0, UI::Box::Horizontal, w, texth, padding);
+
+	// 40 is an ad hoc width estimate for the MultilineTextarea scrollbar + a bit of text.
+	if (!label_text.empty() && (w + padding) <= unit_w - 40) {
+		throw wexception(
+					"SpinBox: Overall width %d must be bigger than unit width %d + %d * %d + 40 for padding",
+					w, unit_w, no_padding, padding);
+	}
 
 #ifndef NDEBUG //  only in debug builds
-	if (w < (big_ ? 7 * texth : 3 * texth)) {
+	if (unit_w < (big_ ? 7 * texth : 3 * texth)) {
 		throw wexception("Not enough space to draw spinbox. Width %d is smaller than required width %d",
-							  w, (big_ ? 7 * texth : 3 * texth));
+							  unit_w, (big_ ? 7 * texth : 3 * texth));
 	}
 #endif
 
+	box_ = new UI::Box(this, 0, 0, UI::Box::Horizontal, actual_w, texth, padding);
+
+	// Find out how much height we need for the label. We give it 6 rows maximum.
+	const Image* rendered_text = UI::g_fh1->render(as_uifont(label_text));
+	uint32_t available_width = w - unit_w - no_padding * padding;
+	uint32_t extra_rows =
+			available_width > 0 ?
+				std::min(static_cast<int>(rendered_text->width() / available_width), 6) : 0;
+
+	UI::MultilineTextarea* label = new UI::MultilineTextarea(box_, 0, 0, available_width,
+																				texth * (extra_rows + 1), label_text);
+
+	box_->add(label, UI::Box::AlignTop);
+
+	sbi_->text = new UI::Textarea(box_, "", Align_Center);
+
 	sbi_->button_minus =
 		new Button
 			(box_, "-",
@@ -142,24 +167,22 @@
 		buttons_.push_back(sbi_->button_ten_minus);
 		buttons_.push_back(sbi_->button_ten_plus);
 
-		sbi_->text =
-				new UI::Textarea(
-					box_, 0, 0,
-					w - 2 * sbi_->button_ten_plus->get_w() - 2 * sbi_->button_minus->get_w() - 4 * padding, texth,
-					"", Align_Center);
+		sbi_->text->set_fixed_width(unit_w
+											 - 2 * sbi_->button_ten_plus->get_w()
+											 - 2 * sbi_->button_minus->get_w()
+											 - 4 * padding);
 
-		box_->add(sbi_->button_ten_minus, UI::Box::AlignCenter);
-		box_->add(sbi_->button_minus, UI::Box::AlignCenter);
-		box_->add(sbi_->text, UI::Box::AlignCenter);
-		box_->add(sbi_->button_plus, UI::Box::AlignCenter);
-		box_->add(sbi_->button_ten_plus, UI::Box::AlignCenter);
+		box_->add(sbi_->button_ten_minus, UI::Box::AlignTop);
+		box_->add(sbi_->button_minus, UI::Box::AlignTop);
+		box_->add(sbi_->text, UI::Box::AlignTop);
+		box_->add(sbi_->button_plus, UI::Box::AlignTop);
+		box_->add(sbi_->button_ten_plus, UI::Box::AlignTop);
 	} else {
-		sbi_->text = new UI::Textarea(box_, 0, 0,
-												w - 2 * sbi_->button_minus->get_w() - 2 * padding, texth,
-												"", Align_Center);
-		box_->add(sbi_->button_minus, UI::Box::AlignCenter);
-		box_->add(sbi_->text, UI::Box::AlignCenter);
-		box_->add(sbi_->button_plus, UI::Box::AlignCenter);
+		sbi_->text->set_fixed_width(unit_w - 2 * sbi_->button_minus->get_w() - 2 * padding);
+
+		box_->add(sbi_->button_minus, UI::Box::AlignTop);
+		box_->add(sbi_->text, UI::Box::AlignTop);
+		box_->add(sbi_->button_plus, UI::Box::AlignTop);
 	}
 
 	sbi_->button_plus->sigclicked.connect(boost::bind(&SpinBox::change_value, boost::ref(*this), 1));
@@ -168,8 +191,8 @@
 	sbi_->button_minus->set_repeating(true);
 	buttons_.push_back(sbi_->button_minus);
 	buttons_.push_back(sbi_->button_plus);
-	box_->set_size(w, texth);
-	set_size(w, texth);
+	box_->set_size(actual_w, texth * (extra_rows + 1));
+	set_size(actual_w, texth * (extra_rows + 1));
 	update();
 }
 

=== modified file 'src/ui_basic/spinbox.h'
--- src/ui_basic/spinbox.h	2015-10-23 10:17:13 +0000
+++ src/ui_basic/spinbox.h	2015-12-01 17:08:30 +0000
@@ -35,12 +35,16 @@
 struct TextStyle;
 
 /// A spinbox is an UI element for setting the integer value of a variable.
+/// w is the overall width of the SpinBox and must be wide enough to fit 2 labels and the buttons.
+/// unit_w is the width alotted for all buttons and the text between them (the actual spinbox).
+/// label_text is a text that precedes the actual spinbox.
 class SpinBox : public Panel {
 public:
 	SpinBox
 		(Panel*,
-		 int32_t x, int32_t y, uint32_t w,
+		 int32_t x, int32_t y, uint32_t w, uint32_t unit_w,
 		 int32_t startval, int32_t minval, int32_t maxval,
+		 const std::string& label_text = std::string(),
 		 const std::string& unit = std::string(),
 		 const Image* buttonbackground = g_gr->images().get("pics/but3.png"),
 		 bool big = false);

=== modified file 'src/ui_basic/textarea.cc'
--- src/ui_basic/textarea.cc	2015-11-22 08:20:56 +0000
+++ src/ui_basic/textarea.cc	2015-12-01 17:08:30 +0000
@@ -80,6 +80,7 @@
  */
 void Textarea::init()
 {
+	fixed_width_ = 0;
 	set_handle_mouse(false);
 	set_thinks(false);
 	set_textstyle(UI::TextStyle::ui_small());
@@ -147,6 +148,14 @@
 
 
 /**
+ * Set the fixed width. The Textarea will still collapse, but then restore this width when expand() is called.
+ */
+void Textarea::set_fixed_width(uint32_t w) {
+	fixed_width_ = w;
+}
+
+
+/**
  * Redraw the Textarea
  */
 void Textarea::draw(RenderTarget & dst)
@@ -223,7 +232,7 @@
 	uint16_t h = 0;
 
 	if (rendered_text_) {
-		w = rendered_text_->width();
+		w = fixed_width_ > 0 ? fixed_width_ : rendered_text_->width();
 		h = rendered_text_->height();
 		// We want empty textareas to have height
 		if (m_text.empty()) {

=== modified file 'src/ui_basic/textarea.h'
--- src/ui_basic/textarea.h	2015-11-22 08:20:56 +0000
+++ src/ui_basic/textarea.h	2015-12-01 17:08:30 +0000
@@ -68,7 +68,13 @@
 		 const std::string & text = std::string(),
 		 Align align = Align_Left);
 
-	void set_fixed_size(const std::string & text);
+	/**
+	 * If fixed_width > 0, the Textarea will not change its width.
+	 * Use this if you need a Textarea that keeps changing its contents, but you don't want the
+	 * surrounding elements to shift, e.g. in a Box.
+	 */
+	void set_fixed_width(uint32_t w);
+
 	void set_text(const std::string &);
 	const std::string& get_text();
 
@@ -99,6 +105,7 @@
 	const Image* rendered_text_;
 	Align m_align;
 	UI::TextStyle m_textstyle;
+	uint32_t fixed_width_;
 };
 
 }

=== modified file 'src/ui_fsmenu/options.cc'
--- src/ui_fsmenu/options.cc	2015-10-10 20:45:27 +0000
+++ src/ui_fsmenu/options.cc	2015-12-01 17:08:30 +0000
@@ -150,16 +150,12 @@
 									 m_fullscreen.get_y() +
 									 m_fullscreen.get_h() + m_padding),
 					 _("Grab Input")),
-	m_label_maxfps
+	m_sb_maxfps
 		(this,
 		 m_hmargin,
 		 m_inputgrab.get_y() + m_inputgrab.get_h() + m_padding,
-		 m_reslist.get_w() - 105, m_inputgrab.get_h(),
-		 _("Maximum FPS:"), UI::Align_VCenter),
-	m_sb_maxfps
-		(this,
-		 m_hmargin + m_reslist.get_w() - 105, m_label_maxfps.get_y(), 105,
-		 opt.maxfps, 0, 99, ""),
+		 m_reslist.get_w(), 105,
+		 opt.maxfps, 0, 99, _("Maximum FPS:"), ""),
 
 
 	// First options block 'general options', second column
@@ -216,38 +212,29 @@
 									 _("Dock windows to edges")),
 	m_sb_autosave
 		(this,
-		 get_w() - m_hmargin - 240,
+		 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"),
 		 /** TRANSLATORS: Options: Save game automatically every: */
 		 /** TRANSLATORS: This will have a number added in front of it */
-		 opt.autosave / 60, 0, 100, ngettext("minute", "minutes", opt.autosave / 60),
+		 ngettext("minute", "minutes", opt.autosave / 60),
 		 g_gr->images().get("pics/but3.png"), true),
-	m_label_autosave
-		(this,
-		 m_dock_windows_to_edges.get_x(),
-		 m_sb_autosave.get_y(),
-		 get_w() - m_sb_autosave.get_w() - 2 * m_hmargin,
-		 m_dock_windows_to_edges.get_h(),
-		 _("Save game automatically every"), UI::Align_VCenter),
 
 	m_sb_remove_replays
 		(this,
-		 get_w() - m_hmargin - 240,
+		 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:"),
 		 /** TRANSLATORS: Options: Remove Replays older than: */
 		 /** TRANSLATORS: This will have a number added in front of it */
-		 opt.remove_replays, 0, 365, ngettext("day", "days", opt.remove_replays),
+		 ngettext("day", "days", opt.remove_replays),
 		 g_gr->images().get("pics/but3.png"), true),
-	m_label_remove_replays
-		(this,
-		 m_label_autosave.get_x(),
-		 m_sb_remove_replays.get_y(),
-		 get_w() - m_sb_remove_replays.get_w() - 2 * m_hmargin,
-		 m_dock_windows_to_edges.get_h(),
-		 _("Remove replays older than:"), UI::Align_VCenter),
-
 	os(opt)
 {
 	m_advanced_options.sigclicked.connect
@@ -469,30 +456,26 @@
 		 get_w() / 2, get_h() * 17 / 150,
 		 _("Advanced Options"), UI::Align_HCenter),
 
-// First options block
-	m_label_snap_dis_panel
-		(this,
-		 m_hmargin, get_h() * 9 / 30,
-		 _("Distance for windows to snap to other panels:"), UI::Align_VCenter),
-	m_label_snap_dis_border
-		(this,
-		 m_hmargin,  m_label_snap_dis_panel.get_y() + m_label_snap_dis_panel.get_h() + 2 * m_padding,
-		 _("Distance for windows to snap to borders:"), UI::Align_VCenter),
-
 	// Spinboxes
 	m_sb_dis_panel
 			(this,
-			 get_w() - m_hmargin - (get_w() / 5), m_label_snap_dis_panel.get_y(), get_w() / 5,
-			 opt.panel_snap_distance, 0, 99, ngettext("pixel", "pixels", opt.panel_snap_distance)),
+			 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,
-			 get_w() - m_hmargin - (get_w() / 5), m_label_snap_dis_border.get_y(), get_w() / 5,
-			 opt.border_snap_distance, 0, 99, ngettext("pixel", "pixels", opt.border_snap_distance)),
+			 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_label_snap_dis_border.get_y() +
-											  m_label_snap_dis_border.get_h() + m_space),
+											  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),
 

=== modified file 'src/ui_fsmenu/options.h'
--- src/ui_fsmenu/options.h	2015-10-09 18:06:41 +0000
+++ src/ui_fsmenu/options.h	2015-12-01 17:08:30 +0000
@@ -102,7 +102,6 @@
 	UI::Listselect<void *>      m_reslist;
 	UI::Checkbox                m_fullscreen;
 	UI::Checkbox                m_inputgrab;
-	UI::Textarea                m_label_maxfps;
 	UI::SpinBox                 m_sb_maxfps;
 
 	UI::Textarea                m_label_language;
@@ -117,9 +116,7 @@
 	UI::Checkbox                m_snap_win_overlap_only;
 	UI::Checkbox                m_dock_windows_to_edges;
 	UI::SpinBox                 m_sb_autosave;
-	UI::Textarea                m_label_autosave;
 	UI::SpinBox                 m_sb_remove_replays;
-	UI::Textarea                m_label_remove_replays;
 
 	OptionsCtrl::OptionsStruct  os;
 
@@ -164,7 +161,6 @@
 	UI::Button                  m_cancel, m_apply;
 	UI::Textarea                m_title;
 
-	UI::Textarea                m_label_snap_dis_panel, m_label_snap_dis_border;
 	UI::SpinBox                 m_sb_dis_panel, m_sb_dis_border;
 	UI::Checkbox                m_transparent_chat;
 	UI::Checkbox                m_message_sound;


Follow ups