diff options
Diffstat (limited to 'src/gui/guiFormSpecMenu.cpp')
-rw-r--r-- | src/gui/guiFormSpecMenu.cpp | 864 |
1 files changed, 671 insertions, 193 deletions
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 6c404728f..aee7da869 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include <iterator> #include <sstream> #include <limits> +#include "guiButton.h" #include "guiFormSpecMenu.h" #include "guiTable.h" #include "constants.h" @@ -66,8 +67,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; \ } /* @@ -86,7 +87,7 @@ inline u32 clamp_u8(s32 value) GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst, - std::string formspecPrepend, + const std::string &formspecPrepend, bool remap_dbl_click): GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr), m_invmgr(client), @@ -270,6 +271,25 @@ v2s32 GUIFormSpecMenu::getElementBasePos(bool absolute, return v2s32(pos_f.X, pos_f.Y); } +v2s32 GUIFormSpecMenu::getRealCoordinateBasePos(bool absolute, + const std::vector<std::string> &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<std::string> &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<std::string> parts = split(element,','); @@ -306,8 +326,8 @@ void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &elemen parts[1] = parts[1].substr(0, parts[1].find(';')); container_stack.push(pos_offset); - pos_offset.X += MYMAX(0, stof(parts[0])); - pos_offset.Y += MYMAX(0, stof(parts[1])); + pos_offset.X += stof(parts[0]); + pos_offset.Y += stof(parts[1]); return; } errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'" << std::endl; @@ -348,13 +368,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 +395,7 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element) if(!data->explicit_size) warningstream<<"invalid use of list without a size[] element"<<std::endl; - m_inventorylists.emplace_back(loc, listname, pos, geom, start_i); + m_inventorylists.emplace_back(loc, listname, pos, geom, start_i, data->real_coordinates); return; } errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl; @@ -430,20 +456,37 @@ 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") fselected = true; std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); - s32 spacing = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH) + 7; + const core::dimension2d<u32> label_size = m_font->getDimension(wlabel.c_str()); + s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH); + s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2; + + v2s32 pos; + core::rect<s32> rect; - core::rect<s32> rect = core::rect<s32>( - pos.X, pos.Y + ((imgsize.Y / 2) - m_btn_height), - pos.X + m_font->getDimension(wlabel.c_str()).Width + spacing, - pos.Y + ((imgsize.Y / 2) + m_btn_height)); + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + + rect = core::rect<s32>( + 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<s32>( + 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, @@ -457,6 +500,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this, spec.fid, spec.flabel.c_str()); + auto style = getStyleForElement("checkbox", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } @@ -474,24 +520,25 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen if (parts.size() >= 5) { std::vector<std::string> v_pos = split(parts[0],','); - std::vector<std::string> v_dim = split(parts[1],','); + std::vector<std::string> 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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y); @@ -512,6 +559,9 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen gui::IGUIScrollBar* e = Environment->addScrollBar(is_horizontal,rect,this,spec.fid); + auto style = getStyleForElement("scrollbar", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setMax(1000); e->setMin(0); e->setPos(stoi(parts[4])); @@ -539,10 +589,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"<<std::endl; @@ -580,10 +637,17 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen MY_CHECKPOS("itemimage",0); MY_CHECKGEOM("itemimage",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 item_image without a size[] element"<<std::endl; @@ -609,14 +673,23 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, MY_CHECKPOS("button",0); MY_CHECKGEOM("button",1); - v2s32 pos = getElementBasePos(false, &v_pos); + 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; + core::rect<s32> rect; - core::rect<s32> rect = - core::rect<s32>(pos.X, pos.Y - m_btn_height, + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect<s32>(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<s32>(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"<<std::endl; @@ -632,8 +705,38 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element, spec.ftype = f_Button; if(type == "button_exit") spec.is_exit = true; - gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid, - spec.flabel.c_str()); + + GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str()); + + auto style = getStyleForElement(type, name, (type != "button") ? "button" : ""); + if (style.isNotDefault(StyleSpec::BGCOLOR)) { + e->setColor(style.getColor(StyleSpec::BGCOLOR)); + } + if (style.isNotDefault(StyleSpec::TEXTCOLOR)) { + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR)); + } + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + + if (style.isNotDefault(StyleSpec::BGIMG)) { + std::string image_name = style.get(StyleSpec::BGIMG, ""); + std::string pressed_image_name = style.get(StyleSpec::BGIMG_PRESSED, ""); + + video::ITexture *texture = 0; + video::ITexture *pressed_texture = 0; + texture = m_tsrc->getTexture(image_name); + if (!pressed_image_name.empty()) + pressed_texture = m_tsrc->getTexture(pressed_image_name); + else + pressed_texture = texture; + + e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); + e->setImage(guiScalingImageButton( + Environment->getVideoDriver(), texture, geom.X, geom.Y)); + e->setPressedImage(guiScalingImageButton( + Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); + e->setScaleImage(true); + } if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -649,9 +752,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme { std::vector<std::string> parts = split(element,';'); - if (((parts.size() == 3) || (parts.size() == 4)) || - ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) - { + if ((parts.size() >= 3 && parts.size() <= 5) || + (parts.size() > 5 && m_formspec_version > FORMSPEC_API_VERSION)) { std::vector<std::string> v_pos = split(parts[0],','); std::vector<std::string> v_geom = split(parts[1],','); std::string name = unescape_string(parts[2]); @@ -659,25 +761,58 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme MY_CHECKPOS("background",0); MY_CHECKGEOM("background",1); - v2s32 pos = getElementBasePos(true, &v_pos); - pos.X -= (spacing.X - (float)imgsize.X) / 2; - pos.Y -= (spacing.Y - (float)imgsize.Y) / 2; - + 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); + 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 (parts.size() >= 4 && is_yes(parts[3])) { + 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; } + core::rect<s32> middle; + if (parts.size() >= 5) { + std::vector<std::string> 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 (!data->explicit_size && !clip) warningstream << "invalid use of unclipped background without a size[] element" << std::endl; - m_backgrounds.emplace_back(name, pos, geom, clip); + m_backgrounds.emplace_back(name, pos, geom, middle, clip); return; } @@ -736,10 +871,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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -773,6 +915,9 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); + auto style = getStyleForElement("table", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + m_tables.emplace_back(spec, e); m_fields.push_back(spec); return; @@ -803,11 +948,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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -841,6 +992,9 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element if (!str_initial_selection.empty() && str_initial_selection != "0") e->setSelected(stoi(str_initial_selection)); + auto style = getStyleForElement("textlist", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + m_tables.emplace_back(spec, e); m_fields.push_back(spec); return; @@ -864,12 +1018,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<s32> rect; + + if (data->real_coordinates) { + std::vector<std::string> v_geom = split(parts[1],','); + + if (v_geom.size() == 1) + v_geom.emplace_back("1"); + + MY_CHECKGEOM("dropdown",1); - s32 width = stof(parts[1]) * spacing.Y; + pos = getRealCoordinateBasePos(false, v_pos); + geom = getRealCoordinateGeometry(v_geom); + rect = core::rect<s32>(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; - core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, - pos.X + width, pos.Y + (m_btn_height * 2)); + rect = core::rect<s32>(pos.X, pos.Y, + pos.X + width, pos.Y + (m_btn_height * 2)); + } FieldSpec spec( name, @@ -896,6 +1067,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element if (!str_initial_selection.empty()) e->setSelected(stoi(str_initial_selection)-1); + auto style = getStyleForElement("dropdown", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + m_fields.push_back(spec); m_dropdowns.emplace_back(spec, std::vector<std::string>()); @@ -934,15 +1108,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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -972,6 +1153,11 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element e->setPasswordBox(true,L'*'); + auto style = getStyleForElement("pwdfield", name, "field"); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + irr::SEvent evt; evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.Key = KEY_END; @@ -1022,12 +1208,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, true, Environment, this, spec.fid, rect, is_editable, is_multiline); e->drop(); } else { - if (is_multiline) + if (is_multiline) { e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment, this, spec.fid, rect, is_editable, true); - else if (is_editable) + e->drop(); + } else if (is_editable) { e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); + } } if (e) { @@ -1048,6 +1236,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec, evt.KeyInput.PressedDown = true; e->OnEvent(evt); } + + auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + if (style.get(StyleSpec::BGCOLOR, "") == "transparent") { + e->setDrawBackground(false); + } } if (!spec.flabel.empty()) { @@ -1117,23 +1313,29 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& 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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1197,46 +1399,79 @@ 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"<<std::endl; std::vector<std::string> 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<s32> rect = core::rect<s32>( - pos.X, posy - m_btn_height, - pos.X + m_font->getDimension(wlabel.c_str()).Width, - posy + m_btn_height); + 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); + + core::rect<s32> 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<s32>( + pos.X, pos.Y, + pos.X + m_font->getDimension(wlabel_plain.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<s32>( + pos.X, pos.Y - m_btn_height, + pos.X + m_font->getDimension(wlabel_plain.c_str()).Width, + pos.Y + m_btn_height); + } + FieldSpec spec( "", - wlabel, + wlabel_colors, L"", 258+m_fields.size() ); gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); + + auto style = getStyleForElement("label", spec.fname); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + m_fields.push_back(spec); } 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) @@ -1252,15 +1487,35 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen MY_CHECKPOS("vertlabel",1); - v2s32 pos = getElementBasePos(false, &v_pos); + v2s32 pos; + core::rect<s32> rect; + + if (data->real_coordinates) { + pos = getRealCoordinateBasePos(false, v_pos); + + // Vertlabels are positioned by center, not left. + pos.X -= imgsize.X / 2; + + // We use text.length + 1 because without it, the rect + // isn't quite tall enough and cuts off the text. + rect = core::rect<s32>(pos.X, pos.Y, + pos.X + imgsize.X, + pos.Y + font_line_height(m_font) * + (text.length() + 1)); - core::rect<s32> rect = core::rect<s32>( - pos.X, pos.Y+((imgsize.Y/2)- m_btn_height), + } 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<s32>( + 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"<<std::endl; @@ -1278,9 +1533,14 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen L"", 258+m_fields.size() ); - gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(), + gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); - t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + + auto style = getStyleForElement("vertlabel", spec.fname, "label"); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); + m_fields.push_back(spec); return; } @@ -1304,11 +1564,6 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem MY_CHECKPOS("imagebutton",0); MY_CHECKGEOM("imagebutton",1); - v2s32 pos = getElementBasePos(false, &v_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); - bool noclip = false; bool drawborder = true; std::string pressed_image_name; @@ -1324,9 +1579,22 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem pressed_image_name = parts[7]; } - core::rect<s32> rect = core::rect<s32>(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<s32> rect = core::rect<s32>(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"<<std::endl; image_name = unescape_string(image_name); @@ -1341,7 +1609,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem 258+m_fields.size() ); spec.ftype = f_Button; - if(type == "image_button_exit") + if (type == "image_button_exit") spec.is_exit = true; video::ITexture *texture = 0; @@ -1358,14 +1626,21 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem Environment->setFocus(e); } - e->setUseAlphaChannel(true); + auto style = getStyleForElement("image_button", spec.fname); + + e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true)); e->setImage(guiScalingImageButton( Environment->getVideoDriver(), texture, geom.X, geom.Y)); e->setPressedImage(guiScalingImageButton( Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y)); e->setScaleImage(true); - e->setNotClipped(noclip); - e->setDrawBorder(drawborder); + if (parts.size() >= 7) { + e->setNotClipped(noclip); + e->setDrawBorder(drawborder); + } else { + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + } m_fields.push_back(spec); return; @@ -1376,25 +1651,43 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element) { - std::vector<std::string> parts = split(element,';'); + std::vector<std::string> 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<std::string> v_pos = split(parts[0],','); - std::string name = parts[1]; - std::vector<std::string> 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<std::string> 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<std::string> 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; } @@ -1408,15 +1701,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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1425,17 +1729,22 @@ 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); } - e->setNotClipped(true); + auto style = getStyleForElement("tabheader", name); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true)); for (const std::string &button : buttons) { - e->addTab(unescape_translate(unescape_string( + auto tab = e->addTab(unescape_translate(unescape_string( utf8_to_wide(button))).c_str(), -1); + if (style.isNotDefault(StyleSpec::BGCOLOR)) + tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR)); + + tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF))); } if ((tab_index >= 0) && @@ -1476,10 +1785,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<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); @@ -1504,6 +1820,10 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string & gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L""); + auto style = getStyleForElement("item_image_button", spec.fname, "image_button"); + e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false)); + e->setDrawBorder(style.getBool(StyleSpec::BORDER, true)); + if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } @@ -1513,7 +1833,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; @@ -1534,10 +1858,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; @@ -1640,13 +1971,20 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) std::vector<std::string> v_pos = split(parts[0], ','); std::vector<std::string> 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<s32> rect(pos, pos + geom); m_tooltip_rects.emplace_back(rect, spec); @@ -1772,12 +2110,70 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) << "'" << std::endl; } +bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, bool style_type) +{ + std::vector<std::string> parts = split(element, ';'); + + if (parts.size() < 2) { + errorstream << "Invalid style element (" << parts.size() << "): '" << element + << "'" << std::endl; + return false; + } + + std::string selector = trim(parts[0]); + if (selector.empty()) { + errorstream << "Invalid style element (Selector required): '" << element + << "'" << std::endl; + return false; + } + + StyleSpec spec; + + for (size_t i = 1; i < parts.size(); i++) { + size_t equal_pos = parts[i].find('='); + if (equal_pos == std::string::npos) { + errorstream << "Invalid style element (Property missing value): '" << element + << "'" << std::endl; + return false; + } + + std::string propname = trim(parts[i].substr(0, equal_pos)); + std::string value = trim(unescape_string(parts[i].substr(equal_pos + 1))); + + std::transform(propname.begin(), propname.end(), propname.begin(), ::tolower); + + StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname); + if (prop == StyleSpec::NONE) { + if (property_warned.find(propname) != property_warned.end()) { + warningstream << "Invalid style element (Unknown property " << propname << "): '" + << element + << "'" << std::endl; + property_warned.insert(propname); + } + return false; + } + + spec.set(prop, value); + } + + if (style_type) { + theme_by_type[selector] |= spec; + } else { + theme_by_name[selector] |= spec; + } + + return true; +} + void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) { //some prechecks if (element.empty()) return; + if (parseVersionDirect(element)) + return; + std::vector<std::string> parts = split(element,'['); // ugly workaround to keep compatibility @@ -1837,8 +2233,8 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } - if (type == "background") { - parseBackground(data,description); + if (type == "background" || type == "background9") { + parseBackground(data, description); return; } @@ -1932,6 +2328,21 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) return; } + if (type == "real_coordinates") { + data->real_coordinates = is_yes(description); + return; + } + + if (type == "style") { + parseStyle(data, description, false); + return; + } + + if (type == "style_type") { + parseStyle(data, description, true); + return; + } + // Ignore others infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\"" << std::endl; @@ -2002,6 +2413,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_inventory_rings.clear(); m_static_texts.clear(); m_dropdowns.clear(); + theme_by_name.clear(); + theme_by_type.clear(); m_bgfullscreen = false; @@ -2096,6 +2509,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) break; } + /* Copy of the "real_coordinates" element for after the form size. */ + mydata.real_coordinates = m_formspec_version >= 2; + for (; i < elements.size(); i++) { + std::vector<std::string> 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) { @@ -2186,10 +2610,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>( (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, @@ -2221,9 +2653,17 @@ 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; + u16 version_backup = m_formspec_version; + mydata.real_coordinates = false; // Old coordinates by default. + std::vector<std::string> prepend_elements = split(m_formspec_prepend, ']'); for (const auto &element : prepend_elements) parseElement(&mydata, element); + + m_formspec_version = version_backup; + mydata.real_coordinates = rc_backup; // Restore coordinates } for (; i< elements.size(); i++) { @@ -2313,8 +2753,16 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) { for(s32 i=0; i<s.geom.X*s.geom.Y; i++) { s32 item_i = i + s.start_item_i; - 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 p0(x,y); core::rect<s32> rect = imgrect + s.pos + p0; if(rect.isPointInside(p)) @@ -2356,8 +2804,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<s32> rect = imgrect + s.pos + p; ItemStack item = ilist->getItem(item_i); @@ -2401,37 +2856,23 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer, } if (layer == 1) { - // Draw item stack if (selected) item.takeItem(m_selected_amount); if (!item.empty()) { + // Draw item stack drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect, m_client, rotation_kind); - } - - // Draw tooltip - std::wstring tooltip_text; - if (hovering && !m_selected_item) { - const std::string &desc = item.metadata.getString("description"); - if (desc.empty()) - tooltip_text = - utf8_to_wide(item.getDefinition(m_client->idef()).description); - else - tooltip_text = utf8_to_wide(desc); - - if (!item.name.empty()) { - if (tooltip_text.empty()) - tooltip_text = utf8_to_wide(item.name); - else if (m_tooltip_append_itemname) - tooltip_text += utf8_to_wide(" [" + item.name + "]"); + // Draw tooltip + if (hovering && !m_selected_item) { + std::string tooltip = item.getDescription(m_client->idef()); + if (m_tooltip_append_itemname) + tooltip += "\n[" + item.name + "]"; + showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color, + m_default_tooltip_bgcolor); } } - if (!tooltip_text.empty()) { - showTooltip(tooltip_text, m_default_tooltip_color, - m_default_tooltip_bgcolor); - } } } } @@ -2510,6 +2951,8 @@ void GUIFormSpecMenu::drawMenu() core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y); // Image rectangle on screen core::rect<s32> rect = imgrect + spec.pos; + // Middle rect for 9-slicing + core::rect<s32> middle = spec.middle; if (spec.clip) { core::dimension2d<s32> absrec_size = AbsoluteRect.getSize(); @@ -2519,12 +2962,23 @@ void GUIFormSpecMenu::drawMenu() AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y); } - const video::SColor color(255,255,255,255); - const video::SColor colors[] = {color,color,color,color}; - draw2DImageFilterScaled(driver, texture, rect, - core::rect<s32>(core::position2d<s32>(0,0), - core::dimension2di(texture->getOriginalSize())), - NULL/*&AbsoluteClippingRect*/, colors, true); + if (middle.getArea() == 0) { + const video::SColor color(255, 255, 255, 255); + const video::SColor colors[] = {color, color, color, color}; + draw2DImageFilterScaled(driver, texture, rect, + core::rect<s32>(core::position2d<s32>(0, 0), + core::dimension2di(texture->getOriginalSize())), + NULL/*&AbsoluteClippingRect*/, colors, true); + } else { + // `-x` is interpreted as `w - x` + if (middle.LowerRightCorner.X < 0) { + middle.LowerRightCorner.X += texture->getOriginalSize().Width; + } + if (middle.LowerRightCorner.Y < 0) { + middle.LowerRightCorner.Y += texture->getOriginalSize().Height; + } + draw2DImage9Slice(driver, texture, rect, middle); + } } else { errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl; errorstream << "\t" << spec.name << std::endl; @@ -3406,7 +3860,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Mouse has been moved and rmb is down and mouse pointer just // entered a new inventory field (checked in the entry-if, this // is the only action here that is generated by mouse movement) - if (m_selected_item && s.isValid()) { + if (m_selected_item && s.isValid() && s.listname != "craftpreview") { // Move 1 item // TODO: middle mouse to move 10 items might be handy if (m_auto_place) { @@ -3740,3 +4194,27 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id) } return L""; } + +StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type, + const std::string &name, const std::string &parent_type) { + StyleSpec ret; + + if (!parent_type.empty()) { + auto it = theme_by_type.find(parent_type); + if (it != theme_by_type.end()) { + ret |= it->second; + } + } + + auto it = theme_by_type.find(type); + if (it != theme_by_type.end()) { + ret |= it->second; + } + + it = theme_by_name.find(name); + if (it != theme_by_name.end()) { + ret |= it->second; + } + + return ret; +} |