X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2FguiFormSpecMenu.cpp;h=9d97821f02ea1569311d06757ab1e5562e026be8;hb=67f97f8d3274e0e96a8f541a05a39f81dfec1a20;hp=d25b6cdf908b22930ed651666bc7a6ba2a7517da;hpb=140245a76f9b73b1f981176ce4a77d45b1edc933;p=oweals%2Fminetest.git diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index d25b6cdf9..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,21 +36,27 @@ 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 "main.h" #include "settings.h" #include "client.h" #include "fontengine.h" #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 +#include "intlGUIEditBox.h" +#endif #define MY_CHECKPOS(a,b) \ if (v_pos.size() != 2) { \ @@ -73,33 +79,25 @@ 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) : - 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(L""), - m_font(NULL) + m_joystick(joystick), + m_remap_dbl_click(remap_dbl_click) #ifdef __ANDROID__ - ,m_JavaDialogFieldName(L"") + , m_JavaDialogFieldName("") #endif { current_keys_pending.key_down = false; @@ -114,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() @@ -173,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; } } @@ -210,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; } } @@ -226,48 +215,25 @@ void GUIFormSpecMenu::setInitialFocus() Environment->setFocus(*(children.begin())); } -GUITable* GUIFormSpecMenu::getTable(std::wstring tablename) +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; } -std::vector split(const std::string &s, char delim) { - std::vector tokens; - - std::string current = ""; - bool last_was_escape = false; - for(unsigned int i=0; i < s.size(); i++) { - if (last_was_escape) { - current += '\\'; - current += s.c_str()[i]; - last_was_escape = false; - } - else { - if (s.c_str()[i] == delim) { - tokens.push_back(current); - current = ""; - last_was_escape = false; - } - else if (s.c_str()[i] == '\\'){ - last_was_escape = true; - } - else { - current += s.c_str()[i]; - last_was_escape = false; - } - } +std::vector* GUIFormSpecMenu::getDropDownValues(const std::string &name) +{ + 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,','); @@ -293,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]; @@ -323,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; @@ -332,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) { @@ -341,14 +333,52 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element) } if(!data->explicit_size) - errorstream<<"WARNING: invalid use of list without a size[] element"< parts = split(element, ';'); + + if (parts.size() == 2) { + std::string location = parts[0]; + std::string listname = parts[1]; + + InventoryLocation loc; + + if (location == "context" || location == "current_name") + loc = m_current_inventory_location; + else + loc.deSerialize(location); + + m_inventory_rings.emplace_back(loc, listname); + return; + } + + 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.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, const std::string &element) { std::vector parts = split(element,';'); @@ -358,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; @@ -374,7 +404,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) if (selected == "true") fselected = true; - std::wstring wlabel = narrow_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), @@ -382,7 +412,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) pos.Y + ((imgsize.Y/2) + m_btn_height)); FieldSpec spec( - narrow_to_wide(name), + name, wlabel, //Needed for displaying text on MSVC wlabel, 258+m_fields.size() @@ -397,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; @@ -434,7 +464,7 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element) core::rect(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y); FieldSpec spec( - narrow_to_wide(name), + name, L"", L"", 258+m_fields.size() @@ -456,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,';'); @@ -474,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; @@ -485,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,';'); @@ -523,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; @@ -532,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,';'); @@ -555,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; @@ -568,14 +598,12 @@ 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,';'); @@ -610,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()) @@ -666,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,';'); @@ -677,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) @@ -686,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; @@ -694,13 +722,10 @@ 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); - std::wstring fname_w = narrow_to_wide(name); - FieldSpec spec( - fname_w, + name, L"", L"", 258+m_fields.size() @@ -708,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 @@ -722,22 +747,21 @@ void GUIFormSpecMenu::parseTable(parserData* data,std::string element) e->setTable(data->table_options, data->table_columns, items); - if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) { - e->setDynamicData(data->table_dyndata[fname_w]); + if (data->table_dyndata.find(name) != data->table_dyndata.end()) { + 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,';'); @@ -748,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) @@ -760,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; @@ -771,10 +795,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - std::wstring fname_w = narrow_to_wide(name); - FieldSpec spec( - fname_w, + name, L"", L"", 258+m_fields.size() @@ -782,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 @@ -796,15 +818,14 @@ void GUIFormSpecMenu::parseTextList(parserData* data,std::string element) e->setTextList(items, is_yes(str_transparent)); - if (data->table_dyndata.find(fname_w) != data->table_dyndata.end()) { - e->setDynamicData(data->table_dyndata[fname_w]); + if (data->table_dyndata.find(name) != data->table_dyndata.end()) { + 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; } @@ -812,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,';'); @@ -822,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; @@ -836,10 +857,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) core::rect rect = core::rect(pos.X, pos.Y, pos.X + width, pos.Y + (m_btn_height * 2)); - std::wstring fname_w = narrow_to_wide(name); - FieldSpec spec( - fname_w, + name, L"", L"", 258+m_fields.size() @@ -855,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(narrow_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],','); @@ -884,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; @@ -897,12 +933,10 @@ 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 = narrow_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); FieldSpec spec( - narrow_to_wide(name), + name, wlabel, L"", 258+m_fields.size() @@ -920,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'*'); @@ -929,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; } @@ -949,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 = narrow_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); FieldSpec spec( - narrow_to_wide(name), + name, wlabel, - narrow_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 = - Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); - + 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 + { +#endif + e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); + } if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); } @@ -1003,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],','); @@ -1023,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; @@ -1046,34 +1099,39 @@ 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 = narrow_to_wide(label); + std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label))); FieldSpec spec( - narrow_to_wide(name), + name, wlabel, - narrow_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 = - Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); + + gui::IGUIEditBox *e; +#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9 + 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 + { +#endif + e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); + } if (spec.fname == data->focused_fieldname) { Environment->setFocus(e); @@ -1100,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,';'); @@ -1116,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; @@ -1125,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,';'); @@ -1137,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++) { @@ -1158,19 +1223,19 @@ 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 = narrow_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, posy + m_btn_height); FieldSpec spec( - L"", + "", wlabel, L"", 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); @@ -1182,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,';'); @@ -1190,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 = narrow_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; @@ -1207,23 +1273,23 @@ 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; @@ -1231,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,';'); @@ -1248,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; @@ -1257,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") @@ -1273,18 +1339,17 @@ 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; @@ -1306,8 +1371,10 @@ void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element, } e->setUseAlphaChannel(true); - e->setImage(texture); - e->setPressedImage(pressed_texture); + 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); @@ -1319,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,';'); @@ -1344,7 +1411,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) } FieldSpec spec( - narrow_to_wide(name), + name, L"", L"", 258+m_fields.size() @@ -1352,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; @@ -1374,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(narrow_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) && @@ -1390,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; } @@ -1411,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; @@ -1424,46 +1494,45 @@ 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[narrow_to_wide(name)] = - TooltipSpec(item.getDefinition(idef).description, + m_tooltips[name] = + TooltipSpec(utf8_to_wide(item.getDefinition(idef).description), m_default_tooltip_bgcolor, m_default_tooltip_color); - label = unescape_string(label); FieldSpec spec( - narrow_to_wide(name), - narrow_to_wide(label), - narrow_to_wide(item_name), - 258+m_fields.size() + name, + utf8_to_wide(label), + utf8_to_wide(item_name), + 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(texture); - e->setPressedImage(texture); - 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,';'); @@ -1476,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; @@ -1499,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,';'); @@ -1545,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[narrow_to_wide(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[narrow_to_wide(name)] = TooltipSpec(unescape_string(parts[1]), + m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])), tmp_color1, tmp_color2); return; } @@ -1565,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,'['); @@ -1589,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,'['); @@ -1613,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,'['); @@ -1638,28 +1780,43 @@ 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; + } + + if (type == "listring") { + parseListRing(data, description); return; } 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; } @@ -1693,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; @@ -1754,9 +1916,8 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) } // Ignore others - infostream - << "Unknown DrawSpec: type="<getDynamicData(); } @@ -1784,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(); @@ -1820,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); @@ -1834,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); @@ -1855,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++; } @@ -1869,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) @@ -1893,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) { @@ -1926,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))); @@ -1952,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 @@ -1964,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); @@ -1975,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, @@ -2025,7 +2228,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) bool GUIFormSpecMenu::getAndroidUIInput() { /* no dialog shown */ - if (m_JavaDialogFieldName == L"") { + if (m_JavaDialogFieldName == "") { return false; } @@ -2034,8 +2237,8 @@ bool GUIFormSpecMenu::getAndroidUIInput() return true; } - std::wstring fieldname = m_JavaDialogFieldName; - m_JavaDialogFieldName = L""; + std::string fieldname = m_JavaDialogFieldName; + m_JavaDialogFieldName = ""; /* no value abort dialog processing */ if (porting::getInputDialogState() != 0) { @@ -2043,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; @@ -2061,7 +2264,7 @@ bool GUIFormSpecMenu::getAndroidUIInput() std::string text = porting::getInputDialogValue(); ((gui::IGUIEditBox*) tochange)-> - setText(narrow_to_wide(text).c_str()); + setText(utf8_to_wide(text).c_str()); } return false; } @@ -2071,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 @@ -2172,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(narrow_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); } } } @@ -2213,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); @@ -2227,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); } @@ -2250,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); @@ -2261,9 +2461,7 @@ void GUIFormSpecMenu::drawMenu() /* Draw backgrounds */ - for(u32 i=0; igetTexture(spec.name); if (texture != 0) { @@ -2272,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, @@ -2282,12 +2480,11 @@ void GUIFormSpecMenu::drawMenu() const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; - driver->draw2DImage(texture, rect, + draw2DImageFilterScaled(driver, texture, rect, 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; } @@ -2296,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) { @@ -2332,7 +2530,7 @@ void GUIFormSpecMenu::drawMenu() core::rect rect = imgrect + spec.pos; const video::SColor color(255,255,255,255); const video::SColor colors[] = {color,color,color,color}; - driver->draw2DImage(texture, rect, + draw2DImageFilterScaled(driver, texture, rect, core::rect(core::position2d(0,0),img_origsize), NULL/*&AbsoluteClippingRect*/, colors, true); } @@ -2345,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}; - driver->draw2DImage(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); } /* @@ -2372,23 +2572,45 @@ 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 */ @@ -2398,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(narrow_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 */ @@ -2457,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. @@ -2467,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); @@ -2576,7 +2822,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) { if(m_text_dst) { - std::map fields; + StringMap fields; if (quitmode == quit_mode_accept) { fields["quit"] = "true"; @@ -2603,19 +2849,22 @@ 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; icheckEvent(); @@ -2631,17 +2880,20 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) } s32 selected = e->getSelected(); if (selected >= 0) { - fields[name] = - wide_to_narrow(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) { @@ -2688,7 +2940,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) { IGUIElement* e = getElementFromId(s.fid); if(e != NULL) { - fields[name] = wide_to_narrow(e->getText()); + fields[name] = wide_to_utf8(e->getText()); } } } @@ -2773,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)) { @@ -2796,7 +3047,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } m_JavaDialogFieldName = getNameByID(hovered->getID()); std::string message = gettext("Enter "); - std::string label = wide_to_narrow(getLabelByID(hovered->getID())); + std::string label = wide_to_utf8(getLabelByID(hovered->getID())); if (label == "") { label = "text"; } @@ -2816,7 +3067,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } porting::showInputDialog(gettext("ok"), "", - wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()), + wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()), type); return retval; } @@ -2932,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; } @@ -2939,24 +3209,27 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) { /* The following code is for capturing double-clicks of the mouse button - * that are *not* in/in a control (i.e. when the mouse if positioned in an - * unused area of the formspec) and translating the double-click into an - * EET_KEY_INPUT_EVENT event which closes the form. + * and translating the double-click into an EET_KEY_INPUT_EVENT event + * -- which closes the form -- under some circumstances. * * There have been many github issues reporting this as a bug even though it - * was an intended feature. For this reason the code has been disabled for - * non-Android builds + * was an intended feature. For this reason, remapping the double-click as + * an ESC must be explicitly set when creating this class via the + * /p remap_dbl_click parameter of the constructor. */ -#ifdef __ANDROID__ + + if (!m_remap_dbl_click) + return false; + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { m_doubleclickdetect[0].pos = m_doubleclickdetect[1].pos; 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; } @@ -2986,27 +3259,35 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) delete translated; return true; } -#endif + 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) { + 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(narrow_to_wide("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 && - (kp == getKeySetting("keymap_screenshot"))) { - m_client->makeScreenshot(m_device); + } + + if (m_client != NULL && event.KeyInput.PressedDown && + (kp == getKeySetting("keymap_screenshot"))) { + m_client->makeScreenshot(); } if (event.KeyInput.PressedDown && (event.KeyInput.Key==KEY_RETURN || @@ -3057,8 +3338,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) Inventory *inv_selected = NULL; Inventory *inv_s = NULL; + InventoryList *list_s = NULL; - if(m_selected_item) { + if (m_selected_item) { inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc); sanity_check(inv_selected); sanity_check(inv_selected->getList(m_selected_item->listname) != NULL); @@ -3066,35 +3348,35 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) u32 s_count = 0; - if(s.isValid()) + if (s.isValid()) do { // breakable inv_s = m_invmgr->getInventory(s.inventoryloc); - if(!inv_s) { - errorstream<<"InventoryMenu: The selected inventory location " - <<"\""<getList(s.listname); - if(list == NULL) { - verbosestream<<"InventoryMenu: The selected inventory list \"" - <getList(s.listname); + if (list_s == NULL) { + verbosestream << "InventoryMenu: The selected inventory list \"" + << s.listname << "\" does not exist" << std::endl; s.i = -1; // make it invalid again break; } - if((u32)s.i >= list->getSize()) { - infostream<<"InventoryMenu: The selected inventory list \"" - <= list_s->getSize()) { + infostream << "InventoryMenu: The selected inventory list \"" + << s.listname << "\" is too small (i=" << s.i << ", size=" + << list_s->getSize() << ")" << std::endl; s.i = -1; // make it invalid again break; } - s_count = list->getItem(s.i).count; + s_count = list_s->getItem(s.i).count; } while(0); bool identical = (m_selected_item != NULL) && s.isValid() && @@ -3106,25 +3388,29 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement int button = 0; int updown = 2; - if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { button = 0; updown = 0; } - else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) + else if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) { button = 1; updown = 0; } - else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) + else if (event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) { button = 2; updown = 0; } - else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) + else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { button = 0; updown = 1; } - else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) + else if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) { button = 1; updown = 1; } - else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) + else if (event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) { button = 2; updown = 1; } - else if(event.MouseInput.Event == EMIE_MOUSE_MOVED) + else if (event.MouseInput.Event == EMIE_MOUSE_MOVED) { updown = -1;} // Set this number to a positive value to generate a move action // from m_selected_item to s. u32 move_amount = 0; + // Set this number to a positive value to generate a move action + // from s to the next inventory ring. + u32 shift_move_amount = 0; + // Set this number to a positive value to generate a drop action // from m_selected_item. u32 drop_amount = 0; @@ -3132,7 +3418,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) // Set this number to a positive value to generate a craft action at s. u32 craft_amount = 0; - if(updown == 0) { + if (updown == 0) { // Some mouse button has been pressed //infostream<<"Mouse button "<= 1); - if(s.isValid()) { + if (s.isValid()) { // Clicked a slot: move - if(button == 1) // right + if (button == 1) // right move_amount = 1; - else if(button == 2) // middle + else if (button == 2) // middle move_amount = MYMIN(m_selected_amount, 10); else // left move_amount = m_selected_amount; - if(identical) { - if(move_amount >= m_selected_amount) + if (identical) { + if (move_amount >= m_selected_amount) m_selected_amount = 0; else m_selected_amount -= move_amount; @@ -3182,29 +3477,28 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) { // Clicked outside of the window: drop - if(button == 1) // right + if (button == 1) // right drop_amount = 1; - else if(button == 2) // middle + else if (button == 2) // middle drop_amount = MYMIN(m_selected_amount, 10); else // left drop_amount = m_selected_amount; } } } - else if(updown == 1) { + else if (updown == 1) { // Some mouse button has been released //infostream<<"Mouse button "<getList(m_selected_item->listname); - InventoryList *list_to = inv_s->getList(s.listname); + InventoryList *list_to = list_s; assert(list_from && list_to); ItemStack stack_from = list_from->getItem(m_selected_item->i); ItemStack stack_to = list_to->getItem(s.i); @@ -3239,23 +3532,22 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } // Possibly send inventory action to server - if(move_amount > 0) - { - // Send IACTION_MOVE + if (move_amount > 0) { + // Send IAction::Move assert(m_selected_item && m_selected_item->isValid()); assert(s.isValid()); assert(inv_selected && inv_s); InventoryList *list_from = inv_selected->getList(m_selected_item->listname); - InventoryList *list_to = inv_s->getList(s.listname); + InventoryList *list_to = list_s; assert(list_from && list_to); ItemStack stack_from = list_from->getItem(m_selected_item->i); ItemStack stack_to = list_to->getItem(s.i); // 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) && @@ -3267,7 +3559,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_selected_content_guess_inventory = s.inventoryloc; } // Source stack goes fully into destination stack - else if(leftover.empty()) { + else if (leftover.empty()) { m_selected_amount -= move_amount; m_selected_content_guess = ItemStack(); // Clear } @@ -3278,7 +3570,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_selected_content_guess = ItemStack(); // Clear } - infostream<<"Handing IACTION_MOVE to manager"<count = move_amount; a->from_inv = m_selected_item->inventoryloc; @@ -3288,11 +3580,71 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) a->to_list = s.listname; a->to_i = s.i; m_invmgr->inventoryAction(a); - } - else if(drop_amount > 0) { + } else if (shift_move_amount > 0) { + u32 mis = m_inventory_rings.size(); + u32 i = 0; + for (; i < mis; i++) { + const ListRingSpec &sp = m_inventory_rings[i]; + if (sp.inventoryloc == s.inventoryloc + && sp.listname == s.listname) + break; + } + do { + if (i >= mis) // if not found + break; + u32 to_inv_ind = (i + 1) % mis; + const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind]; + InventoryList *list_from = list_s; + if (!s.isValid()) + break; + Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc); + if (!inv_to) + break; + InventoryList *list_to = inv_to->getList(to_inv_sp.listname); + if (!list_to) + break; + ItemStack stack_from = list_from->getItem(s.i); + assert(shift_move_amount <= stack_from.count); + 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); @@ -3305,23 +3657,22 @@ 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"<count = drop_amount; a->from_inv = m_selected_item->inventoryloc; a->from_list = m_selected_item->listname; a->from_i = m_selected_item->i; m_invmgr->inventoryAction(a); - } - else if(craft_amount > 0) { + } 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"<count = craft_amount; a->craft_inv = s.inventoryloc; @@ -3329,7 +3680,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } // If m_selected_amount has been decreased to zero, deselect - if(m_selected_amount == 0) { + if (m_selected_amount == 0) { delete m_selected_item; m_selected_item = NULL; m_selected_amount = 0; @@ -3338,13 +3689,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } m_old_pointer = m_pointer; } - if(event.EventType==EET_GUI_EVENT) { + if (event.EventType == EET_GUI_EVENT) { - if(event.GUIEvent.EventType==gui::EGET_TAB_CHANGED + if (event.GUIEvent.EventType == gui::EGET_TAB_CHANGED && isVisible()) { // find the element that was clicked - for(unsigned int i=0; igetID())) { s.send = true; @@ -3354,16 +3704,16 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } } - if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST + if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) { - if(!canTakeFocus(event.GUIEvent.Element)) { + if (!canTakeFocus(event.GUIEvent.Element)) { infostream<<"GUIFormSpecMenu: Not allowing focus change." <gotText(narrow_to_wide("ExitButton")); + m_text_dst->gotText(L"ExitButton"); } // quitMenu deallocates menu return true; } // find the element that was clicked - for(u32 i=0; igetID())) { + if ((s.ftype == f_Button || s.ftype == f_CheckBox) && + s.fid == event.GUIEvent.Caller->getID()) { s.send = true; - if(s.is_exit) { + if (s.is_exit) { if (m_allowclose) { acceptInput(quit_mode_accept); quitMenu(); } else { - m_text_dst->gotText(narrow_to_wide("ExitButton")); + m_text_dst->gotText(L"ExitButton"); } return true; - } else { - acceptInput(quit_mode_no); - s.send = false; - return true; } - } - else if ((s.ftype == f_DropDown) && + + 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; igetID())) - { + } else if ((s.ftype == f_ScrollBar) && + (s.fid == event.GUIEvent.Caller->getID())) { s.fdefault = L"Changed"; acceptInput(quit_mode_no); s.fdefault = L""; @@ -3435,10 +3780,24 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } - if(event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) { - if(event.GUIEvent.Caller->getID() > 257) { + 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; - if (m_allowclose) { + break; + } + } + + if (m_allowclose && close_on_enter) { + current_keys_pending.key_enter = true; acceptInput(quit_mode_accept); quitMenu(); } else { @@ -3450,12 +3809,11 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } } - if(event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) { + if (event.GUIEvent.EventType == gui::EGET_TABLE_CHANGED) { int current_id = event.GUIEvent.Caller->getID(); - if(current_id > 257) { + if (current_id > 257) { // find the element that was clicked - for(u32 i=0; i::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 L""; + return ""; } /** @@ -3495,10 +3852,9 @@ std::wstring 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"";