From 544b9d5c72f690d6a729053616d26e023f7e0e28 Mon Sep 17 00:00:00 2001 From: Vincent Robinson Date: Thu, 30 Dec 2021 12:54:47 -0800 Subject: [PATCH] Add padding[] element to formspecs (#11821) --- doc/lua_api.txt | 17 +++- games/devtest/mods/testformspec/formspec.lua | 65 +++++++++++++-- src/gui/guiFormSpecMenu.cpp | 88 ++++++++++++++++---- src/gui/guiFormSpecMenu.h | 3 + 4 files changed, 149 insertions(+), 24 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index 1950659ab..0879dcfb5 100644 --- a/doc/lua_api.txt +++ b/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[,]` * 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[]` @@ -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 = { diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 501b5e354..c0db695b7 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/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", { diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index dfeea12db..2cf9d9942 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/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 parts = split(element, ','); + std::vector 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 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 parts = split(element, ','); + std::vector 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 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 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 parts = split(element, ';'); + + if (parts.size() == 1 || + (parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) { + std::vector 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 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. diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 4ba9f3959..0b4d3879d 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -368,6 +368,7 @@ private: v2s32 size; v2f32 offset; v2f32 anchor; + v2f32 padding; core::rect 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);