Improve formspec scaling (#14840)

This commit is contained in:
grorp 2024-09-06 12:11:03 +02:00 committed by GitHub
parent 1527cdf6a4
commit 041d67ceca
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 176 additions and 87 deletions

@ -28,10 +28,8 @@ local function buttonbar_formspec(self)
end end
local formspec = { local formspec = {
"style_type[box;noclip=true]",
string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x, string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x,
self.size.y, self.bgcolor), self.size.y, self.bgcolor),
"style_type[box;noclip=false]",
} }
local btn_size = self.size.y - 2*BASE_SPACING local btn_size = self.size.y - 2*BASE_SPACING
@ -71,7 +69,7 @@ local function buttonbar_formspec(self)
y = self.pos.y + BASE_SPACING, y = self.pos.y + BASE_SPACING,
} }
table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;true;false]tooltip[%s;%s]", table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;false;false]tooltip[%s;%s]",
btn_pos.x, btn_pos.y, btn_size, btn_size, btn.image, btn.name, btn_pos.x, btn_pos.y, btn_size, btn_size, btn.image, btn.name,
btn.caption, btn.name, btn.tooltip)) btn.caption, btn.name, btn.tooltip))
end end
@ -86,9 +84,6 @@ local function buttonbar_formspec(self)
y = self.pos.y + BASE_SPACING, y = self.pos.y + BASE_SPACING,
} }
table.insert(formspec, string.format("style[%s,%s;noclip=true]",
self.btn_prev_name, self.btn_next_name))
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]", table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]",
btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size, btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size,
self.btn_prev_name)) self.btn_prev_name))

@ -66,11 +66,22 @@ local function get_formspec(self)
local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize) local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize)
local tsize = tab.tabsize or { width = self.width, height = self.height } local ENABLE_TOUCH = core.settings:get_bool("enable_touch")
local orig_tsize = tab.tabsize or { width = self.width, height = self.height }
local tsize = { width = orig_tsize.width, height = orig_tsize.height }
tsize.height = tsize.height
+ TABHEADER_H -- tabheader included in formspec size
+ (ENABLE_TOUCH and GAMEBAR_OFFSET_TOUCH or GAMEBAR_OFFSET_DESKTOP)
+ GAMEBAR_H -- gamebar included in formspec size
if self.parent == nil and not prepend then if self.parent == nil and not prepend then
prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height, prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height,
dump(self.fixed_size)) dump(self.fixed_size))
local anchor_pos = TABHEADER_H + orig_tsize.height / 2
prepend = prepend .. ("anchor[0.5,%f]"):format(anchor_pos / tsize.height)
if tab.formspec_version then if tab.formspec_version then
prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend
end end
@ -78,12 +89,15 @@ local function get_formspec(self)
local end_button_size = 0.75 local end_button_size = 0.75
local tab_header_size = { width = tsize.width, height = 0.85 } local tab_header_size = { width = tsize.width, height = TABHEADER_H }
if self.end_button then if self.end_button then
tab_header_size.width = tab_header_size.width - end_button_size - 0.1 tab_header_size.width = tab_header_size.width - end_button_size - 0.1
end end
local formspec = (prepend or "") .. self:tab_header(tab_header_size) .. content local formspec = (prepend or "")
formspec = formspec .. ("bgcolor[;neither]container[0,%f]box[0,0;%f,%f;#0000008C]"):format(
TABHEADER_H, orig_tsize.width, orig_tsize.height)
formspec = formspec .. self:tab_header(tab_header_size) .. content
if self.end_button then if self.end_button then
formspec = formspec .. formspec = formspec ..
@ -98,6 +112,8 @@ local function get_formspec(self)
self.end_button.name) self.end_button.name)
end end
formspec = formspec .. "container_end[]"
return formspec return formspec
end end

@ -23,6 +23,13 @@ mt_color_dark_green = "#25C191"
mt_color_orange = "#FF8800" mt_color_orange = "#FF8800"
mt_color_red = "#FF3300" mt_color_red = "#FF3300"
MAIN_TAB_W = 15.5
MAIN_TAB_H = 7.1
TABHEADER_H = 0.85
GAMEBAR_H = 1.25
GAMEBAR_OFFSET_DESKTOP = 0.375
GAMEBAR_OFFSET_TOUCH = 0.15
local menupath = core.get_mainmenu_path() local menupath = core.get_mainmenu_path()
local basepath = core.get_builtin_path() local basepath = core.get_builtin_path()
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
@ -89,7 +96,7 @@ local function init_globals()
mm_game_theme.set_engine() -- This is just a fallback. mm_game_theme.set_engine() -- This is just a fallback.
-- Create main tabview -- Create main tabview
local tv_main = tabview_create("maintab", {x = 15.5, y = 7.1}, {x = 0, y = 0}) local tv_main = tabview_create("maintab", {x = MAIN_TAB_W, y = MAIN_TAB_H}, {x = 0, y = 0})
tv_main:set_autosave_tab(true) tv_main:set_autosave_tab(true)
tv_main:add(tabs.local_game) tv_main:add(tabs.local_game)

@ -92,10 +92,16 @@ function singleplayer_refresh_gamebar()
end end
end end
local ENABLE_TOUCH = core.settings:get_bool("enable_touch")
local gamebar_pos_y = MAIN_TAB_H
+ TABHEADER_H -- tabheader included in formspec size
+ (ENABLE_TOUCH and GAMEBAR_OFFSET_TOUCH or GAMEBAR_OFFSET_DESKTOP)
local btnbar = buttonbar_create( local btnbar = buttonbar_create(
"game_button_bar", "game_button_bar",
core.settings:get_bool("touch_gui") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, {x = 0, y = gamebar_pos_y},
{x = 15.5, y = 1.25}, {x = MAIN_TAB_W, y = GAMEBAR_H},
"#000000", "#000000",
game_buttonbar_button_handler) game_buttonbar_button_handler)

@ -5580,8 +5580,8 @@ Utilities
}, },
-- Estimated maximum formspec size before Minetest will start shrinking the -- Estimated maximum formspec size before Minetest will start shrinking the
-- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than -- formspec to fit. For a fullscreen formspec, use this formspec size and
-- this and `padding[-0.01,-0.01]`. -- `padding[0,0]`. `bgcolor[;true]` is also recommended.
max_formspec_size = { max_formspec_size = {
x = 20, x = 20,
y = 11.25 y = 11.25

@ -253,8 +253,8 @@ GUI
}, },
-- Estimated maximum formspec size before Minetest will start shrinking the -- Estimated maximum formspec size before Minetest will start shrinking the
-- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than -- formspec to fit. For a fullscreen formspec, use this formspec size and
-- this and `padding[-0.01,-0.01]`. -- `padding[0,0]`. `bgcolor[;true]` is also recommended.
max_formspec_size = { max_formspec_size = {
x = 20, x = 20,
y = 11.25 y = 11.25

@ -1,18 +1,30 @@
local function show_fullscreen_fs(name) local function window_info_equal(a, b)
local window = minetest.get_player_window_information(name) return
if not window then -- size
return false, "Unable to get window info" a.size.x == b.size.x and a.size.y == b.size.y and
end -- real_gui_scaling, real_hud_scaling
a.real_gui_scaling == b.real_gui_scaling and
a.real_hud_scaling == b.real_hud_scaling and
-- max_formspec_size
a.max_formspec_size.x == b.max_formspec_size.x and
a.max_formspec_size.y == b.max_formspec_size.y and
-- touch_controls
a.touch_controls == b.touch_controls
end
local last_window_info = {}
local function show_fullscreen_fs(name, window)
print(dump(window)) print(dump(window))
local size = { x = window.max_formspec_size.x * 1.1, y = window.max_formspec_size.y * 1.1 } local size = window.max_formspec_size
local touch_text = window.touch_controls and "Touch controls enabled" or local touch_text = window.touch_controls and "Touch controls enabled" or
"Touch controls disabled" "Touch controls disabled"
local fs = { local fs = {
"formspec_version[4]", "formspec_version[4]",
("size[%f,%f]"):format(size.x, size.y), ("size[%f,%f]"):format(size.x, size.y),
"padding[-0.01,-0.01]", "padding[0,0]",
"bgcolor[;true]",
("button[%f,%f;1,1;%s;%s]"):format(0, 0, "tl", "TL"), ("button[%f,%f;1,1;%s;%s]"):format(0, 0, "tl", "TL"),
("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, 0, "tr", "TR"), ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, 0, "tr", "TR"),
("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"), ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"),
@ -23,10 +35,37 @@ local function show_fullscreen_fs(name)
} }
minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs)) minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs))
return true, ("Calculated size of %f, %f"):format(size.x, size.y) minetest.chat_send_player(name, ("Calculated size of %f, %f"):format(size.x, size.y))
last_window_info[name] = window
end end
minetest.register_chatcommand("testfullscreenfs", { minetest.register_chatcommand("testfullscreenfs", {
func = show_fullscreen_fs, func = function(name)
local window = minetest.get_player_window_information(name)
if not window then
return false, "Unable to get window info"
end
show_fullscreen_fs(name, window)
return true
end,
}) })
minetest.register_globalstep(function()
for name, last_window in pairs(last_window_info) do
local window = minetest.get_player_window_information(name)
if window and not window_info_equal(last_window, window) then
show_fullscreen_fs(name, window)
end
end
end)
minetest.register_on_player_receive_fields(function(player, formname, fields)
if formname == "testfullscreenfs:fs" and fields.quit then
last_window_info[player:get_player_name()] = nil
end
end)
minetest.register_on_leaveplayer(function(player)
last_window_info[player:get_player_name()] = nil
end)

@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "settings.h" #include "settings.h"
#include "client/renderingengine.h" #include "client/renderingengine.h"
#include "gui/guiFormSpecMenu.h"
#include "gui/touchcontrols.h" #include "gui/touchcontrols.h"
ClientDynamicInfo ClientDynamicInfo::getCurrent() ClientDynamicInfo ClientDynamicInfo::getCurrent()
@ -37,19 +38,22 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent()
return { return {
screen_size, real_gui_scaling, real_hud_scaling, screen_size, real_gui_scaling, real_hud_scaling,
ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling), ClientDynamicInfo::calculateMaxFSSize(screen_size, density, gui_scaling),
touch_controls touch_controls
}; };
} }
v2f32 ClientDynamicInfo::calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling) v2f32 ClientDynamicInfo::calculateMaxFSSize(v2u32 render_target_size, f32 density, f32 gui_scaling)
{ {
f32 factor = (g_settings->getBool("touch_gui") ? 10 : 15) / gui_scaling; // must stay in sync with GUIFormSpecMenu::calculateImgsize
f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y;
if (ratio < 1) const double screen_dpi = density * 96;
return { factor, factor / ratio };
else // assume padding[0,0] since max_formspec_size is used for fullscreen formspecs
return { factor * ratio, factor }; double prefer_imgsize = GUIFormSpecMenu::getImgsize(render_target_size,
screen_dpi, gui_scaling);
return v2f32(render_target_size.X / prefer_imgsize,
render_target_size.Y / prefer_imgsize);
} }
#endif #endif

@ -42,6 +42,6 @@ public:
static ClientDynamicInfo getCurrent(); static ClientDynamicInfo getCurrent();
private: private:
static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling); static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 density, f32 gui_scaling);
#endif #endif
}; };

@ -3133,58 +3133,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
offset = v2s32(0,0); offset = v2s32(0,0);
} }
const double gui_scaling = g_settings->getFloat("gui_scaling", 0.5f, 42.0f); double use_imgsize = calculateImgsize(mydata);
const double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
double use_imgsize;
if (m_lock) {
// In fixed-size mode, inventory image size
// is 0.53 inch multiplied by the gui_scaling
// config parameter. This magic size is chosen
// to make the main menu (15.5 inventory images
// wide, including border) just fit into the
// default window (800 pixels wide) at 96 DPI
// and default scaling (1.00).
use_imgsize = 0.5555 * screen_dpi * gui_scaling;
} else {
// Variables for the maximum imgsize that can fit in the screen.
double fitx_imgsize;
double fity_imgsize;
v2f padded_screensize(
mydata.screensize.X * (1.0f - mydata.padding.X * 2.0f),
mydata.screensize.Y * (1.0f - mydata.padding.Y * 2.0f)
);
if (mydata.real_coordinates) {
fitx_imgsize = padded_screensize.X / mydata.invsize.X;
fity_imgsize = padded_screensize.Y / mydata.invsize.Y;
} else {
// The maximum imgsize in the old coordinate system also needs to
// factor in padding and spacing along with 0.1 inventory slot spare
// and help text space, hence the magic numbers.
fitx_imgsize = padded_screensize.X /
((5.0 / 4.0) * (0.5 + mydata.invsize.X));
fity_imgsize = padded_screensize.Y /
((15.0 / 13.0) * (0.85 + mydata.invsize.Y));
}
s32 min_screen_dim = std::min(padded_screensize.X, padded_screensize.Y);
double prefer_imgsize;
if (g_settings->getBool("touch_gui")) {
// The preferred imgsize should be larger to accommodate the
// smaller screensize.
prefer_imgsize = min_screen_dim / 10 * gui_scaling;
} else {
// Desktop computers have more space, so try to fit 15 coordinates.
prefer_imgsize = min_screen_dim / 15 * gui_scaling;
}
// Try to use the preferred imgsize, but if that's bigger than the maximum
// size, use the maximum size.
use_imgsize = std::min(prefer_imgsize,
std::min(fitx_imgsize, fity_imgsize));
}
// Everything else is scaled in proportion to the // Everything else is scaled in proportion to the
// inventory image size. The inventory slot spacing // inventory image size. The inventory slot spacing
@ -5072,3 +5021,68 @@ std::array<StyleSpec, StyleSpec::NUM_STATES> GUIFormSpecMenu::getStyleForElement
return ret; return ret;
} }
double GUIFormSpecMenu::getFixedImgsize(double screen_dpi, double gui_scaling)
{
// In fixed-size mode, inventory image size
// is 0.53 inch multiplied by the gui_scaling
// config parameter. This magic size is chosen
// to make the main menu (15.5 inventory images
// wide, including border) just fit into the
// default window (800 pixels wide) at 96 DPI
// and default scaling (1.00).
return 0.5555 * screen_dpi * gui_scaling;
}
double GUIFormSpecMenu::getImgsize(v2u32 avail_screensize, double screen_dpi, double gui_scaling)
{
double fixed_imgsize = getFixedImgsize(screen_dpi, gui_scaling);
s32 min_screen_dim = std::min(avail_screensize.X, avail_screensize.Y);
double prefer_imgsize = min_screen_dim / 15 * gui_scaling;
// Use the available space more effectively on small windows/screens.
// This is especially important for mobile platforms.
prefer_imgsize = std::max(prefer_imgsize, fixed_imgsize);
return prefer_imgsize;
}
double GUIFormSpecMenu::calculateImgsize(const parserData &data)
{
// must stay in sync with ClientDynamicInfo::calculateMaxFSSize
const double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
const double gui_scaling = g_settings->getFloat("gui_scaling", 0.5f, 42.0f);
// Fixed-size mode
if (m_lock)
return getFixedImgsize(screen_dpi, gui_scaling);
// Variables for the maximum imgsize that can fit in the screen.
double fitx_imgsize;
double fity_imgsize;
v2f padded_screensize(
data.screensize.X * (1.0f - data.padding.X * 2.0f),
data.screensize.Y * (1.0f - data.padding.Y * 2.0f)
);
if (data.real_coordinates) {
fitx_imgsize = padded_screensize.X / data.invsize.X;
fity_imgsize = padded_screensize.Y / data.invsize.Y;
} else {
// The maximum imgsize in the old coordinate system also needs to
// factor in padding and spacing along with 0.1 inventory slot spare
// and help text space, hence the magic numbers.
fitx_imgsize = padded_screensize.X /
((5.0 / 4.0) * (0.5 + data.invsize.X));
fity_imgsize = padded_screensize.Y /
((15.0 / 13.0) * (0.85 + data.invsize.Y));
}
double prefer_imgsize = getImgsize(v2u32(padded_screensize.X, padded_screensize.Y),
screen_dpi, gui_scaling);
// Try to use the preferred imgsize, but if that's bigger than the maximum
// size, use the maximum size.
return std::min(prefer_imgsize, std::min(fitx_imgsize, fity_imgsize));
}

@ -296,6 +296,11 @@ public:
void getAndroidUIInput(); void getAndroidUIInput();
#endif #endif
// Returns the fixed formspec coordinate size for the given parameters.
static double getFixedImgsize(double screen_dpi, double gui_scaling);
// Returns the preferred non-fixed formspec coordinate size for the given parameters.
static double getImgsize(v2u32 avail_screensize, double screen_dpi, double gui_scaling);
protected: protected:
v2s32 getBasePos() const v2s32 getBasePos() const
{ {
@ -514,6 +519,9 @@ private:
// used by getAbsoluteRect // used by getAbsoluteRect
s32 m_tabheader_upper_edge = 0; s32 m_tabheader_upper_edge = 0;
// Determines the size (in pixels) of formspec coordinate units.
double calculateImgsize(const parserData &data);
}; };
class FormspecFormSource: public IFormSource class FormspecFormSource: public IFormSource