Browse Source

Add scrollbaroptions FormSpec element (#8530)

v-rob 4 years ago
parent
commit
4f45bfd08b
5 changed files with 159 additions and 36 deletions
  1. 26 2
      doc/lua_api.txt
  2. 89 30
      src/gui/guiFormSpecMenu.cpp
  3. 12 1
      src/gui/guiFormSpecMenu.h
  4. 24 3
      src/gui/guiScrollBar.cpp
  5. 8 0
      src/gui/guiScrollBar.h

+ 26 - 2
doc/lua_api.txt

@@ -2351,16 +2351,40 @@ Elements
 
 ### `scrollbar[<X>,<Y>;<W>,<H>;<orientation>;<name>;<value>]`
 
-* Show a scrollbar
+* Show a scrollbar using options defined by the previous `scrollbaroptions[]`
 * There are two ways to use it:
     1. handle the changed event (only changed scrollbar is available)
     2. read the value on pressing a button (all scrollbars are available)
 * `orientation`:  `vertical`/`horizontal`
 * Fieldname data is transferred to Lua
-* Value this trackbar is set to (`0`-`1000`)
+* Value of this trackbar is set to (`0`-`1000`) by default
 * See also `minetest.explode_scrollbar_event`
   (main menu: `core.explode_scrollbar_event`).
 
+### `scrollbaroptions[opt1;opt2;...]`
+* Sets options for all following `scrollbar[]` elements
+* `min=<int>`
+    * Sets scrollbar minimum value, defaults to `0`.
+* `max=<int>`
+    * Sets scrollbar maximum value, defaults to `1000`.
+      If the max is equal to the min, the scrollbar will be disabled.
+* `smallstep=<int>`
+    * Sets scrollbar step value when the arrows are clicked or the mouse wheel is
+      scrolled.
+    * If this is set to a negative number, the value will be reset to `10`.
+* `largestep=<int>`
+    * Sets scrollbar step value used by page up and page down.
+    * If this is set to a negative number, the value will be reset to `100`.
+* `thumbsize=<int>`
+    * Sets size of the thumb on the scrollbar. Size is calculated in the number of
+      units the thumb spans out of the range of the scrollbar values.
+    * Example: If a scrollbar has a `min` of 1 and a `max` of 100, a thumbsize of 10
+      would span a tenth of the scrollbar space.
+    * If this is set to zero or less, the value will be reset to `1`.
+* `arrows=<show/hide/default>`
+    * Whether to show the arrow buttons on the scrollbar. `default` hides the arrows
+      when the scrollbar gets too small, but shows them otherwise.
+
 ### `table[<X>,<Y>;<W>,<H>;<name>;<cell 1>,<cell 2>,...,<cell n>;<selected idx>]`
 
 * Show scrollable table using options defined by the previous `tableoptions[]`

+ 89 - 30
src/gui/guiFormSpecMenu.cpp

@@ -24,6 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include <limits>
 #include <sstream>
 #include "guiFormSpecMenu.h"
+#include "guiScrollBar.h"
+#include "guiTable.h"
 #include "constants.h"
 #include "gamedef.h"
 #include "client/keycode.h"
@@ -123,24 +125,18 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
 {
 	removeChildren();
 
-	for (auto &table_it : m_tables) {
+	for (auto &table_it : m_tables)
 		table_it.second->drop();
-	}
-	for (auto &inventorylist_it : m_inventorylists) {
+	for (auto &inventorylist_it : m_inventorylists)
 		inventorylist_it.e->drop();
-	}
-	for (auto &checkbox_it : m_checkboxes) {
+	for (auto &checkbox_it : m_checkboxes)
 		checkbox_it.second->drop();
-	}
-	for (auto &scrollbar_it : m_scrollbars) {
+	for (auto &scrollbar_it : m_scrollbars)
 		scrollbar_it.second->drop();
-	}
-	for (auto &background_it : m_backgrounds) {
+	for (auto &background_it : m_backgrounds)
 		background_it->drop();
-	}
-	for (auto &tooltip_rect_it : m_tooltip_rects) {
+	for (auto &tooltip_rect_it : m_tooltip_rects)
 		tooltip_rect_it.first->drop();
-	}
 
 	delete m_selected_item;
 	delete m_form_src;
@@ -614,22 +610,86 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
 		spec.ftype = f_ScrollBar;
 		spec.send  = true;
 		GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect,
-				is_horizontal, false);
+				is_horizontal, true);
 
 		auto style = getStyleForElement("scrollbar", name);
 		e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+		e->setArrowsVisible(data->scrollbar_options.arrow_visiblity);
+
+		s32 max = data->scrollbar_options.max;
+		s32 min = data->scrollbar_options.min;
+
+		e->setMax(max);
+		e->setMin(min);
 
-		e->setMax(1000);
-		e->setMin(0);
 		e->setPos(stoi(parts[4]));
-		e->setSmallStep(10);
-		e->setLargeStep(100);
+
+		e->setSmallStep(data->scrollbar_options.small_step);
+		e->setLargeStep(data->scrollbar_options.large_step);
+
+		s32 scrollbar_size = is_horizontal ? dim.X : dim.Y;
+
+		e->setPageSize(scrollbar_size * (max - min + 1) / data->scrollbar_options.thumb_size);
 
 		m_scrollbars.emplace_back(spec,e);
 		m_fields.push_back(spec);
 		return;
 	}
-	errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'"  << std::endl;
+	errorstream << "Invalid scrollbar element(" << parts.size() << "): '" << element
+		<< "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string &element)
+{
+	std::vector<std::string> parts = split(element, ';');
+
+	if (parts.size() == 0) {
+		warningstream << "Invalid scrollbaroptions element(" << parts.size() << "): '" <<
+			element << "'"  << std::endl;
+		return;
+	}
+
+	for (const std::string &i : parts) {
+		std::vector<std::string> options = split(i, '=');
+
+		if (options.size() != 2) {
+			warningstream << "Invalid scrollbaroptions option syntax: '" <<
+				element << "'" << std::endl;
+			continue; // Go to next option
+		}
+
+		if (options[0] == "max") {
+			data->scrollbar_options.max = stoi(options[1]);
+			continue;
+		} else if (options[0] == "min") {
+			data->scrollbar_options.min = stoi(options[1]);
+			continue;
+		} else if (options[0] == "smallstep") {
+			int value = stoi(options[1]);
+			data->scrollbar_options.small_step = value < 0 ? 10 : value;
+			continue;
+		} else if (options[0] == "largestep") {
+			int value = stoi(options[1]);
+			data->scrollbar_options.large_step = value < 0 ? 100 : value;
+			continue;
+		} else if (options[0] == "thumbsize") {
+			int value = stoi(options[1]);
+			data->scrollbar_options.thumb_size = value <= 0 ? 1 : value;
+			continue;
+		} else if (options[0] == "arrows") {
+			std::string value = trim(options[1]);
+			if (value == "hide")
+				data->scrollbar_options.arrow_visiblity = GUIScrollBar::HIDE;
+			else if (value == "show")
+				data->scrollbar_options.arrow_visiblity = GUIScrollBar::SHOW;
+			else // Auto hide/show
+				data->scrollbar_options.arrow_visiblity = GUIScrollBar::DEFAULT;
+			continue;
+		}
+
+		warningstream << "Invalid scrollbaroptions option(" << options[0] <<
+			"): '" << element << "'" << std::endl;
+	}
 }
 
 void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
@@ -2591,6 +2651,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
 		return;
 	}
 
+	if (type == "scrollbaroptions") {
+		parseScrollBarOptions(data, description);
+		return;
+	}
+
 	// Ignore others
 	infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
 			<< std::endl;
@@ -2633,24 +2698,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 	// Remove children
 	removeChildren();
 
-	for (auto &table_it : m_tables) {
+	for (auto &table_it : m_tables)
 		table_it.second->drop();
-	}
-	for (auto &inventorylist_it : m_inventorylists) {
+	for (auto &inventorylist_it : m_inventorylists)
 		inventorylist_it.e->drop();
-	}
-	for (auto &checkbox_it : m_checkboxes) {
+	for (auto &checkbox_it : m_checkboxes)
 		checkbox_it.second->drop();
-	}
-	for (auto &scrollbar_it : m_scrollbars) {
+	for (auto &scrollbar_it : m_scrollbars)
 		scrollbar_it.second->drop();
-	}
-	for (auto &background_it : m_backgrounds) {
+	for (auto &background_it : m_backgrounds)
 		background_it->drop();
-	}
-	for (auto &tooltip_rect_it : m_tooltip_rects) {
+	for (auto &tooltip_rect_it : m_tooltip_rects)
 		tooltip_rect_it.first->drop();
-	}
 
 	mydata.size= v2s32(100,100);
 	mydata.screensize = screensize;

+ 12 - 1
src/gui/guiFormSpecMenu.h

@@ -379,7 +379,7 @@ protected:
 	video::SColor m_default_tooltip_bgcolor;
 	video::SColor m_default_tooltip_color;
 
-	
+
 private:
 	IFormSource        *m_form_src;
 	TextDest           *m_text_dst;
@@ -401,6 +401,16 @@ private:
 		std::string focused_fieldname;
 		GUITable::TableOptions table_options;
 		GUITable::TableColumns table_columns;
+
+		struct {
+			s32 max = 1000;
+			s32 min = 0;
+			s32 small_step = 10;
+			s32 large_step = 100;
+			s32 thumb_size = 1;
+			GUIScrollBar::ArrowVisibility arrow_visiblity = GUIScrollBar::DEFAULT;
+		} scrollbar_options;
+
 		// used to restore table selection/scroll/treeview state
 		std::unordered_map<std::string, GUITable::DynamicData> table_dyndata;
 	} parserData;
@@ -455,6 +465,7 @@ private:
 	bool parseVersionDirect(const std::string &data);
 	bool parseSizeDirect(parserData* data, const std::string &element);
 	void parseScrollBar(parserData* data, const std::string &element);
+	void parseScrollBarOptions(parserData *data, const std::string &element);
 	bool parsePositionDirect(parserData *data, const std::string &element);
 	void parsePosition(parserData *data, const std::string &element);
 	bool parseAnchorDirect(parserData *data, const std::string &element);

+ 24 - 3
src/gui/guiScrollBar.cpp

@@ -247,7 +247,7 @@ s32 GUIScrollBar::getPosFromMousePos(const core::position2di &pos) const
 		w = RelativeRect.getHeight() - border_size * 2 - thumb_size;
 		p = pos.Y - AbsoluteRect.UpperLeftCorner.Y - border_size - offset;
 	}
-	return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range()) + min_pos : 0;
+	return core::isnotzero(range()) ? s32(f32(p) / f32(w) * range() + 0.5f) + min_pos : 0;
 }
 
 void GUIScrollBar::setPos(const s32 &pos)
@@ -272,7 +272,8 @@ void GUIScrollBar::setPos(const s32 &pos)
 
 	f32 f = core::isnotzero(range()) ? (f32(thumb_area) - f32(thumb_size)) / range()
 					 : 1.0f;
-	draw_center = s32((f32(scroll_pos) * f) + (f32(thumb_size) * 0.5f)) + border_size;
+	draw_center = s32((f32(scroll_pos - min_pos) * f) + (f32(thumb_size) * 0.5f)) +
+		border_size;
 }
 
 void GUIScrollBar::setSmallStep(const s32 &step)
@@ -315,6 +316,12 @@ void GUIScrollBar::setPageSize(const s32 &size)
 	setPos(scroll_pos);
 }
 
+void GUIScrollBar::setArrowsVisible(ArrowVisibility visible)
+{
+	arrow_visibility = visible;
+	refreshControls();
+}
+
 s32 GUIScrollBar::getPos() const
 {
 	return scroll_pos;
@@ -419,7 +426,21 @@ void GUIScrollBar::refreshControls()
 		down_button->setAlignment(EGUIA_UPPERLEFT, EGUIA_LOWERRIGHT,
 				EGUIA_LOWERRIGHT, EGUIA_LOWERRIGHT);
 	}
-	bool visible = (border_size != 0);
+
+	bool visible;
+	if (arrow_visibility == DEFAULT)
+		visible = (border_size != 0);
+	else if (arrow_visibility == HIDE) {
+		visible = false;
+		border_size = 0;
+	} else {
+		visible = true;
+		if (is_horizontal)
+			border_size = RelativeRect.getHeight();
+		else
+			border_size = RelativeRect.getWidth();
+	}
+
 	up_button->setVisible(visible);
 	down_button->setVisible(visible);
 }

+ 8 - 0
src/gui/guiScrollBar.h

@@ -23,6 +23,12 @@ public:
 	GUIScrollBar(IGUIEnvironment *environment, IGUIElement *parent, s32 id,
 			core::rect<s32> rectangle, bool horizontal, bool auto_scale);
 
+	enum ArrowVisibility {
+		HIDE,
+		SHOW,
+		DEFAULT
+	};
+
 	virtual void draw();
 	virtual void updateAbsolutePosition();
 	virtual bool OnEvent(const SEvent &event);
@@ -39,6 +45,7 @@ public:
 	void setLargeStep(const s32 &step);
 	void setPos(const s32 &pos);
 	void setPageSize(const s32 &size);
+	void setArrowsVisible(ArrowVisibility visible);
 
 private:
 	void refreshControls();
@@ -47,6 +54,7 @@ private:
 
 	IGUIButton *up_button;
 	IGUIButton *down_button;
+	ArrowVisibility arrow_visibility = DEFAULT;
 	bool is_dragging;
 	bool is_horizontal;
 	bool is_auto_scaling;