Browse Source

Add padding[] element to formspecs (#11821)

Vincent Robinson 2 years ago
parent
commit
544b9d5c72
4 changed files with 149 additions and 24 deletions
  1. 14 3
      doc/lua_api.txt
  2. 59 6
      games/devtest/mods/testformspec/formspec.lua
  3. 73 15
      src/gui/guiFormSpecMenu.cpp
  4. 3 0
      src/gui/guiFormSpecMenu.h

+ 14 - 3
doc/lua_api.txt

@@ -117,7 +117,7 @@ Menu music
 -----------
 
 Games can provide custom main menu music. They are put inside a `menu`
-directory inside the game directory. 
+directory inside the game directory.
 
 The music files are named `theme.ogg`.
 If you want to specify multiple music files for one game, add additional
@@ -2326,9 +2326,20 @@ Elements
 * `position` and `anchor` elements need suitable values to avoid a formspec
   extending off the game window due to particular game window sizes.
 
-### `no_prepend[]`
+### `padding[<X>,<Y>]`
 
 * Must be used after the `size`, `position`, and `anchor` elements (if present).
+* Defines how much space is padded around the formspec if the formspec tries to
+  increase past the size of the screen and coordinates have to be shrunk.
+* For X and Y, 0.0 represents no padding (the formspec can touch the edge of the
+  screen), and 0.5 represents half the screen (which forces the coordinate size
+  to 0). If negative, the formspec can extend off the edge of the screen.
+* Defaults to [0.05, 0.05].
+
+### `no_prepend[]`
+
+* Must be used after the `size`, `position`, `anchor`, and `padding` elements
+  (if present).
 * Disables player:set_formspec_prepend() from applying to this formspec.
 
 ### `real_coordinates[<bool>]`
@@ -7915,7 +7926,7 @@ Used by `minetest.register_node`.
                     items = {"default:sand", "default:desert_sand"},
                 },
                 {
-                    -- Only drop if using an item in the "magicwand" group, or 
+                    -- Only drop if using an item in the "magicwand" group, or
                     -- an item that is in both the "pickaxe" and the "lucky"
                     -- groups.
                     tool_groups = {

+ 59 - 6
games/devtest/mods/testformspec/formspec.lua

@@ -270,6 +270,16 @@ local scroll_fs =
 --style_type[label;border=;bgcolor=]
 --label[0.75,2;Reset]
 
+local window = {
+	sizex = 12,
+	sizey = 13,
+	positionx = 0.5,
+	positiony = 0.5,
+	anchorx = 0.5,
+	anchory = 0.5,
+	paddingx = 0.05,
+	paddingy = 0.05
+}
 
 local pages = {
 	-- Real Coordinates
@@ -341,9 +351,28 @@ local pages = {
 		"size[12,13]real_coordinates[true]" ..
 		"container[0.5,1.5]" .. tabheaders_fs .. "container_end[]",
 
-		-- Inv
+	-- Inv
 		"size[12,13]real_coordinates[true]" .. inv_style_fs,
 
+	-- Window
+		function()
+			return "formspec_version[3]" ..
+				string.format("size[%s,%s]position[%s,%s]anchor[%s,%s]padding[%s,%s]",
+					window.sizex, window.sizey, window.positionx, window.positiony,
+					window.anchorx, window.anchory, window.paddingx, window.paddingy) ..
+				string.format("field[0.5,0.5;2.5,0.5;sizex;X Size;%s]field[3.5,0.5;2.5,0.5;sizey;Y Size;%s]" ..
+					"field[0.5,1.5;2.5,0.5;positionx;X Position;%s]field[3.5,1.5;2.5,0.5;positiony;Y Position;%s]" ..
+					"field[0.5,2.5;2.5,0.5;anchorx;X Anchor;%s]field[3.5,2.5;2.5,0.5;anchory;Y Anchor;%s]" ..
+					"field[0.5,3.5;2.5,0.5;paddingx;X Padding;%s]field[3.5,3.5;2.5,0.5;paddingy;Y Padding;%s]" ..
+					"button[2,4.5;2.5,0.5;submit_window;Submit]",
+					window.sizex, window.sizey, window.positionx, window.positiony,
+					window.anchorx, window.anchory, window.paddingx, window.paddingy) ..
+				"field_close_on_enter[sizex;false]field_close_on_enter[sizey;false]" ..
+				"field_close_on_enter[positionx;false]field_close_on_enter[positiony;false]" ..
+				"field_close_on_enter[anchorx;false]field_close_on_enter[anchory;false]" ..
+				"field_close_on_enter[paddingx;false]field_close_on_enter[paddingy;false]"
+		end,
+
 	-- Animation
 		[[
 			formspec_version[3]
@@ -403,10 +432,14 @@ mouse control = true]
 		]],
 }
 
-local function show_test_formspec(pname, page_id)
-	page_id = page_id or 2
+local page_id = 2
+local function show_test_formspec(pname)
+	local page = pages[page_id]
+	if type(page) == "function" then
+		page = page()
+	end
 
-	local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Anim,Model,ScrollC,Sound;" .. page_id .. ";false;false]"
+	local fs = page .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,Hypertext,Tabs,Invs,Window,Anim,Model,ScrollC,Sound;" .. page_id .. ";false;false]"
 
 	minetest.show_formspec(pname, "testformspec:formspec", fs)
 end
@@ -416,9 +449,9 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
 		return false
 	end
 
-
 	if fields.maintabs then
-		show_test_formspec(player:get_player_name(), tonumber(fields.maintabs))
+		page_id = tonumber(fields.maintabs)
+		show_test_formspec(player:get_player_name())
 		return true
 	end
 
@@ -434,6 +467,26 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
 		minetest.chat_send_player(player:get_player_name(), "Hypertext action received: " .. tostring(fields.hypertext))
 		return true
 	end
+
+	for name, value in pairs(fields) do
+		if window[name] then
+			print(name, window[name])
+			local num_val = tonumber(value) or 0
+
+			if name == "sizex" and num_val < 4 then
+				num_val = 6.5
+			elseif name == "sizey" and num_val < 5 then
+				num_val = 5.5
+			end
+
+			window[name] = num_val
+			print(name, window[name])
+		end
+	end
+
+	if fields.submit_window then
+		show_test_formspec(player:get_player_name())
+	end
 end)
 
 minetest.register_chatcommand("test_formspec", {

+ 73 - 15
src/gui/guiFormSpecMenu.cpp

@@ -2470,11 +2470,16 @@ bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &e
 
 void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element)
 {
-	std::vector<std::string> parts = split(element, ',');
+	std::vector<std::string> parts = split(element, ';');
 
-	if (parts.size() == 2) {
-		data->offset.X = stof(parts[0]);
-		data->offset.Y = stof(parts[1]);
+	if (parts.size() == 1 ||
+			(parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) {
+		std::vector<std::string> v_geom = split(parts[0], ',');
+
+		MY_CHECKGEOM("position", 0);
+
+		data->offset.X = stof(v_geom[0]);
+		data->offset.Y = stof(v_geom[1]);
 		return;
 	}
 
@@ -2504,11 +2509,16 @@ bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &ele
 
 void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
 {
-	std::vector<std::string> parts = split(element, ',');
+	std::vector<std::string> parts = split(element, ';');
 
-	if (parts.size() == 2) {
-		data->anchor.X = stof(parts[0]);
-		data->anchor.Y = stof(parts[1]);
+	if (parts.size() == 1 ||
+			(parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) {
+		std::vector<std::string> v_geom = split(parts[0], ',');
+
+		MY_CHECKGEOM("anchor", 0);
+
+		data->anchor.X = stof(v_geom[0]);
+		data->anchor.Y = stof(v_geom[1]);
 		return;
 	}
 
@@ -2516,6 +2526,46 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
 			<< "'" << std::endl;
 }
 
+bool GUIFormSpecMenu::parsePaddingDirect(parserData *data, const std::string &element)
+{
+	if (element.empty())
+		return false;
+
+	std::vector<std::string> parts = split(element, '[');
+
+	if (parts.size() != 2)
+		return false;
+
+	std::string type = trim(parts[0]);
+	std::string description = trim(parts[1]);
+
+	if (type != "padding")
+		return false;
+
+	parsePadding(data, description);
+
+	return true;
+}
+
+void GUIFormSpecMenu::parsePadding(parserData *data, const std::string &element)
+{
+	std::vector<std::string> parts = split(element, ';');
+
+	if (parts.size() == 1 ||
+			(parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) {
+		std::vector<std::string> v_geom = split(parts[0], ',');
+
+		MY_CHECKGEOM("padding", 0);
+
+		data->padding.X = stof(v_geom[0]);
+		data->padding.Y = stof(v_geom[1]);
+		return;
+	}
+
+	errorstream << "Invalid padding element (" << parts.size() << "): '" << element
+			<< "'" << std::endl;
+}
+
 bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, bool style_type)
 {
 	std::vector<std::string> parts = split(element, ';');
@@ -3022,6 +3072,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 	mydata.screensize = screensize;
 	mydata.offset = v2f32(0.5f, 0.5f);
 	mydata.anchor = v2f32(0.5f, 0.5f);
+	mydata.padding = v2f32(0.05f, 0.05f);
 	mydata.simple_field_count = 0;
 
 	// Base position of contents of form
@@ -3124,7 +3175,14 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 		}
 	}
 
-	/* "no_prepend" element is always after "position" (or  "size" element) if it used */
+	/* "padding" element is always after "anchor" and previous if it is used */
+	for (; i < elements.size(); i++) {
+		if (!parsePaddingDirect(&mydata, elements[i])) {
+			break;
+		}
+	}
+
+	/* "no_prepend" element is always after "padding" and previous if it used */
 	bool enable_prepends = true;
 	for (; i < elements.size(); i++) {
 		if (elements[i].empty())
@@ -3189,11 +3247,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 			double fitx_imgsize;
 			double fity_imgsize;
 
-			// Pad the screensize with 5% of the screensize on all sides to ensure
-			// that even the largest formspecs don't touch the screen borders.
 			v2f padded_screensize(
-				mydata.screensize.X * 0.9f,
-				mydata.screensize.Y * 0.9f
+				mydata.screensize.X * (1.0f - mydata.padding.X * 2.0f),
+				mydata.screensize.Y * (1.0f - mydata.padding.Y * 2.0f)
 			);
 
 			if (mydata.real_coordinates) {
@@ -3209,13 +3265,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
 						((15.0 / 13.0) * (0.85 + mydata.invsize.Y));
 			}
 
+			s32 min_screen_dim = std::min(mydata.screensize.X, mydata.screensize.Y);
+
 #ifdef HAVE_TOUCHSCREENGUI
 			// In Android, the preferred imgsize should be larger to accommodate the
 			// smaller screensize.
-			double prefer_imgsize = padded_screensize.Y / 10 * gui_scaling;
+			double prefer_imgsize = min_screen_dim / 10 * gui_scaling;
 #else
 			// Desktop computers have more space, so try to fit 15 coordinates.
-			double prefer_imgsize = padded_screensize.Y / 15 * gui_scaling;
+			double prefer_imgsize = min_screen_dim / 15 * gui_scaling;
 #endif
 			// Try to use the preferred imgsize, but if that's bigger than the maximum
 			// size, use the maximum size.

+ 3 - 0
src/gui/guiFormSpecMenu.h

@@ -368,6 +368,7 @@ private:
 		v2s32 size;
 		v2f32 offset;
 		v2f32 anchor;
+		v2f32 padding;
 		core::rect<s32> rect;
 		v2s32 basepos;
 		v2u32 screensize;
@@ -449,6 +450,8 @@ private:
 	void parsePosition(parserData *data, const std::string &element);
 	bool parseAnchorDirect(parserData *data, const std::string &element);
 	void parseAnchor(parserData *data, const std::string &element);
+	bool parsePaddingDirect(parserData *data, const std::string &element);
+	void parsePadding(parserData *data, const std::string &element);
 	bool parseStyle(parserData *data, const std::string &element, bool style_type);
 	void parseSetFocus(const std::string &element);
 	void parseModel(parserData *data, const std::string &element);