Skip to content
Permalink
Browse files
Add padding[] element to formspecs (#11821)
  • Loading branch information
v-rob committed Dec 30, 2021
1 parent 4a16ab3 commit 544b9d5c72f690d6a729053616d26e023f7e0e28
Showing 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
@@ -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,18 +2509,63 @@ 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;
}

errorstream << "Invalid anchor element (" << parts.size() << "): '" << 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 @@ class GUIFormSpecMenu : public GUIModalMenu
v2s32 size;
v2f32 offset;
v2f32 anchor;
v2f32 padding;
core::rect<s32> rect;
v2s32 basepos;
v2u32 screensize;
@@ -449,6 +450,8 @@ class GUIFormSpecMenu : public GUIModalMenu
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);

0 comments on commit 544b9d5

Please sign in to comment.