From 5e7004e7af71fa7f2f980414c9951a93c0a0e994 Mon Sep 17 00:00:00 2001 From: v-rob <31123645+v-rob@users.noreply.github.com> Date: Thu, 27 Jun 2019 05:40:49 -0700 Subject: Add compatible, consistent coordinate system to FormSpecs. (#8524) --- src/gui/guiFormSpecMenu.cpp | 551 ++++++++++++++++++++++++++++++++------------ src/gui/guiFormSpecMenu.h | 11 +- 2 files changed, 418 insertions(+), 144 deletions(-) (limited to 'src/gui') diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 9240dc4c8..da462c7e3 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -66,8 +66,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #define MY_CHECKGEOM(a,b) \ if (v_geom.size() != 2) { \ - errorstream<< "Invalid pos for element " << a << "specified: \"" \ - << parts[b] << "\"" << std::endl; \ + errorstream<< "Invalid geometry for element " << a << \ + "specified: \"" << parts[b] << "\"" << std::endl; \ return; \ } /* @@ -270,6 +270,25 @@ v2s32 GUIFormSpecMenu::getElementBasePos(bool absolute, return v2s32(pos_f.X, pos_f.Y); } +v2s32 GUIFormSpecMenu::getRealCoordinateBasePos(bool absolute, + const std::vector &v_pos) +{ + v2f32 pos_f = v2f32(0.0f, 0.0f); + + pos_f.X += stof(v_pos[0]) + pos_offset.X; + pos_f.Y += stof(v_pos[1]) + pos_offset.Y; + + if (absolute) + return v2s32(pos_f.X * imgsize.X + AbsoluteRect.UpperLeftCorner.X, + pos_f.Y * imgsize.Y + AbsoluteRect.UpperLeftCorner.Y); + return v2s32(pos_f.X * imgsize.X, pos_f.Y * imgsize.Y); +} + +v2s32 GUIFormSpecMenu::getRealCoordinateGeometry(const std::vector &v_geom) +{ + return v2s32(stof(v_geom[0]) * imgsize.X, stof(v_geom[1]) * imgsize.Y); +} + void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element) { std::vector parts = split(element,','); @@ -348,13 +367,19 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element) InventoryLocation loc; - if(location == "context" || location == "current_name") + if (location == "context" || location == "current_name") loc = m_current_inventory_location; else loc.deSerialize(location); - v2s32 pos = getElementBasePos(true, &v_pos); + v2s32 pos; v2s32 geom; + + if (data->real_coordinates) + pos = getRealCoordinateBasePos(true, v_pos); + else + pos = getElementBasePos(true, &v_pos); + geom.X = stoi(v_geom[0]); geom.Y = stoi(v_geom[1]); @@ -369,7 +394,7 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element) if(!data->explicit_size) warningstream<<"invalid use of list without a size[] element"<real_coordinates); return; } errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl; @@ -430,8 +455,6 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element MY_CHECKPOS("checkbox",0); - v2s32 pos = getElementBasePos(false, &v_pos); - bool fselected = false; if (selected == "true") @@ -442,12 +465,27 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH); s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2; - core::rect rect = core::rect( - pos.X, - pos.Y + imgsize.Y / 2 - y_center, - pos.X + label_size.Width + cb_size + 7, - pos.Y + imgsize.Y / 2 + y_center - ); + v2s32 pos; + core::rect rect; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + + rect = core::rect( + pos.X, + pos.Y - y_center, + pos.X + label_size.Width + cb_size + 7, + pos.Y + y_center + ); + } else { + pos = getElementBasePos(false, &v_pos); + rect = core::rect( + pos.X, + pos.Y + imgsize.Y / 2 - y_center, + pos.X + label_size.Width + cb_size + 7, + pos.Y + imgsize.Y / 2 + y_center + ); + } FieldSpec spec( name, @@ -478,24 +516,25 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen if (parts.size() >= 5) { std::vector v_pos = split(parts[0],','); - std::vector v_dim = split(parts[1],','); + std::vector v_geom = split(parts[1],','); std::string name = parts[3]; std::string value = parts[4]; MY_CHECKPOS("scrollbar",0); + MY_CHECKGEOM("scrollbar",1); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; + v2s32 dim; - if (v_dim.size() != 2) { - errorstream<< "Invalid size for element " << "scrollbar" - << "specified: \"" << parts[1] << "\"" << std::endl; - return; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + dim = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + dim.X = stof(v_geom[0]) * spacing.X; + dim.Y = stof(v_geom[1]) * spacing.Y; } - v2s32 dim; - dim.X = stof(v_dim[0]) * spacing.X; - dim.Y = stof(v_dim[1]) * spacing.Y; - core::rect rect = core::rect(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y); @@ -543,10 +582,17 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) MY_CHECKPOS("image", 0); MY_CHECKGEOM("image", 1); - v2s32 pos = getElementBasePos(true, &v_pos); + v2s32 pos; v2s32 geom; - geom.X = stof(v_geom[0]) * (float)imgsize.X; - geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(true, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(true, &v_pos); + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + } if (!data->explicit_size) warningstream<<"invalid use of image without a size[] element"<real_coordinates) { + pos = getRealCoordinateBasePos(true, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(true, &v_pos); + geom.X = stof(v_geom[0]) * (float)imgsize.X; + geom.Y = stof(v_geom[1]) * (float)imgsize.Y; + } if(!data->explicit_size) warningstream<<"invalid use of item_image without a size[] element"< rect; - core::rect rect = - core::rect(pos.X, pos.Y - m_btn_height, + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect(pos.X, pos.Y, pos.X+geom.X, + pos.Y+geom.Y); + } else { + pos = getElementBasePos(false, &v_pos); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; + + rect = core::rect(pos.X, pos.Y - m_btn_height, pos.X + geom.X, pos.Y + m_btn_height); + } if(!data->explicit_size) warningstream<<"invalid use of button without a size[] element"<real_coordinates) { + pos = getRealCoordinateBasePos(true, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(true, &v_pos); + pos.X -= (spacing.X - (float)imgsize.X) / 2; + pos.Y -= (spacing.Y - (float)imgsize.Y) / 2; + + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } bool clip = false; if (parts.size() >= 4 && is_yes(parts[3])) { - pos.X = stoi(v_pos[0]); //acts as offset - pos.Y = stoi(v_pos[1]); //acts as offset + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos) * -1; + geom = v2s32(0, 0); + } else { + pos.X = stoi(v_pos[0]); //acts as offset + pos.Y = stoi(v_pos[1]); + } clip = true; } @@ -760,10 +834,17 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) MY_CHECKPOS("table",0); MY_CHECKGEOM("table",1); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; v2s32 geom; - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -827,11 +908,17 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element MY_CHECKPOS("textlist",0); MY_CHECKGEOM("textlist",1); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; v2s32 geom; - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -888,12 +975,29 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element MY_CHECKPOS("dropdown",0); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; + v2s32 geom; + core::rect rect; + + if (data->real_coordinates) { + std::vector v_geom = split(parts[1],','); - s32 width = stof(parts[1]) * spacing.Y; + if (v_geom.size() == 1) + v_geom.emplace_back("1"); - core::rect rect = core::rect(pos.X, pos.Y, - pos.X + width, pos.Y + (m_btn_height * 2)); + MY_CHECKGEOM("dropdown",1); + + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + } else { + pos = getElementBasePos(false, &v_pos); + + s32 width = stof(parts[1]) * spacing.Y; + + rect = core::rect(pos.X, pos.Y, + pos.X + width, pos.Y + (m_btn_height * 2)); + } FieldSpec spec( name, @@ -958,15 +1062,22 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element MY_CHECKPOS("pwdfield",0); MY_CHECKGEOM("pwdfield",1); - v2s32 pos = getElementBasePos(false, &v_pos); - pos -= padding; - + v2s32 pos; v2s32 geom; - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); - pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; - pos.Y -= m_btn_height; - geom.Y = m_btn_height*2; + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + pos -= padding; + + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + + pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; + pos.Y -= m_btn_height; + geom.Y = m_btn_height*2; + } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1141,23 +1252,29 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector& MY_CHECKPOS(type,0); MY_CHECKGEOM(type,1); - v2s32 pos = getElementBasePos(false, &v_pos); - pos -= padding; - + v2s32 pos; v2s32 geom; - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + pos -= padding; - if (type == "textarea") - { - geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y); - pos.Y += m_btn_height; - } - else - { - pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; - pos.Y -= m_btn_height; - geom.Y = m_btn_height*2; + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + + if (type == "textarea") + { + geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y); + pos.Y += m_btn_height; + } + else + { + pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2; + pos.Y -= m_btn_height; + geom.Y = m_btn_height*2; + } } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1221,31 +1338,55 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) MY_CHECKPOS("label",0); - v2s32 pos = getElementBasePos(false, nullptr); - pos.X += stof(v_pos[0]) * spacing.X; - pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y; - if(!data->explicit_size) warningstream<<"invalid use of label without a size[] element"< lines = split(text, '\n'); for (unsigned int i = 0; i != lines.size(); i++) { - // Lines are spaced at the nominal distance of - // 2/5 inventory slot, even if the font doesn't - // quite match that. This provides consistent - // form layout, at the expense of sometimes - // having sub-optimal spacing for the font. - // We multiply by 2 and then divide by 5, rather - // than multiply by 0.4, to get exact results - // in the integer cases: 0.4 is not exactly - // representable in binary floating point. - s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0; std::wstring wlabel = utf8_to_wide(unescape_string(lines[i])); - core::rect rect = core::rect( - pos.X, posy - m_btn_height, - pos.X + m_font->getDimension(wlabel.c_str()).Width, - posy + m_btn_height); + + core::rect rect; + + if (data->real_coordinates) { + // Lines are spaced at the distance of 1/2 imgsize. + // This alows lines that line up with the new elements + // easily without sacrificing good line distance. If + // it was one whole imgsize, it would have too much + // spacing. + v2s32 pos = getRealCoordinateBasePos(false, v_pos); + + // Labels are positioned by their center, not their top. + pos.Y += (((float) imgsize.Y) / -2) + (((float) imgsize.Y) * i / 2); + + rect = core::rect( + pos.X, pos.Y, + pos.X + m_font->getDimension(wlabel.c_str()).Width, + pos.Y + imgsize.Y); + + } else { + // Lines are spaced at the nominal distance of + // 2/5 inventory slot, even if the font doesn't + // quite match that. This provides consistent + // form layout, at the expense of sometimes + // having sub-optimal spacing for the font. + // We multiply by 2 and then divide by 5, rather + // than multiply by 0.4, to get exact results + // in the integer cases: 0.4 is not exactly + // representable in binary floating point. + + v2s32 pos = getElementBasePos(false, nullptr); + pos.X += stof(v_pos[0]) * spacing.X; + pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y; + + pos.Y += ((float) i) * spacing.Y * 2.0 / 5.0; + + rect = core::rect( + pos.X, pos.Y - m_btn_height, + pos.X + m_font->getDimension(wlabel.c_str()).Width, + pos.Y + m_btn_height); + } + FieldSpec spec( "", wlabel, @@ -1260,7 +1401,8 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) return; } - errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl; + errorstream << "Invalid label element(" << parts.size() << "): '" << element + << "'" << std::endl; } void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element) @@ -1276,15 +1418,35 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen MY_CHECKPOS("vertlabel",1); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; + core::rect rect; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + + // Vertlabels are positioned by center, not left. + pos.X -= imgsize.X / 2; - core::rect rect = core::rect( - pos.X, pos.Y+((imgsize.Y/2)- m_btn_height), + // We use text.length + 1 because without it, the rect + // isn't quite tall enough and cuts off the text. + rect = core::rect(pos.X, pos.Y, + pos.X + imgsize.X, + pos.Y + font_line_height(m_font) * + (text.length() + 1)); + + } else { + pos = getElementBasePos(false, &v_pos); + + // As above, the length must be one longer. The width of + // the rect (15 pixels) seems rather arbitrary, but + // changing it might break something. + rect = core::rect( + pos.X, pos.Y+((imgsize.Y/2) - m_btn_height), pos.X+15, pos.Y + - font_line_height(m_font) - * (text.length()+1) - +((imgsize.Y/2)- m_btn_height)); - //actually text.length() would be correct but adding +1 avoids to break all mods + font_line_height(m_font) * + (text.length() + 1) + + ((imgsize.Y/2) - m_btn_height)); + } if(!data->explicit_size) warningstream<<"invalid use of label without a size[] element"< rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + v2s32 pos; + v2s32 geom; - if(!data->explicit_size) + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); + } + + core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, + pos.Y+geom.Y); + + if (!data->explicit_size) warningstream<<"invalid use of image_button without a size[] element"< parts = split(element,';'); + std::vector parts = split(element, ';'); - if (((parts.size() == 4) || (parts.size() == 6)) || - ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION))) + if (((parts.size() == 4) || (parts.size() == 6)) || (parts.size() == 7 && + data->real_coordinates) || ((parts.size() > 6) && + (m_formspec_version > FORMSPEC_API_VERSION))) { std::vector v_pos = split(parts[0],','); - std::string name = parts[1]; - std::vector buttons = split(parts[2],','); - std::string str_index = parts[3]; + + // If we're using real coordinates, add an extra field for height. + // Width is not here because tabs are the width of the text, and + // there's no reason to change that. + unsigned int i = 0; + std::vector v_geom = {"1", "0.75"}; // Dummy width and default height + bool auto_width = true; + if (parts.size() == 7) { + i++; + + v_geom = split(parts[1], ','); + if (v_geom.size() == 1) + v_geom.insert(v_geom.begin(), "1"); // Dummy value + else + auto_width = false; + } + + std::string name = parts[i+1]; + std::vector buttons = split(parts[i+2], ','); + std::string str_index = parts[i+3]; bool show_background = true; bool show_border = true; - int tab_index = stoi(str_index) -1; + int tab_index = stoi(str_index) - 1; - MY_CHECKPOS("tabheader",0); + MY_CHECKPOS("tabheader", 0); - if (parts.size() == 6) { - if (parts[4] == "true") + if (parts.size() == 6 + i) { + if (parts[4+i] == "true") show_background = false; - if (parts[5] == "false") + if (parts[5+i] == "false") show_border = false; } @@ -1432,15 +1620,26 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen spec.ftype = f_TabHeader; v2s32 pos; - { + v2s32 geom; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + + geom = getRealCoordinateGeometry(v_geom); + pos.Y -= geom.Y; // TabHeader base pos is the bottom, not the top. + if (auto_width) + geom.X = DesiredRect.getWidth(); // Set automatic width + + MY_CHECKGEOM("tabheader", 1); + } else { v2f32 pos_f = pos_offset * spacing; pos_f.X += stof(v_pos[0]) * spacing.X; pos_f.Y += stof(v_pos[1]) * spacing.Y - m_btn_height * 2; pos = v2s32(pos_f.X, pos_f.Y); + + geom.Y = m_btn_height * 2; + geom.X = DesiredRect.getWidth(); } - v2s32 geom; - geom.X = DesiredRect.getWidth(); - geom.Y = m_btn_height*2; core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1449,7 +1648,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen show_background, show_border, spec.fid); e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT); - e->setTabHeight(m_btn_height*2); + e->setTabHeight(geom.Y); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1500,10 +1699,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & MY_CHECKPOS("itemimagebutton",0); MY_CHECKGEOM("itemimagebutton",1); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; v2s32 geom; - geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); - geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(false, &v_pos); + geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X); + geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y); + } core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1537,7 +1743,11 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & spec.rect=rect; m_fields.push_back(spec); - pos = getElementBasePos(true, &v_pos); + if (data->real_coordinates) + pos = getRealCoordinateBasePos(true, v_pos); + else + pos = getElementBasePos(true, &v_pos); + m_itemimages.emplace_back("", item_name, e, pos, geom); m_static_texts.emplace_back(utf8_to_wide(label), rect, e); return; @@ -1558,10 +1768,17 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) MY_CHECKPOS("box",0); MY_CHECKGEOM("box",1); - v2s32 pos = getElementBasePos(true, &v_pos); + v2s32 pos; v2s32 geom; - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(true, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(true, &v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } video::SColor tmp_color; @@ -1664,13 +1881,20 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) std::vector v_pos = split(parts[0], ','); std::vector v_geom = split(parts[1], ','); - MY_CHECKPOS("tooltip", 0); + MY_CHECKPOS("tooltip", 0); MY_CHECKGEOM("tooltip", 1); - v2s32 pos = getElementBasePos(true, &v_pos); + v2s32 pos; v2s32 geom; - geom.X = stof(v_geom[0]) * spacing.X; - geom.Y = stof(v_geom[1]) * spacing.Y; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(true, v_pos); + geom = getRealCoordinateGeometry(v_geom); + } else { + pos = getElementBasePos(true, &v_pos); + geom.X = stof(v_geom[0]) * spacing.X; + geom.Y = stof(v_geom[1]) * spacing.Y; + } irr::core::rect rect(pos, pos + geom); m_tooltip_rects.emplace_back(rect, spec); @@ -1956,6 +2180,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "real_coordinates") { + data->real_coordinates = is_yes(description); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; @@ -2120,6 +2349,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) break; } + /* Copy of the "real_coordinates" element for after the form size. */ + mydata.real_coordinates = false; + for (; i < elements.size(); i++) { + std::vector parts = split(elements[i], '['); + std::string name = trim(parts[0]); + if (name != "real_coordinates" || parts.size() != 2) + break; // Invalid format + + mydata.real_coordinates = is_yes(trim(parts[1])); + } + if (mydata.explicit_size) { // compute scaling for specified form size if (m_lock) { @@ -2210,10 +2450,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_font = g_fontengine->getFont(); - mydata.size = v2s32( - padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X, - padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0 - ); + if (mydata.real_coordinates) { + mydata.size = v2s32( + mydata.invsize.X*imgsize.X, + mydata.invsize.Y*imgsize.Y + ); + } else { + mydata.size = v2s32( + padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X, + padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0 + ); + } + DesiredRect = mydata.rect = core::rect( (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X, (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y, @@ -2245,9 +2493,13 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) pos_offset = v2f32(); if (enable_prepends) { + // Backup the coordinates so that prepends can use the coordinates of choice. + bool rc_backup = mydata.real_coordinates; + mydata.real_coordinates = false; // Old coordinates by default. std::vector prepend_elements = split(m_formspec_prepend, ']'); for (const auto &element : prepend_elements) parseElement(&mydata, element); + mydata.real_coordinates = rc_backup; // Restore coordinates } for (; i< elements.size(); i++) { @@ -2337,8 +2589,16 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) { for(s32 i=0; i rect = imgrect + s.pos + p0; if(rect.isPointInside(p)) @@ -2380,8 +2640,15 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer, if (item_i >= (s32)ilist->getSize()) break; - s32 x = (i%s.geom.X) * spacing.X; - s32 y = (i/s.geom.X) * spacing.Y; + s32 x; + s32 y; + if (s.real_coordinates) { + x = (i%s.geom.X) * (imgsize.X * 1.25); + y = (i/s.geom.X) * (imgsize.Y * 1.25); + } else { + x = (i%s.geom.X) * spacing.X; + y = (i/s.geom.X) * spacing.Y; + } v2s32 p(x,y); core::rect rect = imgrect + s.pos + p; ItemStack item = ilist->getItem(item_i); diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index b1ca9a48a..9c1ecb635 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -99,12 +99,14 @@ class GUIFormSpecMenu : public GUIModalMenu ListDrawSpec(const InventoryLocation &a_inventoryloc, const std::string &a_listname, - v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i): + v2s32 a_pos, v2s32 a_geom, s32 a_start_item_i, + bool a_real_coordinates): inventoryloc(a_inventoryloc), listname(a_listname), pos(a_pos), geom(a_geom), - start_item_i(a_start_item_i) + start_item_i(a_start_item_i), + real_coordinates(a_real_coordinates) { } @@ -113,6 +115,7 @@ class GUIFormSpecMenu : public GUIModalMenu v2s32 pos; v2s32 geom; s32 start_item_i; + bool real_coordinates; }; struct ListRingSpec @@ -394,6 +397,9 @@ protected: std::string getNameByID(s32 id); v2s32 getElementBasePos(bool absolute, const std::vector *v_pos); + v2s32 getRealCoordinateBasePos(bool absolute, + const std::vector &v_pos); + v2s32 getRealCoordinateGeometry(const std::vector &v_geom); v2s32 padding; v2f32 spacing; @@ -463,6 +469,7 @@ private: typedef struct { bool explicit_size; + bool real_coordinates; v2f invsize; v2s32 size; v2f32 offset; -- cgit v1.2.3