From 3638d93b77ab444318ec1fc08284a0a69f2e84c5 Mon Sep 17 00:00:00 2001 From: Jean-Patrick Guerrero Date: Sat, 8 Jun 2024 11:06:31 +0200 Subject: [PATCH] Allow to bind supertip to a formspec element --- doc/lua_api.md | 12 +++ src/gui/guiFormSpecMenu.cpp | 210 +++++++++++++++++++++++++----------- src/gui/guiFormSpecMenu.h | 9 +- 3 files changed, 169 insertions(+), 62 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index e65c899c2..70547a57e 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -2849,6 +2849,18 @@ Elements * `bgcolor` tooltip background color as `ColorString` (optional) * `fontcolor` tooltip font color as `ColorString` (optional) +### `supertip[;;;;]` + +* Adds an advanced tooltip for an element. Displays a formatted text using + `Markup Language` in a tooltip. + This supertip have to be declared *after* its parent element that is bound to. +* `staticPos` is an optional position of the form `posX,posY` in formspec coordinates. + If specified, the tooltip will always appear at these given formspec coordinates. + If this field is empty, the tooltip will follow the cursor. +* `width` sets the tooltip width (in formspec units). +* `name` is the name of the field. +* `text` is the formatted text using `Markup Language` described below. + ### `supertip[,;,;;;;]` * Adds an advanced tooltip for an area. Displays a formatted text using diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 59334a59f..c2a629732 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -1758,75 +1758,135 @@ void GUIFormSpecMenu::parseSuperTip(parserData *data, const std::string &element { std::vector parts; - if (!precheckElement("supertip", element, 6, 6, parts)) + if (!precheckElement("supertip", element, 5, 6, parts)) return; - std::vector v_pos = split(parts[0], ','); - std::vector v_geom = split(parts[1], ','); + // Get mode and check size + bool rect_mode = parts[0].find(',') != std::string::npos; + size_t base_size = rect_mode ? 6 : 5; + if (parts.size() != base_size && parts.size() != base_size + 2) { + errorstream << "Invalid supertip element(" << parts.size() << "): '" + << element << "'" << std::endl; + return; + } - MY_CHECKPOS("supertip", 0); - MY_CHECKGEOM("supertip", 1); + if (rect_mode) { + std::vector v_pos = split(parts[0], ','); + std::vector v_geom = split(parts[1], ','); - std::vector v_stpos; - bool floating = true; - if(parts[2] != "") { - v_stpos = split(parts[2], ','); - if (v_stpos.size() != 2) { - errorstream << "Invalid staticPos in supertip element(" << parts.size() << - "): \"" << parts[2] << "\"" << std::endl; - return; + MY_CHECKPOS("supertip", 0); + MY_CHECKGEOM("supertip", 1); + + std::vector v_stpos; + bool floating = true; + if(parts[2] != "") { + v_stpos = split(parts[2], ','); + if (v_stpos.size() != 2) { + errorstream << "Invalid staticPos in supertip element(" << parts.size() << + "): \"" << parts[2] << "\"" << std::endl; + return; + } + floating = false; } - floating = false; - } - s32 width = stof(parts[3]) * spacing.Y; - std::string name = parts[4]; - std::string text = parts[5]; + s32 width = stof(parts[3]) * spacing.Y; + std::string name = parts[4]; + std::string text = parts[5]; - v2s32 pos; - v2s32 geom; - v2s32 stpos; + v2s32 pos; + v2s32 geom; + v2s32 stpos; - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + geom = getRealCoordinateGeometry(v_geom); - if (!floating) - stpos = getRealCoordinateBasePos(v_stpos); + if (!floating) + stpos = getRealCoordinateBasePos(v_stpos); + } else { + pos = getElementBasePos(&v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + + if (!floating) + stpos = getElementBasePos(&v_stpos); + } + + core::rect rect(pos, pos + geom); + + if (m_form_src) + text = m_form_src->resolveText(text); + + FieldSpec spec( + name, + translate_string(utf8_to_wide(unescape_string(text))), + L"", + 258 + m_fields.size() + ); + + GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, + data->current_parent, spec.fid, rect, m_client, m_tsrc); + + auto style = getStyleForElement("supertip", spec.fname); + e->setStyles(style); + + SuperTipSpec geospec(name, text, e->getAbsoluteClippingRect(), stpos, width, floating); + + m_fields.push_back(spec); + m_supertips.emplace_back(e, geospec); + + e->setVisible(false); + e->drop(); } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; + std::string fieldname = parts[0]; + core::rect rect; - if (!floating) - stpos = getElementBasePos(&v_stpos); + for (const auto &f : m_fields) { + if (f.fname == fieldname) { + auto *e = getElementFromId(f.fid, true); + rect = e->getAbsoluteClippingRect(); + break; + } + } + + std::vector v_stpos; + bool floating = true; + if(parts[1] != "") { + v_stpos = split(parts[1], ','); + if (v_stpos.size() != 2) { + errorstream << "Invalid staticPos in supertip element(" << parts.size() << + "): \"" << parts[2] << "\"" << std::endl; + return; + } + floating = false; + } + + s32 width = stof(parts[2]) * spacing.Y; + v2s32 stpos; + + if (!floating) { + if (data->real_coordinates) + stpos = getRealCoordinateBasePos(v_stpos); + else + stpos = getElementBasePos(&v_stpos); + } + + std::string name = parts[3]; + std::string text = parts[4]; + + if (m_form_src) + text = m_form_src->resolveText(text); + + FieldSpec spec( + name, + translate_string(utf8_to_wide(unescape_string(text))), + L"", + 258 + m_fields.size() + ); + + m_fields.push_back(spec); + m_supertip_map[fieldname] = SuperTipSpec(name, text, rect, stpos, width, floating); } - - core::rect rect(pos, pos + geom); - - if (m_form_src) - text = m_form_src->resolveText(text); - - FieldSpec spec( - name, - translate_string(utf8_to_wide(unescape_string(text))), - L"", - 258 + m_fields.size() - ); - - GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, - data->current_parent, spec.fid, rect, m_client, m_tsrc); - - auto style = getStyleForElement("supertip", spec.fname); - e->setStyles(style); - - SuperTipSpec geospec(e->getAbsoluteClippingRect(), stpos, width, floating); - - m_fields.push_back(spec); - m_supertips.emplace_back(e, geospec); - - e->setVisible(false); - e->drop(); } void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) @@ -3190,6 +3250,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_fields.clear(); m_tooltips.clear(); m_supertips.clear(); + m_supertip_map.clear(); m_tooltip_rects.clear(); m_inventory_rings.clear(); m_dropdowns.clear(); @@ -3722,12 +3783,16 @@ void GUIFormSpecMenu::drawMenu() /* Draw supertip */ - for (const auto &pair : m_supertips) { - const auto &hover_rect = pair.second.hover_rect; + for (auto &pair : m_supertips) { + auto &spec = pair.second; + const auto &hover_rect = spec.hover_rect; + if (hover_rect.getArea() > 0 && hover_rect.isPointInside(m_pointer)) - showSuperTip(pair.first, pair.second); - else + showSuperTip(pair.first, spec); + else { pair.first->setVisible(false); + spec.bound = false; + } } // Some elements are only visible while being drawn @@ -3812,6 +3877,29 @@ void GUIFormSpecMenu::drawMenu() if (!text.empty()) showTooltip(text, m_tooltips[field.fname].color, m_tooltips[field.fname].bgcolor); + + if (m_supertip_map.count(field.fname) != 0) { + auto &spec = m_supertip_map[field.fname]; + + if (!spec.bound) { + spec.bound = true; + auto *parent_element = getElementFromId(field.fid, true); + auto txt = translate_string(utf8_to_wide(unescape_string(spec.text))); + + GUIHyperText *e = new GUIHyperText( + txt.c_str(), Environment, + parent_element->getParent(), field.fid, + spec.hover_rect, m_client, m_tsrc); + + auto style = getStyleForElement("supertip", spec.name); + e->setStyles(style); + + m_supertips.emplace_back(e, spec); + + e->setVisible(false); + e->drop(); + } + } } if (cursor_control && diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 1296aacd9..77a8dba6a 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -169,8 +169,11 @@ class GUIFormSpecMenu : public GUIModalMenu struct SuperTipSpec { SuperTipSpec() = default; - SuperTipSpec(const core::rect &a_rect, v2s32 a_stpos, s32 a_width, + SuperTipSpec(const std::string &a_name, const std::string &a_text, + const core::rect &a_rect, v2s32 a_stpos, s32 a_width, bool a_floating) : + name(a_name), + text(a_text), hover_rect(a_rect), stpos(a_stpos), width(a_width), @@ -178,10 +181,13 @@ class GUIFormSpecMenu : public GUIModalMenu { } + std::string name; + std::string text; core::rect hover_rect; v2s32 stpos; s32 width; bool floating; + bool bound = false; }; public: @@ -363,6 +369,7 @@ class GUIFormSpecMenu : public GUIModalMenu std::vector> m_checkboxes; std::map m_tooltips; std::vector> m_supertips; + std::map m_supertip_map; std::vector> m_tooltip_rects; std::vector> m_scrollbars; std::vector>> m_dropdowns;