From: darkrose Date: Sun, 15 Jul 2012 16:19:38 +0000 (+1000) Subject: Implement formspec X-Git-Url: http://81.2.79.47:8989/gitweb/?a=commitdiff_plain;h=506203345ba2795aa0af68a434f4b77cf50e664a;p=zefram%2Fminetest%2Fminetest_engine.git Implement formspec --- diff --git a/games/minimal/mods/default/init.lua b/games/minimal/mods/default/init.lua index cb424cb5..b3bbc5fe 100644 --- a/games/minimal/mods/default/init.lua +++ b/games/minimal/mods/default/init.lua @@ -1125,7 +1125,7 @@ minetest.register_node("default:sign_wall", { on_construct = function(pos) --local n = minetest.env:get_node(pos) local meta = minetest.env:get_meta(pos) - meta:set_string("formspec", "hack:sign_text_input") + meta:set_string("formspec", "field[text;;${text}]") meta:set_string("infotext", "\"\"") end, on_receive_fields = function(pos, formname, fields, sender) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3e9f6d35..a0276cfa 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -251,7 +251,7 @@ set(minetest_SRCS guiKeyChangeMenu.cpp guiMessageMenu.cpp guiTextInputMenu.cpp - guiInventoryMenu.cpp + guiFormSpecMenu.cpp guiPauseMenu.cpp guiPasswordChange.cpp guiDeathScreen.cpp diff --git a/src/content_nodemeta.cpp b/src/content_nodemeta.cpp index 12c8aa42..0e4c3dd7 100644 --- a/src/content_nodemeta.cpp +++ b/src/content_nodemeta.cpp @@ -63,7 +63,7 @@ static bool content_nodemeta_deserialize_legacy_body( //meta->setString("infotext","\"${text}\""); meta->setString("infotext", std::string("\"") + meta->getString("text") + "\""); - meta->setString("formspec","hack:sign_text_input"); + meta->setString("formspec","field[text;;${text}]"); return false; } else if(id == NODEMETA_CHEST) // ChestNodeMetadata @@ -77,7 +77,7 @@ static bool content_nodemeta_deserialize_legacy_body( } assert(inv->getList("main") && !inv->getList("0")); - meta->setString("formspec","invsize[8,9;]" + meta->setString("formspec","size[8,9]" "list[current_name;main;0,0;8,4;]" "list[current_player;main;0,5;8,4;]"); return false; @@ -94,7 +94,7 @@ static bool content_nodemeta_deserialize_legacy_body( } assert(inv->getList("main") && !inv->getList("0")); - meta->setString("formspec","invsize[8,9;]" + meta->setString("formspec","size[8,9]" "list[current_name;main;0,0;8,4;]" "list[current_player;main;0,5;8,4;]"); return false; @@ -115,7 +115,7 @@ static bool content_nodemeta_deserialize_legacy_body( is>>temp; meta->setString("src_time", ftos((float)temp/10)); - meta->setString("formspec","invsize[8,9;]" + meta->setString("formspec","size[8,9]" "list[current_name;fuel;2,3;1,1;]" "list[current_name;src;2,1;1,1;]" "list[current_name;dst;5,1;2,2;]" diff --git a/src/game.cpp b/src/game.cpp index 347dbf44..3ba90789 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "server.h" #include "guiPauseMenu.h" #include "guiPasswordChange.h" -#include "guiInventoryMenu.h" +#include "guiFormSpecMenu.h" #include "guiTextInputMenu.h" #include "guiDeathScreen.h" #include "tool.h" @@ -77,6 +77,10 @@ struct TextDestChat : public TextDest { m_client->typeChatMessage(text); } + void gotText(std::map fields) + { + m_client->typeChatMessage(narrow_to_wide(fields["text"])); + } Client *m_client; }; @@ -88,15 +92,20 @@ struct TextDestNodeMetadata : public TextDest m_p = p; m_client = client; } + // This is deprecated I guess? -celeron55 void gotText(std::wstring text) { std::string ntext = wide_to_narrow(text); - infostream<<"Changing text of a sign node: " - < fields; fields["text"] = ntext; m_client->sendNodemetaFields(m_p, "", fields); } + void gotText(std::map fields) + { + m_client->sendNodemetaFields(m_p, "", fields); + } v3s16 m_p; Client *m_client; @@ -139,6 +148,13 @@ public: return ""; return meta->getString("formspec"); } + std::string resolveText(std::string str) + { + NodeMetadata *meta = m_map->getNodeMetadata(m_p); + if(!meta) + return str; + return meta->resolveString(str); + } ClientMap *m_map; v3s16 m_p; @@ -1479,8 +1495,8 @@ void the_game( infostream<<"the_game: " <<"Launching inventory"<setFormSpec(src->getForm(), inventoryloc); - menu->setFormSource(new PlayerInventoryFormSource(&client)); + menu->setFormSource(src); menu->drop(); } else if(input->wasKeyDown(EscapeKey)) @@ -2219,7 +2235,8 @@ void the_game( { infostream<<"Ground right-clicked"<getString("formspec") == "hack:sign_text_input" && !random_input) { infostream<<"Launching metadata text input"<setFormSpec(meta->getString("formspec"), inventoryloc); menu->setFormSource(new NodeMetadataFormSource( &client.getEnv().getClientMap(), nodepos)); + menu->setTextDest(new TextDestNodeMetadata(nodepos, &client)); menu->drop(); } // Otherwise report right click to server diff --git a/src/guiFormSpecMenu.cpp b/src/guiFormSpecMenu.cpp new file mode 100644 index 00000000..02a4fcaa --- /dev/null +++ b/src/guiFormSpecMenu.cpp @@ -0,0 +1,1174 @@ +/* +Minetest-c55 +Copyright (C) 2010 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#include "guiFormSpecMenu.h" +#include "constants.h" +#include "gamedef.h" +#include "keycode.h" +#include "strfnd.h" +#include +#include +#include +#include +#include +#include "log.h" +#include "tile.h" // ITextureSource +#include "util/string.h" +#include "util/numeric.h" + +#include "gettext.h" + +void drawItemStack(video::IVideoDriver *driver, + gui::IGUIFont *font, + const ItemStack &item, + const core::rect &rect, + const core::rect *clip, + IGameDef *gamedef) +{ + if(item.empty()) + return; + + const ItemDefinition &def = item.getDefinition(gamedef->idef()); + video::ITexture *texture = def.inventory_texture; + + // Draw the inventory texture + if(texture != NULL) + { + 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())), + clip, colors, true); + } + + if(def.type == ITEM_TOOL && item.wear != 0) + { + // Draw a progressbar + float barheight = rect.getHeight()/16; + float barpad_x = rect.getWidth()/16; + float barpad_y = rect.getHeight()/16; + core::rect progressrect( + rect.UpperLeftCorner.X + barpad_x, + rect.LowerRightCorner.Y - barpad_y - barheight, + rect.LowerRightCorner.X - barpad_x, + rect.LowerRightCorner.Y - barpad_y); + + // Shrink progressrect by amount of tool damage + float wear = item.wear / 65535.0; + int progressmid = + wear * progressrect.UpperLeftCorner.X + + (1-wear) * progressrect.LowerRightCorner.X; + + // Compute progressbar color + // wear = 0.0: green + // wear = 0.5: yellow + // wear = 1.0: red + video::SColor color(255,255,255,255); + int wear_i = MYMIN(floor(wear * 600), 511); + wear_i = MYMIN(wear_i + 10, 511); + if(wear_i <= 255) + color.set(255, wear_i, 255, 0); + else + color.set(255, 255, 511-wear_i, 0); + + core::rect progressrect2 = progressrect; + progressrect2.LowerRightCorner.X = progressmid; + driver->draw2DRectangle(color, progressrect2, clip); + + color = video::SColor(255,0,0,0); + progressrect2 = progressrect; + progressrect2.UpperLeftCorner.X = progressmid; + driver->draw2DRectangle(color, progressrect2, clip); + } + + if(font != NULL && item.count >= 2) + { + // Get the item count as a string + std::string text = itos(item.count); + v2u32 dim = font->getDimension(narrow_to_wide(text).c_str()); + v2s32 sdim(dim.X,dim.Y); + + core::rect rect2( + /*rect.UpperLeftCorner, + core::dimension2d(rect.getWidth(), 15)*/ + rect.LowerRightCorner - sdim, + sdim + ); + + video::SColor bgcolor(128,0,0,0); + driver->draw2DRectangle(bgcolor, rect2, clip); + + video::SColor color(255,255,255,255); + font->draw(text.c_str(), rect2, color, false, false, clip); + } +} + +/* + GUIFormSpecMenu +*/ + +GUIFormSpecMenu::GUIFormSpecMenu(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, s32 id, + IMenuManager *menumgr, + InventoryManager *invmgr, + IGameDef *gamedef +): + GUIModalMenu(env, parent, id, menumgr), + m_invmgr(invmgr), + m_gamedef(gamedef), + m_form_src(NULL), + m_text_dst(NULL), + m_selected_item(NULL), + m_selected_amount(0), + m_selected_dragging(false), + m_tooltip_element(NULL) +{ +} + +GUIFormSpecMenu::~GUIFormSpecMenu() +{ + removeChildren(); + + delete m_selected_item; + delete m_form_src; +} + +void GUIFormSpecMenu::removeChildren() +{ + const core::list &children = getChildren(); + core::list children_copy; + for(core::list::ConstIterator + i = children.begin(); i != children.end(); i++) + { + children_copy.push_back(*i); + } + for(core::list::Iterator + i = children_copy.begin(); + i != children_copy.end(); i++) + { + (*i)->remove(); + } + /*{ + gui::IGUIElement *e = getElementFromId(256); + if(e != NULL) + e->remove(); + }*/ + if(m_tooltip_element) + { + m_tooltip_element->remove(); + m_tooltip_element = NULL; + } +} + +void GUIFormSpecMenu::regenerateGui(v2u32 screensize) +{ + // Remove children + removeChildren(); + + v2s32 size(100,100); + s32 helptext_h = 15; + core::rect rect; + + // Base position of contents of form + v2s32 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 + int bp_set = 0; + + /* Convert m_init_draw_spec to m_inventorylists */ + + m_inventorylists.clear(); + m_images.clear(); + m_fields.clear(); + + Strfnd f(m_formspec_string); + while(f.atend() == false) + { + std::string type = trim(f.next("[")); + if(type == "invsize" || type == "size") + { + v2f invsize; + invsize.X = stof(f.next(",")); + if(type == "size") + { + invsize.Y = stof(f.next("]")); + } + else{ + invsize.Y = stof(f.next(";")); + errorstream<<"WARNING: invsize is deprecated, use size"<( + screensize.X/2 - size.X/2, + screensize.Y/2 - size.Y/2, + screensize.X/2 + size.X/2, + screensize.Y/2 + size.Y/2 + ); + DesiredRect = rect; + recalculateAbsolutePosition(false); + basepos = getBasePos(); + bp_set = 2; + } + else if(type == "list") + { + std::string name = f.next(";"); + InventoryLocation loc; + if(name == "context" || name == "current_name") + loc = m_current_inventory_location; + else + loc.deSerialize(name); + std::string listname = f.next(";"); + v2s32 pos = basepos; + pos.X += stof(f.next(",")) * (float)spacing.X; + pos.Y += stof(f.next(";")) * (float)spacing.Y; + v2s32 geom; + geom.X = stoi(f.next(",")); + geom.Y = stoi(f.next(";")); + infostream<<"list inv="<( + screensize.X/2 - 580/2, + screensize.Y/2 - 300/2, + screensize.X/2 + 580/2, + screensize.Y/2 + 300/2 + ); + DesiredRect = rect; + recalculateAbsolutePosition(false); + basepos = getBasePos(); + bp_set = 1; + } + else if(bp_set == 2) + errorstream<<"WARNING: invalid use of unpositioned field in inventory"<(size.X/2-150, pos.Y, (size.X/2-150)+300, pos.Y+30); + } + else + { + v2s32 pos; + pos.X = stof(fname.substr(0,fname.find(","))) * (float)spacing.X; + pos.Y = stof(fname.substr(fname.find(",")+1)) * (float)spacing.Y; + v2s32 geom; + geom.X = (stof(flabel.substr(0,flabel.find(","))) * (float)spacing.X)-(spacing.X-imgsize.X); + pos.Y += (stof(flabel.substr(flabel.find(",")+1)) * (float)imgsize.Y)/2; + + rect = core::rect(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15); + + fname = f.next(";"); + flabel = f.next(";"); + if(bp_set != 2) + errorstream<<"WARNING: invalid use of positioned field without a size[] element"<resolveText(odefault); + else + fdefault = odefault; + + FieldSpec spec = FieldSpec( + narrow_to_wide(fname.c_str()), + narrow_to_wide(flabel.c_str()), + narrow_to_wide(fdefault.c_str()), + 258+m_fields.size() + ); + + // three cases: field and no label, label and no field, label and field + if (flabel == "") + { + spec.send = true; + gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); + Environment->setFocus(e); + + irr::SEvent evt; + evt.EventType = EET_KEY_INPUT_EVENT; + evt.KeyInput.Key = KEY_END; + evt.KeyInput.PressedDown = true; + e->OnEvent(evt); + } + else if (fname == "") + { + // set 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 + { + spec.send = true; + gui::IGUIElement *e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid); + Environment->setFocus(e); + rect.UpperLeftCorner.Y -= 15; + rect.LowerRightCorner.Y -= 15; + Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0); + + irr::SEvent evt; + evt.EventType = EET_KEY_INPUT_EVENT; + evt.KeyInput.Key = KEY_END; + evt.KeyInput.PressedDown = true; + e->OnEvent(evt); + } + + m_fields.push_back(spec); + } + else if(type == "label") + { + v2s32 pos; + pos.X = stof(f.next(",")) * (float)spacing.X; + pos.Y = stof(f.next(";")) * (float)spacing.Y; + + rect = core::rect(pos.X, pos.Y+((imgsize.Y/2)-15), pos.X+300, pos.Y+((imgsize.Y/2)+15)); + + std::string flabel = f.next("]"); + if(bp_set != 2) + errorstream<<"WARNING: invalid use of label without a size[] element"<addStaticText(spec.flabel.c_str(), rect, false, true, this, spec.fid); + m_fields.push_back(spec); + } + else if(type == "button") + { + v2s32 pos; + pos.X = stof(f.next(",")) * (float)spacing.X; + pos.Y = stof(f.next(";")) * (float)spacing.Y; + v2s32 geom; + geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X); + pos.Y += (stof(f.next(";")) * (float)imgsize.Y)/2; + + rect = core::rect(pos.X, pos.Y-15, pos.X+geom.X, pos.Y+15); + + std::string fname = f.next(";"); + std::string flabel = f.next("]"); + if(bp_set != 2) + errorstream<<"WARNING: invalid use of button without a size[] element"<addButton(rect, this, spec.fid, spec.flabel.c_str()); + m_fields.push_back(spec); + } + else if(type == "image_button") + { + v2s32 pos; + pos.X = stof(f.next(",")) * (float)spacing.X; + pos.Y = stof(f.next(";")) * (float)spacing.Y; + v2s32 geom; + geom.X = (stof(f.next(",")) * (float)spacing.X)-(spacing.X-imgsize.X); + geom.Y = (stof(f.next(";")) * (float)spacing.Y)-(spacing.Y-imgsize.Y); + + rect = core::rect(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y); + + std::string fimage = f.next(";"); + std::string fname = f.next(";"); + std::string flabel = f.next("]"); + if(bp_set != 2) + errorstream<<"WARNING: invalid use of image_button without a size[] element"<tsrc()->getTextureRaw(fimage); + gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, spec.flabel.c_str()); + e->setImage(texture); + e->setPressedImage(texture); + e->setScaleImage(true); + + m_fields.push_back(spec); + } + else + { + // Ignore others + std::string ts = f.next("]"); + infostream<<"Unknown DrawSpec: type="< rect(0, 0, size.X-padding.X*2, helptext_h); + rect = rect + v2s32(size.X/2 - rect.getWidth()/2, + size.Y-rect.getHeight()-5); + const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item"); + Environment->addStaticText(text, rect, false, true, this, 256); + changeCtype("C"); + } + // If there's fields, add a Proceed button + if (m_fields.size() && bp_set != 2) + { + // if the size wasn't set by an invsize[] or size[] adjust it now to fit all the fields + rect = core::rect( + screensize.X/2 - 580/2, + screensize.Y/2 - 300/2, + screensize.X/2 + 580/2, + screensize.Y/2 + 240/2+(m_fields.size()*60) + ); + DesiredRect = rect; + recalculateAbsolutePosition(false); + basepos = getBasePos(); + + changeCtype(""); + { + v2s32 pos = basepos; + pos.Y = ((m_fields.size()+2)*60); + + v2s32 size = DesiredRect.getSize(); + rect = core::rect(size.X/2-70, pos.Y, (size.X/2-70)+140, pos.Y+30); + Environment->addButton(rect, this, 257, wgettext("Proceed")); + } + changeCtype("C"); + } + // Add tooltip + { + // 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->enableOverrideColor(true); + m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60)); + m_tooltip_element->setDrawBackground(true); + m_tooltip_element->setDrawBorder(true); + m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255)); + m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); + m_tooltip_element->setWordWrap(false); + } +} + +GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const +{ + core::rect imgrect(0,0,imgsize.X,imgsize.Y); + + for(u32 i=0; i rect = imgrect + s.pos + p0; + if(rect.isPointInside(p)) + { + return ItemSpec(s.inventoryloc, s.listname, i); + } + } + } + + return ItemSpec(InventoryLocation(), "", -1); +} + +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: " + <<"The inventory location " + <<"\""<getList(s.listname); + if(!ilist){ + infostream<<"GUIFormSpecMenu::drawList(): WARNING: " + <<"The inventory list \""< imgrect(0,0,imgsize.X,imgsize.Y); + + for(s32 i=0; i rect = imgrect + s.pos + p; + ItemStack item; + if(ilist) + item = ilist->getItem(i); + + bool selected = m_selected_item + && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv + && m_selected_item->listname == s.listname + && m_selected_item->i == i; + bool hovering = rect.isPointInside(m_pointer); + + if(phase == 0) + { + if(hovering && m_selected_item) + { + video::SColor bgcolor(255,192,192,192); + driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); + } + else + { + video::SColor bgcolor(255,128,128,128); + driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); + } + } + + if(phase == 1) + { + // Draw item stack + if(selected) + { + item.takeItem(m_selected_amount); + } + if(!item.empty()) + { + drawItemStack(driver, font, item, + rect, &AbsoluteClippingRect, m_gamedef); + } + + // Draw tooltip + std::string tooltip_text = ""; + if(hovering && !m_selected_item) + tooltip_text = item.getDefinition(m_gamedef->idef()).description; + if(tooltip_text != "") + { + 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 + 15; + s32 tooltip_y = m_pointer.Y + 15; + s32 tooltip_width = m_tooltip_element->getTextWidth() + 15; + s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; + m_tooltip_element->setRelativePosition(core::rect( + core::position2d(tooltip_x, tooltip_y), + core::dimension2d(tooltip_width, tooltip_height))); + } + } + } +} + +void GUIFormSpecMenu::drawSelectedItem() +{ + if(!m_selected_item) + return; + + 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); + InventoryList *list = inv->getList(m_selected_item->listname); + assert(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); +} + +void GUIFormSpecMenu::drawMenu() +{ + if(m_form_src){ + std::string newform = m_form_src->getForm(); + if(newform != m_formspec_string){ + m_formspec_string = newform; + regenerateGui(m_screensize_old); + } + } + + updateSelectedItem(); + + gui::IGUISkin* skin = Environment->getSkin(); + if (!skin) + return; + video::IVideoDriver* driver = Environment->getVideoDriver(); + + video::SColor bgcolor(140,0,0,0); + driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect); + + m_tooltip_element->setVisible(false); + + /* + Draw items + Phase 0: Item slot rectangles + Phase 1: Item images; prepare tooltip + */ + + for(int phase=0; phase<=1; phase++) + for(u32 i=0; itsrc()->getTextureRaw(spec.name); + // Image size on screen + core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); + // Image 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); + } + + /* + Draw dragged item stack + */ + drawSelectedItem(); + + /* + Call base class + */ + gui::IGUIElement::draw(); +} + +void GUIFormSpecMenu::updateSelectedItem() +{ + // If the selected stack has become empty for some reason, deselect it. + // If the selected stack has become smaller, adjust m_selected_amount. + if(m_selected_item) + { + bool selection_valid = false; + if(m_selected_item->isValid()) + { + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); + if(inv) + { + InventoryList *list = inv->getList(m_selected_item->listname); + if(list && (u32) m_selected_item->i < list->getSize()) + { + ItemStack stack = list->getItem(m_selected_item->i); + if(m_selected_amount > stack.count) + m_selected_amount = stack.count; + if(!stack.empty()) + selection_valid = true; + } + } + } + if(!selection_valid) + { + delete m_selected_item; + m_selected_item = NULL; + m_selected_amount = 0; + m_selected_dragging = false; + } + } + + // If craftresult is nonempty and nothing else is selected, select it now. + if(!m_selected_item) + { + for(u32 i=0; igetInventory(s.inventoryloc); + InventoryList *list = inv->getList("craftresult"); + if(list && list->getSize() >= 1 && !list->getItem(0).empty()) + { + m_selected_item = new ItemSpec; + m_selected_item->inventoryloc = s.inventoryloc; + m_selected_item->listname = "craftresult"; + m_selected_item->i = 0; + m_selected_amount = 0; + m_selected_dragging = false; + break; + } + } + } + } + + // If craftresult is selected, keep the whole stack selected + if(m_selected_item && m_selected_item->listname == "craftresult") + { + Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); + assert(inv); + InventoryList *list = inv->getList(m_selected_item->listname); + assert(list); + m_selected_amount = list->getItem(m_selected_item->i).count; + } +} + +void GUIFormSpecMenu::acceptInput() +{ + if(m_text_dst) + { + std::map fields; + gui::IGUIElement *e; + for(u32 i=0; igetText()); + } + } + } + } + m_text_dst->gotText(fields); + delete m_text_dst; + m_text_dst = NULL; + } +} + +bool GUIFormSpecMenu::OnEvent(const SEvent& event) +{ + if(event.EventType==EET_KEY_INPUT_EVENT) + { + KeyPress kp(event.KeyInput); + if (event.KeyInput.PressedDown && (kp == EscapeKey || + kp == getKeySetting("keymap_inventory"))) + { + quitMenu(); + return true; + } + if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown) + { + acceptInput(); + quitMenu(); + return true; + } + } + if(event.EventType==EET_MOUSE_INPUT_EVENT + && event.MouseInput.Event == EMIE_MOUSE_MOVED) + { + // Mouse moved + m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y); + } + if(event.EventType==EET_MOUSE_INPUT_EVENT + && event.MouseInput.Event != EMIE_MOUSE_MOVED) + { + // Mouse event other than movement + + v2s32 p(event.MouseInput.X, event.MouseInput.Y); + m_pointer = p; + + // Get selected item and hovered/clicked item (s) + + updateSelectedItem(); + ItemSpec s = getItemAtPos(p); + + Inventory *inv_selected = NULL; + Inventory *inv_s = NULL; + + 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); + } + + u32 s_count = 0; + + 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){ + errorstream<<"InventoryMenu: The selected inventory list \"" + <= list->getSize()){ + errorstream<<"InventoryMenu: The selected inventory list \"" + <getItem(s.i).count; + }while(0); + + bool identical = (m_selected_item != NULL) && s.isValid() && + (inv_selected == inv_s) && + (m_selected_item->listname == s.listname) && + (m_selected_item->i == s.i); + + // buttons: 0 = left, 1 = right, 2 = middle + // up/down: 0 = down (press), 1 = up (release), 2 = unknown event + int button = 0; + int updown = 2; + if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) + { button = 0; updown = 0; } + else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) + { button = 1; updown = 0; } + else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) + { button = 2; updown = 0; } + else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) + { button = 0; updown = 1; } + else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) + { button = 1; updown = 1; } + else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) + { button = 2; 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 drop action + // from m_selected_item. + u32 drop_amount = 0; + + // Set this number to a positive value to generate a craft action at s. + u32 craft_amount = 0; + + if(updown == 0) + { + // Some mouse button has been pressed + + //infostream<<"Mouse button "<= 1); + + if(s.isValid()) + { + // Clicked a slot: move + if(button == 1) // right + move_amount = 1; + 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) + m_selected_amount = 0; + else + m_selected_amount -= move_amount; + move_amount = 0; + } + } + else if(getAbsoluteClippingRect().isPointInside(m_pointer)) + { + // Clicked somewhere else: deselect + m_selected_amount = 0; + } + else + { + // Clicked outside of the window: drop + if(button == 1) // right + drop_amount = 1; + else if(button == 2) // middle + drop_amount = MYMIN(m_selected_amount, 10); + else // left + drop_amount = m_selected_amount; + } + } + } + else if(updown == 1) + { + // Some mouse button has been released + + //infostream<<"Mouse button "< 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); + 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()); + if(leftover.count == stack_from.count) + { + // Swap the stacks + m_selected_amount -= stack_to.count; + } + else if(leftover.empty()) + { + // Item fits + m_selected_amount -= move_amount; + } + else + { + // Item only fits partially + move_amount -= leftover.count; + m_selected_amount -= move_amount; + } + + infostream<<"Handing IACTION_MOVE to manager"<count = move_amount; + a->from_inv = m_selected_item->inventoryloc; + a->from_list = m_selected_item->listname; + a->from_i = m_selected_item->i; + a->to_inv = s.inventoryloc; + a->to_list = s.listname; + a->to_i = s.i; + m_invmgr->inventoryAction(a); + } + else if(drop_amount > 0) + { + // Send IACTION_DROP + + assert(m_selected_item && m_selected_item->isValid()); + assert(inv_selected); + InventoryList *list_from = inv_selected->getList(m_selected_item->listname); + assert(list_from); + ItemStack stack_from = list_from->getItem(m_selected_item->i); + + // Check how many items can be dropped + drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count); + 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) + { + // Send IACTION_CRAFT + + assert(s.isValid()); + assert(inv_s); + + infostream<<"Handing IACTION_CRAFT to manager"<count = craft_amount; + a->craft_inv = s.inventoryloc; + m_invmgr->inventoryAction(a); + } + + // If m_selected_amount has been decreased to zero, deselect + if(m_selected_amount == 0) + { + delete m_selected_item; + m_selected_item = NULL; + m_selected_amount = 0; + m_selected_dragging = false; + } + } + if(event.EventType==EET_GUI_EVENT) + { + if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST + && isVisible()) + { + if(!canTakeFocus(event.GUIEvent.Element)) + { + infostream<<"GUIFormSpecMenu: Not allowing focus change." + <getID()) + { + case 257: + acceptInput(); + quitMenu(); + // quitMenu deallocates menu + return true; + } + // find the element that was clicked + for(u32 i=0; igetID()) + { + s.send = true; + acceptInput(); + quitMenu(); + return true; + } + } + } + if(event.GUIEvent.EventType==gui::EGET_EDITBOX_ENTER) + { + if(event.GUIEvent.Caller->getID() > 257) + { + acceptInput(); + quitMenu(); + // quitMenu deallocates menu + return true; + } + } + } + + return Parent ? Parent->OnEvent(event) : false; +} + diff --git a/src/guiFormSpecMenu.h b/src/guiFormSpecMenu.h new file mode 100644 index 00000000..a6062915 --- /dev/null +++ b/src/guiFormSpecMenu.h @@ -0,0 +1,217 @@ +/* +Minetest-c55 +Copyright (C) 2010 celeron55, Perttu Ahola + +This program is free software; you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation; either version 2.1 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License along +with this program; if not, write to the Free Software Foundation, Inc., +51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ + + +#ifndef GUIINVENTORYMENU_HEADER +#define GUIINVENTORYMENU_HEADER + +#include "irrlichttypes_extrabloated.h" +#include "inventory.h" +#include "inventorymanager.h" +#include "modalMenu.h" + +class IGameDef; +class InventoryManager; + +struct TextDest +{ + virtual ~TextDest() {}; + // This is deprecated I guess? -celeron55 + virtual void gotText(std::wstring text) = 0; + virtual void gotText(std::map fields) = 0; +}; + +class IFormSource +{ +public: + virtual ~IFormSource(){} + virtual std::string getForm() = 0; + // Fill in variables in field text + virtual std::string resolveText(std::string str){ return str; } +}; + +void drawItemStack(video::IVideoDriver *driver, + gui::IGUIFont *font, + const ItemStack &item, + const core::rect &rect, + const core::rect *clip, + IGameDef *gamedef); + +class GUIFormSpecMenu : public GUIModalMenu +{ + struct ItemSpec + { + ItemSpec() + { + i = -1; + } + ItemSpec(const InventoryLocation &a_inventoryloc, + const std::string &a_listname, + s32 a_i) + { + inventoryloc = a_inventoryloc; + listname = a_listname; + i = a_i; + } + bool isValid() const + { + return i != -1; + } + + InventoryLocation inventoryloc; + std::string listname; + s32 i; + }; + + struct ListDrawSpec + { + ListDrawSpec() + { + } + ListDrawSpec(const InventoryLocation &a_inventoryloc, + const std::string &a_listname, + v2s32 a_pos, v2s32 a_geom): + inventoryloc(a_inventoryloc), + listname(a_listname), + pos(a_pos), + geom(a_geom) + { + } + + InventoryLocation inventoryloc; + std::string listname; + v2s32 pos; + v2s32 geom; + }; + + struct ImageDrawSpec + { + ImageDrawSpec() + { + } + ImageDrawSpec(const std::string &a_name, + v2s32 a_pos, v2s32 a_geom): + name(a_name), + pos(a_pos), + geom(a_geom) + { + } + std::string name; + v2s32 pos; + v2s32 geom; + }; + + struct FieldSpec + { + FieldSpec() + { + } + FieldSpec(const std::wstring name, const std::wstring label, const std::wstring fdeflt, int id): + fname(name), + flabel(label), + fdefault(fdeflt), + fid(id) + { + send = false; + is_button = false; + } + std::wstring fname; + std::wstring flabel; + std::wstring fdefault; + int fid; + bool send; + bool is_button; + }; + +public: + GUIFormSpecMenu(gui::IGUIEnvironment* env, + gui::IGUIElement* parent, s32 id, + IMenuManager *menumgr, + InventoryManager *invmgr, + IGameDef *gamedef + ); + ~GUIFormSpecMenu(); + + void setFormSpec(const std::string &formspec_string, + InventoryLocation current_inventory_location) + { + m_formspec_string = formspec_string; + m_current_inventory_location = current_inventory_location; + regenerateGui(m_screensize_old); + } + + // form_src is deleted by this GUIFormSpecMenu + void setFormSource(IFormSource *form_src) + { + m_form_src = form_src; + } + + // text_dst is deleted by this GUIFormSpecMenu + void setTextDest(TextDest *text_dst) + { + m_text_dst = text_dst; + } + + void removeChildren(); + /* + Remove and re-add (or reposition) stuff + */ + void regenerateGui(v2u32 screensize); + + ItemSpec getItemAtPos(v2s32 p) const; + void drawList(const ListDrawSpec &s, int phase); + void drawSelectedItem(); + void drawMenu(); + void updateSelectedItem(); + + void acceptInput(); + bool OnEvent(const SEvent& event); + +protected: + v2s32 getBasePos() const + { + return padding + AbsoluteRect.UpperLeftCorner; + } + + v2s32 padding; + v2s32 spacing; + v2s32 imgsize; + + InventoryManager *m_invmgr; + IGameDef *m_gamedef; + + std::string m_formspec_string; + InventoryLocation m_current_inventory_location; + IFormSource *m_form_src; + TextDest *m_text_dst; + + core::array m_inventorylists; + core::array m_images; + core::array m_fields; + + ItemSpec *m_selected_item; + u32 m_selected_amount; + bool m_selected_dragging; + + v2s32 m_pointer; + gui::IGUIStaticText *m_tooltip_element; +}; + +#endif + diff --git a/src/guiInventoryMenu.cpp b/src/guiInventoryMenu.cpp deleted file mode 100644 index f60c5b45..00000000 --- a/src/guiInventoryMenu.cpp +++ /dev/null @@ -1,885 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - - -#include "guiInventoryMenu.h" -#include "constants.h" -#include "gamedef.h" -#include "keycode.h" -#include "strfnd.h" -#include -#include -#include -#include -#include -#include "log.h" -#include "tile.h" // ITextureSource -#include "util/string.h" -#include "util/numeric.h" - -void drawItemStack(video::IVideoDriver *driver, - gui::IGUIFont *font, - const ItemStack &item, - const core::rect &rect, - const core::rect *clip, - IGameDef *gamedef) -{ - if(item.empty()) - return; - - const ItemDefinition &def = item.getDefinition(gamedef->idef()); - video::ITexture *texture = def.inventory_texture; - - // Draw the inventory texture - if(texture != NULL) - { - 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())), - clip, colors, true); - } - - if(def.type == ITEM_TOOL && item.wear != 0) - { - // Draw a progressbar - float barheight = rect.getHeight()/16; - float barpad_x = rect.getWidth()/16; - float barpad_y = rect.getHeight()/16; - core::rect progressrect( - rect.UpperLeftCorner.X + barpad_x, - rect.LowerRightCorner.Y - barpad_y - barheight, - rect.LowerRightCorner.X - barpad_x, - rect.LowerRightCorner.Y - barpad_y); - - // Shrink progressrect by amount of tool damage - float wear = item.wear / 65535.0; - int progressmid = - wear * progressrect.UpperLeftCorner.X + - (1-wear) * progressrect.LowerRightCorner.X; - - // Compute progressbar color - // wear = 0.0: green - // wear = 0.5: yellow - // wear = 1.0: red - video::SColor color(255,255,255,255); - int wear_i = MYMIN(floor(wear * 600), 511); - wear_i = MYMIN(wear_i + 10, 511); - if(wear_i <= 255) - color.set(255, wear_i, 255, 0); - else - color.set(255, 255, 511-wear_i, 0); - - core::rect progressrect2 = progressrect; - progressrect2.LowerRightCorner.X = progressmid; - driver->draw2DRectangle(color, progressrect2, clip); - - color = video::SColor(255,0,0,0); - progressrect2 = progressrect; - progressrect2.UpperLeftCorner.X = progressmid; - driver->draw2DRectangle(color, progressrect2, clip); - } - - if(font != NULL && item.count >= 2) - { - // Get the item count as a string - std::string text = itos(item.count); - v2u32 dim = font->getDimension(narrow_to_wide(text).c_str()); - v2s32 sdim(dim.X,dim.Y); - - core::rect rect2( - /*rect.UpperLeftCorner, - core::dimension2d(rect.getWidth(), 15)*/ - rect.LowerRightCorner - sdim, - sdim - ); - - video::SColor bgcolor(128,0,0,0); - driver->draw2DRectangle(bgcolor, rect2, clip); - - video::SColor color(255,255,255,255); - font->draw(text.c_str(), rect2, color, false, false, clip); - } -} - -/* - GUIInventoryMenu -*/ - -GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr, - InventoryManager *invmgr, - IGameDef *gamedef -): - GUIModalMenu(env, parent, id, menumgr), - m_invmgr(invmgr), - m_gamedef(gamedef), - m_form_src(NULL), - m_selected_item(NULL), - m_selected_amount(0), - m_selected_dragging(false), - m_tooltip_element(NULL) -{ -} - -GUIInventoryMenu::~GUIInventoryMenu() -{ - removeChildren(); - - delete m_selected_item; - delete m_form_src; -} - -void GUIInventoryMenu::removeChildren() -{ - const core::list &children = getChildren(); - core::list children_copy; - for(core::list::ConstIterator - i = children.begin(); i != children.end(); i++) - { - children_copy.push_back(*i); - } - for(core::list::Iterator - i = children_copy.begin(); - i != children_copy.end(); i++) - { - (*i)->remove(); - } - /*{ - gui::IGUIElement *e = getElementFromId(256); - if(e != NULL) - e->remove(); - }*/ - if(m_tooltip_element) - { - m_tooltip_element->remove(); - m_tooltip_element = NULL; - } -} - -void GUIInventoryMenu::regenerateGui(v2u32 screensize) -{ - // Remove children - removeChildren(); - - v2s32 size(100,100); - s32 helptext_h = 15; - core::rect rect; - v2s32 basepos = getBasePos(); - - /* Convert m_init_draw_spec to m_inventorylists */ - - m_inventorylists.clear(); - m_images.clear(); - - Strfnd f(m_formspec_string); - while(f.atend() == false) - { - std::string type = trim(f.next("[")); - if(type == "invsize") - { - v2f invsize; - invsize.X = stof(f.next(",")); - invsize.Y = stof(f.next(";")); - infostream<<"invsize ("<( - screensize.X/2 - size.X/2, - screensize.Y/2 - size.Y/2, - screensize.X/2 + size.X/2, - screensize.Y/2 + size.Y/2 - ); - DesiredRect = rect; - recalculateAbsolutePosition(false); - basepos = getBasePos(); - } - else if(type == "list") - { - std::string name = f.next(";"); - InventoryLocation loc; - if(name == "context" || name == "current_name") - loc = m_current_inventory_location; - else - loc.deSerialize(name); - std::string listname = f.next(";"); - v2s32 pos = basepos; - pos.X += stof(f.next(",")) * (float)spacing.X; - pos.Y += stof(f.next(";")) * (float)spacing.Y; - v2s32 geom; - geom.X = stoi(f.next(",")); - geom.Y = stoi(f.next(";")); - infostream<<"list inv="< rect(0, 0, size.X-padding.X*2, helptext_h); - rect = rect + v2s32(size.X/2 - rect.getWidth()/2, - size.Y-rect.getHeight()-5); - const wchar_t *text = - L"Left click: Move all items, Right click: Move single item"; - Environment->addStaticText(text, rect, false, true, this, 256); - - // Add tooltip - // 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->enableOverrideColor(true); - m_tooltip_element->setBackgroundColor(video::SColor(255,110,130,60)); - m_tooltip_element->setDrawBackground(true); - m_tooltip_element->setDrawBorder(true); - m_tooltip_element->setOverrideColor(video::SColor(255,255,255,255)); - m_tooltip_element->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER); - m_tooltip_element->setWordWrap(false); - } -} - -GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const -{ - core::rect imgrect(0,0,imgsize.X,imgsize.Y); - - for(u32 i=0; i rect = imgrect + s.pos + p0; - if(rect.isPointInside(p)) - { - return ItemSpec(s.inventoryloc, s.listname, i); - } - } - } - - return ItemSpec(InventoryLocation(), "", -1); -} - -void GUIInventoryMenu::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<<"GUIInventoryMenu::drawList(): WARNING: " - <<"The inventory location " - <<"\""<getList(s.listname); - if(!ilist){ - infostream<<"GUIInventoryMenu::drawList(): WARNING: " - <<"The inventory list \""< imgrect(0,0,imgsize.X,imgsize.Y); - - for(s32 i=0; i rect = imgrect + s.pos + p; - ItemStack item; - if(ilist) - item = ilist->getItem(i); - - bool selected = m_selected_item - && m_invmgr->getInventory(m_selected_item->inventoryloc) == inv - && m_selected_item->listname == s.listname - && m_selected_item->i == i; - bool hovering = rect.isPointInside(m_pointer); - - if(phase == 0) - { - if(hovering && m_selected_item) - { - video::SColor bgcolor(255,192,192,192); - driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); - } - else - { - video::SColor bgcolor(255,128,128,128); - driver->draw2DRectangle(bgcolor, rect, &AbsoluteClippingRect); - } - } - - if(phase == 1) - { - // Draw item stack - if(selected) - { - item.takeItem(m_selected_amount); - } - if(!item.empty()) - { - drawItemStack(driver, font, item, - rect, &AbsoluteClippingRect, m_gamedef); - } - - // Draw tooltip - std::string tooltip_text = ""; - if(hovering && !m_selected_item) - tooltip_text = item.getDefinition(m_gamedef->idef()).description; - if(tooltip_text != "") - { - 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 + 15; - s32 tooltip_y = m_pointer.Y + 15; - s32 tooltip_width = m_tooltip_element->getTextWidth() + 15; - s32 tooltip_height = m_tooltip_element->getTextHeight() + 5; - m_tooltip_element->setRelativePosition(core::rect( - core::position2d(tooltip_x, tooltip_y), - core::dimension2d(tooltip_width, tooltip_height))); - } - } - } -} - -void GUIInventoryMenu::drawSelectedItem() -{ - if(!m_selected_item) - return; - - 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); - InventoryList *list = inv->getList(m_selected_item->listname); - assert(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); -} - -void GUIInventoryMenu::drawMenu() -{ - if(m_form_src){ - std::string newform = m_form_src->getForm(); - if(newform != m_formspec_string){ - m_formspec_string = newform; - regenerateGui(m_screensize_old); - } - } - - updateSelectedItem(); - - gui::IGUISkin* skin = Environment->getSkin(); - if (!skin) - return; - video::IVideoDriver* driver = Environment->getVideoDriver(); - - video::SColor bgcolor(140,0,0,0); - driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect); - - m_tooltip_element->setVisible(false); - - /* - Draw items - Phase 0: Item slot rectangles - Phase 1: Item images; prepare tooltip - */ - - for(int phase=0; phase<=1; phase++) - for(u32 i=0; itsrc()->getTextureRaw(spec.name); - // Image size on screen - core::rect imgrect(0, 0, spec.geom.X, spec.geom.Y); - // Image 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); - } - - /* - Draw dragged item stack - */ - drawSelectedItem(); - - /* - Call base class - */ - gui::IGUIElement::draw(); -} - -void GUIInventoryMenu::updateSelectedItem() -{ - // If the selected stack has become empty for some reason, deselect it. - // If the selected stack has become smaller, adjust m_selected_amount. - if(m_selected_item) - { - bool selection_valid = false; - if(m_selected_item->isValid()) - { - Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); - if(inv) - { - InventoryList *list = inv->getList(m_selected_item->listname); - if(list && (u32) m_selected_item->i < list->getSize()) - { - ItemStack stack = list->getItem(m_selected_item->i); - if(m_selected_amount > stack.count) - m_selected_amount = stack.count; - if(!stack.empty()) - selection_valid = true; - } - } - } - if(!selection_valid) - { - delete m_selected_item; - m_selected_item = NULL; - m_selected_amount = 0; - m_selected_dragging = false; - } - } - - // If craftresult is nonempty and nothing else is selected, select it now. - if(!m_selected_item) - { - for(u32 i=0; igetInventory(s.inventoryloc); - InventoryList *list = inv->getList("craftresult"); - if(list && list->getSize() >= 1 && !list->getItem(0).empty()) - { - m_selected_item = new ItemSpec; - m_selected_item->inventoryloc = s.inventoryloc; - m_selected_item->listname = "craftresult"; - m_selected_item->i = 0; - m_selected_amount = 0; - m_selected_dragging = false; - break; - } - } - } - } - - // If craftresult is selected, keep the whole stack selected - if(m_selected_item && m_selected_item->listname == "craftresult") - { - Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); - assert(inv); - InventoryList *list = inv->getList(m_selected_item->listname); - assert(list); - m_selected_amount = list->getItem(m_selected_item->i).count; - } -} - -bool GUIInventoryMenu::OnEvent(const SEvent& event) -{ - if(event.EventType==EET_KEY_INPUT_EVENT) - { - KeyPress kp(event.KeyInput); - if (event.KeyInput.PressedDown && (kp == EscapeKey || - kp == getKeySetting("keymap_inventory"))) - { - quitMenu(); - return true; - } - } - if(event.EventType==EET_MOUSE_INPUT_EVENT - && event.MouseInput.Event == EMIE_MOUSE_MOVED) - { - // Mouse moved - m_pointer = v2s32(event.MouseInput.X, event.MouseInput.Y); - } - if(event.EventType==EET_MOUSE_INPUT_EVENT - && event.MouseInput.Event != EMIE_MOUSE_MOVED) - { - // Mouse event other than movement - - v2s32 p(event.MouseInput.X, event.MouseInput.Y); - m_pointer = p; - - // Get selected item and hovered/clicked item (s) - - updateSelectedItem(); - ItemSpec s = getItemAtPos(p); - - Inventory *inv_selected = NULL; - Inventory *inv_s = NULL; - - 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); - } - - u32 s_count = 0; - - 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){ - errorstream<<"InventoryMenu: The selected inventory list \"" - <= list->getSize()){ - errorstream<<"InventoryMenu: The selected inventory list \"" - <getItem(s.i).count; - }while(0); - - bool identical = (m_selected_item != NULL) && s.isValid() && - (inv_selected == inv_s) && - (m_selected_item->listname == s.listname) && - (m_selected_item->i == s.i); - - // buttons: 0 = left, 1 = right, 2 = middle - // up/down: 0 = down (press), 1 = up (release), 2 = unknown event - int button = 0; - int updown = 2; - if(event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) - { button = 0; updown = 0; } - else if(event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN) - { button = 1; updown = 0; } - else if(event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN) - { button = 2; updown = 0; } - else if(event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) - { button = 0; updown = 1; } - else if(event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP) - { button = 1; updown = 1; } - else if(event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP) - { button = 2; 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 drop action - // from m_selected_item. - u32 drop_amount = 0; - - // Set this number to a positive value to generate a craft action at s. - u32 craft_amount = 0; - - if(updown == 0) - { - // Some mouse button has been pressed - - //infostream<<"Mouse button "<= 1); - - if(s.isValid()) - { - // Clicked a slot: move - if(button == 1) // right - move_amount = 1; - 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) - m_selected_amount = 0; - else - m_selected_amount -= move_amount; - move_amount = 0; - } - } - else if(getAbsoluteClippingRect().isPointInside(m_pointer)) - { - // Clicked somewhere else: deselect - m_selected_amount = 0; - } - else - { - // Clicked outside of the window: drop - if(button == 1) // right - drop_amount = 1; - else if(button == 2) // middle - drop_amount = MYMIN(m_selected_amount, 10); - else // left - drop_amount = m_selected_amount; - } - } - } - else if(updown == 1) - { - // Some mouse button has been released - - //infostream<<"Mouse button "< 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); - 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()); - if(leftover.count == stack_from.count) - { - // Swap the stacks - m_selected_amount -= stack_to.count; - } - else if(leftover.empty()) - { - // Item fits - m_selected_amount -= move_amount; - } - else - { - // Item only fits partially - move_amount -= leftover.count; - m_selected_amount -= move_amount; - } - - infostream<<"Handing IACTION_MOVE to manager"<count = move_amount; - a->from_inv = m_selected_item->inventoryloc; - a->from_list = m_selected_item->listname; - a->from_i = m_selected_item->i; - a->to_inv = s.inventoryloc; - a->to_list = s.listname; - a->to_i = s.i; - m_invmgr->inventoryAction(a); - } - else if(drop_amount > 0) - { - // Send IACTION_DROP - - assert(m_selected_item && m_selected_item->isValid()); - assert(inv_selected); - InventoryList *list_from = inv_selected->getList(m_selected_item->listname); - assert(list_from); - ItemStack stack_from = list_from->getItem(m_selected_item->i); - - // Check how many items can be dropped - drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count); - 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) - { - // Send IACTION_CRAFT - - assert(s.isValid()); - assert(inv_s); - - infostream<<"Handing IACTION_CRAFT to manager"<count = craft_amount; - a->craft_inv = s.inventoryloc; - m_invmgr->inventoryAction(a); - } - - // If m_selected_amount has been decreased to zero, deselect - if(m_selected_amount == 0) - { - delete m_selected_item; - m_selected_item = NULL; - m_selected_amount = 0; - m_selected_dragging = false; - } - } - if(event.EventType==EET_GUI_EVENT) - { - if(event.GUIEvent.EventType==gui::EGET_ELEMENT_FOCUS_LOST - && isVisible()) - { - if(!canTakeFocus(event.GUIEvent.Element)) - { - infostream<<"GUIInventoryMenu: Not allowing focus change." - <getID()) - { - case 256: // continue - setVisible(false); - break; - case 257: // exit - dev->closeDevice(); - break; - }*/ - } - } - - return Parent ? Parent->OnEvent(event) : false; -} - diff --git a/src/guiInventoryMenu.h b/src/guiInventoryMenu.h deleted file mode 100644 index 5613db35..00000000 --- a/src/guiInventoryMenu.h +++ /dev/null @@ -1,176 +0,0 @@ -/* -Minetest-c55 -Copyright (C) 2010 celeron55, Perttu Ahola - -This program is free software; you can redistribute it and/or modify -it under the terms of the GNU Lesser General Public License as published by -the Free Software Foundation; either version 2.1 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public License along -with this program; if not, write to the Free Software Foundation, Inc., -51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -*/ - - -#ifndef GUIINVENTORYMENU_HEADER -#define GUIINVENTORYMENU_HEADER - -#include "irrlichttypes_extrabloated.h" -#include "inventory.h" -#include "inventorymanager.h" -#include "modalMenu.h" - -class IGameDef; -class InventoryManager; - -class IFormSource -{ -public: - virtual ~IFormSource(){} - virtual std::string getForm() = 0; -}; - -void drawItemStack(video::IVideoDriver *driver, - gui::IGUIFont *font, - const ItemStack &item, - const core::rect &rect, - const core::rect *clip, - IGameDef *gamedef); - -class GUIInventoryMenu : public GUIModalMenu -{ - struct ItemSpec - { - ItemSpec() - { - i = -1; - } - ItemSpec(const InventoryLocation &a_inventoryloc, - const std::string &a_listname, - s32 a_i) - { - inventoryloc = a_inventoryloc; - listname = a_listname; - i = a_i; - } - bool isValid() const - { - return i != -1; - } - - InventoryLocation inventoryloc; - std::string listname; - s32 i; - }; - - struct ListDrawSpec - { - ListDrawSpec() - { - } - ListDrawSpec(const InventoryLocation &a_inventoryloc, - const std::string &a_listname, - v2s32 a_pos, v2s32 a_geom): - inventoryloc(a_inventoryloc), - listname(a_listname), - pos(a_pos), - geom(a_geom) - { - } - - InventoryLocation inventoryloc; - std::string listname; - v2s32 pos; - v2s32 geom; - }; - - struct ImageDrawSpec - { - ImageDrawSpec() - { - } - ImageDrawSpec(const std::string &a_name, - v2s32 a_pos, v2s32 a_geom): - name(a_name), - pos(a_pos), - geom(a_geom) - { - } - std::string name; - v2s32 pos; - v2s32 geom; - }; - -public: - GUIInventoryMenu(gui::IGUIEnvironment* env, - gui::IGUIElement* parent, s32 id, - IMenuManager *menumgr, - InventoryManager *invmgr, - IGameDef *gamedef - ); - ~GUIInventoryMenu(); - - void setFormSpec(const std::string &formspec_string, - InventoryLocation current_inventory_location) - { - m_formspec_string = formspec_string; - m_current_inventory_location = current_inventory_location; - regenerateGui(m_screensize_old); - } - - // form_src is deleted by this GUIInventoryMenu - void setFormSource(IFormSource *form_src) - { - m_form_src = form_src; - } - - void removeChildren(); - /* - Remove and re-add (or reposition) stuff - */ - void regenerateGui(v2u32 screensize); - - ItemSpec getItemAtPos(v2s32 p) const; - void drawList(const ListDrawSpec &s, int phase); - void drawSelectedItem(); - void drawMenu(); - void updateSelectedItem(); - - bool OnEvent(const SEvent& event); - -protected: - v2s32 getBasePos() const - { - return padding + AbsoluteRect.UpperLeftCorner; - } - - v2s32 padding; - v2s32 spacing; - v2s32 imgsize; - - InventoryManager *m_invmgr; - IGameDef *m_gamedef; - - std::string m_formspec_string; - InventoryLocation m_current_inventory_location; - IFormSource *m_form_src; - - core::array m_inventorylists; - core::array m_images; - - ItemSpec *m_selected_item; - u32 m_selected_amount; - bool m_selected_dragging; - - v2s32 m_pointer; - gui::IGUIStaticText *m_tooltip_element; -}; - -#endif - diff --git a/src/guiTextInputMenu.h b/src/guiTextInputMenu.h index 76417894..1a55525c 100644 --- a/src/guiTextInputMenu.h +++ b/src/guiTextInputMenu.h @@ -22,14 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes_extrabloated.h" #include "modalMenu.h" +#include "guiFormSpecMenu.h" #include -struct TextDest -{ - virtual void gotText(std::wstring text) = 0; - virtual ~TextDest() {}; -}; - class GUITextInputMenu : public GUIModalMenu { public: diff --git a/src/nodemetadata.h b/src/nodemetadata.h index 24779a1e..262b64d7 100644 --- a/src/nodemetadata.h +++ b/src/nodemetadata.h @@ -55,7 +55,7 @@ public: i = m_stringvars.find(name); if(i == m_stringvars.end()) return ""; - return i->second; + return resolveString(i->second); } void setString(const std::string &name, const std::string &var) { @@ -64,6 +64,13 @@ public: else m_stringvars[name] = var; } + // support variable names in values + std::string resolveString(const std::string &str) const + { + if(str.substr(0,2) == "${" && str[str.length()-1] == '}') + return resolveString(getString(str.substr(2,str.length()-3))); + return str; + } std::map getStrings() const { return m_stringvars;