Add padding[] element to formspecs (#11821)

This commit is contained in:
Vincent Robinson 2021-12-30 12:54:47 -08:00 committed by GitHub
parent 4a16ab3585
commit 544b9d5c72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 149 additions and 24 deletions

@ -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 = {

@ -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", {

@ -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.

@ -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);