diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua index 64ac37f03..e53814751 100644 --- a/builtin/fstk/buttonbar.lua +++ b/builtin/fstk/buttonbar.lua @@ -28,10 +28,8 @@ local function buttonbar_formspec(self) end local formspec = { - "style_type[box;noclip=true]", string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x, self.size.y, self.bgcolor), - "style_type[box;noclip=false]", } local btn_size = self.size.y - 2*BASE_SPACING @@ -71,7 +69,7 @@ local function buttonbar_formspec(self) 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.caption, btn.name, btn.tooltip)) end @@ -86,9 +84,6 @@ local function buttonbar_formspec(self) 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;<]", btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size, self.btn_prev_name)) diff --git a/builtin/fstk/tabview.lua b/builtin/fstk/tabview.lua index f09c4df2d..9f8889143 100644 --- a/builtin/fstk/tabview.lua +++ b/builtin/fstk/tabview.lua @@ -66,11 +66,22 @@ local function get_formspec(self) 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 prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height, 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 prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend end @@ -78,12 +89,15 @@ local function get_formspec(self) 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 tab_header_size.width = tab_header_size.width - end_button_size - 0.1 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 formspec = formspec .. @@ -98,6 +112,8 @@ local function get_formspec(self) self.end_button.name) end + formspec = formspec .. "container_end[]" + return formspec end diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 41885e298..dd35334c2 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -23,6 +23,13 @@ mt_color_dark_green = "#25C191" mt_color_orange = "#FF8800" 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 basepath = core.get_builtin_path() 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. -- 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:add(tabs.local_game) diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 7f46be213..f0a7255d7 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -92,10 +92,16 @@ function singleplayer_refresh_gamebar() 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( "game_button_bar", - core.settings:get_bool("touch_gui") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, - {x = 15.5, y = 1.25}, + {x = 0, y = gamebar_pos_y}, + {x = MAIN_TAB_W, y = GAMEBAR_H}, "#000000", game_buttonbar_button_handler) diff --git a/doc/lua_api.md b/doc/lua_api.md index 6989f3483..ea728cfbe 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5580,8 +5580,8 @@ Utilities }, -- Estimated maximum formspec size before Minetest will start shrinking the - -- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than - -- this and `padding[-0.01,-0.01]`. + -- formspec to fit. For a fullscreen formspec, use this formspec size and + -- `padding[0,0]`. `bgcolor[;true]` is also recommended. max_formspec_size = { x = 20, y = 11.25 diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index df14b859d..be63af904 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -253,8 +253,8 @@ GUI }, -- Estimated maximum formspec size before Minetest will start shrinking the - -- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than - -- this and `padding[-0.01,-0.01]`. + -- formspec to fit. For a fullscreen formspec, use this formspec size and + -- `padding[0,0]`. `bgcolor[;true]` is also recommended. max_formspec_size = { x = 20, y = 11.25 diff --git a/games/devtest/mods/testfullscreenfs/init.lua b/games/devtest/mods/testfullscreenfs/init.lua index e1af3ae33..7abc7f817 100644 --- a/games/devtest/mods/testfullscreenfs/init.lua +++ b/games/devtest/mods/testfullscreenfs/init.lua @@ -1,18 +1,30 @@ -local function show_fullscreen_fs(name) - local window = minetest.get_player_window_information(name) - if not window then - return false, "Unable to get window info" - end +local function window_info_equal(a, b) + return + -- size + a.size.x == b.size.x and a.size.y == b.size.y and + -- 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)) - 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 "Touch controls disabled" local fs = { "formspec_version[4]", ("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(size.x - 1, 0, "tr", "TR"), ("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)) - 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 - 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) diff --git a/src/clientdynamicinfo.cpp b/src/clientdynamicinfo.cpp index c206018f3..12bc23abd 100644 --- a/src/clientdynamicinfo.cpp +++ b/src/clientdynamicinfo.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/renderingengine.h" +#include "gui/guiFormSpecMenu.h" #include "gui/touchcontrols.h" ClientDynamicInfo ClientDynamicInfo::getCurrent() @@ -37,19 +38,22 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent() return { screen_size, real_gui_scaling, real_hud_scaling, - ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling), + ClientDynamicInfo::calculateMaxFSSize(screen_size, density, gui_scaling), 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; - f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y; - if (ratio < 1) - return { factor, factor / ratio }; - else - return { factor * ratio, factor }; + // must stay in sync with GUIFormSpecMenu::calculateImgsize + + const double screen_dpi = density * 96; + + // assume padding[0,0] since max_formspec_size is used for fullscreen formspecs + 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 diff --git a/src/clientdynamicinfo.h b/src/clientdynamicinfo.h index 39faeeecc..c43fcb8d8 100644 --- a/src/clientdynamicinfo.h +++ b/src/clientdynamicinfo.h @@ -42,6 +42,6 @@ public: static ClientDynamicInfo getCurrent(); private: - static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling); + static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 density, f32 gui_scaling); #endif }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 8b572276c..0371c26f3 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3133,58 +3133,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) offset = v2s32(0,0); } - const double gui_scaling = g_settings->getFloat("gui_scaling", 0.5f, 42.0f); - 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)); - } + double use_imgsize = calculateImgsize(mydata); // Everything else is scaled in proportion to the // inventory image size. The inventory slot spacing @@ -5072,3 +5021,68 @@ std::array GUIFormSpecMenu::getStyleForElement 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)); +} diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 12add12e6..7c4be4301 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -296,6 +296,11 @@ public: void getAndroidUIInput(); #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: v2s32 getBasePos() const { @@ -514,6 +519,9 @@ private: // used by getAbsoluteRect s32 m_tabheader_upper_edge = 0; + + // Determines the size (in pixels) of formspec coordinate units. + double calculateImgsize(const parserData &data); }; class FormspecFormSource: public IFormSource