Implement formspec

This commit is contained in:
darkrose 2012-07-16 02:19:38 +10:00 committed by Perttu Ahola
parent c259f7c8bd
commit 506203345b
8 changed files with 407 additions and 57 deletions

@ -1125,7 +1125,7 @@ minetest.register_node("default:sign_wall", {
on_construct = function(pos) on_construct = function(pos)
--local n = minetest.env:get_node(pos) --local n = minetest.env:get_node(pos)
local meta = minetest.env:get_meta(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", "\"\"") meta:set_string("infotext", "\"\"")
end, end,
on_receive_fields = function(pos, formname, fields, sender) on_receive_fields = function(pos, formname, fields, sender)

@ -251,7 +251,7 @@ set(minetest_SRCS
guiKeyChangeMenu.cpp guiKeyChangeMenu.cpp
guiMessageMenu.cpp guiMessageMenu.cpp
guiTextInputMenu.cpp guiTextInputMenu.cpp
guiInventoryMenu.cpp guiFormSpecMenu.cpp
guiPauseMenu.cpp guiPauseMenu.cpp
guiPasswordChange.cpp guiPasswordChange.cpp
guiDeathScreen.cpp guiDeathScreen.cpp

@ -63,7 +63,7 @@ static bool content_nodemeta_deserialize_legacy_body(
//meta->setString("infotext","\"${text}\""); //meta->setString("infotext","\"${text}\"");
meta->setString("infotext", meta->setString("infotext",
std::string("\"") + meta->getString("text") + "\""); std::string("\"") + meta->getString("text") + "\"");
meta->setString("formspec","hack:sign_text_input"); meta->setString("formspec","field[text;;${text}]");
return false; return false;
} }
else if(id == NODEMETA_CHEST) // ChestNodeMetadata else if(id == NODEMETA_CHEST) // ChestNodeMetadata
@ -77,7 +77,7 @@ static bool content_nodemeta_deserialize_legacy_body(
} }
assert(inv->getList("main") && !inv->getList("0")); 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_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]"); "list[current_player;main;0,5;8,4;]");
return false; return false;
@ -94,7 +94,7 @@ static bool content_nodemeta_deserialize_legacy_body(
} }
assert(inv->getList("main") && !inv->getList("0")); 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_name;main;0,0;8,4;]"
"list[current_player;main;0,5;8,4;]"); "list[current_player;main;0,5;8,4;]");
return false; return false;
@ -115,7 +115,7 @@ static bool content_nodemeta_deserialize_legacy_body(
is>>temp; is>>temp;
meta->setString("src_time", ftos((float)temp/10)); 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;fuel;2,3;1,1;]"
"list[current_name;src;2,1;1,1;]" "list[current_name;src;2,1;1,1;]"
"list[current_name;dst;5,1;2,2;]" "list[current_name;dst;5,1;2,2;]"

@ -28,7 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "server.h" #include "server.h"
#include "guiPauseMenu.h" #include "guiPauseMenu.h"
#include "guiPasswordChange.h" #include "guiPasswordChange.h"
#include "guiInventoryMenu.h" #include "guiFormSpecMenu.h"
#include "guiTextInputMenu.h" #include "guiTextInputMenu.h"
#include "guiDeathScreen.h" #include "guiDeathScreen.h"
#include "tool.h" #include "tool.h"
@ -77,6 +77,10 @@ struct TextDestChat : public TextDest
{ {
m_client->typeChatMessage(text); m_client->typeChatMessage(text);
} }
void gotText(std::map<std::string, std::string> fields)
{
m_client->typeChatMessage(narrow_to_wide(fields["text"]));
}
Client *m_client; Client *m_client;
}; };
@ -88,15 +92,20 @@ struct TextDestNodeMetadata : public TextDest
m_p = p; m_p = p;
m_client = client; m_client = client;
} }
// This is deprecated I guess? -celeron55
void gotText(std::wstring text) void gotText(std::wstring text)
{ {
std::string ntext = wide_to_narrow(text); std::string ntext = wide_to_narrow(text);
infostream<<"Changing text of a sign node: " infostream<<"Submitting 'text' field of node at ("<<m_p.X<<","
<<ntext<<std::endl; <<m_p.Y<<","<<m_p.Z<<"): "<<ntext<<std::endl;
std::map<std::string, std::string> fields; std::map<std::string, std::string> fields;
fields["text"] = ntext; fields["text"] = ntext;
m_client->sendNodemetaFields(m_p, "", fields); m_client->sendNodemetaFields(m_p, "", fields);
} }
void gotText(std::map<std::string, std::string> fields)
{
m_client->sendNodemetaFields(m_p, "", fields);
}
v3s16 m_p; v3s16 m_p;
Client *m_client; Client *m_client;
@ -139,6 +148,13 @@ public:
return ""; return "";
return meta->getString("formspec"); 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; ClientMap *m_map;
v3s16 m_p; v3s16 m_p;
@ -1479,8 +1495,8 @@ void the_game(
infostream<<"the_game: " infostream<<"the_game: "
<<"Launching inventory"<<std::endl; <<"Launching inventory"<<std::endl;
GUIInventoryMenu *menu = GUIFormSpecMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1, new GUIFormSpecMenu(guienv, guiroot, -1,
&g_menumgr, &g_menumgr,
&client, gamedef); &client, gamedef);
@ -1490,7 +1506,7 @@ void the_game(
PlayerInventoryFormSource *src = new PlayerInventoryFormSource(&client); PlayerInventoryFormSource *src = new PlayerInventoryFormSource(&client);
assert(src); assert(src);
menu->setFormSpec(src->getForm(), inventoryloc); menu->setFormSpec(src->getForm(), inventoryloc);
menu->setFormSource(new PlayerInventoryFormSource(&client)); menu->setFormSource(src);
menu->drop(); menu->drop();
} }
else if(input->wasKeyDown(EscapeKey)) else if(input->wasKeyDown(EscapeKey))
@ -2219,7 +2235,8 @@ void the_game(
{ {
infostream<<"Ground right-clicked"<<std::endl; infostream<<"Ground right-clicked"<<std::endl;
// sign special case, at least until formspec is properly implemented // Sign special case, at least until formspec is properly implemented.
// Deprecated?
if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input) if(meta && meta->getString("formspec") == "hack:sign_text_input" && !random_input)
{ {
infostream<<"Launching metadata text input"<<std::endl; infostream<<"Launching metadata text input"<<std::endl;
@ -2244,14 +2261,15 @@ void the_game(
/* Create menu */ /* Create menu */
GUIInventoryMenu *menu = GUIFormSpecMenu *menu =
new GUIInventoryMenu(guienv, guiroot, -1, new GUIFormSpecMenu(guienv, guiroot, -1,
&g_menumgr, &g_menumgr,
&client, gamedef); &client, gamedef);
menu->setFormSpec(meta->getString("formspec"), menu->setFormSpec(meta->getString("formspec"),
inventoryloc); inventoryloc);
menu->setFormSource(new NodeMetadataFormSource( menu->setFormSource(new NodeMetadataFormSource(
&client.getEnv().getClientMap(), nodepos)); &client.getEnv().getClientMap(), nodepos));
menu->setTextDest(new TextDestNodeMetadata(nodepos, &client));
menu->drop(); menu->drop();
} }
// Otherwise report right click to server // Otherwise report right click to server

@ -18,7 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
*/ */
#include "guiInventoryMenu.h" #include "guiFormSpecMenu.h"
#include "constants.h" #include "constants.h"
#include "gamedef.h" #include "gamedef.h"
#include "keycode.h" #include "keycode.h"
@ -33,6 +33,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "util/string.h" #include "util/string.h"
#include "util/numeric.h" #include "util/numeric.h"
#include "gettext.h"
void drawItemStack(video::IVideoDriver *driver, void drawItemStack(video::IVideoDriver *driver,
gui::IGUIFont *font, gui::IGUIFont *font,
const ItemStack &item, const ItemStack &item,
@ -120,10 +122,10 @@ void drawItemStack(video::IVideoDriver *driver,
} }
/* /*
GUIInventoryMenu GUIFormSpecMenu
*/ */
GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env, GUIFormSpecMenu::GUIFormSpecMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id, gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IMenuManager *menumgr,
InventoryManager *invmgr, InventoryManager *invmgr,
@ -133,6 +135,7 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
m_invmgr(invmgr), m_invmgr(invmgr),
m_gamedef(gamedef), m_gamedef(gamedef),
m_form_src(NULL), m_form_src(NULL),
m_text_dst(NULL),
m_selected_item(NULL), m_selected_item(NULL),
m_selected_amount(0), m_selected_amount(0),
m_selected_dragging(false), m_selected_dragging(false),
@ -140,7 +143,7 @@ GUIInventoryMenu::GUIInventoryMenu(gui::IGUIEnvironment* env,
{ {
} }
GUIInventoryMenu::~GUIInventoryMenu() GUIFormSpecMenu::~GUIFormSpecMenu()
{ {
removeChildren(); removeChildren();
@ -148,7 +151,7 @@ GUIInventoryMenu::~GUIInventoryMenu()
delete m_form_src; delete m_form_src;
} }
void GUIInventoryMenu::removeChildren() void GUIFormSpecMenu::removeChildren()
{ {
const core::list<gui::IGUIElement*> &children = getChildren(); const core::list<gui::IGUIElement*> &children = getChildren();
core::list<gui::IGUIElement*> children_copy; core::list<gui::IGUIElement*> children_copy;
@ -175,7 +178,7 @@ void GUIInventoryMenu::removeChildren()
} }
} }
void GUIInventoryMenu::regenerateGui(v2u32 screensize) void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{ {
// Remove children // Remove children
removeChildren(); removeChildren();
@ -183,24 +186,38 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
v2s32 size(100,100); v2s32 size(100,100);
s32 helptext_h = 15; s32 helptext_h = 15;
core::rect<s32> rect; core::rect<s32> rect;
// Base position of contents of form
v2s32 basepos = getBasePos(); 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 */ /* Convert m_init_draw_spec to m_inventorylists */
m_inventorylists.clear(); m_inventorylists.clear();
m_images.clear(); m_images.clear();
m_fields.clear();
Strfnd f(m_formspec_string); Strfnd f(m_formspec_string);
while(f.atend() == false) while(f.atend() == false)
{ {
std::string type = trim(f.next("[")); std::string type = trim(f.next("["));
if(type == "invsize") if(type == "invsize" || type == "size")
{ {
v2f invsize; v2f invsize;
invsize.X = stof(f.next(",")); invsize.X = stof(f.next(","));
invsize.Y = stof(f.next(";")); if(type == "size")
infostream<<"invsize ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl; {
f.next("]"); invsize.Y = stof(f.next("]"));
}
else{
invsize.Y = stof(f.next(";"));
errorstream<<"WARNING: invsize is deprecated, use size"<<std::endl;
f.next("]");
}
infostream<<"size ("<<invsize.X<<","<<invsize.Y<<")"<<std::endl;
padding = v2s32(screensize.Y/40, screensize.Y/40); padding = v2s32(screensize.Y/40, screensize.Y/40);
spacing = v2s32(screensize.Y/12, screensize.Y/13); spacing = v2s32(screensize.Y/12, screensize.Y/13);
@ -218,6 +235,7 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
DesiredRect = rect; DesiredRect = rect;
recalculateAbsolutePosition(false); recalculateAbsolutePosition(false);
basepos = getBasePos(); basepos = getBasePos();
bp_set = 2;
} }
else if(type == "list") else if(type == "list")
{ {
@ -239,6 +257,8 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
<<", geom=("<<geom.X<<","<<geom.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl; <<std::endl;
f.next("]"); f.next("]");
if(bp_set != 2)
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom)); m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom));
} }
else if(type == "image") else if(type == "image")
@ -254,8 +274,189 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
<<", pos=("<<pos.X<<","<<pos.Y<<")" <<", pos=("<<pos.X<<","<<pos.Y<<")"
<<", geom=("<<geom.X<<","<<geom.Y<<")" <<", geom=("<<geom.X<<","<<geom.Y<<")"
<<std::endl; <<std::endl;
if(bp_set != 2)
errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos, geom)); m_images.push_back(ImageDrawSpec(name, pos, geom));
} }
else if(type == "field")
{
std::string fname = f.next(";");
std::string flabel = f.next(";");
if(fname.find(",") == std::string::npos && flabel.find(",") == std::string::npos)
{
if(!bp_set)
{
rect = core::rect<s32>(
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"<<std::endl;
v2s32 pos = basepos;
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
rect = core::rect<s32>(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<s32>(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"<<std::endl;
}
std::string odefault = f.next("]");
std::string fdefault;
// fdefault may contain a variable reference, which
// needs to be resolved from the node metadata
if(m_form_src)
fdefault = m_form_src->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<s32>(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"<<std::endl;
FieldSpec spec = FieldSpec(
narrow_to_wide(""),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(""),
258+m_fields.size()
);
Environment->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<s32>(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"<<std::endl;
FieldSpec spec = FieldSpec(
narrow_to_wide(fname.c_str()),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(""),
258+m_fields.size()
);
spec.is_button = true;
Environment->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<s32>(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"<<std::endl;
FieldSpec spec = FieldSpec(
narrow_to_wide(fname.c_str()),
narrow_to_wide(flabel.c_str()),
narrow_to_wide(fimage.c_str()),
258+m_fields.size()
);
spec.is_button = true;
video::ITexture *texture = m_gamedef->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 else
{ {
// Ignore others // Ignore others
@ -265,16 +466,44 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
} }
} }
// Add children // If there's inventory, put the usage string at the bottom
if (m_inventorylists.size())
{ {
changeCtype("");
core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h); core::rect<s32> rect(0, 0, size.X-padding.X*2, helptext_h);
rect = rect + v2s32(size.X/2 - rect.getWidth()/2, rect = rect + v2s32(size.X/2 - rect.getWidth()/2,
size.Y-rect.getHeight()-5); size.Y-rect.getHeight()-5);
const wchar_t *text = const wchar_t *text = wgettext("Left click: Move all items, Right click: Move single item");
L"Left click: Move all items, Right click: Move single item";
Environment->addStaticText(text, rect, false, true, this, 256); 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<s32>(
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();
// Add tooltip changeCtype("");
{
v2s32 pos = basepos;
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
rect = core::rect<s32>(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 // Note: parent != this so that the tooltip isn't clipped by the menu rectangle
m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18)); m_tooltip_element = Environment->addStaticText(L"",core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true); m_tooltip_element->enableOverrideColor(true);
@ -287,7 +516,7 @@ void GUIInventoryMenu::regenerateGui(v2u32 screensize)
} }
} }
GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
{ {
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y); core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
@ -311,7 +540,7 @@ GUIInventoryMenu::ItemSpec GUIInventoryMenu::getItemAtPos(v2s32 p) const
return ItemSpec(InventoryLocation(), "", -1); return ItemSpec(InventoryLocation(), "", -1);
} }
void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase) void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
{ {
video::IVideoDriver* driver = Environment->getVideoDriver(); video::IVideoDriver* driver = Environment->getVideoDriver();
@ -323,7 +552,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
Inventory *inv = m_invmgr->getInventory(s.inventoryloc); Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if(!inv){ if(!inv){
infostream<<"GUIInventoryMenu::drawList(): WARNING: " infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
<<"The inventory location " <<"The inventory location "
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist" <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl; <<std::endl;
@ -331,7 +560,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
} }
InventoryList *ilist = inv->getList(s.listname); InventoryList *ilist = inv->getList(s.listname);
if(!ilist){ if(!ilist){
infostream<<"GUIInventoryMenu::drawList(): WARNING: " infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
<<"The inventory list \""<<s.listname<<"\" @ \"" <<"The inventory list \""<<s.listname<<"\" @ \""
<<s.inventoryloc.dump()<<"\" doesn't exist" <<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl; <<std::endl;
@ -404,7 +633,7 @@ void GUIInventoryMenu::drawList(const ListDrawSpec &s, int phase)
} }
} }
void GUIInventoryMenu::drawSelectedItem() void GUIFormSpecMenu::drawSelectedItem()
{ {
if(!m_selected_item) if(!m_selected_item)
return; return;
@ -429,7 +658,7 @@ void GUIInventoryMenu::drawSelectedItem()
drawItemStack(driver, font, stack, rect, NULL, m_gamedef); drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
} }
void GUIInventoryMenu::drawMenu() void GUIFormSpecMenu::drawMenu()
{ {
if(m_form_src){ if(m_form_src){
std::string newform = m_form_src->getForm(); std::string newform = m_form_src->getForm();
@ -491,7 +720,7 @@ void GUIInventoryMenu::drawMenu()
gui::IGUIElement::draw(); gui::IGUIElement::draw();
} }
void GUIInventoryMenu::updateSelectedItem() void GUIFormSpecMenu::updateSelectedItem()
{ {
// If the selected stack has become empty for some reason, deselect it. // If the selected stack has become empty for some reason, deselect it.
// If the selected stack has become smaller, adjust m_selected_amount. // If the selected stack has become smaller, adjust m_selected_amount.
@ -558,7 +787,38 @@ void GUIInventoryMenu::updateSelectedItem()
} }
} }
bool GUIInventoryMenu::OnEvent(const SEvent& event) void GUIFormSpecMenu::acceptInput()
{
if(m_text_dst)
{
std::map<std::string, std::string> fields;
gui::IGUIElement *e;
for(u32 i=0; i<m_fields.size(); i++)
{
const FieldSpec &s = m_fields[i];
if(s.send)
{
if(s.is_button)
{
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(s.flabel.c_str());
}
else
{
e = getElementFromId(s.fid);
if(e != NULL)
{
fields[wide_to_narrow(s.fname.c_str())] = wide_to_narrow(e->getText());
}
}
}
}
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) if(event.EventType==EET_KEY_INPUT_EVENT)
{ {
@ -569,6 +829,12 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
quitMenu(); quitMenu();
return true; return true;
} }
if(event.KeyInput.Key==KEY_RETURN && event.KeyInput.PressedDown)
{
acceptInput();
quitMenu();
return true;
}
} }
if(event.EventType==EET_MOUSE_INPUT_EVENT if(event.EventType==EET_MOUSE_INPUT_EVENT
&& event.MouseInput.Event == EMIE_MOUSE_MOVED) && event.MouseInput.Event == EMIE_MOUSE_MOVED)
@ -860,7 +1126,7 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
{ {
if(!canTakeFocus(event.GUIEvent.Element)) if(!canTakeFocus(event.GUIEvent.Element))
{ {
infostream<<"GUIInventoryMenu: Not allowing focus change." infostream<<"GUIFormSpecMenu: Not allowing focus change."
<<std::endl; <<std::endl;
// Returning true disables focus change // Returning true disables focus change
return true; return true;
@ -868,15 +1134,38 @@ bool GUIInventoryMenu::OnEvent(const SEvent& event)
} }
if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED) if(event.GUIEvent.EventType==gui::EGET_BUTTON_CLICKED)
{ {
/*switch(event.GUIEvent.Caller->getID()) switch(event.GUIEvent.Caller->getID())
{ {
case 256: // continue case 257:
setVisible(false); acceptInput();
break; quitMenu();
case 257: // exit // quitMenu deallocates menu
dev->closeDevice(); return true;
break; }
}*/ // find the element that was clicked
for(u32 i=0; i<m_fields.size(); i++)
{
FieldSpec &s = m_fields[i];
// if its a button, set the send field so
// lua knows which button was pressed
if (s.is_button && s.fid == event.GUIEvent.Caller->getID())
{
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;
}
} }
} }

@ -29,11 +29,21 @@ with this program; if not, write to the Free Software Foundation, Inc.,
class IGameDef; class IGameDef;
class InventoryManager; class InventoryManager;
struct TextDest
{
virtual ~TextDest() {};
// This is deprecated I guess? -celeron55
virtual void gotText(std::wstring text) = 0;
virtual void gotText(std::map<std::string, std::string> fields) = 0;
};
class IFormSource class IFormSource
{ {
public: public:
virtual ~IFormSource(){} virtual ~IFormSource(){}
virtual std::string getForm() = 0; 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, void drawItemStack(video::IVideoDriver *driver,
@ -43,7 +53,7 @@ void drawItemStack(video::IVideoDriver *driver,
const core::rect<s32> *clip, const core::rect<s32> *clip,
IGameDef *gamedef); IGameDef *gamedef);
class GUIInventoryMenu : public GUIModalMenu class GUIFormSpecMenu : public GUIModalMenu
{ {
struct ItemSpec struct ItemSpec
{ {
@ -106,15 +116,37 @@ class GUIInventoryMenu : public GUIModalMenu
v2s32 pos; v2s32 pos;
v2s32 geom; 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: public:
GUIInventoryMenu(gui::IGUIEnvironment* env, GUIFormSpecMenu(gui::IGUIEnvironment* env,
gui::IGUIElement* parent, s32 id, gui::IGUIElement* parent, s32 id,
IMenuManager *menumgr, IMenuManager *menumgr,
InventoryManager *invmgr, InventoryManager *invmgr,
IGameDef *gamedef IGameDef *gamedef
); );
~GUIInventoryMenu(); ~GUIFormSpecMenu();
void setFormSpec(const std::string &formspec_string, void setFormSpec(const std::string &formspec_string,
InventoryLocation current_inventory_location) InventoryLocation current_inventory_location)
@ -124,12 +156,18 @@ public:
regenerateGui(m_screensize_old); regenerateGui(m_screensize_old);
} }
// form_src is deleted by this GUIInventoryMenu // form_src is deleted by this GUIFormSpecMenu
void setFormSource(IFormSource *form_src) void setFormSource(IFormSource *form_src)
{ {
m_form_src = 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(); void removeChildren();
/* /*
Remove and re-add (or reposition) stuff Remove and re-add (or reposition) stuff
@ -142,6 +180,7 @@ public:
void drawMenu(); void drawMenu();
void updateSelectedItem(); void updateSelectedItem();
void acceptInput();
bool OnEvent(const SEvent& event); bool OnEvent(const SEvent& event);
protected: protected:
@ -160,9 +199,11 @@ protected:
std::string m_formspec_string; std::string m_formspec_string;
InventoryLocation m_current_inventory_location; InventoryLocation m_current_inventory_location;
IFormSource *m_form_src; IFormSource *m_form_src;
TextDest *m_text_dst;
core::array<ListDrawSpec> m_inventorylists; core::array<ListDrawSpec> m_inventorylists;
core::array<ImageDrawSpec> m_images; core::array<ImageDrawSpec> m_images;
core::array<FieldSpec> m_fields;
ItemSpec *m_selected_item; ItemSpec *m_selected_item;
u32 m_selected_amount; u32 m_selected_amount;

@ -22,14 +22,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "modalMenu.h" #include "modalMenu.h"
#include "guiFormSpecMenu.h"
#include <string> #include <string>
struct TextDest
{
virtual void gotText(std::wstring text) = 0;
virtual ~TextDest() {};
};
class GUITextInputMenu : public GUIModalMenu class GUITextInputMenu : public GUIModalMenu
{ {
public: public:

@ -55,7 +55,7 @@ public:
i = m_stringvars.find(name); i = m_stringvars.find(name);
if(i == m_stringvars.end()) if(i == m_stringvars.end())
return ""; return "";
return i->second; return resolveString(i->second);
} }
void setString(const std::string &name, const std::string &var) void setString(const std::string &name, const std::string &var)
{ {
@ -64,6 +64,13 @@ public:
else else
m_stringvars[name] = var; 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<std::string, std::string> getStrings() const std::map<std::string, std::string> getStrings() const
{ {
return m_stringvars; return m_stringvars;