X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2FguiFormSpecMenu.cpp;h=9d97821f02ea1569311d06757ab1e5562e026be8;hb=67f97f8d3274e0e96a8f541a05a39f81dfec1a20;hp=c11fc303a6d6762cb399e5bdfb223e6ea927ac31;hpb=ea1b17b88f24eac7125a0765c37bbcd4fe8399db;p=oweals%2Fminetest.git diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index c11fc303a..9d97821f0 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "gamedef.h" #include "keycode.h" -#include "strfnd.h" +#include "util/strfnd.h" #include #include #include @@ -36,13 +36,14 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "client/renderingengine.h" #include "log.h" #include "client/tile.h" // ITextureSource #include "hud.h" // drawItemStack #include "filesys.h" #include "gettime.h" #include "gettext.h" -#include "scripting_game.h" +#include "scripting_server.h" #include "porting.h" #include "settings.h" #include "client.h" @@ -50,6 +51,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/hex.h" #include "util/numeric.h" #include "util/string.h" // for parseColorString() +#include "irrlicht_changes/static_text.h" #include "guiscalingfilter.h" #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 @@ -77,31 +79,22 @@ static unsigned int font_line_height(gui::IGUIFont *font) return font->getDimension(L"Ay").Height + font->getKerningHeight(); } -GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, - gui::IGUIElement* parent, s32 id, IMenuManager *menumgr, - InventoryManager *invmgr, IGameDef *gamedef, - ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst, - Client* client, bool remap_dbl_click) : - GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr), - m_device(dev), - m_invmgr(invmgr), - m_gamedef(gamedef), +inline u32 clamp_u8(s32 value) +{ + return (u32) MYMIN(MYMAX(value, 0), 255); +} + +GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick, + gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, + Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst, + bool remap_dbl_click) : + GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr), + m_invmgr(client), m_tsrc(tsrc), m_client(client), - m_selected_item(NULL), - m_selected_amount(0), - m_selected_dragging(false), - m_tooltip_element(NULL), - m_hovered_time(0), - m_old_tooltip_id(-1), - m_rmouse_auto_place(false), - m_allowclose(true), - m_lock(false), m_form_src(fsrc), m_text_dst(tdst), - m_formspec_version(0), - m_focused_element(""), - m_font(NULL), + m_joystick(joystick), m_remap_dbl_click(remap_dbl_click) #ifdef __ANDROID__ , m_JavaDialogFieldName("") @@ -119,25 +112,20 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_doubleclickdetect[1].pos = v2s32(0, 0); m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay"); + m_tooltip_append_itemname = g_settings->getBool("tooltip_append_itemname"); } GUIFormSpecMenu::~GUIFormSpecMenu() { removeChildren(); - for (u32 i = 0; i < m_tables.size(); ++i) { - GUITable *table = m_tables[i].second; - table->drop(); + for (auto &table_it : m_tables) { + table_it.second->drop(); } delete m_selected_item; - - if (m_form_src != NULL) { - delete m_form_src; - } - if (m_text_dst != NULL) { - delete m_text_dst; - } + delete m_form_src; + delete m_text_dst; } void GUIFormSpecMenu::removeChildren() @@ -178,29 +166,26 @@ void GUIFormSpecMenu::setInitialFocus() } // 1. first empty editbox - for (core::list::Iterator it = children.begin(); - it != children.end(); ++it) { - if ((*it)->getType() == gui::EGUIET_EDIT_BOX - && (*it)->getText()[0] == 0) { - Environment->setFocus(*it); + for (gui::IGUIElement *it : children) { + if (it->getType() == gui::EGUIET_EDIT_BOX + && it->getText()[0] == 0) { + Environment->setFocus(it); return; } } // 2. first editbox - for (core::list::Iterator it = children.begin(); - it != children.end(); ++it) { - if ((*it)->getType() == gui::EGUIET_EDIT_BOX) { - Environment->setFocus(*it); + for (gui::IGUIElement *it : children) { + if (it->getType() == gui::EGUIET_EDIT_BOX) { + Environment->setFocus(it); return; } } // 3. first table - for (core::list::Iterator it = children.begin(); - it != children.end(); ++it) { - if ((*it)->getTypeName() == std::string("GUITable")) { - Environment->setFocus(*it); + for (gui::IGUIElement *it : children) { + if (it->getTypeName() == std::string("GUITable")) { + Environment->setFocus(it); return; } } @@ -215,11 +200,10 @@ void GUIFormSpecMenu::setInitialFocus() } // 5. first focusable (not statictext, not tabheader) - for (core::list::Iterator it = children.begin(); - it != children.end(); ++it) { - if ((*it)->getType() != gui::EGUIET_STATIC_TEXT && - (*it)->getType() != gui::EGUIET_TAB_CONTROL) { - Environment->setFocus(*it); + for (gui::IGUIElement *it : children) { + if (it->getType() != gui::EGUIET_STATIC_TEXT && + it->getType() != gui::EGUIET_TAB_CONTROL) { + Environment->setFocus(it); return; } } @@ -233,45 +217,23 @@ void GUIFormSpecMenu::setInitialFocus() GUITable* GUIFormSpecMenu::getTable(const std::string &tablename) { - for (u32 i = 0; i < m_tables.size(); ++i) { - if (tablename == m_tables[i].first.fname) - return m_tables[i].second; + for (auto &table : m_tables) { + if (tablename == table.first.fname) + return table.second; } return 0; } -static std::vector split(const std::string &s, char delim) +std::vector* GUIFormSpecMenu::getDropDownValues(const std::string &name) { - std::vector tokens; - - std::string current = ""; - bool last_was_escape = false; - for (unsigned int i = 0; i < s.size(); i++) { - char si = s.c_str()[i]; - if (last_was_escape) { - current += '\\'; - current += si; - last_was_escape = false; - } else { - if (si == delim) { - tokens.push_back(current); - current = ""; - last_was_escape = false; - } else if (si == '\\') { - last_was_escape = true; - } else { - current += si; - last_was_escape = false; - } - } + for (auto &dropdown : m_dropdowns) { + if (name == dropdown.first.fname) + return &dropdown.second; } - //push last element - tokens.push_back(current); - - return tokens; + return NULL; } -void GUIFormSpecMenu::parseSize(parserData* data,std::string element) +void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element) { std::vector parts = split(element,','); @@ -297,10 +259,36 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element) errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseList(parserData* data,std::string element) +void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &element) { - if (m_gamedef == 0) { - errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"< parts = split(element, ','); + + if (parts.size() >= 2) { + if (parts[1].find(';') != std::string::npos) + 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])); + return; + } + errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'" << std::endl; +} + +void GUIFormSpecMenu::parseContainerEnd(parserData* data) +{ + if (container_stack.empty()) { + errorstream<< "Invalid container end element, no matching container start element" << std::endl; + } else { + pos_offset = container_stack.top(); + container_stack.pop(); + } +} + +void GUIFormSpecMenu::parseList(parserData* data, const std::string &element) +{ + if (m_client == 0) { + warningstream<<"invalid use of 'list' with m_client==0"< v_pos = split(parts[2],','); std::vector v_geom = split(parts[3],','); - std::string startindex = ""; + std::string startindex; if (parts.size() == 5) startindex = parts[4]; @@ -327,7 +315,7 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element) else loc.deSerialize(location); - v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -336,7 +324,7 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element) geom.Y = stoi(v_geom[1]); s32 start_i = 0; - if(startindex != "") + if (!startindex.empty()) start_i = stoi(startindex); if (geom.X < 0 || geom.Y < 0 || start_i < 0) { @@ -345,17 +333,17 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element) } if(!data->explicit_size) - errorstream<<"WARNING: invalid use of list without a size[] element"< 1)) { + } + + if (element.empty() && m_inventorylists.size() > 1) { size_t siz = m_inventorylists.size(); // insert the last two inv list elements into the list ring const ListDrawSpec &spa = m_inventorylists[siz - 2]; const ListDrawSpec &spb = m_inventorylists[siz - 1]; - m_inventory_rings.push_back(ListRingSpec(spa.inventoryloc, spa.listname)); - m_inventory_rings.push_back(ListRingSpec(spb.inventoryloc, spb.listname)); + m_inventory_rings.emplace_back(spa.inventoryloc, spa.listname); + m_inventory_rings.emplace_back(spb.inventoryloc, spb.listname); return; } + errorstream<< "Invalid list ring element(" << parts.size() << ", " << m_inventorylists.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) +void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -397,14 +388,14 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) std::vector v_pos = split(parts[0],','); std::string name = parts[1]; std::string label = parts[2]; - std::string selected = ""; + std::string selected; if (parts.size() >= 4) selected = parts[3]; MY_CHECKPOS("checkbox",0); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; @@ -413,7 +404,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) if (selected == "true") fselected = true; - std::wstring wlabel = utf8_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); core::rect rect = core::rect( pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height), @@ -436,26 +427,26 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) Environment->setFocus(e); } - m_checkboxes.push_back(std::pair(spec,e)); + m_checkboxes.emplace_back(spec,e); m_fields.push_back(spec); return; } errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element) +void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element) { std::vector parts = split(element,';'); if (parts.size() >= 5) { std::vector v_pos = split(parts[0],','); std::vector v_dim = split(parts[1],','); - std::string name = parts[2]; + std::string name = parts[3]; std::string value = parts[4]; MY_CHECKPOS("scrollbar",0); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; @@ -495,14 +486,14 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element) e->setSmallStep(10); e->setLargeStep(100); - m_scrollbars.push_back(std::pair(spec,e)); + m_scrollbars.emplace_back(spec,e); m_fields.push_back(spec); return; } errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseImage(parserData* data,std::string element) +void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -513,10 +504,10 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element) std::vector v_geom = split(parts[1],','); std::string name = unescape_string(parts[2]); - MY_CHECKPOS("image",0); - MY_CHECKGEOM("image",1); + MY_CHECKPOS("image", 0); + MY_CHECKGEOM("image", 1); - v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; @@ -524,9 +515,9 @@ void GUIFormSpecMenu::parseImage(parserData* data,std::string element) geom.X = stof(v_geom[0]) * (float)imgsize.X; geom.Y = stof(v_geom[1]) * (float)imgsize.Y; - if(!data->explicit_size) - errorstream<<"WARNING: invalid use of image without a size[] element"<explicit_size) + warningstream<<"invalid use of image without a size[] element"< v_pos = split(parts[0],','); std::string name = unescape_string(parts[1]); - MY_CHECKPOS("image",0); + MY_CHECKPOS("image", 0); - v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; - if(!data->explicit_size) - errorstream<<"WARNING: invalid use of image without a size[] element"<explicit_size) + warningstream<<"invalid use of image without a size[] element"< parts = split(element,';'); @@ -562,7 +553,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element) MY_CHECKPOS("itemimage",0); MY_CHECKGEOM("itemimage",1); - v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; @@ -571,15 +562,15 @@ void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element) geom.Y = stof(v_geom[1]) * (float)imgsize.Y; if(!data->explicit_size) - errorstream<<"WARNING: invalid use of item_image without a size[] element"< parts = split(element,';'); @@ -594,7 +585,7 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element, MY_CHECKPOS("button",0); MY_CHECKGEOM("button",1); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -607,11 +598,9 @@ void GUIFormSpecMenu::parseButton(parserData* data,std::string element, pos.X + geom.X, pos.Y + m_btn_height); if(!data->explicit_size) - errorstream<<"WARNING: invalid use of button without a size[] element"< parts = split(element,';'); @@ -649,49 +638,49 @@ void GUIFormSpecMenu::parseBackground(parserData* data,std::string element) MY_CHECKPOS("background",0); MY_CHECKGEOM("background",1); - v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; - pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2; - pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2; + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; + pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X - (float)imgsize.X)/2; + pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y - (float)imgsize.Y)/2; v2s32 geom; geom.X = stof(v_geom[0]) * (float)spacing.X; geom.Y = stof(v_geom[1]) * (float)spacing.Y; - if (parts.size() == 4) { - m_clipbackground = is_yes(parts[3]); - if (m_clipbackground) { - pos.X = stoi(v_pos[0]); //acts as offset - pos.Y = stoi(v_pos[1]); //acts as offset - } + if (!data->explicit_size) + warningstream<<"invalid use of background without a size[] element"<explicit_size) - errorstream<<"WARNING: invalid use of background without a size[] element"< parts = split(element,';'); data->table_options.clear(); - for (size_t i = 0; i < parts.size(); ++i) { + for (const std::string &part : parts) { // Parse table option - std::string opt = unescape_string(parts[i]); + std::string opt = unescape_string(part); data->table_options.push_back(GUITable::splitOption(opt)); } } -void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element) +void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &element) { std::vector parts = split(element,';'); data->table_columns.clear(); - for (size_t i = 0; i < parts.size(); ++i) { - std::vector col_parts = split(parts[i],','); + for (const std::string &part : parts) { + std::vector col_parts = split(part,','); GUITable::TableColumn column; // Parse column type if (!col_parts.empty()) @@ -705,7 +694,7 @@ void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element) } } -void GUIFormSpecMenu::parseTable(parserData* data,std::string element) +void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -716,7 +705,7 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element) std::vector v_geom = split(parts[1],','); std::string name = parts[2]; std::vector items = split(parts[3],','); - std::string str_initial_selection = ""; + std::string str_initial_selection; std::string str_transparent = "false"; if (parts.size() >= 5) @@ -725,7 +714,7 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element) MY_CHECKPOS("table",0); MY_CHECKGEOM("table",1); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -733,7 +722,6 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element) geom.X = stof(v_geom[0]) * (float)spacing.X; geom.Y = stof(v_geom[1]) * (float)spacing.Y; - core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); FieldSpec spec( @@ -745,8 +733,8 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element) spec.ftype = f_Table; - for (unsigned int i = 0; i < items.size(); ++i) { - items[i] = unescape_string(items[i]); + for (std::string &item : items) { + item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); } //now really show table @@ -763,18 +751,17 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element) e->setDynamicData(data->table_dyndata[name]); } - if ((str_initial_selection != "") && - (str_initial_selection != "0")) - e->setSelected(stoi(str_initial_selection.c_str())); + if (!str_initial_selection.empty() && str_initial_selection != "0") + e->setSelected(stoi(str_initial_selection)); - m_tables.push_back(std::pair(spec, e)); + m_tables.emplace_back(spec, e); m_fields.push_back(spec); return; } errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) +void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -785,7 +772,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) std::vector v_geom = split(parts[1],','); std::string name = parts[2]; std::vector items = split(parts[3],','); - std::string str_initial_selection = ""; + std::string str_initial_selection; std::string str_transparent = "false"; if (parts.size() >= 5) @@ -797,7 +784,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) MY_CHECKPOS("textlist",0); MY_CHECKGEOM("textlist",1); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -817,8 +804,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) spec.ftype = f_Table; - for (unsigned int i = 0; i < items.size(); ++i) { - items[i] = unescape_string(items[i]); + for (std::string &item : items) { + item = wide_to_utf8(unescape_translate(utf8_to_wide(unescape_string(item)))); } //now really show list @@ -835,11 +822,10 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) e->setDynamicData(data->table_dyndata[name]); } - if ((str_initial_selection != "") && - (str_initial_selection != "0")) - e->setSelected(stoi(str_initial_selection.c_str())); + if (!str_initial_selection.empty() && str_initial_selection != "0") + e->setSelected(stoi(str_initial_selection)); - m_tables.push_back(std::pair(spec, e)); + m_tables.emplace_back(spec, e); m_fields.push_back(spec); return; } @@ -847,7 +833,7 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) } -void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) +void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -857,12 +843,12 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) std::vector v_pos = split(parts[0],','); std::string name = parts[2]; std::vector items = split(parts[3],','); - std::string str_initial_selection = ""; + std::string str_initial_selection; str_initial_selection = parts[4]; MY_CHECKPOS("dropdown",0); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -888,26 +874,43 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) Environment->setFocus(e); } - for (unsigned int i=0; i < items.size(); i++) { - e->addItem(utf8_to_wide(items[i]).c_str()); + for (const std::string &item : items) { + e->addItem(unescape_translate(unescape_string( + utf8_to_wide(item))).c_str()); } - if (str_initial_selection != "") - e->setSelected(stoi(str_initial_selection.c_str())-1); + if (!str_initial_selection.empty()) + e->setSelected(stoi(str_initial_selection)-1); m_fields.push_back(spec); + + m_dropdowns.emplace_back(spec, std::vector()); + std::vector &values = m_dropdowns.back().second; + for (const std::string &item : items) { + values.push_back(unescape_string(item)); + } + return; } errorstream << "Invalid dropdown element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) +void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element) { std::vector parts = split(element,';'); + if (parts.size() == 2 || + (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) { + field_close_on_enter[parts[0]] = is_yes(parts[1]); + } +} - if ((parts.size() == 4) || - ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) +void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element) +{ + std::vector parts = split(element,';'); + + if ((parts.size() == 4) || (parts.size() == 5) || + ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) { std::vector v_pos = split(parts[0],','); std::vector v_geom = split(parts[1],','); @@ -917,7 +920,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) MY_CHECKPOS("pwdfield",0); MY_CHECKGEOM("pwdfield",1); - v2s32 pos; + v2s32 pos = pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -930,9 +933,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - label = unescape_string(label); - - std::wstring wlabel = utf8_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); FieldSpec spec( name, @@ -953,7 +954,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); } e->setPasswordBox(true,L'*'); @@ -962,10 +963,18 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) evt.EventType = EET_KEY_INPUT_EVENT; evt.KeyInput.Key = KEY_END; evt.KeyInput.Char = 0; - evt.KeyInput.Control = 0; - evt.KeyInput.Shift = 0; + evt.KeyInput.Control = false; + evt.KeyInput.Shift = false; evt.KeyInput.PressedDown = true; e->OnEvent(evt); + + if (parts.size() >= 5) { + // TODO: remove after 2016-11-03 + warningstream << "pwdfield: use field_close_on_enter[name, enabled]" << + " instead of the 5th param" << std::endl; + field_close_on_enter[name] = is_yes(parts[4]); + } + m_fields.push_back(spec); return; } @@ -982,9 +991,9 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, core::rect rect; if(data->explicit_size) - errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<resolveText(default_val); - default_val = unescape_string(default_val); - label = unescape_string(label); - std::wstring wlabel = utf8_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); FieldSpec spec( name, wlabel, - utf8_to_wide(default_val), + utf8_to_wide(unescape_string(default_val)), 258+m_fields.size() ); - if (name == "") - { + if (name.empty()) { // spec field id to 0, this stops submit searching for a value that isn't there - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); - } - else - { + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid); + } else { spec.send = true; gui::IGUIElement *e; #if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 if (g_settings->getBool("freetype")) { e = (gui::IGUIElement *) new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment, this, spec.fid, rect); + e->drop(); } else { #else { @@ -1044,15 +1049,22 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); } } + if (parts.size() >= 4) { + // TODO: remove after 2016-11-03 + warningstream << "field/simple: use field_close_on_enter[name, enabled]" << + " instead of the 4th param" << std::endl; + field_close_on_enter[name] = is_yes(parts[3]); + } + m_fields.push_back(spec); } -void GUIFormSpecMenu::parseTextArea(parserData* data, - std::vector& parts,std::string type) +void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector& parts, + const std::string &type) { std::vector v_pos = split(parts[0],','); @@ -1064,9 +1076,9 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, MY_CHECKPOS(type,0); MY_CHECKGEOM(type,1); - v2s32 pos; - pos.X = stof(v_pos[0]) * (float) spacing.X; - pos.Y = stof(v_pos[1]) * (float) spacing.Y; + v2s32 pos = pos_offset * spacing; + pos.X += stof(v_pos[0]) * (float) spacing.X; + pos.Y += stof(v_pos[1]) * (float) spacing.Y; v2s32 geom; @@ -1087,31 +1099,25 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); if(!data->explicit_size) - errorstream<<"WARNING: invalid use of positioned "<resolveText(default_val); - default_val = unescape_string(default_val); - label = unescape_string(label); - - std::wstring wlabel = utf8_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); FieldSpec spec( name, wlabel, - utf8_to_wide(default_val), + utf8_to_wide(unescape_string(default_val)), 258+m_fields.size() ); - if (name == "") - { + if (name.empty()) { // spec field id to 0, this stops submit searching for a value that isn't there - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); - } - else - { + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid); + } else { spec.send = true; gui::IGUIEditBox *e; @@ -1119,6 +1125,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, if (g_settings->getBool("freetype")) { e = (gui::IGUIEditBox *) new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment, this, spec.fid, rect); + e->drop(); } else { #else { @@ -1151,14 +1158,22 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, int font_height = g_fontengine->getTextHeight(); rect.UpperLeftCorner.Y -= font_height; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height; - Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0); } } + + if (parts.size() >= 6) { + // TODO: remove after 2016-11-03 + warningstream << "field/textarea: use field_close_on_enter[name, enabled]" << + " instead of the 6th param" << std::endl; + field_close_on_enter[name] = is_yes(parts[5]); + } + m_fields.push_back(spec); } -void GUIFormSpecMenu::parseField(parserData* data,std::string element, - std::string type) +void GUIFormSpecMenu::parseField(parserData* data, const std::string &element, + const std::string &type) { std::vector parts = split(element,';'); @@ -1167,8 +1182,8 @@ void GUIFormSpecMenu::parseField(parserData* data,std::string element, return; } - if ((parts.size() == 5) || - ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) + if ((parts.size() == 5) || (parts.size() == 6) || + ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION))) { parseTextArea(data,parts,type); return; @@ -1176,7 +1191,7 @@ void GUIFormSpecMenu::parseField(parserData* data,std::string element, errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) +void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -1188,14 +1203,13 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) MY_CHECKPOS("label",0); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y; if(!data->explicit_size) - errorstream<<"WARNING: invalid use of label without a size[] element"< lines = split(text, '\n'); for (unsigned int i = 0; i != lines.size(); i++) { @@ -1209,7 +1223,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) // 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(lines[i]); + 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, @@ -1221,7 +1235,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) 258+m_fields.size() ); gui::IGUIStaticText *e = - Environment->addStaticText(spec.flabel.c_str(), + addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER); @@ -1233,7 +1247,7 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) +void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -1241,11 +1255,12 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) { std::vector v_pos = split(parts[0],','); - std::wstring text = utf8_to_wide(unescape_string(parts[1])); + std::wstring text = unescape_translate( + unescape_string(utf8_to_wide(parts[1]))); MY_CHECKPOS("vertlabel",1); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; @@ -1258,12 +1273,12 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) //actually text.length() would be correct but adding +1 avoids to break all mods if(!data->explicit_size) - errorstream<<"WARNING: invalid use of label without a size[] element"<addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid); + addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid); t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); m_fields.push_back(spec); return; @@ -1282,8 +1297,8 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, - std::string type) +void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element, + const std::string &type) { std::vector parts = split(element,';'); @@ -1299,7 +1314,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, MY_CHECKPOS("imagebutton",0); MY_CHECKGEOM("imagebutton",1); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; @@ -1308,7 +1323,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, bool noclip = false; bool drawborder = true; - std::string pressed_image_name = ""; + std::string pressed_image_name; if (parts.size() >= 7) { if (parts[5] == "true") @@ -1324,13 +1339,12 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); if(!data->explicit_size) - errorstream<<"WARNING: invalid use of image_button without a size[] element"<getTexture(image_name); - if (pressed_image_name != "") + if (!pressed_image_name.empty()) pressed_texture = m_tsrc->getTexture(pressed_image_name); else pressed_texture = texture; @@ -1372,7 +1386,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) +void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -1405,7 +1419,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) spec.ftype = f_TabHeader; - v2s32 pos(0,0); + v2s32 pos = pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2; v2s32 geom; @@ -1427,8 +1441,9 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) e->setNotClipped(true); - for (unsigned int i = 0; i < buttons.size(); i++) { - e->addTab(utf8_to_wide(buttons[i]).c_str(), -1); + for (const std::string &button : buttons) { + e->addTab(unescape_translate(unescape_string( + utf8_to_wide(button))).c_str(), -1); } if ((tab_index >= 0) && @@ -1443,13 +1458,12 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) << element << "'" << std::endl; } -void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) +void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element) { - if (m_gamedef == 0) { - errorstream << - "WARNING: invalid use of item_image_button with m_gamedef==0" - << std::endl; + if (m_client == 0) { + warningstream << "invalid use of item_image_button with m_client==0" + << std::endl; return; } @@ -1464,10 +1478,13 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) std::string name = parts[3]; std::string label = parts[4]; + label = unescape_string(label); + item_name = unescape_string(item_name); + MY_CHECKPOS("itemimagebutton",0); MY_CHECKGEOM("itemimagebutton",1); - v2s32 pos = padding; + v2s32 pos = padding + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y; v2s32 geom; @@ -1477,19 +1494,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); if(!data->explicit_size) - errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<idef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; item.deSerialize(item_name, idef); - video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); m_tooltips[name] = - TooltipSpec(item.getDefinition(idef).description, + TooltipSpec(utf8_to_wide(item.getDefinition(idef).description), m_default_tooltip_bgcolor, m_default_tooltip_color); - label = unescape_string(label); FieldSpec spec( name, utf8_to_wide(label), @@ -1497,26 +1512,27 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) 258 + m_fields.size() ); - gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str()); + gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L""); if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } - e->setUseAlphaChannel(true); - e->setImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y)); - e->setPressedImage(guiScalingImageButton(Environment->getVideoDriver(), texture, geom.X, geom.Y)); - e->setScaleImage(true); spec.ftype = f_Button; rect+=data->basepos-padding; spec.rect=rect; m_fields.push_back(spec); + pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; + pos.X += stof(v_pos[0]) * (float) spacing.X; + pos.Y += stof(v_pos[1]) * (float) spacing.Y; + m_itemimages.emplace_back("", item_name, e, pos, geom); + m_static_texts.emplace_back(utf8_to_wide(label), rect, e); return; } errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseBox(parserData* data,std::string element) +void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -1529,7 +1545,7 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element) MY_CHECKPOS("box",0); MY_CHECKGEOM("box",1); - v2s32 pos = padding + AbsoluteRect.UpperLeftCorner; + v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing; pos.X += stof(v_pos[0]) * (float) spacing.X; pos.Y += stof(v_pos[1]) * (float) spacing.Y; @@ -1552,25 +1568,27 @@ void GUIFormSpecMenu::parseBox(parserData* data,std::string element) errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element) +void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element) { std::vector parts = split(element,';'); if (((parts.size() == 1) || (parts.size() == 2)) || - ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) - { - parseColorString(parts[0],m_bgcolor,false); + ((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION))) { + parseColorString(parts[0], m_bgcolor, false); if (parts.size() == 2) { std::string fullscreen = parts[1]; m_bgfullscreen = is_yes(fullscreen); } + return; } - errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl; + + errorstream << "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" + << std::endl; } -void GUIFormSpecMenu::parseListColors(parserData* data,std::string element) +void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element) { std::vector parts = split(element,';'); @@ -1598,19 +1616,21 @@ void GUIFormSpecMenu::parseListColors(parserData* data,std::string element) errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl; } -void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element) +void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element) { std::vector parts = split(element,';'); if (parts.size() == 2) { std::string name = parts[0]; - m_tooltips[name] = TooltipSpec(unescape_string(parts[1]), + m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])), m_default_tooltip_bgcolor, m_default_tooltip_color); return; - } else if (parts.size() == 4) { + } + + if (parts.size() == 4) { std::string name = parts[0]; video::SColor tmp_color1, tmp_color2; if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) { - m_tooltips[name] = TooltipSpec(unescape_string(parts[1]), + m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])), tmp_color1, tmp_color2); return; } @@ -1618,10 +1638,10 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element) errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl; } -bool GUIFormSpecMenu::parseVersionDirect(std::string data) +bool GUIFormSpecMenu::parseVersionDirect(const std::string &data) { //some prechecks - if (data == "") + if (data.empty()) return false; std::vector parts = split(data,'['); @@ -1642,9 +1662,9 @@ bool GUIFormSpecMenu::parseVersionDirect(std::string data) return false; } -bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element) +bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &element) { - if (element == "") + if (element.empty()) return false; std::vector parts = split(element,'['); @@ -1666,10 +1686,79 @@ bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element) return true; } -void GUIFormSpecMenu::parseElement(parserData* data, std::string element) +bool GUIFormSpecMenu::parsePositionDirect(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 != "position") + return false; + + parsePosition(data, description); + + return true; +} + +void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ','); + + if (parts.size() == 2) { + data->offset.X = stof(parts[0]); + data->offset.Y = stof(parts[1]); + return; + } + + errorstream << "Invalid position element (" << parts.size() << "): '" << element << "'" << std::endl; +} + +bool GUIFormSpecMenu::parseAnchorDirect(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 != "anchor") + return false; + + parseAnchor(data, description); + + return true; +} + +void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element) +{ + std::vector parts = split(element, ','); + + if (parts.size() == 2) { + data->anchor.X = stof(parts[0]); + data->anchor.Y = stof(parts[1]); + return; + } + + errorstream << "Invalid anchor element (" << parts.size() << "): '" << element + << "'" << std::endl; +} + +void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element) { //some prechecks - if (element == "") + if (element.empty()) return; std::vector parts = split(element,'['); @@ -1691,8 +1780,18 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) std::string type = trim(parts[0]); std::string description = trim(parts[1]); + if (type == "container") { + parseContainer(data, description); + return; + } + + if (type == "container_end") { + parseContainerEnd(data); + return; + } + if (type == "list") { - parseList(data,description); + parseList(data, description); return; } @@ -1702,22 +1801,22 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) } if (type == "checkbox") { - parseCheckbox(data,description); + parseCheckbox(data, description); return; } if (type == "image") { - parseImage(data,description); + parseImage(data, description); return; } if (type == "item_image") { - parseItemImage(data,description); + parseItemImage(data, description); return; } - if ((type == "button") || (type == "button_exit")) { - parseButton(data,description,type); + if (type == "button" || type == "button_exit") { + parseButton(data, description, type); return; } @@ -1751,6 +1850,11 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) return; } + if (type == "field_close_on_enter") { + parseFieldCloseOnEnter(data, description); + return; + } + if (type == "pwdfield") { parsePwdField(data,description); return; @@ -1812,9 +1916,8 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) } // Ignore others - infostream - << "Unknown DrawSpec: type="<getDynamicData(); } @@ -1842,10 +1945,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) if (focused_element && focused_element->getParent() == this) { s32 focused_id = focused_element->getID(); if (focused_id > 257) { - for (u32 i=0; idrop(); + for (auto &table_it : m_tables) { + table_it.second->drop(); } mydata.size= v2s32(100,100); mydata.screensize = screensize; + mydata.offset = v2f32(0.5f, 0.5f); + mydata.anchor = v2f32(0.5f, 0.5f); // Base position of contents of form mydata.basepos = getBasePos(); @@ -1878,11 +1981,33 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_fields.clear(); m_boxes.clear(); m_tooltips.clear(); + m_inventory_rings.clear(); + m_static_texts.clear(); + m_dropdowns.clear(); - // Set default values (fits old formspec values) - m_bgcolor = video::SColor(140,0,0,0); m_bgfullscreen = false; + { + v3f formspec_bgcolor = g_settings->getV3F("formspec_default_bg_color"); + m_bgcolor = video::SColor( + (u8) clamp_u8(g_settings->getS32("formspec_default_bg_opacity")), + clamp_u8(myround(formspec_bgcolor.X)), + clamp_u8(myround(formspec_bgcolor.Y)), + clamp_u8(myround(formspec_bgcolor.Z)) + ); + } + + { + v3f formspec_bgcolor = g_settings->getV3F("formspec_fullscreen_bg_color"); + m_fullscreen_bgcolor = video::SColor( + (u8) clamp_u8(g_settings->getS32("formspec_fullscreen_bg_opacity")), + clamp_u8(myround(formspec_bgcolor.X)), + clamp_u8(myround(formspec_bgcolor.Y)), + clamp_u8(myround(formspec_bgcolor.Z)) + ); + } + + m_slotbg_n = video::SColor(255,128,128,128); m_slotbg_h = video::SColor(255,192,192,192); @@ -1892,12 +2017,11 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_slotbordercolor = video::SColor(200,0,0,0); m_slotborder = false; - m_clipbackground = false; // Add tooltip { - assert(m_tooltip_element == NULL); + assert(!m_tooltip_element); // Note: parent != this so that the tooltip isn't clipped by the menu rectangle - m_tooltip_element = Environment->addStaticText(L"",core::rect(0,0,110,18)); + m_tooltip_element = addStaticText(Environment, L"",core::rect(0,0,110,18)); m_tooltip_element->enableOverrideColor(true); m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); m_tooltip_element->setDrawBackground(true); @@ -1913,7 +2037,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) unsigned int i = 0; /* try to read version from first element only */ - if (elements.size() >= 1) { + if (!elements.empty()) { if ( parseVersionDirect(elements[0]) ) { i++; } @@ -1927,10 +2051,25 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } } + /* "position" element is always after "size" element if it used */ + for (; i< elements.size(); i++) { + if (!parsePositionDirect(&mydata, elements[i])) { + break; + } + } + + /* "anchor" element is always after "position" (or "size" element) if it used */ + for (; i< elements.size(); i++) { + if (!parseAnchorDirect(&mydata, elements[i])) { + break; + } + } + + if (mydata.explicit_size) { // compute scaling for specified form size if (m_lock) { - v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize(); + v2u32 current_screensize = RenderingEngine::get_video_driver()->getScreenSize(); v2u32 delta = current_screensize - m_lockscreensize; if (current_screensize.Y > m_lockscreensize.Y) @@ -1951,7 +2090,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } double gui_scaling = g_settings->getFloat("gui_scaling"); - double screen_dpi = porting::getDisplayDensity() * 96; + double screen_dpi = RenderingEngine::getDisplayDensity() * 96; double use_imgsize; if (m_lock) { @@ -1984,7 +2123,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) ((5.0/4.0) * (0.5 + mydata.invsize.X)); double fity_imgsize = mydata.screensize.Y / ((15.0/13.0) * (0.85 * mydata.invsize.Y)); - double screen_dpi = porting::getDisplayDensity() * 96; + double screen_dpi = RenderingEngine::getDisplayDensity() * 96; double min_imgsize = 0.3 * screen_dpi * gui_scaling; use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize, MYMIN(fitx_imgsize, fity_imgsize))); @@ -2010,10 +2149,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0 ); DesiredRect = mydata.rect = core::rect( - mydata.screensize.X/2 - mydata.size.X/2 + offset.X, - mydata.screensize.Y/2 - mydata.size.Y/2 + offset.Y, - mydata.screensize.X/2 + mydata.size.X/2 + offset.X, - mydata.screensize.Y/2 + mydata.size.Y/2 + offset.Y + (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, + (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * (f32)mydata.size.X) + offset.X, + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * (f32)mydata.size.Y) + offset.Y ); } else { // Non-size[] form must consist only of text fields and @@ -2022,10 +2161,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_font = g_fontengine->getFont(); m_btn_height = font_line_height(m_font) * 0.875; DesiredRect = core::rect( - mydata.screensize.X/2 - 580/2, - mydata.screensize.Y/2 - 300/2, - mydata.screensize.X/2 + 580/2, - mydata.screensize.Y/2 + 300/2 + (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * 580.0), + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * 300.0), + (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * 580.0), + (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * 300.0) ); } recalculateAbsolutePosition(false); @@ -2033,17 +2172,23 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_tooltip_element->setOverrideFont(m_font); gui::IGUISkin* skin = Environment->getSkin(); - sanity_check(skin != NULL); + sanity_check(skin); gui::IGUIFont *old_font = skin->getFont(); skin->setFont(m_font); + pos_offset = v2s32(); for (; i< elements.size(); i++) { parseElement(&mydata, elements[i]); } + if (!container_stack.empty()) { + errorstream << "Invalid formspec string: container was never closed!" + << std::endl; + } + // If there are fields without explicit size[], add a "Proceed" // button and adjust size to fit all the fields. - if (m_fields.size() && !mydata.explicit_size) { + if (!m_fields.empty() && !mydata.explicit_size) { mydata.rect = core::rect( mydata.screensize.X/2 - 580/2, mydata.screensize.Y/2 - 300/2, @@ -2101,7 +2246,7 @@ bool GUIFormSpecMenu::getAndroidUIInput() } for(std::vector::iterator iter = m_fields.begin(); - iter != m_fields.end(); iter++) { + iter != m_fields.end(); ++iter) { if (iter->fname != fieldname) { continue; @@ -2129,10 +2274,7 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const { core::rect imgrect(0,0,imgsize.X,imgsize.Y); - for(u32 i=0; igetVideoDriver(); Inventory *inv = m_invmgr->getInventory(s.inventoryloc); if(!inv){ - infostream<<"GUIFormSpecMenu::drawList(): WARNING: " + warningstream<<"GUIFormSpecMenu::drawList(): " <<"The inventory location " <<"\""<getList(s.listname); if(!ilist){ - infostream<<"GUIFormSpecMenu::drawList(): WARNING: " + warningstream<<"GUIFormSpecMenu::drawList(): " <<"The inventory list \""<listname == s.listname && m_selected_item->i == item_i; bool hovering = rect.isPointInside(m_pointer); + ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED : + (hovering ? IT_ROT_HOVERED : IT_ROT_NONE); - if(phase == 0) - { - if(hovering) + if (phase == 0) { + if (hovering) { + item_hovered = true; driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect); - else + } else { driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect); + } } //Draw inv slot borders @@ -2230,40 +2376,30 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) if(!item.empty()) { drawItemStack(driver, m_font, item, - rect, &AbsoluteClippingRect, m_gamedef); + rect, &AbsoluteClippingRect, m_client, + rotation_kind); } // Draw tooltip - std::string tooltip_text = ""; - if (hovering && !m_selected_item) - tooltip_text = item.getDefinition(m_gamedef->idef()).description; - if (tooltip_text != "") { - std::vector tt_rows = str_split(tooltip_text, '\n'); - m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor); - m_tooltip_element->setOverrideColor(m_default_tooltip_color); - m_tooltip_element->setVisible(true); - this->bringToFront(m_tooltip_element); - m_tooltip_element->setText(utf8_to_wide(tooltip_text).c_str()); - s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; - s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; - v2u32 screenSize = driver->getScreenSize(); - int tooltip_offset_x = m_btn_height; - int tooltip_offset_y = m_btn_height; -#ifdef __ANDROID__ - tooltip_offset_x *= 3; - tooltip_offset_y = 0; - if (m_pointer.X > (s32)screenSize.X / 2) - tooltip_offset_x = (tooltip_offset_x + tooltip_width) * -1; -#endif - s32 tooltip_x = m_pointer.X + tooltip_offset_x; - s32 tooltip_y = m_pointer.Y + tooltip_offset_y; - if (tooltip_x + tooltip_width > (s32)screenSize.X) - tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height; - if (tooltip_y + tooltip_height > (s32)screenSize.Y) - tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height; - m_tooltip_element->setRelativePosition(core::rect( - core::position2d(tooltip_x, tooltip_y), - core::dimension2d(tooltip_width, tooltip_height))); + 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); + if (m_tooltip_append_itemname) + tooltip_text += utf8_to_wide(" [" + item.name + "]"); + } + } + if (!tooltip_text.empty()) { + showTooltip(tooltip_text, m_default_tooltip_color, + m_default_tooltip_bgcolor); } } } @@ -2271,11 +2407,15 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) void GUIFormSpecMenu::drawSelectedItem() { - if(!m_selected_item) - return; - video::IVideoDriver* driver = Environment->getVideoDriver(); + if (!m_selected_item) { + drawItemStack(driver, m_font, ItemStack(), + core::rect(v2s32(0, 0), v2s32(0, 0)), + NULL, m_client, IT_ROT_DRAGGED); + return; + } + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); sanity_check(inv); InventoryList *list = inv->getList(m_selected_item->listname); @@ -2285,14 +2425,15 @@ void GUIFormSpecMenu::drawSelectedItem() core::rect imgrect(0,0,imgsize.X,imgsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); - drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef); + rect.constrainTo(driver->getViewPort()); + drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED); } void GUIFormSpecMenu::drawMenu() { - if(m_form_src){ - std::string newform = m_form_src->getForm(); - if(newform != m_formspec_string){ + if (m_form_src) { + const std::string &newform = m_form_src->getForm(); + if (newform != m_formspec_string) { m_formspec_string = newform; regenerateGui(m_screensize_old); } @@ -2308,9 +2449,10 @@ void GUIFormSpecMenu::drawMenu() video::IVideoDriver* driver = Environment->getVideoDriver(); v2u32 screenSize = driver->getScreenSize(); - core::rect allbg(0, 0, screenSize.X , screenSize.Y); + core::rect allbg(0, 0, screenSize.X, screenSize.Y); + if (m_bgfullscreen) - driver->draw2DRectangle(m_bgcolor, allbg, &allbg); + driver->draw2DRectangle(m_fullscreen_bgcolor, allbg, &allbg); else driver->draw2DRectangle(m_bgcolor, AbsoluteRect, &AbsoluteClippingRect); @@ -2319,9 +2461,7 @@ void GUIFormSpecMenu::drawMenu() /* Draw backgrounds */ - for(u32 i=0; igetTexture(spec.name); if (texture != 0) { @@ -2330,7 +2470,7 @@ void GUIFormSpecMenu::drawMenu() // Image rectangle on screen core::rect rect = imgrect + spec.pos; - if (m_clipbackground) { + if (spec.clip) { core::dimension2d absrec_size = AbsoluteRect.getSize(); rect = core::rect(AbsoluteRect.UpperLeftCorner.X - spec.pos.X, AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y, @@ -2344,8 +2484,7 @@ void GUIFormSpecMenu::drawMenu() core::rect(core::position2d(0,0), core::dimension2di(texture->getOriginalSize())), NULL/*&AbsoluteClippingRect*/, colors, true); - } - else { + } else { errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl; errorstream << "\t" << spec.name << std::endl; } @@ -2354,10 +2493,7 @@ void GUIFormSpecMenu::drawMenu() /* Draw Boxes */ - for(u32 i=0; idraw2DRectangle(todraw, rect, 0); } + + /* + Call base class + */ + gui::IGUIElement::draw(); + /* Draw images */ - for(u32 i=0; igetTexture(spec.name); if (texture != 0) { @@ -2403,26 +2543,28 @@ void GUIFormSpecMenu::drawMenu() /* Draw item images */ - for(u32 i=0; iidef(); + IItemDefManager *idef = m_client->idef(); ItemStack item; - item.deSerialize(spec.name, idef); - video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); - // Image size on screen + item.deSerialize(spec.item_name, idef); core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); - // Image rectangle on screen + // Viewport rectangle on screen core::rect rect = imgrect + spec.pos; - const video::SColor color(255,255,255,255); - const video::SColor colors[] = {color,color,color,color}; - draw2DImageFilterScaled(driver, texture, rect, - core::rect(core::position2d(0,0), - core::dimension2di(texture->getOriginalSize())), - NULL/*&AbsoluteClippingRect*/, colors, true); + if (spec.parent_button && spec.parent_button->isPressed()) { +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + rect += core::dimension2d( + 0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight()); +#else + rect += core::dimension2d( + skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X), + skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)); +#endif + } + drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect, + m_client, IT_ROT_NONE); } /* @@ -2430,22 +2572,44 @@ void GUIFormSpecMenu::drawMenu() Phase 0: Item slot rectangles Phase 1: Item images; prepare tooltip */ - int start_phase=0; - for(int phase=start_phase; phase<=1; phase++) - for(u32 i=0; i(v2s32(0, 0), v2s32(0, 0)), + NULL, m_client, IT_ROT_HOVERED); } - - /* - Call base class - */ - gui::IGUIElement::draw(); /* TODO find way to show tooltips on touchscreen */ #ifndef HAVE_TOUCHSCREENGUI - m_pointer = m_device->getCursorControl()->getPosition(); + m_pointer = RenderingEngine::get_raw_device()->getCursorControl()->getPosition(); +#endif + + /* + Draw static text elements + */ + for (const GUIFormSpecMenu::StaticTextSpec &spec : m_static_texts) { + core::rect rect = spec.rect; + if (spec.parent_button && spec.parent_button->isPressed()) { +#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8) + rect += core::dimension2d( + 0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight()); +#else + // Use image offset instead of text's because its a bit smaller + // and fits better, also TEXT_OFFSET_X is always 0 + rect += core::dimension2d( + skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X), + skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)); #endif + } + video::SColor color(255, 255, 255, 255); + m_font->draw(spec.text.c_str(), rect, color, true, true, &rect); + } /* Draw fields/buttons tooltips @@ -2456,57 +2620,37 @@ void GUIFormSpecMenu::drawMenu() if (hovered != NULL) { s32 id = hovered->getID(); - u32 delta = 0; + u64 delta = 0; if (id == -1) { m_old_tooltip_id = id; - m_old_tooltip = ""; } else { if (id == m_old_tooltip_id) { - delta = porting::getDeltaMs(m_hovered_time, getTimeMs()); + delta = porting::getDeltaMs(m_hovered_time, porting::getTimeMs()); } else { - m_hovered_time = getTimeMs(); + m_hovered_time = porting::getTimeMs(); m_old_tooltip_id = id; } } + // Find and update the current tooltip if (id != -1 && delta >= m_tooltip_show_delay) { - for(std::vector::iterator iter = m_fields.begin(); - iter != m_fields.end(); iter++) { - if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){ - if (m_old_tooltip != m_tooltips[iter->fname].tooltip) { - m_old_tooltip = m_tooltips[iter->fname].tooltip; - m_tooltip_element->setText(utf8_to_wide(m_tooltips[iter->fname].tooltip).c_str()); - std::vector tt_rows = str_split(m_tooltips[iter->fname].tooltip, '\n'); - s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; - s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5; - int tooltip_offset_x = m_btn_height; - int tooltip_offset_y = m_btn_height; -#ifdef __ANDROID__ - tooltip_offset_x *= 3; - tooltip_offset_y = 0; - if (m_pointer.X > (s32)screenSize.X / 2) - tooltip_offset_x = (tooltip_offset_x + tooltip_width) * -1; -#endif - s32 tooltip_x = m_pointer.X + tooltip_offset_x; - s32 tooltip_y = m_pointer.Y + tooltip_offset_y; - if (tooltip_x + tooltip_width > (s32)screenSize.X) - tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height; - if (tooltip_y + tooltip_height > (s32)screenSize.Y) - tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height; - m_tooltip_element->setRelativePosition(core::rect( - core::position2d(tooltip_x, tooltip_y), - core::dimension2d(tooltip_width, tooltip_height))); - } - m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor); - m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color); - m_tooltip_element->setVisible(true); - this->bringToFront(m_tooltip_element); - break; - } + for (const FieldSpec &field : m_fields) { + + if (field.fid != id) + continue; + + const std::wstring &text = m_tooltips[field.fname].tooltip; + if (!text.empty()) + showTooltip(text, m_tooltips[field.fname].color, + m_tooltips[field.fname].bgcolor); + + break; } } } + m_tooltip_element->draw(); + /* Draw dragged item stack */ @@ -2515,6 +2659,53 @@ void GUIFormSpecMenu::drawMenu() skin->setFont(old_font); } + +void GUIFormSpecMenu::showTooltip(const std::wstring &text, + const irr::video::SColor &color, const irr::video::SColor &bgcolor) +{ + const std::wstring ntext = translate_string(text); + m_tooltip_element->setOverrideColor(color); + m_tooltip_element->setBackgroundColor(bgcolor); + setStaticText(m_tooltip_element, ntext.c_str()); + + // Tooltip size and offset + s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; +#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1 + std::vector text_rows = str_split(ntext, L'\n'); + s32 tooltip_height = m_tooltip_element->getTextHeight() * text_rows.size() + 5; +#else + s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; +#endif + v2u32 screenSize = Environment->getVideoDriver()->getScreenSize(); + int tooltip_offset_x = m_btn_height; + int tooltip_offset_y = m_btn_height; +#ifdef __ANDROID__ + tooltip_offset_x *= 3; + tooltip_offset_y = 0; + if (m_pointer.X > (s32)screenSize.X / 2) + tooltip_offset_x = -(tooltip_offset_x + tooltip_width); +#endif + + // Calculate and set the tooltip position + s32 tooltip_x = m_pointer.X + tooltip_offset_x; + s32 tooltip_y = m_pointer.Y + tooltip_offset_y; + if (tooltip_x + tooltip_width > (s32)screenSize.X) + tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height; + if (tooltip_y + tooltip_height > (s32)screenSize.Y) + tooltip_y = (s32)screenSize.Y - tooltip_height - m_btn_height; + + m_tooltip_element->setRelativePosition( + core::rect( + core::position2d(tooltip_x, tooltip_y), + core::dimension2d(tooltip_width, tooltip_height) + ) + ); + + // Display the tooltip + m_tooltip_element->setVisible(true); + bringToFront(m_tooltip_element); +} + void GUIFormSpecMenu::updateSelectedItem() { // If the selected stack has become empty for some reason, deselect it. @@ -2525,12 +2716,11 @@ void GUIFormSpecMenu::updateSelectedItem() // WARNING: BLACK MAGIC // See if there is a stack suited for our current guess. // If such stack does not exist, clear the guess. - if(m_selected_content_guess.name != "" && + if (!m_selected_content_guess.name.empty() && selected.name == m_selected_content_guess.name && selected.count == m_selected_content_guess.count){ // Selected item fits the guess. Skip the black magic. - } - else if(m_selected_content_guess.name != ""){ + } else if (!m_selected_content_guess.name.empty()) { bool found = false; for(u32 i=0; igetInventory(s.inventoryloc); @@ -2661,13 +2849,17 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) current_keys_pending.key_enter = false; } + if (!current_field_enter_pending.empty()) { + fields["key_enter_field"] = current_field_enter_pending; + current_field_enter_pending = ""; + } + if (current_keys_pending.key_escape) { fields["key_escape"] = "true"; current_keys_pending.key_escape = false; } - for(unsigned int i=0; igetSelected(); if (selected >= 0) { - fields[name] = - wide_to_utf8(e->getItem(selected)); + std::vector *dropdown_values = + getDropDownValues(s.fname); + if (dropdown_values && selected < (s32)dropdown_values->size()) { + fields[name] = (*dropdown_values)[selected]; + } } } else if (s.ftype == f_TabHeader) { // no dynamic cast possible due to some distributions shipped - // without rtti support in irrlicht + // without rttzi support in irrlicht IGUIElement * element = getElementFromId(s.fid); gui::IGUITabControl *e = NULL; if ((element) && (element->getType() == gui::EGUIET_TAB_CONTROL)) { - e = static_cast(element); + e = static_cast(element); } if (e != 0) { @@ -2830,7 +3025,6 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) core::position2d(x, y)); if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { m_old_tooltip_id = -1; - m_old_tooltip = ""; } if (!isChild(hovered,this)) { if (DoubleClickDetection(event)) { @@ -2989,6 +3183,25 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } #endif + if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { + /* TODO add a check like: + if (event.JoystickEvent != joystick_we_listen_for) + return false; + */ + bool handled = m_joystick->handleEvent(event.JoystickEvent); + if (handled) { + if (m_joystick->wasKeyDown(KeyType::ESC)) { + tryClose(); + } else if (m_joystick->wasKeyDown(KeyType::JUMP)) { + if (m_allowclose) { + acceptInput(quit_mode_accept); + quitMenu(); + } + } + } + return handled; + } + return false; } @@ -3013,10 +3226,10 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) m_doubleclickdetect[0].time = m_doubleclickdetect[1].time; m_doubleclickdetect[1].pos = m_pointer; - m_doubleclickdetect[1].time = getTimeMs(); + m_doubleclickdetect[1].time = porting::getTimeMs(); } else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { - u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs()); + u64 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs()); if (delta > 400) { return false; } @@ -3050,23 +3263,31 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) return false; } +void GUIFormSpecMenu::tryClose() +{ + if (m_allowclose) { + doPause = false; + acceptInput(quit_mode_cancel); + quitMenu(); + } else { + m_text_dst->gotText(L"MenuQuit"); + } +} + bool GUIFormSpecMenu::OnEvent(const SEvent& event) { if (event.EventType==EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); - if (event.KeyInput.PressedDown && ( (kp == EscapeKey) || - (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) { - if (m_allowclose) { - doPause = false; - acceptInput(quit_mode_cancel); - quitMenu(); - } else { - m_text_dst->gotText(L"MenuQuit"); - } + if (event.KeyInput.PressedDown && ( + (kp == EscapeKey) || (kp == CancelKey) || + ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) { + tryClose(); return true; - } else if (m_client != NULL && event.KeyInput.PressedDown && + } + + if (m_client != NULL && event.KeyInput.PressedDown && (kp == getKeySetting("keymap_screenshot"))) { - m_client->makeScreenshot(m_device); + m_client->makeScreenshot(); } if (event.KeyInput.PressedDown && (event.KeyInput.Key==KEY_RETURN || @@ -3312,7 +3533,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Possibly send inventory action to server if (move_amount > 0) { - // Send IACTION_MOVE + // Send IAction::Move assert(m_selected_item && m_selected_item->isValid()); assert(s.isValid()); @@ -3326,7 +3547,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Check how many items can be moved move_amount = stack_from.count = MYMIN(move_amount, stack_from.count); - ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef()); + ItemStack leftover = stack_to.addItem(stack_from, m_client->idef()); // If source stack cannot be added to destination stack at all, // they are swapped if ((leftover.count == stack_from.count) && @@ -3349,7 +3570,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_selected_content_guess = ItemStack(); // Clear } - infostream << "Handing IACTION_MOVE to manager" << std::endl; + infostream << "Handing IAction::Move to manager" << std::endl; IMoveAction *a = new IMoveAction(); a->count = move_amount; a->from_inv = m_selected_item->inventoryloc; @@ -3384,35 +3605,46 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) break; ItemStack stack_from = list_from->getItem(s.i); assert(shift_move_amount <= stack_from.count); - - // find a place (or more than one) to add the new item - u32 ilt_size = list_to->getSize(); - ItemStack leftover; - for (u32 slot_to = 0; slot_to < ilt_size - && shift_move_amount > 0; slot_to++) { - list_to->itemFits(slot_to, stack_from, &leftover); - if (leftover.count < stack_from.count) { - infostream << "Handing IACTION_MOVE to manager" << std::endl; - IMoveAction *a = new IMoveAction(); - a->count = MYMIN(shift_move_amount, - (u32) (stack_from.count - leftover.count)); - shift_move_amount -= a->count; - a->from_inv = s.inventoryloc; - a->from_list = s.listname; - a->from_i = s.i; - a->to_inv = to_inv_sp.inventoryloc; - a->to_list = to_inv_sp.listname; - a->to_i = slot_to; - m_invmgr->inventoryAction(a); - stack_from = leftover; + if (m_client->getProtoVersion() >= 25) { + infostream << "Handing IAction::Move to manager" << std::endl; + IMoveAction *a = new IMoveAction(); + a->count = shift_move_amount; + a->from_inv = s.inventoryloc; + a->from_list = s.listname; + a->from_i = s.i; + a->to_inv = to_inv_sp.inventoryloc; + a->to_list = to_inv_sp.listname; + a->move_somewhere = true; + m_invmgr->inventoryAction(a); + } else { + // find a place (or more than one) to add the new item + u32 ilt_size = list_to->getSize(); + ItemStack leftover; + for (u32 slot_to = 0; slot_to < ilt_size + && shift_move_amount > 0; slot_to++) { + list_to->itemFits(slot_to, stack_from, &leftover); + if (leftover.count < stack_from.count) { + infostream << "Handing IAction::Move to manager" << std::endl; + IMoveAction *a = new IMoveAction(); + a->count = MYMIN(shift_move_amount, + (u32) (stack_from.count - leftover.count)); + shift_move_amount -= a->count; + a->from_inv = s.inventoryloc; + a->from_list = s.listname; + a->from_i = s.i; + a->to_inv = to_inv_sp.inventoryloc; + a->to_list = to_inv_sp.listname; + a->to_i = slot_to; + m_invmgr->inventoryAction(a); + stack_from = leftover; + } } } } while (0); - } else if (drop_amount > 0) { m_selected_content_guess = ItemStack(); // Clear - // Send IACTION_DROP + // Send IAction::Drop assert(m_selected_item && m_selected_item->isValid()); assert(inv_selected); @@ -3425,7 +3657,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) assert(drop_amount > 0 && drop_amount <= m_selected_amount); m_selected_amount -= drop_amount; - infostream << "Handing IACTION_DROP to manager" << std::endl; + infostream << "Handing IAction::Drop to manager" << std::endl; IDropAction *a = new IDropAction(); a->count = drop_amount; a->from_inv = m_selected_item->inventoryloc; @@ -3435,12 +3667,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } else if (craft_amount > 0) { m_selected_content_guess = ItemStack(); // Clear - // Send IACTION_CRAFT + // Send IAction::Craft assert(s.isValid()); assert(inv_s); - infostream << "Handing IACTION_CRAFT to manager" << std::endl; + infostream << "Handing IAction::Craft to manager" << std::endl; ICraftAction *a = new ICraftAction(); a->count = craft_amount; a->craft_inv = s.inventoryloc; @@ -3462,8 +3694,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED && isVisible()) { // find the element that was clicked - for (unsigned int i=0; igetID())) { s.send = true; @@ -3501,12 +3732,11 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } // find the element that was clicked - for (u32 i = 0; i < m_fields.size(); i++) { - FieldSpec &s = m_fields[i]; + for (GUIFormSpecMenu::FieldSpec &s : m_fields) { // if its a button, set the send field so // lua knows which button was pressed - if (((s.ftype == f_Button) || (s.ftype == f_CheckBox)) && - (s.fid == event.GUIEvent.Caller->getID())) { + if ((s.ftype == f_Button || s.ftype == f_CheckBox) && + s.fid == event.GUIEvent.Caller->getID()) { s.send = true; if (s.is_exit) { if (m_allowclose) { @@ -3516,16 +3746,16 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_text_dst->gotText(L"ExitButton"); } return true; - } else { - acceptInput(quit_mode_no); - s.send = false; - return true; } + + acceptInput(quit_mode_no); + s.send = false; + return true; + } else if ((s.ftype == f_DropDown) && (s.fid == event.GUIEvent.Caller->getID())) { // only send the changed dropdown - for (u32 i = 0; i < m_fields.size(); i++) { - FieldSpec &s2 = m_fields[i]; + for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) { if (s2.ftype == f_DropDown) { s2.send = false; } @@ -3535,8 +3765,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // revert configuration to make sure dropdowns are sent on // regular button click - for (u32 i = 0; i < m_fields.size(); i++) { - FieldSpec &s2 = m_fields[i]; + for (GUIFormSpecMenu::FieldSpec &s2 : m_fields) { if (s2.ftype == f_DropDown) { s2.send = true; } @@ -3553,8 +3782,22 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) { if (event.GUIEvent.Caller->getID() > 257) { + bool close_on_enter = true; + for (GUIFormSpecMenu::FieldSpec &s : m_fields) { + if (s.ftype == f_Unknown && + s.fid == event.GUIEvent.Caller->getID()) { + current_field_enter_pending = s.fname; + std::unordered_map::const_iterator it = + field_close_on_enter.find(s.fname); + if (it != field_close_on_enter.end()) + close_on_enter = (*it).second; + + break; + } + } - if (m_allowclose) { + if (m_allowclose && close_on_enter) { + current_keys_pending.key_enter = true; acceptInput(quit_mode_accept); quitMenu(); } else { @@ -3570,8 +3813,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) int current_id = event.GUIEvent.Caller->getID(); if (current_id > 257) { // find the element that was clicked - for (u32 i = 0; i < m_fields.size(); i++) { - FieldSpec &s = m_fields[i]; + for (GUIFormSpecMenu::FieldSpec &s : m_fields) { // if it's a table, set the send field // so lua knows which table was changed if ((s.ftype == f_Table) && (s.fid == current_id)) { @@ -3595,10 +3837,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) */ std::string GUIFormSpecMenu::getNameByID(s32 id) { - for(std::vector::iterator iter = m_fields.begin(); - iter != m_fields.end(); iter++) { - if (iter->fid == id) { - return iter->fname; + for (FieldSpec &spec : m_fields) { + if (spec.fid == id) { + return spec.fname; } } return ""; @@ -3611,10 +3852,9 @@ std::string GUIFormSpecMenu::getNameByID(s32 id) */ std::wstring GUIFormSpecMenu::getLabelByID(s32 id) { - for(std::vector::iterator iter = m_fields.begin(); - iter != m_fields.end(); iter++) { - if (iter->fid == id) { - return iter->flabel; + for (FieldSpec &spec : m_fields) { + if (spec.fid == id) { + return spec.flabel; } } return L"";