X-Git-Url: https://git.librecmc.org/?a=blobdiff_plain;f=src%2FguiFormSpecMenu.cpp;h=dca7618b9852f794aadea571cde6db73a7d6eb51;hb=e0b57c1140554fccbf3e57a036cc4100887ab8f1;hp=2439de7fad4c675ec38385c219ad9c7ce3187721;hpb=28438bba27168289be59a26d3ae55e3f3658d8d3;p=oweals%2Fminetest.git diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp index 2439de7fa..dca7618b9 100644 --- a/src/guiFormSpecMenu.cpp +++ b/src/guiFormSpecMenu.cpp @@ -37,20 +37,24 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "log.h" -#include "tile.h" // ITextureSource +#include "client/tile.h" // ITextureSource #include "hud.h" // drawItemStack -#include "hex.h" -#include "util/string.h" -#include "util/numeric.h" #include "filesys.h" #include "gettime.h" #include "gettext.h" #include "scripting_game.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 "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) { \ @@ -68,12 +72,16 @@ with this program; if not, write to the Free Software Foundation, Inc., /* GUIFormSpecMenu */ +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, - GUIFormSpecMenu** ext_ptr, Client* client) : + Client* client, bool remap_dbl_click) : GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr), m_device(dev), m_invmgr(invmgr), @@ -84,16 +92,19 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, 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_ext_ptr(ext_ptr), - m_font(dev->getGUIEnvironment()->getSkin()->getFont()), - m_formspec_version(0) + m_formspec_version(0), + m_focused_element(""), + m_font(NULL), + m_remap_dbl_click(remap_dbl_click) #ifdef __ANDROID__ - ,m_JavaDialogFieldName(L"") + , m_JavaDialogFieldName("") #endif { current_keys_pending.key_down = false; @@ -108,9 +119,6 @@ GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev, m_doubleclickdetect[1].pos = v2s32(0, 0); m_tooltip_show_delay = (u32)g_settings->getS32("tooltip_show_delay"); - - m_btn_height = g_settings->getS32("font_size") +2; - assert(m_btn_height > 0); } GUIFormSpecMenu::~GUIFormSpecMenu() @@ -130,11 +138,6 @@ GUIFormSpecMenu::~GUIFormSpecMenu() if (m_text_dst != NULL) { delete m_text_dst; } - - if (m_ext_ptr != NULL) { - assert(*m_ext_ptr == this); - *m_ext_ptr = NULL; - } } void GUIFormSpecMenu::removeChildren() @@ -228,7 +231,7 @@ 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) @@ -237,28 +240,27 @@ GUITable* GUIFormSpecMenu::getTable(std::wstring tablename) return 0; } -std::vector split(const std::string &s, char delim) { +static 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++) { + for (unsigned int i = 0; i < s.size(); i++) { + char si = s.c_str()[i]; if (last_was_escape) { current += '\\'; - current += s.c_str()[i]; + current += si; last_was_escape = false; - } - else { - if (s.c_str()[i] == delim) { + } else { + if (si == delim) { tokens.push_back(current); current = ""; last_was_escape = false; - } - else if (s.c_str()[i] == '\\'){ + } else if (si == '\\') { last_was_escape = true; - } - else { - current += s.c_str()[i]; + } else { + current += si; last_was_escape = false; } } @@ -276,13 +278,11 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element) if (((parts.size() == 2) || parts.size() == 3) || ((parts.size() > 3) && (m_formspec_version > FORMSPEC_API_VERSION))) { - v2f invsize; - if (parts[1].find(';') != std::string::npos) parts[1] = parts[1].substr(0,parts[1].find(';')); - invsize.X = stof(parts[0]); - invsize.Y = stof(parts[1]); + data->invsize.X = MYMAX(0, stof(parts[0])); + data->invsize.Y = MYMAX(0, stof(parts[1])); lockSize(false); if (parts.size() == 3) { @@ -291,70 +291,7 @@ void GUIFormSpecMenu::parseSize(parserData* data,std::string element) } } - double cur_scaling = porting::getDisplayDensity() * - g_settings->getFloat("gui_scaling"); - - if (m_lock) { - v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize(); - v2u32 delta = current_screensize - m_lockscreensize; - - if (current_screensize.Y > m_lockscreensize.Y) - delta.Y /= 2; - else - delta.Y = 0; - - if (current_screensize.X > m_lockscreensize.X) - delta.X /= 2; - else - delta.X = 0; - - offset = v2s32(delta.X,delta.Y); - - data->screensize = m_lockscreensize; - - // fixed scaling for fixed size gui elements */ - cur_scaling = LEGACY_SCALING; - } - else { - offset = v2s32(0,0); - } - - /* adjust image size to dpi */ - int y_partition = 15; - imgsize = v2s32(data->screensize.Y/y_partition, data->screensize.Y/y_partition); - int min_imgsize = DEFAULT_IMGSIZE * cur_scaling; - while ((min_imgsize > imgsize.Y) && (y_partition > 1)) { - y_partition--; - imgsize = v2s32(data->screensize.Y/y_partition, data->screensize.Y/y_partition); - } - assert(y_partition > 0); - - /* adjust spacing to dpi */ - spacing = v2s32(imgsize.X+(DEFAULT_XSPACING * cur_scaling), - imgsize.Y+(DEFAULT_YSPACING * cur_scaling)); - - padding = v2s32(data->screensize.Y/imgsize.Y, data->screensize.Y/imgsize.Y); - - /* adjust padding to dpi */ - padding = v2s32( - (padding.X/(2.0/3.0)) * cur_scaling, - (padding.X/(2.0/3.0)) * cur_scaling - ); - data->size = v2s32( - padding.X*2+spacing.X*(invsize.X-1.0)+imgsize.X, - padding.Y*2+spacing.Y*(invsize.Y-1.0)+imgsize.Y + m_btn_height - 5 - ); - data->rect = core::rect( - data->screensize.X/2 - data->size.X/2 + offset.X, - data->screensize.Y/2 - data->size.Y/2 + offset.Y, - data->screensize.X/2 + data->size.X/2 + offset.X, - data->screensize.Y/2 + data->size.Y/2 + offset.Y - ); - - DesiredRect = data->rect; - recalculateAbsolutePosition(false); - data->basepos = getBasePos(); - data->bp_set = 2; + data->explicit_size = true; return; } errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl; @@ -407,7 +344,7 @@ void GUIFormSpecMenu::parseList(parserData* data,std::string element) return; } - if(data->bp_set != 2) + 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.push_back(ListRingSpec(loc, listname)); + return; + } else if ((element == "") && (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)); + return; + } + errorstream<< "Invalid list ring element(" << parts.size() << ", " + << m_inventorylists.size() << "): '" << element << "'" << std::endl; +} + void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) { std::vector parts = split(element,';'); - if (((parts.size() >= 3) || (parts.size() <= 4)) || + if (((parts.size() >= 3) && (parts.size() <= 4)) || ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION))) { std::vector v_pos = split(parts[0],','); @@ -441,7 +413,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) if (selected == "true") fselected = true; - std::wstring wlabel = narrow_to_wide(label.c_str()); + std::wstring wlabel = utf8_to_wide(label); core::rect rect = core::rect( pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height), @@ -449,7 +421,7 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element) pos.Y + ((imgsize.Y/2) + m_btn_height)); FieldSpec spec( - narrow_to_wide(name.c_str()), + name, wlabel, //Needed for displaying text on MSVC wlabel, 258+m_fields.size() @@ -501,7 +473,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.c_str()), + name, L"", L"", 258+m_fields.size() @@ -523,35 +495,6 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element) e->setSmallStep(10); e->setLargeStep(100); - if (!m_lock) { - core::rect relative_rect = e->getRelativePosition(); - - if (!is_horizontal) { - s32 original_width = relative_rect.getWidth(); - s32 width = (original_width/(2.0/3.0)) - * porting::getDisplayDensity() - * g_settings->getFloat("gui_scaling"); - e->setRelativePosition(core::rect( - relative_rect.UpperLeftCorner.X, - relative_rect.UpperLeftCorner.Y, - relative_rect.LowerRightCorner.X + (width - original_width), - relative_rect.LowerRightCorner.Y - )); - } - else { - s32 original_height = relative_rect.getHeight(); - s32 height = (original_height/(2.0/3.0)) - * porting::getDisplayDensity() - * g_settings->getFloat("gui_scaling"); - e->setRelativePosition(core::rect( - relative_rect.UpperLeftCorner.X, - relative_rect.UpperLeftCorner.Y, - relative_rect.LowerRightCorner.X, - relative_rect.LowerRightCorner.Y + (height - original_height) - )); - } - } - m_scrollbars.push_back(std::pair(spec,e)); m_fields.push_back(spec); return; @@ -581,7 +524,7 @@ 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->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of image without a size[] element"<bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of image without a size[] element"<bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of item_image without a size[] element"<(pos.X, pos.Y - m_btn_height, pos.X + geom.X, pos.Y + m_btn_height); - if(data->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of button without a size[] element"<bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of background without a size[] element"< rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - std::wstring fname_w = narrow_to_wide(name.c_str()); - FieldSpec spec( - fname_w, + name, L"", L"", 258+m_fields.size() @@ -818,8 +759,8 @@ 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 != "") && @@ -867,10 +808,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.c_str()); - FieldSpec spec( - fname_w, + name, L"", L"", 258+m_fields.size() @@ -892,8 +831,8 @@ 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 != "") && @@ -932,10 +871,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.c_str()); - FieldSpec spec( - fname_w, + name, L"", L"", 258+m_fields.size() @@ -952,7 +889,7 @@ void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element) } for (unsigned int i=0; i < items.size(); i++) { - e->addItem(narrow_to_wide(items[i]).c_str()); + e->addItem(utf8_to_wide(items[i]).c_str()); } if (str_initial_selection != "") @@ -995,10 +932,10 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) label = unescape_string(label); - std::wstring wlabel = narrow_to_wide(label.c_str()); + std::wstring wlabel = utf8_to_wide(label); FieldSpec spec( - narrow_to_wide(name.c_str()), + name, wlabel, L"", 258+m_fields.size() @@ -1013,8 +950,9 @@ void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element) if (label.length() >= 1) { - rect.UpperLeftCorner.Y -= m_btn_height; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height; + 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); } @@ -1043,20 +981,7 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, core::rect rect; - if(!data->bp_set) - { - rect = core::rect( - data->screensize.X/2 - 580/2, - data->screensize.Y/2 - 300/2, - data->screensize.X/2 + 580/2, - data->screensize.Y/2 + 300/2 - ); - DesiredRect = rect; - recalculateAbsolutePosition(false); - data->basepos = getBasePos(); - data->bp_set = 1; - } - else if(data->bp_set == 2) + if(data->explicit_size) errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<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); } @@ -1108,8 +1042,9 @@ void GUIFormSpecMenu::parseSimpleField(parserData* data, if (label.length() >= 1) { - rect.UpperLeftCorner.Y -= m_btn_height; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height; + 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); } } @@ -1152,7 +1087,7 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - if(data->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of positioned "<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); @@ -1204,8 +1150,9 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, if (label.length() >= 1) { - rect.UpperLeftCorner.Y -= m_btn_height; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height; + 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); } } @@ -1245,28 +1192,44 @@ void GUIFormSpecMenu::parseLabel(parserData* data,std::string element) v2s32 pos = padding; pos.X += stof(v_pos[0]) * (float)spacing.X; - pos.Y += stof(v_pos[1]) * (float)spacing.Y; + pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y; - if(data->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of label without a size[] element"< rect = core::rect( - pos.X, pos.Y+((imgsize.Y/2) - m_btn_height), + std::vector lines = split(text, '\n'); + + for (unsigned int i = 0; i != lines.size(); i++) { + // Lines are spaced at the nominal distance of + // 2/5 inventory slot, even if the font doesn't + // quite match that. This provides consistent + // form layout, at the expense of sometimes + // having sub-optimal spacing for the font. + // We multiply by 2 and then divide by 5, rather + // than multiply by 0.4, to get exact results + // in the integer cases: 0.4 is not exactly + // representable in binary floating point. + s32 posy = pos.Y + ((float)i) * spacing.Y * 2.0 / 5.0; + std::wstring wlabel = utf8_to_wide(lines[i]); + core::rect rect = core::rect( + pos.X, posy - m_btn_height, pos.X + m_font->getDimension(wlabel.c_str()).Width, - pos.Y+((imgsize.Y/2) + m_btn_height)); + posy + m_btn_height); + FieldSpec spec( + "", + wlabel, + L"", + 258+m_fields.size() + ); + gui::IGUIStaticText *e = + Environment->addStaticText(spec.flabel.c_str(), + rect, false, false, this, spec.fid); + e->setTextAlignment(gui::EGUIA_UPPERLEFT, + gui::EGUIA_CENTER); + m_fields.push_back(spec); + } - FieldSpec spec( - L"", - wlabel, - L"", - 258+m_fields.size() - ); - Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid); - m_fields.push_back(spec); return; } errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl; @@ -1280,7 +1243,7 @@ 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 = utf8_to_wide(unescape_string(parts[1])); MY_CHECKPOS("vertlabel",1); @@ -1291,12 +1254,12 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element) core::rect rect = core::rect( pos.X, pos.Y+((imgsize.Y/2)- m_btn_height), pos.X+15, pos.Y + - (m_font->getKerningHeight() + - m_font->getDimension(text.c_str()).Height) - * (text.length()+1)); + font_line_height(m_font) + * (text.length()+1) + +((imgsize.Y/2)- m_btn_height)); //actually text.length() would be correct but adding +1 avoids to break all mods - if(data->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of label without a size[] element"< rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); - if(data->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of image_button without a size[] element"<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); @@ -1434,7 +1399,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) } FieldSpec spec( - narrow_to_wide(name.c_str()), + name, L"", L"", 258+m_fields.size() @@ -1446,7 +1411,7 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element) pos.X += stof(v_pos[0]) * (float)spacing.X; pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2; v2s32 geom; - geom.X = data->screensize.Y; + geom.X = DesiredRect.getWidth(); geom.Y = m_btn_height*2; core::rect rect = core::rect(pos.X, pos.Y, pos.X+geom.X, @@ -1464,8 +1429,8 @@ 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 (unsigned int i = 0; i < buttons.size(); i++) { + e->addTab(utf8_to_wide(buttons[i]).c_str(), -1); } if ((tab_index >= 0) && @@ -1513,7 +1478,7 @@ 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->bp_set != 2) + if(!data->explicit_size) errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<idef(); @@ -1521,17 +1486,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) item.deSerialize(item_name, idef); video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef); - m_tooltips[narrow_to_wide(name.c_str())] = - TooltipSpec (item.getDefinition(idef).description, + m_tooltips[name] = + TooltipSpec(item.getDefinition(idef).description, m_default_tooltip_bgcolor, m_default_tooltip_color); label = unescape_string(label); FieldSpec spec( - narrow_to_wide(name.c_str()), - narrow_to_wide(label.c_str()), - narrow_to_wide(item_name.c_str()), - 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()); @@ -1541,8 +1506,8 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element) } e->setUseAlphaChannel(true); - e->setImage(texture); - e->setPressedImage(texture); + 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; @@ -1640,13 +1605,15 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element) std::vector parts = split(element,';'); if (parts.size() == 2) { std::string name = parts[0]; - m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], m_default_tooltip_bgcolor, m_default_tooltip_color); + m_tooltips[name] = TooltipSpec(unescape_string(parts[1]), + m_default_tooltip_bgcolor, m_default_tooltip_color); return; } else 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.c_str())] = TooltipSpec (parts[1], tmp_color1, tmp_color2); + m_tooltips[name] = TooltipSpec(unescape_string(parts[1]), + tmp_color1, tmp_color2); return; } } @@ -1677,6 +1644,30 @@ bool GUIFormSpecMenu::parseVersionDirect(std::string data) return false; } +bool GUIFormSpecMenu::parseSizeDirect(parserData* data, std::string element) +{ + if (element == "") + 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 != "size" && type != "invsize") + return false; + + if (type == "invsize") + log_deprecated("Deprecated formspec element \"invsize\" is used"); + + parseSize(data, description); + + return true; +} + void GUIFormSpecMenu::parseElement(parserData* data, std::string element) { //some prechecks @@ -1702,19 +1693,13 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) std::string type = trim(parts[0]); std::string description = trim(parts[1]); - if (type == "size") { - parseSize(data,description); - return; - } - - if (type == "invsize") { - log_deprecated("Deprecated formspec element \"invsize\" is used"); - parseSize(data,description); + if (type == "list") { + parseList(data,description); return; } - if (type == "list") { - parseList(data,description); + if (type == "listring") { + parseListRing(data, description); return; } @@ -1834,8 +1819,6 @@ void GUIFormSpecMenu::parseElement(parserData* data, std::string element) <getDynamicData(); } + //set focus + if (!m_focused_element.empty()) + mydata.focused_fieldname = m_focused_element; + //preserve focus gui::IGUIElement *focused_element = Environment->getFocus(); if (focused_element && focused_element->getParent() == this) { @@ -1881,12 +1868,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) // Base position of contents of form mydata.basepos = getBasePos(); - // State of basepos, 0 = not set, 1= set by formspec, 2 = set by size[] element - // Used to adjust form size automatically if needed - // A proceed button is added if there is no size[] element - mydata.bp_set = 0; - - /* Convert m_init_draw_spec to m_inventorylists */ m_inventorylists.clear(); @@ -1899,6 +1880,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) m_fields.clear(); m_boxes.clear(); m_tooltips.clear(); + m_inventory_rings.clear(); // Set default values (fits old formspec values) m_bgcolor = video::SColor(140,0,0,0); @@ -1940,13 +1922,131 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) } } + /* we need size first in order to calculate image scale */ + mydata.explicit_size = false; + for (; i< elements.size(); i++) { + if (!parseSizeDirect(&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 delta = current_screensize - m_lockscreensize; + + if (current_screensize.Y > m_lockscreensize.Y) + delta.Y /= 2; + else + delta.Y = 0; + + if (current_screensize.X > m_lockscreensize.X) + delta.X /= 2; + else + delta.X = 0; + + offset = v2s32(delta.X,delta.Y); + + mydata.screensize = m_lockscreensize; + } else { + offset = v2s32(0,0); + } + + double gui_scaling = g_settings->getFloat("gui_scaling"); + double screen_dpi = porting::getDisplayDensity() * 96; + + double use_imgsize; + if (m_lock) { + // In fixed-size mode, inventory image size + // is 0.53 inch multiplied by the gui_scaling + // config parameter. This magic size is chosen + // to make the main menu (15.5 inventory images + // wide, including border) just fit into the + // default window (800 pixels wide) at 96 DPI + // and default scaling (1.00). + use_imgsize = 0.5555 * screen_dpi * gui_scaling; + } else { + // In variable-size mode, we prefer to make the + // inventory image size 1/15 of screen height, + // multiplied by the gui_scaling config parameter. + // If the preferred size won't fit the whole + // form on the screen, either horizontally or + // vertically, then we scale it down to fit. + // (The magic numbers in the computation of what + // fits arise from the scaling factors in the + // following stanza, including the form border, + // help text space, and 0.1 inventory slot spare.) + // However, a minimum size is also set, that + // the image size can't be less than 0.3 inch + // multiplied by gui_scaling, even if this means + // the form doesn't fit the screen. + double prefer_imgsize = mydata.screensize.Y / 15 * + gui_scaling; + double fitx_imgsize = mydata.screensize.X / + ((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 min_imgsize = 0.3 * screen_dpi * gui_scaling; + use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize, + MYMIN(fitx_imgsize, fity_imgsize))); + } + + // Everything else is scaled in proportion to the + // inventory image size. The inventory slot spacing + // is 5/4 image size horizontally and 15/13 image size + // vertically. The padding around the form (incorporating + // the border of the outer inventory slots) is 3/8 + // image size. Font height (baseline to baseline) + // is 2/5 vertical inventory slot spacing, and button + // half-height is 7/8 of font height. + imgsize = v2s32(use_imgsize, use_imgsize); + spacing = v2s32(use_imgsize*5.0/4, use_imgsize*15.0/13); + padding = v2s32(use_imgsize*3.0/8, use_imgsize*3.0/8); + m_btn_height = use_imgsize*15.0/13 * 0.35; + + m_font = g_fontengine->getFont(); + + mydata.size = v2s32( + padding.X*2+spacing.X*(mydata.invsize.X-1.0)+imgsize.X, + padding.Y*2+spacing.Y*(mydata.invsize.Y-1.0)+imgsize.Y + m_btn_height*2.0/3.0 + ); + 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 + ); + } else { + // Non-size[] form must consist only of text fields and + // implicit "Proceed" button. Use default font, and + // temporary form size which will be recalculated below. + 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 + ); + } + recalculateAbsolutePosition(false); + mydata.basepos = getBasePos(); + m_tooltip_element->setOverrideFont(m_font); + + gui::IGUISkin* skin = Environment->getSkin(); + sanity_check(skin != NULL); + gui::IGUIFont *old_font = skin->getFont(); + skin->setFont(m_font); + for (; i< elements.size(); i++) { parseElement(&mydata, elements[i]); } - // If there's fields, add a Proceed button - if (m_fields.size() && mydata.bp_set != 2) { - // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields + // 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) { mydata.rect = core::rect( mydata.screensize.X/2 - 580/2, mydata.screensize.Y/2 - 300/2, @@ -1965,7 +2065,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) mydata.rect = core::rect(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y + (m_btn_height*2)); - wchar_t* text = wgettext("Proceed"); + const wchar_t *text = wgettext("Proceed"); Environment->addButton(mydata.rect, this, 257, text); delete[] text; } @@ -1978,13 +2078,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) || !isMyChild(focused_element) || focused_element->getType() == gui::EGUIET_TAB_CONTROL) setInitialFocus(); + + skin->setFont(old_font); } #ifdef __ANDROID__ bool GUIFormSpecMenu::getAndroidUIInput() { /* no dialog shown */ - if (m_JavaDialogFieldName == L"") { + if (m_JavaDialogFieldName == "") { return false; } @@ -1993,8 +2095,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) { @@ -2002,7 +2104,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; @@ -2020,7 +2122,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; } @@ -2054,12 +2156,6 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) { video::IVideoDriver* driver = Environment->getVideoDriver(); - // Get font - gui::IGUIFont *font = NULL; - gui::IGUISkin* skin = Environment->getSkin(); - if (skin) - font = skin->getFont(); - Inventory *inv = m_invmgr->getInventory(s.inventoryloc); if(!inv){ infostream<<"GUIFormSpecMenu::drawList(): WARNING: " @@ -2136,7 +2232,7 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) } if(!item.empty()) { - drawItemStack(driver, font, item, + drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect, m_gamedef); } @@ -2150,11 +2246,24 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase) 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_x = m_pointer.X + m_btn_height; - s32 tooltip_y = m_pointer.Y + m_btn_height; + 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))); @@ -2170,22 +2279,16 @@ void GUIFormSpecMenu::drawSelectedItem() video::IVideoDriver* driver = Environment->getVideoDriver(); - // Get font - gui::IGUIFont *font = NULL; - gui::IGUISkin* skin = Environment->getSkin(); - if (skin) - font = skin->getFont(); - Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); - assert(inv); + sanity_check(inv); InventoryList *list = inv->getList(m_selected_item->listname); - assert(list); + sanity_check(list); ItemStack stack = list->getItem(m_selected_item->i); stack.count = m_selected_amount; core::rect imgrect(0,0,imgsize.X,imgsize.Y); core::rect rect = imgrect + (m_pointer - imgrect.getCenter()); - drawItemStack(driver, font, stack, rect, NULL, m_gamedef); + drawItemStack(driver, m_font, stack, rect, NULL, m_gamedef); } void GUIFormSpecMenu::drawMenu() @@ -2198,11 +2301,13 @@ void GUIFormSpecMenu::drawMenu() } } + gui::IGUISkin* skin = Environment->getSkin(); + sanity_check(skin != NULL); + gui::IGUIFont *old_font = skin->getFont(); + skin->setFont(m_font); + updateSelectedItem(); - gui::IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; video::IVideoDriver* driver = Environment->getVideoDriver(); v2u32 screenSize = driver->getScreenSize(); @@ -2238,7 +2343,7 @@ 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); @@ -2288,7 +2393,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); } @@ -2317,7 +2422,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), core::dimension2di(texture->getOriginalSize())), NULL/*&AbsoluteClippingRect*/, colors, true); @@ -2353,32 +2458,44 @@ void GUIFormSpecMenu::drawMenu() if (hovered != NULL) { s32 id = hovered->getID(); - u32 delta; + + u32 delta = 0; if (id == -1) { m_old_tooltip_id = id; m_old_tooltip = ""; - delta = 0; - } else if (id != m_old_tooltip_id) { - m_hoovered_time = getTimeMs(); - m_old_tooltip_id = id; - delta = 0; - } else if (id == m_old_tooltip_id) { - delta = porting::getDeltaMs(m_hoovered_time, getTimeMs()); + } else { + if (id == m_old_tooltip_id) { + delta = porting::getDeltaMs(m_hovered_time, getTimeMs()); + } else { + m_hovered_time = getTimeMs(); + m_old_tooltip_id = id; + } } + if (id != -1 && delta >= m_tooltip_show_delay) { for(std::vector::iterator iter = m_fields.begin(); - iter != m_fields.end(); iter++) { + 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()); - s32 tooltip_x = m_pointer.X + m_btn_height; - s32 tooltip_y = m_pointer.Y + m_btn_height; - s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height; - if (tooltip_x + tooltip_width > (s32)screenSize.X) - tooltip_x = (s32)screenSize.X - tooltip_width - m_btn_height; + 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))); @@ -2397,6 +2514,8 @@ void GUIFormSpecMenu::drawMenu() Draw dragged item stack */ drawSelectedItem(); + + skin->setFont(old_font); } void GUIFormSpecMenu::updateSelectedItem() @@ -2518,7 +2637,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"; @@ -2553,11 +2672,10 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) for(unsigned int i=0; icheckEvent(); @@ -2574,7 +2692,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no) s32 selected = e->getSelected(); if (selected >= 0) { fields[name] = - wide_to_narrow(e->getItem(selected)); + wide_to_utf8(e->getItem(selected)); } } else if (s.ftype == f_TabHeader) { @@ -2630,7 +2748,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()); } } } @@ -2653,6 +2771,31 @@ static bool isChild(gui::IGUIElement * tocheck, gui::IGUIElement * parent) bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) { + // The IGUITabControl renders visually using the skin's selected + // font, which we override for the duration of form drawing, + // but computes tab hotspots based on how it would have rendered + // using the font that is selected at the time of button release. + // To make these two consistent, temporarily override the skin's + // font while the IGUITabControl is processing the event. + if (event.EventType == EET_MOUSE_INPUT_EVENT && + event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) { + s32 x = event.MouseInput.X; + s32 y = event.MouseInput.Y; + gui::IGUIElement *hovered = + Environment->getRootGUIElement()->getElementFromPoint( + core::position2d(x, y)); + if (hovered && isMyChild(hovered) && + hovered->getType() == gui::EGUIET_TAB_CONTROL) { + gui::IGUISkin* skin = Environment->getSkin(); + sanity_check(skin != NULL); + gui::IGUIFont *old_font = skin->getFont(); + skin->setFont(m_font); + bool retval = hovered->OnEvent(event); + skin->setFont(old_font); + return retval; + } + } + // Fix Esc/Return key being eaten by checkboxen and tables if(event.EventType==EET_KEY_INPUT_EVENT) { KeyPress kp(event.KeyInput); @@ -2713,7 +2856,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"; } @@ -2733,7 +2876,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; } @@ -2855,6 +2998,19 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) /******************************************************************************/ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) { + /* The following code is for capturing double-clicks of the mouse button + * 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, 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. + */ + + 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; @@ -2893,26 +3049,27 @@ bool GUIFormSpecMenu::DoubleClickDetection(const SEvent event) delete translated; return true; } + return false; } 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))) { + (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")); + m_text_dst->gotText(L"MenuQuit"); } return true; } else if (m_client != NULL && event.KeyInput.PressedDown && - (kp == getKeySetting("keymap_screenshot"))) { - m_client->makeScreenshot(m_device); + (kp == getKeySetting("keymap_screenshot"))) { + m_client->makeScreenshot(m_device); } if (event.KeyInput.PressedDown && (event.KeyInput.Key==KEY_RETURN || @@ -2932,7 +3089,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) break; default: //can't happen at all! - assert("reached a source line that can't ever been reached" == 0); + FATAL_ERROR("Reached a source line that can't ever been reached"); break; } if (current_keys_pending.key_enter && m_allowclose) { @@ -2946,11 +3103,14 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } - if((event.EventType==EET_MOUSE_INPUT_EVENT && - event.MouseInput.Event != EMIE_MOUSE_MOVED) || - (event.MouseInput.Event == EMIE_MOUSE_MOVED && - event.MouseInput.isRightPressed() && getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i)){ - // Mouse event other than movement or crossing the border of inventory field while holding rmb + /* Mouse event other than movement, or crossing the border of inventory + field while holding right mouse button + */ + if (event.EventType == EET_MOUSE_INPUT_EVENT && + (event.MouseInput.Event != EMIE_MOUSE_MOVED || + (event.MouseInput.Event == EMIE_MOUSE_MOVED && + event.MouseInput.isRightPressed() && + getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) { // Get selected item and hovered/clicked item (s) @@ -2960,44 +3120,45 @@ 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); - assert(inv_selected); - assert(inv_selected->getList(m_selected_item->listname) != NULL); + sanity_check(inv_selected); + sanity_check(inv_selected->getList(m_selected_item->listname) != NULL); } 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() && @@ -3009,25 +3170,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; @@ -3035,7 +3200,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; @@ -3084,49 +3259,62 @@ 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 = 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); + if (stack_to.empty() || stack_to.name == stack_from.name) + move_amount = 1; + } } } // Possibly send inventory action to server - if(move_amount > 0) - { + if (move_amount > 0) { // Send IACTION_MOVE assert(m_selected_item && m_selected_item->isValid()); @@ -3134,7 +3322,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) 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); @@ -3153,7 +3341,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 } @@ -3164,7 +3352,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; @@ -3174,8 +3362,68 @@ 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 @@ -3191,15 +3439,14 @@ 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 @@ -3207,7 +3454,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) assert(s.isValid()); assert(inv_s); - infostream<<"Handing IACTION_CRAFT to manager"<count = craft_amount; a->craft_inv = s.inventoryloc; @@ -3215,7 +3462,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; @@ -3224,12 +3471,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())) { @@ -3240,16 +3487,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())) { 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 { @@ -3288,11 +3535,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) s.send = false; return true; } - } - else if ((s.ftype == f_DropDown) && + } 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""; @@ -3321,8 +3565,8 @@ 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) { if (m_allowclose) { acceptInput(quit_mode_accept); @@ -3336,11 +3580,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++) { + iter != m_fields.end(); ++iter) { if (iter->fid == id) { return iter->fname; } } - return L""; + return ""; } /** @@ -3382,7 +3626,7 @@ 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++) { + iter != m_fields.end(); ++iter) { if (iter->fid == id) { return iter->flabel; }