From 4255ac302287f6ac1cfa0135c5748a4020b20ae3 Mon Sep 17 00:00:00 2001 From: grorp <82708541+grorp@users.noreply.github.com> Date: Sat, 25 Nov 2023 17:04:33 +0100 Subject: [PATCH] Mainmenu: Avoid the header being displayed behind the formspec (#13924) This change keeps the current header placement code, but adds additional code to make sure the header doesn't end up behind the formspec. --- src/gui/guiEngine.cpp | 52 ++++++++++++++++++++++++++++++------- src/gui/guiFormSpecMenu.cpp | 10 +++++++ src/gui/guiFormSpecMenu.h | 6 +++++ 3 files changed, 58 insertions(+), 10 deletions(-) diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 64b2e9fa5..fd1121346 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -283,11 +283,16 @@ void GUIEngine::run() else drawBackground(driver); - drawHeader(driver); drawFooter(driver); m_rendering_engine->get_gui_env()->drawAll(); + // The header *must* be drawn after the menu because it uses + // GUIFormspecMenu::getAbsoluteRect(). + // The header *can* be drawn after the menu because it never intersects + // the menu. + drawHeader(driver); + driver->endScene(); IrrlichtDevice *device = m_rendering_engine->get_raw_device(); @@ -478,29 +483,56 @@ void GUIEngine::drawHeader(video::IVideoDriver *driver) video::ITexture* texture = m_textures[TEX_LAYER_HEADER].texture; - /* If no texture, draw nothing */ - if(!texture) + // If no texture, draw nothing + if (!texture) return; + /* + * Calculate the maximum rectangle + */ + core::rect formspec_rect = m_menu->getAbsoluteRect(); + // 4 px of padding on each side + core::rect max_rect(4, 4, screensize.Width - 8, formspec_rect.UpperLeftCorner.Y - 8); + + // If no space (less than 16x16 px), draw nothing + if (max_rect.getWidth() < 16 || max_rect.getHeight() < 16) + return; + + /* + * Calculate the preferred rectangle + */ f32 mult = (((f32)screensize.Width / 2.0)) / ((f32)texture->getOriginalSize().Width); v2s32 splashsize(((f32)texture->getOriginalSize().Width) * mult, ((f32)texture->getOriginalSize().Height) * mult); - // Don't draw the header if there isn't enough room s32 free_space = (((s32)screensize.Height)-320)/2; - if (free_space > splashsize.Y) { - core::rect splashrect(0, 0, splashsize.X, splashsize.Y); - splashrect += v2s32((screensize.Width/2)-(splashsize.X/2), - ((free_space/2)-splashsize.Y/2)+10); + core::rect desired_rect(0, 0, splashsize.X, splashsize.Y); + desired_rect += v2s32((screensize.Width/2)-(splashsize.X/2), + ((free_space/2)-splashsize.Y/2)+10); - draw2DImageFilterScaled(driver, texture, splashrect, + /* + * Make the preferred rectangle fit into the maximum rectangle + */ + // 1. Scale + f32 scale = std::min((f32)max_rect.getWidth() / (f32)desired_rect.getWidth(), + (f32)max_rect.getHeight() / (f32)desired_rect.getHeight()); + if (scale < 1.0f) { + v2s32 old_center = desired_rect.getCenter(); + desired_rect.LowerRightCorner.X = desired_rect.UpperLeftCorner.X + desired_rect.getWidth() * scale; + desired_rect.LowerRightCorner.Y = desired_rect.UpperLeftCorner.Y + desired_rect.getHeight() * scale; + desired_rect += old_center - desired_rect.getCenter(); + } + + // 2. Move + desired_rect.constrainTo(max_rect); + + draw2DImageFilterScaled(driver, texture, desired_rect, core::rect(core::position2d(0,0), core::dimension2di(texture->getOriginalSize())), NULL, NULL, true); - } } /******************************************************************************/ diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index a51a02669..1cd9ee85f 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -247,6 +247,14 @@ std::vector* GUIFormSpecMenu::getDropDownValues(const std::string & return NULL; } +// This will only return a meaningful value if called after drawMenu(). +core::rect GUIFormSpecMenu::getAbsoluteRect() +{ + core::rect rect = AbsoluteRect; + rect.UpperLeftCorner.Y += m_tabheader_upper_edge; + return rect; +} + v2s32 GUIFormSpecMenu::getElementBasePos(const std::vector *v_pos) { v2f32 pos_f = v2f32(padding.X, padding.Y) + pos_offset * spacing; @@ -2104,6 +2112,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen e->setActiveTab(tab_index); m_fields.push_back(spec); + m_tabheader_upper_edge = MYMIN(m_tabheader_upper_edge, rect.UpperLeftCorner.Y); } void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element) @@ -3105,6 +3114,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_formspec_version = 1; m_bgcolor = video::SColor(140, 0, 0, 0); + m_tabheader_upper_edge = 0; { v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color"); diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 5641e39a9..eb464747f 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -282,6 +282,9 @@ public: GUITable* getTable(const std::string &tablename); std::vector* getDropDownValues(const std::string &name); + // This will only return a meaningful value if called after drawMenu(). + core::rect getAbsoluteRect(); + #ifdef __ANDROID__ bool getAndroidUIInput(); #endif @@ -499,6 +502,9 @@ private: int m_btn_height; gui::IGUIFont *m_font = nullptr; + + // used by getAbsoluteRect + s32 m_tabheader_upper_edge = 0; }; class FormspecFormSource: public IFormSource