X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=src%2Fgui%2FguiFormSpecMenu.cpp;h=68222b319def52a913c0350da1302550494a7086;hb=69fc20610947610b7829f3bfad82e23ed705b764;hp=dfeea12db7432cd15ec906ebcb480214b8260960;hpb=14c7fae378fc40f88d3c430dea2cb726afc005b1;p=minetest.git diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index dfeea12db..68222b319 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -127,24 +127,7 @@ GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, GUIFormSpecMenu::~GUIFormSpecMenu() { - removeChildren(); - - for (auto &table_it : m_tables) - table_it.second->drop(); - for (auto &inventorylist_it : m_inventorylists) - inventorylist_it->drop(); - for (auto &checkbox_it : m_checkboxes) - checkbox_it.second->drop(); - for (auto &scrollbar_it : m_scrollbars) - scrollbar_it.second->drop(); - for (auto &background_it : m_backgrounds) - background_it->drop(); - for (auto &tooltip_rect_it : m_tooltip_rects) - tooltip_rect_it.first->drop(); - for (auto &clickthrough_it : m_clickthrough_elements) - clickthrough_it->drop(); - for (auto &scroll_container_it : m_scroll_containers) - scroll_container_it.second->drop(); + removeAll(); delete m_selected_item; delete m_form_src; @@ -176,14 +159,8 @@ void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client, } } -void GUIFormSpecMenu::removeChildren() +void GUIFormSpecMenu::removeTooltip() { - const core::list &children = getChildren(); - - while (!children.empty()) { - (*children.getLast())->remove(); - } - if (m_tooltip_element) { m_tooltip_element->remove(); m_tooltip_element->drop(); @@ -201,16 +178,7 @@ void GUIFormSpecMenu::setInitialFocus() // 5. first focusable (not statictext, not tabheader) // 6. first child element - core::list children = getChildren(); - - // in case "children" contains any NULL elements, remove them - for (core::list::Iterator it = children.begin(); - it != children.end();) { - if (*it) - ++it; - else - it = children.erase(it); - } + const auto& children = getChildren(); // 1. first empty editbox for (gui::IGUIElement *it : children) { @@ -238,8 +206,7 @@ void GUIFormSpecMenu::setInitialFocus() } // 4. last button - for (core::list::Iterator it = children.getLast(); - it != children.end(); --it) { + for (auto it = children.rbegin(); it != children.rend(); ++it) { if ((*it)->getType() == gui::EGUIET_BUTTON) { Environment->setFocus(*it); return; @@ -259,7 +226,7 @@ void GUIFormSpecMenu::setInitialFocus() if (children.empty()) Environment->setFocus(this); else - Environment->setFocus(*(children.begin())); + Environment->setFocus(children.front()); } GUITable* GUIFormSpecMenu::getTable(const std::string &tablename) @@ -711,7 +678,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen e->setMax(max); e->setMin(min); - e->setPos(stoi(parts[4])); + e->setPos(stoi(value)); e->setSmallStep(data->scrollbar_options.small_step); e->setLargeStep(data->scrollbar_options.large_step); @@ -784,101 +751,99 @@ void GUIFormSpecMenu::parseScrollBarOptions(parserData* data, const std::string void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) { std::vector parts; - if (!precheckElement("image", element, 2, 3, parts)) + if (!precheckElement("image", element, 2, 4, parts)) return; - if (parts.size() >= 3) { - std::vector v_pos = split(parts[0],','); - std::vector v_geom = split(parts[1],','); - std::string name = unescape_string(parts[2]); + size_t offset = parts.size() >= 3; + + std::vector v_pos = split(parts[0],','); + MY_CHECKPOS("image", 0); - MY_CHECKPOS("image", 0); + std::vector v_geom; + if (parts.size() >= 3) { + v_geom = split(parts[1],','); MY_CHECKGEOM("image", 1); + } - v2s32 pos; - v2s32 geom; + std::string name = unescape_string(parts[1 + offset]); + video::ITexture *texture = m_tsrc->getTexture(name); - if (data->real_coordinates) { - pos = getRealCoordinateBasePos(v_pos); - geom = getRealCoordinateGeometry(v_geom); + v2s32 pos; + v2s32 geom; + + if (parts.size() < 3) { + if (texture != nullptr) { + core::dimension2du dim = texture->getOriginalSize(); + geom.X = dim.Width; + geom.Y = dim.Height; } else { - pos = getElementBasePos(&v_pos); - geom.X = stof(v_geom[0]) * (float)imgsize.X; - geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + geom = v2s32(0); } + } - if (!data->explicit_size) - warningstream<<"invalid use of image without a size[] element"<getTexture(name); - if (!texture) { - errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" - << std::endl << "\t" << name << std::endl; - return; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(v_pos); + if (parts.size() >= 3) + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(&v_pos); + if (parts.size() >= 3) { + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; } - - FieldSpec spec( - name, - L"", - L"", - 258 + m_fields.size(), - 1 - ); - core::rect rect(pos, pos + geom); - gui::IGUIImage *e = Environment->addImage(rect, data->current_parent, - spec.fid, 0, true); - e->setImage(texture); - e->setScaleImage(true); - auto style = getDefaultStyleForElement("image", spec.fname); - e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); - m_fields.push_back(spec); - - // images should let events through - e->grab(); - m_clickthrough_elements.push_back(e); - return; } - // Else: 2 arguments in "parts" - - std::vector v_pos = split(parts[0],','); - std::string name = unescape_string(parts[1]); - - MY_CHECKPOS("image", 0); - - v2s32 pos = getElementBasePos(&v_pos); - if (!data->explicit_size) - warningstream<<"invalid use of image without a size[] element"<getTexture(name); - if (!texture) { - errorstream << "GUIFormSpecMenu::parseImage() Unable to load texture:" - << std::endl << "\t" << name << std::endl; - return; - } + warningstream << "Invalid use of image without a size[] element" << std::endl; FieldSpec spec( name, L"", L"", - 258 + m_fields.size() + 258 + m_fields.size(), + 1 ); - gui::IGUIImage *e = Environment->addImage(texture, pos, true, - data->current_parent, spec.fid, 0); + + core::rect rect = core::rect(pos, pos + geom); + + core::rect middle; + if (parts.size() >= 4) + parseMiddleRect(parts[3], &middle); + + // Temporary fix for issue #12581 in 5.6.0. + // Use legacy image when not rendering 9-slice image because GUIAnimatedImage + // uses NNAA filter which causes visual artifacts when image uses alpha blending. + + gui::IGUIElement *e; + if (middle.getArea() > 0) { + GUIAnimatedImage *image = new GUIAnimatedImage(Environment, data->current_parent, + spec.fid, rect); + + image->setTexture(texture); + image->setMiddleRect(middle); + e = image; + } + else { + gui::IGUIImage *image = Environment->addImage(rect, data->current_parent, spec.fid, nullptr, true); + image->setImage(texture); + image->setScaleImage(true); + image->grab(); // compensate for drop in addImage + e = image; + } + auto style = getDefaultStyleForElement("image", spec.fname); e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3)); - m_fields.push_back(spec); - // images should let events through - e->grab(); + // Animated images should let events through m_clickthrough_elements.push_back(e); + + m_fields.push_back(spec); } void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &element) { std::vector parts; - if (!precheckElement("animated_image", element, 6, 7, parts)) + if (!precheckElement("animated_image", element, 6, 8, parts)) return; std::vector v_pos = split(parts[0], ','); @@ -904,7 +869,8 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el } if (!data->explicit_size) - warningstream << "Invalid use of animated_image without a size[] element" << std::endl; + warningstream << "Invalid use of animated_image without a size[] element" + << std::endl; FieldSpec spec( name, @@ -917,9 +883,17 @@ void GUIFormSpecMenu::parseAnimatedImage(parserData *data, const std::string &el core::rect rect = core::rect(pos, pos + geom); - GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, spec.fid, - rect, texture_name, frame_count, frame_duration, m_tsrc); + core::rect middle; + if (parts.size() >= 8) + parseMiddleRect(parts[7], &middle); + + GUIAnimatedImage *e = new GUIAnimatedImage(Environment, data->current_parent, + spec.fid, rect); + e->setTexture(m_tsrc->getTexture(texture_name)); + e->setMiddleRect(middle); + e->setFrameDuration(frame_duration); + e->setFrameCount(frame_count); if (parts.size() >= 7) e->setFrameIndex(stoi(parts[6]) - 1); @@ -1044,6 +1018,35 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, m_fields.push_back(spec); } +bool GUIFormSpecMenu::parseMiddleRect(const std::string &value, core::rect *parsed_rect) +{ + core::rect rect; + std::vector v_rect = split(value, ','); + + if (v_rect.size() == 1) { + s32 x = stoi(v_rect[0]); + rect.UpperLeftCorner = core::vector2di(x, x); + rect.LowerRightCorner = core::vector2di(-x, -x); + } else if (v_rect.size() == 2) { + s32 x = stoi(v_rect[0]); + s32 y = stoi(v_rect[1]); + rect.UpperLeftCorner = core::vector2di(x, y); + rect.LowerRightCorner = core::vector2di(-x, -y); + // `-x` is interpreted as `w - x` + } else if (v_rect.size() == 4) { + rect.UpperLeftCorner = core::vector2di(stoi(v_rect[0]), stoi(v_rect[1])); + rect.LowerRightCorner = core::vector2di(stoi(v_rect[2]), stoi(v_rect[3])); + } else { + warningstream << "Invalid rectangle string format: \"" << value + << "\"" << std::endl; + return false; + } + + *parsed_rect = rect; + + return true; +} + void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element) { std::vector parts; @@ -1085,25 +1088,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme } core::rect middle; - if (parts.size() >= 5) { - std::vector v_middle = split(parts[4], ','); - if (v_middle.size() == 1) { - s32 x = stoi(v_middle[0]); - middle.UpperLeftCorner = core::vector2di(x, x); - middle.LowerRightCorner = core::vector2di(-x, -x); - } else if (v_middle.size() == 2) { - s32 x = stoi(v_middle[0]); - s32 y = stoi(v_middle[1]); - middle.UpperLeftCorner = core::vector2di(x, y); - middle.LowerRightCorner = core::vector2di(-x, -y); - // `-x` is interpreted as `w - x` - } else if (v_middle.size() == 4) { - middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1])); - middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3])); - } else { - warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl; - } - } + if (parts.size() >= 5) + parseMiddleRect(parts[4], &middle); if (!data->explicit_size && !clip) warningstream << "invalid use of unclipped background without a size[] element" << std::endl; @@ -1124,17 +1110,15 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme rect = core::rect(-pos, pos); } - GUIBackgroundImage *e = new GUIBackgroundImage(Environment, this, spec.fid, - rect, name, middle, m_tsrc, clip); + GUIBackgroundImage *e = new GUIBackgroundImage(Environment, data->background_parent.get(), + spec.fid, rect, name, middle, m_tsrc, clip); FATAL_ERROR_IF(!e, "Failed to create background formspec element"); e->setNotClipped(true); - e->setVisible(false); // the element is drawn manually before all others - - m_backgrounds.push_back(e); m_fields.push_back(spec); + e->drop(); } void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element) @@ -1180,7 +1164,6 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) std::string name = parts[2]; std::vector items = split(parts[3],','); std::string str_initial_selection; - std::string str_transparent = "false"; if (parts.size() >= 5) str_initial_selection = parts[4]; @@ -1686,7 +1669,7 @@ void GUIFormSpecMenu::parseField(parserData* data, const std::string &element, void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &element) { - MY_CHECKCLIENT("list"); + MY_CHECKCLIENT("hypertext"); std::vector parts; if (!precheckElement("hypertext", element, 4, 4, parts)) @@ -1746,25 +1729,27 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) return; std::vector v_pos = split(parts[0],','); - std::string text = parts[1]; MY_CHECKPOS("label",0); if(!data->explicit_size) warningstream<<"invalid use of label without a size[] element"< lines = split(text, '\n'); - auto style = getDefaultStyleForElement("label", ""); gui::IGUIFont *font = style.getFont(); if (!font) font = m_font; - for (unsigned int i = 0; i != lines.size(); i++) { - std::wstring wlabel_colors = translate_string( - utf8_to_wide(unescape_string(lines[i]))); - // Without color escapes to get the font dimensions - std::wstring wlabel_plain = unescape_enriched(wlabel_colors); + EnrichedString str(unescape_string(utf8_to_wide(parts[1]))); + size_t str_pos = 0; + + for (size_t i = 0; str_pos < str.size(); ++i) { + // Split per line + size_t str_nl = str.getString().find(L'\n', str_pos); + if (str_nl == std::wstring::npos) + str_nl = str.getString().size(); + EnrichedString line = str.substr(str_pos, str_nl - str_pos); + str_pos += line.size() + 1; core::rect rect; @@ -1781,7 +1766,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) rect = core::rect( pos.X, pos.Y, - pos.X + font->getDimension(wlabel_plain.c_str()).Width, + pos.X + font->getDimension(line.c_str()).Width, pos.Y + imgsize.Y); } else { @@ -1803,19 +1788,19 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) rect = core::rect( pos.X, pos.Y - m_btn_height, - pos.X + font->getDimension(wlabel_plain.c_str()).Width, + pos.X + font->getDimension(line.c_str()).Width, pos.Y + m_btn_height); } FieldSpec spec( "", - wlabel_colors, + L"", L"", 258 + m_fields.size(), 4 ); gui::IGUIStaticText *e = gui::StaticText::add(Environment, - spec.flabel.c_str(), rect, false, false, data->current_parent, + line, rect, false, false, data->current_parent, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); @@ -2250,7 +2235,7 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) { std::vector parts; - if (!precheckElement("bgcolor", element, 2, 3, parts)) + if (!precheckElement("bgcolor", element, 1, 3, parts)) return; const u32 parameter_count = parts.size(); @@ -2262,7 +2247,7 @@ void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string & } // bgcolor - if (parameter_count >= 1 && parts[0] != "") + if (parameter_count >= 1 && !parts[0].empty()) parseColorString(parts[0], m_bgcolor, false); // fullscreen @@ -2273,14 +2258,14 @@ void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string & } else if (parts[1] == "neither") { m_bgnonfullscreen = false; m_bgfullscreen = false; - } else if (parts[1] != "" || m_formspec_version < 3) { + } else if (!parts[1].empty() || m_formspec_version < 3) { m_bgfullscreen = is_yes(parts[1]); m_bgnonfullscreen = !m_bgfullscreen; } } // fbgcolor - if (parameter_count >= 3 && parts[2] != "") + if (parameter_count >= 3 && !parts[2].empty()) parseColorString(parts[2], m_fullscreen_bgcolor, false); } @@ -2470,11 +2455,16 @@ bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &e void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element) { - std::vector parts = split(element, ','); + std::vector 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 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,11 +2494,16 @@ bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &ele void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) { - std::vector parts = split(element, ','); + std::vector 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 v_geom = split(parts[0], ','); + + MY_CHECKGEOM("anchor", 0); + + data->anchor.X = stof(v_geom[0]); + data->anchor.Y = stof(v_geom[1]); return; } @@ -2516,6 +2511,46 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) << "'" << std::endl; } +bool GUIFormSpecMenu::parsePaddingDirect(parserData *data, const std::string &element) +{ + if (element.empty()) + return false; + + std::vector 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 parts = split(element, ';'); + + if (parts.size() == 1 || + (parts.size() > 1 && m_formspec_version > FORMSPEC_API_VERSION)) { + std::vector 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 parts = split(element, ';'); @@ -2655,7 +2690,7 @@ bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, b void GUIFormSpecMenu::parseSetFocus(const std::string &element) { std::vector parts; - if (!precheckElement("set_focus", element, 2, 2, parts)) + if (!precheckElement("set_focus", element, 1, 2, parts)) return; if (m_is_form_regenerated) @@ -2756,6 +2791,28 @@ void GUIFormSpecMenu::parseModel(parserData *data, const std::string &element) m_fields.push_back(spec); } +void GUIFormSpecMenu::removeAll() +{ + // Remove children + removeAllChildren(); + removeTooltip(); + + for (auto &table_it : m_tables) + table_it.second->drop(); + for (auto &inventorylist_it : m_inventorylists) + inventorylist_it->drop(); + for (auto &checkbox_it : m_checkboxes) + checkbox_it.second->drop(); + for (auto &scrollbar_it : m_scrollbars) + scrollbar_it.second->drop(); + for (auto &tooltip_rect_it : m_tooltip_rects) + tooltip_rect_it.first->drop(); + for (auto &clickthrough_it : m_clickthrough_elements) + clickthrough_it->drop(); + for (auto &scroll_container_it : m_scroll_containers) + scroll_container_it.second->drop(); +} + void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) { //some prechecks @@ -2995,33 +3052,16 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } } else { // Don't keep old focus value - m_focused_element = ""; + m_focused_element = nullopt; } - // Remove children - removeChildren(); - - for (auto &table_it : m_tables) - table_it.second->drop(); - for (auto &inventorylist_it : m_inventorylists) - inventorylist_it->drop(); - for (auto &checkbox_it : m_checkboxes) - checkbox_it.second->drop(); - for (auto &scrollbar_it : m_scrollbars) - scrollbar_it.second->drop(); - for (auto &background_it : m_backgrounds) - background_it->drop(); - for (auto &tooltip_rect_it : m_tooltip_rects) - tooltip_rect_it.first->drop(); - for (auto &clickthrough_it : m_clickthrough_elements) - clickthrough_it->drop(); - for (auto &scroll_container_it : m_scroll_containers) - scroll_container_it.second->drop(); + removeAll(); mydata.size = v2s32(100, 100); 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 @@ -3031,7 +3071,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) mydata.current_parent = this; m_inventorylists.clear(); - m_backgrounds.clear(); m_tables.clear(); m_checkboxes.clear(); m_scrollbars.clear(); @@ -3124,7 +3163,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()) @@ -3171,8 +3217,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) offset = v2s32(0,0); } - double gui_scaling = g_settings->getFloat("gui_scaling"); - double screen_dpi = RenderingEngine::getDisplayDensity() * 96; + 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) { @@ -3189,11 +3235,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 +3253,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); } + s32 min_screen_dim = std::min(padded_screensize.X, padded_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. @@ -3278,10 +3324,19 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) gui::IGUIFont *old_font = skin->getFont(); skin->setFont(m_font); + // Add a new element that will hold all the background elements as its children. + // Because it is the first added element, all backgrounds will be behind all + // the other elements. + // (We use an arbitrarily big rect. The actual size is determined later by + // clipping to `this`.) + core::rect background_parent_rect(0, 0, 100000, 100000); + mydata.background_parent.reset(new gui::IGUIElement(EGUIET_ELEMENT, Environment, + this, -1, background_parent_rect)); + pos_offset = v2f32(); // used for formspec versions < 3 - core::list::Iterator legacy_sort_start = Children.getLast(); + std::list::iterator legacy_sort_start = std::prev(Children.end()); // last element if (enable_prepends) { // Backup the coordinates so that prepends can use the coordinates of choice. @@ -3296,7 +3351,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // legacy sorting for formspec versions < 3 if (m_formspec_version >= 3) // prepends do not need to be reordered - legacy_sort_start = Children.getLast(); + legacy_sort_start = std::prev(Children.end()); // last element else if (version_backup >= 3) // only prepends elements have to be reordered legacySortElements(legacy_sort_start); @@ -3377,7 +3432,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } } -void GUIFormSpecMenu::legacySortElements(core::list::Iterator from) +void GUIFormSpecMenu::legacySortElements(std::list::iterator from) { /* Draw order for formspec_version <= 2: @@ -3394,17 +3449,16 @@ void GUIFormSpecMenu::legacySortElements(core::list::Iterator fro if (from == Children.end()) from = Children.begin(); else - from++; + ++from; - core::list::Iterator to = Children.end(); + std::list::iterator to = Children.end(); // 1: Copy into a sortable container - std::vector elements; - for (auto it = from; it != to; ++it) - elements.emplace_back(*it); + std::vector elements(from, to); // 2: Sort the container std::stable_sort(elements.begin(), elements.end(), [this] (const IGUIElement *a, const IGUIElement *b) -> bool { + // TODO: getSpecByID is a linear search. It should made O(1), or cached here. const FieldSpec *spec_a = getSpecByID(a->getID()); const FieldSpec *spec_b = getSpecByID(b->getID()); return spec_a && spec_b && @@ -3412,10 +3466,7 @@ void GUIFormSpecMenu::legacySortElements(core::list::Iterator fro }); // 3: Re-assign the pointers - for (auto e : elements) { - *from = e; - from++; - } + reorderChildren(from, to, elements); } #ifdef __ANDROID__ @@ -3453,10 +3504,10 @@ GUIInventoryList::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const s32 item_index = e->getItemIndexAtPos(p); if (item_index != -1) return GUIInventoryList::ItemSpec(e->getInventoryloc(), e->getListname(), - item_index); + item_index, e->getSlotSize()); } - return GUIInventoryList::ItemSpec(InventoryLocation(), "", -1); + return GUIInventoryList::ItemSpec(InventoryLocation(), "", -1, {0,0}); } void GUIFormSpecMenu::drawSelectedItem() @@ -3478,7 +3529,8 @@ void GUIFormSpecMenu::drawSelectedItem() ItemStack stack = list->getItem(m_selected_item->i); stack.count = m_selected_amount; - core::rect imgrect(0,0,imgsize.X,imgsize.Y); + v2s32 slotsize = m_selected_item->slotsize; + core::rect imgrect(0, 0, slotsize.X, slotsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); rect.constrainTo(driver->getViewPort()); drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED); @@ -3533,15 +3585,6 @@ void GUIFormSpecMenu::drawMenu() } } - /* - Draw backgrounds - */ - for (gui::IGUIElement *e : m_backgrounds) { - e->setVisible(true); - e->draw(); - e->setVisible(false); - } - // Some elements are only visible while being drawn for (gui::IGUIElement *e : m_clickthrough_elements) e->setVisible(true); @@ -3549,12 +3592,11 @@ void GUIFormSpecMenu::drawMenu() /* This is where all the drawing happens. */ - core::list::Iterator it = Children.begin(); - for (; it != Children.end(); ++it) - if ((*it)->isNotClipped() || + for (auto child : Children) + if (child->isNotClipped() || AbsoluteClippingRect.isRectCollided( - (*it)->getAbsolutePosition())) - (*it)->draw(); + child->getAbsolutePosition())) + child->draw(); for (gui::IGUIElement *e : m_clickthrough_elements) e->setVisible(false); @@ -3590,13 +3632,21 @@ void GUIFormSpecMenu::drawMenu() #endif bool hovered_element_found = false; - if (hovered != NULL) { + if (hovered) { if (m_show_debug) { core::rect rect = hovered->getAbsoluteClippingRect(); driver->draw2DRectangle(0x22FFFF00, rect, &rect); } - s32 id = hovered->getID(); + // find the formspec-element of the hovered IGUIElement (a parent) + s32 id; + for (gui::IGUIElement *hovered_fselem = hovered; hovered_fselem; + hovered_fselem = hovered_fselem->getParent()) { + id = hovered_fselem->getID(); + if (id != -1) + break; + } + u64 delta = 0; if (id == -1) { m_old_tooltip_id = id; @@ -3731,6 +3781,7 @@ void GUIFormSpecMenu::updateSelectedItem() m_selected_item->inventoryloc = e->getInventoryloc(); m_selected_item->listname = "craftresult"; m_selected_item->i = 0; + m_selected_item->slotsize = e->getSlotSize(); m_selected_amount = item.count; m_selected_dragging = false; break; @@ -3812,7 +3863,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode) if (!current_field_enter_pending.empty()) { fields["key_enter_field"] = current_field_enter_pending; - current_field_enter_pending = ""; + current_field_enter_pending.clear(); } if (current_keys_pending.key_escape) { @@ -4462,7 +4513,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if ((s.ftype == f_TabHeader) && (s.fid == event.GUIEvent.Caller->getID())) { if (!s.sound.empty() && m_sound_manager) - m_sound_manager->playSound(s.sound, false, 1.0f); + m_sound_manager->playSound(SimpleSoundSpec(s.sound, 1.0f)); s.send = true; acceptInput(); s.send = false; @@ -4507,7 +4558,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (s.ftype == f_Button || s.ftype == f_CheckBox) { if (!s.sound.empty() && m_sound_manager) - m_sound_manager->playSound(s.sound, false, 1.0f); + m_sound_manager->playSound(SimpleSoundSpec(s.sound, 1.0f)); s.send = true; if (s.is_exit) { @@ -4532,7 +4583,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } if (!s.sound.empty() && m_sound_manager) - m_sound_manager->playSound(s.sound, false, 1.0f); + m_sound_manager->playSound(SimpleSoundSpec(s.sound, 1.0f)); s.send = true; acceptInput(quit_mode_no); @@ -4547,10 +4598,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } else if (s.ftype == f_ScrollBar) { s.fdefault = L"Changed"; acceptInput(quit_mode_no); - s.fdefault = L""; + s.fdefault.clear(); } else if (s.ftype == f_Unknown || s.ftype == f_HyperText) { if (!s.sound.empty() && m_sound_manager) - m_sound_manager->playSound(s.sound, false, 1.0f); + m_sound_manager->playSound(SimpleSoundSpec(s.sound, 1.0f)); s.send = true; acceptInput(); s.send = false;