#include "constants.h"
#include "gamedef.h"
#include "keycode.h"
-#include "strfnd.h"
+#include "util/strfnd.h"
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include <IGUIButton.h>
#include <IGUITabControl.h>
#include <IGUIComboBox.h>
#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 "scripting_server.h"
#include "porting.h"
-#include "main.h"
#include "settings.h"
+#include "client.h"
+#include "fontengine.h"
+#include "util/hex.h"
+#include "util/numeric.h"
+#include "util/string.h" // for parseColorString()
+#include "irrlicht_changes/static_text.h"
+#include "guiscalingfilter.h"
+
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+#include "intlGUIEditBox.h"
+#endif
#define MY_CHECKPOS(a,b) \
if (v_pos.size() != 2) { \
/*
GUIFormSpecMenu
*/
+static unsigned int font_line_height(gui::IGUIFont *font)
+{
+ return font->getDimension(L"Ay").Height + font->getKerningHeight();
+}
GUIFormSpecMenu::GUIFormSpecMenu(irr::IrrlichtDevice* dev,
+ JoystickController *joystick,
gui::IGUIElement* parent, s32 id, IMenuManager *menumgr,
- InventoryManager *invmgr, IGameDef *gamedef,
+ Client *client,
ISimpleTextureSource *tsrc, IFormSource* fsrc, TextDest* tdst,
- GUIFormSpecMenu** ext_ptr) :
+ bool remap_dbl_click) :
GUIModalMenu(dev->getGUIEnvironment(), parent, id, menumgr),
m_device(dev),
- m_invmgr(invmgr),
- m_gamedef(gamedef),
+ m_invmgr(client),
m_tsrc(tsrc),
+ m_client(client),
m_selected_item(NULL),
m_selected_amount(0),
m_selected_dragging(false),
m_tooltip_element(NULL),
+ m_hovered_time(0),
m_old_tooltip_id(-1),
+ m_rmouse_auto_place(false),
m_allowclose(true),
m_lock(false),
m_form_src(fsrc),
m_text_dst(tdst),
- m_ext_ptr(ext_ptr),
- m_font(dev->getGUIEnvironment()->getSkin()->getFont()),
- m_formspec_version(0)
+ m_formspec_version(0),
+ m_focused_element(""),
+ m_joystick(joystick),
+ current_field_enter_pending(""),
+ m_font(NULL),
+ m_remap_dbl_click(remap_dbl_click)
#ifdef __ANDROID__
- ,m_JavaDialogFieldName(L"")
+ , m_JavaDialogFieldName("")
#endif
{
current_keys_pending.key_down = false;
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()
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()
Environment->setFocus(*(children.begin()));
}
-GUITable* GUIFormSpecMenu::getTable(std::wstring tablename)
+GUITable* GUIFormSpecMenu::getTable(const std::string &tablename)
{
for (u32 i = 0; i < m_tables.size(); ++i) {
if (tablename == m_tables[i].first.fname)
return 0;
}
-std::vector<std::string> split(const std::string &s, char delim) {
- std::vector<std::string> tokens;
-
- std::string current = "";
- bool last_was_escape = false;
- for(unsigned int i=0; i < s.size(); i++) {
- if (last_was_escape) {
- current += '\\';
- current += s.c_str()[i];
- last_was_escape = false;
- }
- else {
- if (s.c_str()[i] == delim) {
- tokens.push_back(current);
- current = "";
- last_was_escape = false;
- }
- else if (s.c_str()[i] == '\\'){
- last_was_escape = true;
- }
- else {
- current += s.c_str()[i];
- last_was_escape = false;
- }
- }
+std::vector<std::string>* GUIFormSpecMenu::getDropDownValues(const std::string &name)
+{
+ for (u32 i = 0; i < m_dropdowns.size(); ++i) {
+ if (name == m_dropdowns[i].first.fname)
+ return &m_dropdowns[i].second;
}
- //push last element
- tokens.push_back(current);
-
- return tokens;
+ return NULL;
}
-void GUIFormSpecMenu::parseSize(parserData* data,std::string element)
+void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(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) {
}
}
- 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));
+ data->explicit_size = true;
+ return;
+ }
+ errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
+}
- padding = v2s32(data->screensize.Y/imgsize.Y, data->screensize.Y/imgsize.Y);
+void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ',');
- /* 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<s32>(
- 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
- );
+ if (parts.size() >= 2) {
+ if (parts[1].find(';') != std::string::npos)
+ parts[1] = parts[1].substr(0, parts[1].find(';'));
- DesiredRect = data->rect;
- recalculateAbsolutePosition(false);
- data->basepos = getBasePos();
- data->bp_set = 2;
+ container_stack.push(pos_offset);
+ pos_offset.X += MYMAX(0, stof(parts[0]));
+ pos_offset.Y += MYMAX(0, stof(parts[1]));
return;
}
- errorstream<< "Invalid size element (" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseList(parserData* data,std::string element)
+void GUIFormSpecMenu::parseContainerEnd(parserData* data)
{
- if (m_gamedef == 0) {
- errorstream<<"WARNING: invalid use of 'list' with m_gamedef==0"<<std::endl;
+ if (container_stack.empty()) {
+ errorstream<< "Invalid container end element, no matching container start element" << std::endl;
+ } else {
+ pos_offset = container_stack.top();
+ container_stack.pop();
+ }
+}
+
+void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
+{
+ if (m_client == 0) {
+ warningstream<<"invalid use of 'list' with m_client==0"<<std::endl;
return;
}
else
loc.deSerialize(location);
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
return;
}
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of list without a size[] element"<<std::endl;
+ if(!data->explicit_size)
+ warningstream<<"invalid use of list without a size[] element"<<std::endl;
m_inventorylists.push_back(ListDrawSpec(loc, listname, pos, geom, start_i));
return;
}
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseCheckbox(parserData* data,std::string element)
+void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element)
+{
+ if (m_client == 0) {
+ errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl;
+ return;
+ }
+
+ std::vector<std::string> 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, const std::string &element)
{
std::vector<std::string> 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<std::string> v_pos = split(parts[0],',');
MY_CHECKPOS("checkbox",0);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float) spacing.X;
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
if (selected == "true")
fselected = true;
- std::wstring wlabel = narrow_to_wide(label.c_str());
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
core::rect<s32> rect = core::rect<s32>(
pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
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()
errorstream<< "Invalid checkbox element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseScrollBar(parserData* data, std::string element)
+void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
if (parts.size() >= 5) {
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_dim = split(parts[1],',');
- std::string name = parts[2];
+ std::string name = parts[3];
std::string value = parts[4];
MY_CHECKPOS("scrollbar",0);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float) spacing.X;
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
core::rect<s32>(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()
e->setSmallStep(10);
e->setLargeStep(100);
- if (!m_lock) {
- core::rect<s32> 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<s32>(
- 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<s32>(
- 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<FieldSpec,gui::IGUIScrollBar*>(spec,e));
m_fields.push_back(spec);
return;
errorstream<< "Invalid scrollbar element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseImage(parserData* data,std::string element)
+void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
std::vector<std::string> v_geom = split(parts[1],',');
std::string name = unescape_string(parts[2]);
- MY_CHECKPOS("image",0);
- MY_CHECKGEOM("image",1);
+ MY_CHECKPOS("image", 0);
+ MY_CHECKGEOM("image", 1);
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float) spacing.X;
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
geom.X = stof(v_geom[0]) * (float)imgsize.X;
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+ if (!data->explicit_size)
+ warningstream<<"invalid use of image without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos, geom));
return;
- }
-
- if (parts.size() == 2) {
+ } else if (parts.size() == 2) {
std::vector<std::string> v_pos = split(parts[0],',');
std::string name = unescape_string(parts[1]);
- MY_CHECKPOS("image",0);
+ MY_CHECKPOS("image", 0);
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float) spacing.X;
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of image without a size[] element"<<std::endl;
+ if (!data->explicit_size)
+ warningstream<<"invalid use of image without a size[] element"<<std::endl;
m_images.push_back(ImageDrawSpec(name, pos));
return;
}
errorstream<< "Invalid image element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseItemImage(parserData* data,std::string element)
+void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("itemimage",0);
MY_CHECKGEOM("itemimage",1);
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float) spacing.X;
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
geom.X = stof(v_geom[0]) * (float)imgsize.X;
geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of item_image without a size[] element"<<std::endl;
- m_itemimages.push_back(ImageDrawSpec(name, pos, geom));
+ if(!data->explicit_size)
+ warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
+ m_itemimages.push_back(ImageDrawSpec("", name, pos, geom));
return;
}
errorstream<< "Invalid ItemImage element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseButton(parserData* data,std::string element,
- std::string type)
+void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
+ const std::string &type)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("button",0);
MY_CHECKGEOM("button",1);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
core::rect<s32>(pos.X, pos.Y - m_btn_height,
pos.X + geom.X, pos.Y + m_btn_height);
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of button without a size[] element"<<std::endl;
-
- label = unescape_string(label);
+ if(!data->explicit_size)
+ warningstream<<"invalid use of button without a size[] element"<<std::endl;
- std::wstring wlabel = narrow_to_wide(label.c_str());
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
- narrow_to_wide(name.c_str()),
+ name,
wlabel,
L"",
258+m_fields.size()
errorstream<< "Invalid button element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseBackground(parserData* data,std::string element)
+void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("background",0);
MY_CHECKGEOM("background",1);
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
- pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X-(float)imgsize.X)/2;
- pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y-(float)imgsize.Y)/2;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+ pos.X += stof(v_pos[0]) * (float)spacing.X - ((float)spacing.X - (float)imgsize.X)/2;
+ pos.Y += stof(v_pos[1]) * (float)spacing.Y - ((float)spacing.Y - (float)imgsize.Y)/2;
v2s32 geom;
geom.X = stof(v_geom[0]) * (float)spacing.X;
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
- if (parts.size() == 4) {
- m_clipbackground = is_yes(parts[3]);
- if (m_clipbackground) {
- pos.X = stoi(v_pos[0]); //acts as offset
- pos.Y = stoi(v_pos[1]); //acts as offset
- }
+ if (!data->explicit_size)
+ warningstream<<"invalid use of background without a size[] element"<<std::endl;
+
+ bool clip = false;
+ if (parts.size() == 4 && is_yes(parts[3])) {
+ pos.X = stoi(v_pos[0]); //acts as offset
+ pos.Y = stoi(v_pos[1]); //acts as offset
+ clip = true;
}
+ m_backgrounds.push_back(ImageDrawSpec(name, pos, geom, clip));
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of background without a size[] element"<<std::endl;
- m_backgrounds.push_back(ImageDrawSpec(name, pos, geom));
return;
}
errorstream<< "Invalid background element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTableOptions(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTableOptions(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
}
}
-void GUIFormSpecMenu::parseTableColumns(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTableColumns(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
}
}
-void GUIFormSpecMenu::parseTable(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("table",0);
MY_CHECKGEOM("table",1);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
geom.X = stof(v_geom[0]) * (float)spacing.X;
geom.Y = stof(v_geom[1]) * (float)spacing.Y;
-
core::rect<s32> rect = core::rect<s32>(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()
spec.ftype = f_Table;
for (unsigned int i = 0; i < items.size(); ++i) {
- items[i] = unescape_string(items[i]);
+ items[i] = unescape_enriched(unescape_string(items[i]));
}
//now really show table
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 != "") &&
errorstream<< "Invalid table element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTextList(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("textlist",0);
MY_CHECKGEOM("textlist",1);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
core::rect<s32> rect = core::rect<s32>(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()
spec.ftype = f_Table;
for (unsigned int i = 0; i < items.size(); ++i) {
- items[i] = unescape_string(items[i]);
+ items[i] = unescape_enriched(unescape_string(items[i]));
}
//now really show list
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 != "") &&
}
-void GUIFormSpecMenu::parseDropDown(parserData* data,std::string element)
+void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("dropdown",0);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
core::rect<s32> rect = core::rect<s32>(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()
}
for (unsigned int i=0; i < items.size(); i++) {
- e->addItem(narrow_to_wide(items[i]).c_str());
+ e->addItem(unescape_enriched(unescape_string(
+ utf8_to_wide(items[i]))).c_str());
}
if (str_initial_selection != "")
e->setSelected(stoi(str_initial_selection.c_str())-1);
m_fields.push_back(spec);
+
+ m_dropdowns.push_back(std::pair<FieldSpec,
+ std::vector<std::string> >(spec, std::vector<std::string>()));
+ std::vector<std::string> &values = m_dropdowns.back().second;
+ for (unsigned int i = 0; i < items.size(); i++) {
+ values.push_back(unescape_string(items[i]));
+ }
+
return;
}
errorstream << "Invalid dropdown element(" << parts.size() << "): '"
<< element << "'" << std::endl;
}
-void GUIFormSpecMenu::parsePwdField(parserData* data,std::string element)
+void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
+ if (parts.size() == 2 ||
+ (parts.size() > 2 && m_formspec_version > FORMSPEC_API_VERSION)) {
+ field_close_on_enter[parts[0]] = is_yes(parts[1]);
+ }
+}
- if ((parts.size() == 4) ||
- ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
+void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element,';');
+
+ if ((parts.size() == 4) || (parts.size() == 5) ||
+ ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
std::vector<std::string> v_geom = split(parts[1],',');
MY_CHECKPOS("pwdfield",0);
MY_CHECKGEOM("pwdfield",1);
- v2s32 pos;
+ v2s32 pos = pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- label = unescape_string(label);
-
- std::wstring wlabel = narrow_to_wide(label.c_str());
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
- narrow_to_wide(name.c_str()),
+ name,
wlabel,
L"",
258+m_fields.size()
if (label.length() >= 1)
{
- rect.UpperLeftCorner.Y -= m_btn_height;
- rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
- Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+ int font_height = g_fontengine->getTextHeight();
+ rect.UpperLeftCorner.Y -= font_height;
+ rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
+ addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
}
e->setPasswordBox(true,L'*');
evt.KeyInput.Shift = 0;
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
+
+ if (parts.size() >= 5) {
+ // TODO: remove after 2016-11-03
+ warningstream << "pwdfield: use field_close_on_enter[name, enabled]" <<
+ " instead of the 5th param" << std::endl;
+ field_close_on_enter[name] = is_yes(parts[4]);
+ }
+
m_fields.push_back(spec);
return;
}
core::rect<s32> rect;
- if(!data->bp_set)
- {
- rect = core::rect<s32>(
- 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)
- errorstream<<"WARNING: invalid use of unpositioned \"field\" in inventory"<<std::endl;
+ if(data->explicit_size)
+ warningstream<<"invalid use of unpositioned \"field\" in inventory"<<std::endl;
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
if(m_form_src)
default_val = m_form_src->resolveText(default_val);
- default_val = unescape_string(default_val);
- label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label.c_str());
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
- narrow_to_wide(name.c_str()),
+ name,
wlabel,
- narrow_to_wide(default_val.c_str()),
+ utf8_to_wide(unescape_string(default_val)),
258+m_fields.size()
);
if (name == "")
{
// 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);
+ addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
}
else
{
spec.send = true;
- gui::IGUIEditBox *e =
- Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
-
+ gui::IGUIElement *e;
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+ if (g_settings->getBool("freetype")) {
+ e = (gui::IGUIElement *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
+ true, Environment, this, spec.fid, rect);
+ e->drop();
+ } else {
+#else
+ {
+#endif
+ e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+ }
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
if (label.length() >= 1)
{
- rect.UpperLeftCorner.Y -= m_btn_height;
- rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
- Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+ int font_height = g_fontengine->getTextHeight();
+ rect.UpperLeftCorner.Y -= font_height;
+ rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
+ addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
}
}
+ if (parts.size() >= 4) {
+ // TODO: remove after 2016-11-03
+ warningstream << "field/simple: use field_close_on_enter[name, enabled]" <<
+ " instead of the 4th param" << std::endl;
+ field_close_on_enter[name] = is_yes(parts[3]);
+ }
+
m_fields.push_back(spec);
}
-void GUIFormSpecMenu::parseTextArea(parserData* data,
- std::vector<std::string>& parts,std::string type)
+void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>& parts,
+ const std::string &type)
{
std::vector<std::string> v_pos = split(parts[0],',');
MY_CHECKPOS(type,0);
MY_CHECKGEOM(type,1);
- v2s32 pos;
- pos.X = stof(v_pos[0]) * (float) spacing.X;
- pos.Y = stof(v_pos[1]) * (float) spacing.Y;
+ v2s32 pos = pos_offset * spacing;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
v2s32 geom;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
+ if(!data->explicit_size)
+ warningstream<<"invalid use of positioned "<<type<<" without a size[] element"<<std::endl;
if(m_form_src)
default_val = m_form_src->resolveText(default_val);
- default_val = unescape_string(default_val);
- label = unescape_string(label);
-
- std::wstring wlabel = narrow_to_wide(label.c_str());
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
- narrow_to_wide(name.c_str()),
+ name,
wlabel,
- narrow_to_wide(default_val.c_str()),
+ utf8_to_wide(unescape_string(default_val)),
258+m_fields.size()
);
if (name == "")
{
// 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);
+ addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, spec.fid);
}
else
{
spec.send = true;
- gui::IGUIEditBox *e =
- Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+
+ gui::IGUIEditBox *e;
+#if USE_FREETYPE && IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 9
+ if (g_settings->getBool("freetype")) {
+ e = (gui::IGUIEditBox *) new gui::intlGUIEditBox(spec.fdefault.c_str(),
+ true, Environment, this, spec.fid, rect);
+ e->drop();
+ } else {
+#else
+ {
+#endif
+ e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this, spec.fid);
+ }
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
if (label.length() >= 1)
{
- rect.UpperLeftCorner.Y -= m_btn_height;
- rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + m_btn_height;
- Environment->addStaticText(spec.flabel.c_str(), rect, false, true, this, 0);
+ int font_height = g_fontengine->getTextHeight();
+ rect.UpperLeftCorner.Y -= font_height;
+ rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
+ addStaticText(Environment, spec.flabel.c_str(), rect, false, true, this, 0);
}
}
+
+ if (parts.size() >= 6) {
+ // TODO: remove after 2016-11-03
+ warningstream << "field/textarea: use field_close_on_enter[name, enabled]" <<
+ " instead of the 6th param" << std::endl;
+ field_close_on_enter[name] = is_yes(parts[5]);
+ }
+
m_fields.push_back(spec);
}
-void GUIFormSpecMenu::parseField(parserData* data,std::string element,
- std::string type)
+void GUIFormSpecMenu::parseField(parserData* data, const std::string &element,
+ const std::string &type)
{
std::vector<std::string> parts = split(element,';');
return;
}
- if ((parts.size() == 5) ||
- ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
+ if ((parts.size() == 5) || (parts.size() == 6) ||
+ ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
parseTextArea(data,parts,type);
return;
errorstream<< "Invalid field element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseLabel(parserData* data,std::string element)
+void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("label",0);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
- pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
-
- text = unescape_string(text);
-
- std::wstring wlabel = narrow_to_wide(text.c_str());
-
- core::rect<s32> rect = core::rect<s32>(
- pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
+ pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
+
+ if(!data->explicit_size)
+ warningstream<<"invalid use of label without a size[] element"<<std::endl;
+
+ std::vector<std::string> 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(unescape_string(lines[i]));
+ core::rect<s32> rect = core::rect<s32>(
+ 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 =
+ addStaticText(Environment, 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;
}
-void GUIFormSpecMenu::parseVertLabel(parserData* data,std::string element)
+void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
- std::wstring text = narrow_to_wide(unescape_string(parts[1]));
+ std::wstring text = unescape_enriched(
+ unescape_string(utf8_to_wide(parts[1])));
MY_CHECKPOS("vertlabel",1);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
core::rect<s32> rect = core::rect<s32>(
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)
- errorstream<<"WARNING: invalid use of label without a size[] element"<<std::endl;
+ if(!data->explicit_size)
+ warningstream<<"invalid use of label without a size[] element"<<std::endl;
std::wstring label = L"";
}
FieldSpec spec(
- L"",
+ "",
label,
L"",
258+m_fields.size()
);
gui::IGUIStaticText *t =
- Environment->addStaticText(spec.flabel.c_str(), rect, false, false, this, spec.fid);
+ addStaticText(Environment, spec.flabel.c_str(), rect, false, false, this, spec.fid);
t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
m_fields.push_back(spec);
return;
errorstream<< "Invalid vertlabel element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseImageButton(parserData* data,std::string element,
- std::string type)
+void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &element,
+ const std::string &type)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("imagebutton",0);
MY_CHECKGEOM("imagebutton",1);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
v2s32 geom;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of image_button without a size[] element"<<std::endl;
+ if(!data->explicit_size)
+ warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
image_name = unescape_string(image_name);
pressed_image_name = unescape_string(pressed_image_name);
- label = unescape_string(label);
- std::wstring wlabel = narrow_to_wide(label.c_str());
+ std::wstring wlabel = utf8_to_wide(unescape_string(label));
FieldSpec spec(
- narrow_to_wide(name.c_str()),
+ name,
wlabel,
- narrow_to_wide(image_name.c_str()),
+ utf8_to_wide(image_name),
258+m_fields.size()
);
spec.ftype = f_Button;
}
e->setUseAlphaChannel(true);
- e->setImage(texture);
- e->setPressedImage(pressed_texture);
+ e->setImage(guiScalingImageButton(
+ Environment->getVideoDriver(), texture, geom.X, geom.Y));
+ e->setPressedImage(guiScalingImageButton(
+ Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
e->setScaleImage(true);
e->setNotClipped(noclip);
e->setDrawBorder(drawborder);
errorstream<< "Invalid imagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTabHeader(parserData* data,std::string element)
+void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
}
FieldSpec spec(
- narrow_to_wide(name.c_str()),
+ name,
L"",
L"",
258+m_fields.size()
spec.ftype = f_TabHeader;
- v2s32 pos(0,0);
+ v2s32 pos = pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y - m_btn_height * 2;
v2s32 geom;
- geom.X = data->screensize.Y;
+ geom.X = DesiredRect.getWidth();
geom.Y = m_btn_height*2;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
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(unescape_enriched(unescape_string(
+ utf8_to_wide(buttons[i]))).c_str(), -1);
}
if ((tab_index >= 0) &&
<< element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseItemImageButton(parserData* data,std::string element)
+void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element)
{
- if (m_gamedef == 0) {
- errorstream <<
- "WARNING: invalid use of item_image_button with m_gamedef==0"
- << std::endl;
+ if (m_client == 0) {
+ warningstream << "invalid use of item_image_button with m_client==0"
+ << std::endl;
return;
}
std::string name = parts[3];
std::string label = parts[4];
+ label = unescape_string(label);
+ item_name = unescape_string(item_name);
+
MY_CHECKPOS("itemimagebutton",0);
MY_CHECKGEOM("itemimagebutton",1);
- v2s32 pos = padding;
+ v2s32 pos = padding + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float)spacing.X;
pos.Y += stof(v_pos[1]) * (float)spacing.Y;
v2s32 geom;
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
- if(data->bp_set != 2)
- errorstream<<"WARNING: invalid use of item_image_button without a size[] element"<<std::endl;
+ if(!data->explicit_size)
+ warningstream<<"invalid use of item_image_button without a size[] element"<<std::endl;
- IItemDefManager *idef = m_gamedef->idef();
+ IItemDefManager *idef = m_client->idef();
ItemStack item;
item.deSerialize(item_name, idef);
- video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
- m_tooltips[narrow_to_wide(name.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());
+ gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L"");
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
- e->setUseAlphaChannel(true);
- e->setImage(texture);
- e->setPressedImage(texture);
- e->setScaleImage(true);
spec.ftype = f_Button;
rect+=data->basepos-padding;
spec.rect=rect;
m_fields.push_back(spec);
+ pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+ pos.X += stof(v_pos[0]) * (float) spacing.X;
+ pos.Y += stof(v_pos[1]) * (float) spacing.Y;
+ m_itemimages.push_back(ImageDrawSpec("", item_name, e, pos, geom));
+ m_static_texts.push_back(StaticTextSpec(utf8_to_wide(label), rect, e));
return;
}
errorstream<< "Invalid ItemImagebutton element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseBox(parserData* data,std::string element)
+void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
MY_CHECKPOS("box",0);
MY_CHECKGEOM("box",1);
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner;
+ v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
pos.X += stof(v_pos[0]) * (float) spacing.X;
pos.Y += stof(v_pos[1]) * (float) spacing.Y;
video::SColor tmp_color;
- if (parseColor(parts[2], tmp_color, false)) {
+ if (parseColorString(parts[2], tmp_color, false)) {
BoxDrawSpec spec(pos, geom, tmp_color);
m_boxes.push_back(spec);
errorstream<< "Invalid Box element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseBackgroundColor(parserData* data,std::string element)
+void GUIFormSpecMenu::parseBackgroundColor(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
if (((parts.size() == 1) || (parts.size() == 2)) ||
((parts.size() > 2) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
- parseColor(parts[0],m_bgcolor,false);
+ parseColorString(parts[0],m_bgcolor,false);
if (parts.size() == 2) {
std::string fullscreen = parts[1];
errorstream<< "Invalid bgcolor element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseListColors(parserData* data,std::string element)
+void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,';');
if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
{
- parseColor(parts[0], m_slotbg_n, false);
- parseColor(parts[1], m_slotbg_h, false);
+ parseColorString(parts[0], m_slotbg_n, false);
+ parseColorString(parts[1], m_slotbg_h, false);
if (parts.size() >= 3) {
- if (parseColor(parts[2], m_slotbordercolor, false)) {
+ if (parseColorString(parts[2], m_slotbordercolor, false)) {
m_slotborder = true;
}
}
if (parts.size() == 5) {
video::SColor tmp_color;
- if (parseColor(parts[3], tmp_color, false))
+ if (parseColorString(parts[3], tmp_color, false))
m_default_tooltip_bgcolor = tmp_color;
- if (parseColor(parts[4], tmp_color, false))
+ if (parseColorString(parts[4], tmp_color, false))
m_default_tooltip_color = tmp_color;
}
return;
errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-void GUIFormSpecMenu::parseTooltip(parserData* data, std::string element)
+void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
{
std::vector<std::string> 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 ( parseColor(parts[2], tmp_color1, false) && parseColor(parts[3], tmp_color2, false) ) {
- m_tooltips[narrow_to_wide(name.c_str())] = TooltipSpec (parts[1], tmp_color1, tmp_color2);
+ if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
+ m_tooltips[name] = TooltipSpec(unescape_string(parts[1]),
+ tmp_color1, tmp_color2);
return;
}
}
errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl;
}
-bool GUIFormSpecMenu::parseVersionDirect(std::string data)
+bool GUIFormSpecMenu::parseVersionDirect(const std::string &data)
{
//some prechecks
if (data == "")
return false;
}
-void GUIFormSpecMenu::parseElement(parserData* data, std::string element)
+bool GUIFormSpecMenu::parseSizeDirect(parserData* data, const std::string &element)
+{
+ if (element == "")
+ return false;
+
+ std::vector<std::string> 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;
+}
+
+bool GUIFormSpecMenu::parsePositionDirect(parserData *data, const std::string &element)
+{
+ if (element.empty())
+ return false;
+
+ std::vector<std::string> parts = split(element, '[');
+
+ if (parts.size() != 2)
+ return false;
+
+ std::string type = trim(parts[0]);
+ std::string description = trim(parts[1]);
+
+ if (type != "position")
+ return false;
+
+ parsePosition(data, description);
+
+ return true;
+}
+
+void GUIFormSpecMenu::parsePosition(parserData *data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ',');
+
+ if (parts.size() == 2) {
+ data->offset.X = stof(parts[0]);
+ data->offset.Y = stof(parts[1]);
+ return;
+ }
+
+ errorstream << "Invalid position element (" << parts.size() << "): '" << element << "'" << std::endl;
+}
+
+bool GUIFormSpecMenu::parseAnchorDirect(parserData *data, const std::string &element)
+{
+ if (element.empty())
+ return false;
+
+ std::vector<std::string> parts = split(element, '[');
+
+ if (parts.size() != 2)
+ return false;
+
+ std::string type = trim(parts[0]);
+ std::string description = trim(parts[1]);
+
+ if (type != "anchor")
+ return false;
+
+ parseAnchor(data, description);
+
+ return true;
+}
+
+void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ',');
+
+ if (parts.size() == 2) {
+ data->anchor.X = stof(parts[0]);
+ data->anchor.Y = stof(parts[1]);
+ return;
+ }
+
+ errorstream << "Invalid anchor element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+}
+
+void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
if (element == "")
std::string type = trim(parts[0]);
std::string description = trim(parts[1]);
- if (type == "size") {
- parseSize(data,description);
+ if (type == "container") {
+ parseContainer(data, description);
return;
}
- if (type == "invsize") {
- log_deprecated("Deprecated formspec element \"invsize\" is used");
- parseSize(data,description);
+ if (type == "container_end") {
+ parseContainerEnd(data);
return;
}
if (type == "list") {
- parseList(data,description);
+ parseList(data, description);
+ return;
+ }
+
+ if (type == "listring") {
+ parseListRing(data, description);
return;
}
if (type == "checkbox") {
- parseCheckbox(data,description);
+ parseCheckbox(data, description);
return;
}
if (type == "image") {
- parseImage(data,description);
+ parseImage(data, description);
return;
}
if (type == "item_image") {
- parseItemImage(data,description);
+ parseItemImage(data, description);
return;
}
- if ((type == "button") || (type == "button_exit")) {
- parseButton(data,description,type);
+ if (type == "button" || type == "button_exit") {
+ parseButton(data, description, type);
return;
}
return;
}
+ if (type == "field_close_on_enter") {
+ parseFieldCloseOnEnter(data, description);
+ return;
+ }
+
if (type == "pwdfield") {
parsePwdField(data,description);
return;
<<std::endl;
}
-
-
void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
{
/* useless to regenerate without a screensize */
//preserve tables
for (u32 i = 0; i < m_tables.size(); ++i) {
- std::wstring tablename = m_tables[i].first.fname;
+ std::string tablename = m_tables[i].first.fname;
GUITable *table = m_tables[i].second;
mydata.table_dyndata[tablename] = table->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) {
mydata.size= v2s32(100,100);
mydata.screensize = screensize;
+ mydata.offset = v2f32(0.5f, 0.5f);
+ mydata.anchor = v2f32(0.5f, 0.5f);
// Base position of contents of form
mydata.basepos = getBasePos();
- // 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();
m_fields.clear();
m_boxes.clear();
m_tooltips.clear();
+ m_inventory_rings.clear();
+ m_static_texts.clear();
// Set default values (fits old formspec values)
m_bgcolor = video::SColor(140,0,0,0);
m_slotbordercolor = video::SColor(200,0,0,0);
m_slotborder = false;
- m_clipbackground = false;
// Add tooltip
{
assert(m_tooltip_element == NULL);
// 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 = addStaticText(Environment, L"",core::rect<s32>(0,0,110,18));
m_tooltip_element->enableOverrideColor(true);
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setDrawBackground(true);
}
}
+ /* 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;
+ }
+ }
+
+ /* "position" element is always after "size" element if it used */
+ for (; i< elements.size(); i++) {
+ if (!parsePositionDirect(&mydata, elements[i])) {
+ break;
+ }
+ }
+
+ /* "anchor" element is always after "position" (or "size" element) if it used */
+ for (; i< elements.size(); i++) {
+ if (!parseAnchorDirect(&mydata, elements[i])) {
+ break;
+ }
+ }
+
+
+ if (mydata.explicit_size) {
+ // compute scaling for specified form size
+ if (m_lock) {
+ v2u32 current_screensize = m_device->getVideoDriver()->getScreenSize();
+ v2u32 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<s32>(
+ (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * (f32)mydata.size.X) + offset.X,
+ (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * (f32)mydata.size.Y) + offset.Y,
+ (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * (f32)mydata.size.X) + offset.X,
+ (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * (f32)mydata.size.Y) + offset.Y
+ );
+ } else {
+ // Non-size[] form must consist only of text fields and
+ // 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<s32>(
+ (s32)((f32)mydata.screensize.X * mydata.offset.X) - (s32)(mydata.anchor.X * 580.0),
+ (s32)((f32)mydata.screensize.Y * mydata.offset.Y) - (s32)(mydata.anchor.Y * 300.0),
+ (s32)((f32)mydata.screensize.X * mydata.offset.X) + (s32)((1.0 - mydata.anchor.X) * 580.0),
+ (s32)((f32)mydata.screensize.Y * mydata.offset.Y) + (s32)((1.0 - mydata.anchor.Y) * 300.0)
+ );
+ }
+ recalculateAbsolutePosition(false);
+ 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);
+
+ pos_offset = v2s32();
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 (!container_stack.empty()) {
+ errorstream << "Invalid formspec string: container was never closed!"
+ << std::endl;
+ }
+
+ // If there are fields without explicit size[], add a "Proceed"
+ // button and adjust size to fit all the fields.
+ if (m_fields.size() && !mydata.explicit_size) {
mydata.rect = core::rect<s32>(
mydata.screensize.X/2 - 580/2,
mydata.screensize.Y/2 - 300/2,
mydata.rect =
core::rect<s32>(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;
}
|| !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;
}
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) {
}
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
+ iter != m_fields.end(); ++iter) {
if (iter->fname != fieldname) {
continue;
std::string text = porting::getInputDialogValue();
((gui::IGUIEditBox*) tochange)->
- setText(narrow_to_wide(text).c_str());
+ setText(utf8_to_wide(text).c_str());
}
return false;
}
return ItemSpec(InventoryLocation(), "", -1);
}
-void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase)
+void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
+ bool &item_hovered)
{
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: "
+ warningstream<<"GUIFormSpecMenu::drawList(): "
<<"The inventory location "
<<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
}
InventoryList *ilist = inv->getList(s.listname);
if(!ilist){
- infostream<<"GUIFormSpecMenu::drawList(): WARNING: "
+ warningstream<<"GUIFormSpecMenu::drawList(): "
<<"The inventory list \""<<s.listname<<"\" @ \""
<<s.inventoryloc.dump()<<"\" doesn't exist"
<<std::endl;
&& m_selected_item->listname == s.listname
&& m_selected_item->i == item_i;
bool hovering = rect.isPointInside(m_pointer);
+ ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
+ (hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
- if(phase == 0)
- {
- if(hovering)
+ if (phase == 0) {
+ if (hovering) {
+ item_hovered = true;
driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
- else
+ } else {
driver->draw2DRectangle(m_slotbg_n, rect, &AbsoluteClippingRect);
+ }
}
//Draw inv slot borders
}
if(!item.empty())
{
- drawItemStack(driver, font, item,
- rect, &AbsoluteClippingRect, m_gamedef);
+ drawItemStack(driver, m_font, item,
+ rect, &AbsoluteClippingRect, m_client,
+ rotation_kind);
}
// Draw tooltip
- std::string tooltip_text = "";
- if(hovering && !m_selected_item)
- tooltip_text = item.getDefinition(m_gamedef->idef()).description;
- if(tooltip_text != "")
- {
+ std::wstring tooltip_text = L"";
+ if (hovering && !m_selected_item) {
+ const std::string &desc = item.metadata.getString("description");
+ if (desc.empty())
+ tooltip_text =
+ utf8_to_wide(item.getDefinition(m_client->idef()).description);
+ else
+ tooltip_text = utf8_to_wide(desc);
+ // Show itemstring as fallback for easier debugging
+ if (!item.name.empty() && tooltip_text.empty())
+ tooltip_text = utf8_to_wide(item.name);
+ }
+ if (tooltip_text != L"") {
+ std::vector<std::wstring> tt_rows = str_split(tooltip_text, L'\n');
m_tooltip_element->setBackgroundColor(m_default_tooltip_bgcolor);
m_tooltip_element->setOverrideColor(m_default_tooltip_color);
m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element);
- m_tooltip_element->setText(narrow_to_wide(tooltip_text).c_str());
- s32 tooltip_x = m_pointer.X + m_btn_height;
- s32 tooltip_y = m_pointer.Y + m_btn_height;
+ setStaticText(m_tooltip_element, tooltip_text.c_str());
s32 tooltip_width = m_tooltip_element->getTextWidth() + m_btn_height;
+#if (IRRLICHT_VERSION_MAJOR <= 1 && IRRLICHT_VERSION_MINOR <= 8 && IRRLICHT_VERSION_REVISION < 2) || USE_FREETYPE == 1
+ s32 tooltip_height = m_tooltip_element->getTextHeight() * tt_rows.size() + 5;
+#else
s32 tooltip_height = m_tooltip_element->getTextHeight() + 5;
+#endif
+ 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<s32>(
core::position2d<s32>(tooltip_x, tooltip_y),
core::dimension2d<s32>(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();
+ if (!m_selected_item) {
+ drawItemStack(driver, m_font, ItemStack(),
+ core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
+ NULL, m_client, IT_ROT_DRAGGED);
+ return;
+ }
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<s32> imgrect(0,0,imgsize.X,imgsize.Y);
core::rect<s32> rect = imgrect + (m_pointer - imgrect.getCenter());
- drawItemStack(driver, font, stack, rect, NULL, m_gamedef);
+ rect.constrainTo(driver->getViewPort());
+ drawItemStack(driver, m_font, stack, rect, NULL, m_client, IT_ROT_DRAGGED);
}
void GUIFormSpecMenu::drawMenu()
}
}
+ 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();
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
- if (m_clipbackground) {
+ if (spec.clip) {
core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
rect = core::rect<s32>(AbsoluteRect.UpperLeftCorner.X - spec.pos.X,
AbsoluteRect.UpperLeftCorner.Y - spec.pos.Y,
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<s32>(core::position2d<s32>(0,0),
core::dimension2di(texture->getOriginalSize())),
NULL/*&AbsoluteClippingRect*/, colors, true);
- }
- else {
+ } else {
errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
errorstream << "\t" << spec.name << std::endl;
}
driver->draw2DRectangle(todraw, rect, 0);
}
+
+ /*
+ Call base class
+ */
+ gui::IGUIElement::draw();
+
/*
Draw images
*/
core::rect<s32> 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<s32>(core::position2d<s32>(0,0),img_origsize),
NULL/*&AbsoluteClippingRect*/, colors, true);
}
*/
for(u32 i=0; i<m_itemimages.size(); i++)
{
- if (m_gamedef == 0)
+ if (m_client == 0)
break;
const ImageDrawSpec &spec = m_itemimages[i];
- IItemDefManager *idef = m_gamedef->idef();
+ IItemDefManager *idef = m_client->idef();
ItemStack item;
- item.deSerialize(spec.name, idef);
- video::ITexture *texture = idef->getInventoryTexture(item.getDefinition(idef).name, m_gamedef);
- // Image size on screen
+ item.deSerialize(spec.item_name, idef);
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
- // Image rectangle on screen
+ // Viewport rectangle on screen
core::rect<s32> 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<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- NULL/*&AbsoluteClippingRect*/, colors, true);
+ if (spec.parent_button && spec.parent_button->isPressed()) {
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ rect += core::dimension2d<s32>(
+ 0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
+#else
+ rect += core::dimension2d<s32>(
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+#endif
+ }
+ drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
+ m_client, IT_ROT_NONE);
}
/*
Phase 0: Item slot rectangles
Phase 1: Item images; prepare tooltip
*/
- int start_phase=0;
- for(int phase=start_phase; phase<=1; phase++)
- for(u32 i=0; i<m_inventorylists.size(); i++)
- {
- drawList(m_inventorylists[i], phase);
+ bool item_hovered = false;
+ int start_phase = 0;
+ for (int phase = start_phase; phase <= 1; phase++) {
+ for (u32 i = 0; i < m_inventorylists.size(); i++) {
+ drawList(m_inventorylists[i], phase, item_hovered);
+ }
+ }
+ if (!item_hovered) {
+ drawItemStack(driver, m_font, ItemStack(),
+ core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
+ NULL, m_client, IT_ROT_HOVERED);
}
-
- /*
- Call base class
- */
- gui::IGUIElement::draw();
/* TODO find way to show tooltips on touchscreen */
#ifndef HAVE_TOUCHSCREENGUI
m_pointer = m_device->getCursorControl()->getPosition();
#endif
+ /*
+ Draw static text elements
+ */
+ for (u32 i = 0; i < m_static_texts.size(); i++) {
+ const StaticTextSpec &spec = m_static_texts[i];
+ core::rect<s32> rect = spec.rect;
+ if (spec.parent_button && spec.parent_button->isPressed()) {
+#if (IRRLICHT_VERSION_MAJOR == 1 && IRRLICHT_VERSION_MINOR < 8)
+ rect += core::dimension2d<s32>(
+ 0.05 * (float)rect.getWidth(), 0.05 * (float)rect.getHeight());
+#else
+ // Use image offset instead of text's because its a bit smaller
+ // and fits better, also TEXT_OFFSET_X is always 0
+ rect += core::dimension2d<s32>(
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X),
+ skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y));
+#endif
+ }
+ video::SColor color(255, 255, 255, 255);
+ m_font->draw(spec.text.c_str(), rect, color, true, true, &rect);
+ }
+
/*
Draw fields/buttons tooltips
*/
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());
+ m_old_tooltip = L"";
+ } else {
+ if (id == m_old_tooltip_id) {
+ delta = porting::getDeltaMs(m_hovered_time, porting::getTimeMs());
+ } else {
+ m_hovered_time = porting::getTimeMs();
+ m_old_tooltip_id = id;
+ }
}
+
if (id != -1 && delta >= m_tooltip_show_delay) {
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
- if ( (iter->fid == id) && (m_tooltips[iter->fname].tooltip != "") ){
+ iter != m_fields.end(); ++iter) {
+ if (iter->fid == id && m_tooltips[iter->fname].tooltip != L"") {
if (m_old_tooltip != m_tooltips[iter->fname].tooltip) {
+ m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
+ m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
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;
+ setStaticText(m_tooltip_element, m_tooltips[iter->fname].tooltip.c_str());
+ std::vector<std::wstring> tt_rows = str_split(m_tooltips[iter->fname].tooltip, L'\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;
- int lines_count = 1;
- size_t i = 0;
- while ((i = m_tooltips[iter->fname].tooltip.find("\n", i)) != std::string::npos) {
- lines_count++;
- i += 2;
- }
- s32 tooltip_height = m_tooltip_element->getTextHeight() * lines_count + 5;
+ 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<s32>(
core::position2d<s32>(tooltip_x, tooltip_y),
core::dimension2d<s32>(tooltip_width, tooltip_height)));
}
- m_tooltip_element->setBackgroundColor(m_tooltips[iter->fname].bgcolor);
- m_tooltip_element->setOverrideColor(m_tooltips[iter->fname].color);
m_tooltip_element->setVisible(true);
this->bringToFront(m_tooltip_element);
break;
}
}
+ m_tooltip_element->draw();
+
/*
Draw dragged item stack
*/
drawSelectedItem();
+
+ skin->setFont(old_font);
}
void GUIFormSpecMenu::updateSelectedItem()
{
if(m_text_dst)
{
- std::map<std::string, std::string> fields;
+ StringMap fields;
if (quitmode == quit_mode_accept) {
fields["quit"] = "true";
current_keys_pending.key_enter = false;
}
+ if (!current_field_enter_pending.empty()) {
+ fields["key_enter_field"] = current_field_enter_pending;
+ current_field_enter_pending = "";
+ }
+
if (current_keys_pending.key_escape) {
fields["key_escape"] = "true";
current_keys_pending.key_escape = false;
for(unsigned int i=0; i<m_fields.size(); i++) {
const FieldSpec &s = m_fields[i];
if(s.send) {
- std::string name = wide_to_narrow(s.fname);
- if(s.ftype == f_Button) {
- fields[name] = wide_to_narrow(s.flabel);
- }
- else if(s.ftype == f_Table) {
+ std::string name = s.fname;
+ if (s.ftype == f_Button) {
+ fields[name] = wide_to_utf8(s.flabel);
+ } else if (s.ftype == f_Table) {
GUITable *table = getTable(s.fname);
if (table) {
fields[name] = table->checkEvent();
}
s32 selected = e->getSelected();
if (selected >= 0) {
- fields[name] =
- wide_to_narrow(e->getItem(selected));
+ std::vector<std::string> *dropdown_values =
+ getDropDownValues(s.fname);
+ if (dropdown_values && selected < (s32)dropdown_values->size()) {
+ fields[name] = (*dropdown_values)[selected];
+ }
}
}
else if (s.ftype == f_TabHeader) {
{
IGUIElement* e = getElementFromId(s.fid);
if(e != NULL) {
- fields[name] = wide_to_narrow(e->getText());
+ fields[name] = wide_to_utf8(e->getText());
}
}
}
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<s32>(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);
core::position2d<s32>(x, y));
if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
m_old_tooltip_id = -1;
- m_old_tooltip = "";
+ m_old_tooltip = L"";
}
if (!isChild(hovered,this)) {
if (DoubleClickDetection(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";
}
}
porting::showInputDialog(gettext("ok"), "",
- wide_to_narrow(((gui::IGUIEditBox*) hovered)->getText()),
+ wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()),
type);
return retval;
}
}
#endif
+ if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
+ /* TODO add a check like:
+ if (event.JoystickEvent != joystick_we_listen_for)
+ return false;
+ */
+ bool handled = m_joystick->handleEvent(event.JoystickEvent);
+ if (handled) {
+ if (m_joystick->wasKeyDown(KeyType::ESC)) {
+ tryClose();
+ } else if (m_joystick->wasKeyDown(KeyType::JUMP)) {
+ if (m_allowclose) {
+ acceptInput(quit_mode_accept);
+ quitMenu();
+ }
+ }
+ }
+ return handled;
+ }
+
return false;
}
/******************************************************************************/
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;
m_doubleclickdetect[1].pos = m_pointer;
- m_doubleclickdetect[1].time = getTimeMs();
+ m_doubleclickdetect[1].time = porting::getTimeMs();
}
else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP) {
- u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, getTimeMs());
+ u32 delta = porting::getDeltaMs(m_doubleclickdetect[0].time, porting::getTimeMs());
if (delta > 400) {
return false;
}
delete translated;
return true;
}
+
return false;
}
+void GUIFormSpecMenu::tryClose()
+{
+ if (m_allowclose) {
+ doPause = false;
+ acceptInput(quit_mode_cancel);
+ quitMenu();
+ } else {
+ m_text_dst->gotText(L"MenuQuit");
+ }
+}
+
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
- if(event.EventType==EET_KEY_INPUT_EVENT) {
+ if (event.EventType==EET_KEY_INPUT_EVENT) {
KeyPress kp(event.KeyInput);
- if (event.KeyInput.PressedDown && ( (kp == EscapeKey) ||
- (kp == getKeySetting("keymap_inventory")) || (kp == CancelKey))) {
- if (m_allowclose) {
- doPause = false;
- acceptInput(quit_mode_cancel);
- quitMenu();
- } else {
- m_text_dst->gotText(narrow_to_wide("MenuQuit"));
- }
+ if (event.KeyInput.PressedDown && (
+ (kp == EscapeKey) || (kp == CancelKey) ||
+ ((m_client != NULL) && (kp == getKeySetting("keymap_inventory"))))) {
+ tryClose();
return true;
+ } else if (m_client != NULL && event.KeyInput.PressedDown &&
+ (kp == getKeySetting("keymap_screenshot"))) {
+ m_client->makeScreenshot(m_device);
}
if (event.KeyInput.PressedDown &&
(event.KeyInput.Key==KEY_RETURN ||
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) {
}
- if(event.EventType==EET_MOUSE_INPUT_EVENT
- && event.MouseInput.Event != EMIE_MOUSE_MOVED) {
- // Mouse event other than movement
+ /* 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)
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 "
- <<"\""<<s.inventoryloc.dump()<<"\" doesn't exist"
- <<std::endl;
+ if (!inv_s) {
+ errorstream << "InventoryMenu: The selected inventory location "
+ << "\"" << s.inventoryloc.dump() << "\" doesn't exist"
+ << std::endl;
s.i = -1; // make it invalid again
break;
}
- InventoryList *list = inv_s->getList(s.listname);
- if(list == NULL) {
- verbosestream<<"InventoryMenu: The selected inventory list \""
- <<s.listname<<"\" does not exist"<<std::endl;
+ list_s = inv_s->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 \""
- <<s.listname<<"\" is too small (i="<<s.i<<", size="
- <<list->getSize()<<")"<<std::endl;
+ if ((u32)s.i >= 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() &&
(m_selected_item->i == s.i);
// buttons: 0 = left, 1 = right, 2 = middle
- // up/down: 0 = down (press), 1 = up (release), 2 = unknown 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)
+ { 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;
// 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 "<<button<<" pressed at p=("
m_selected_dragging = false;
- if(s.isValid() && s.listname == "craftpreview") {
+ if (s.isValid() && s.listname == "craftpreview") {
// Craft preview has been clicked: craft
craft_amount = (button == 2 ? 10 : 1);
- }
- else if(m_selected_item == NULL) {
- if(s_count != 0) {
- // Non-empty stack has been clicked: select it
+ } else if (m_selected_item == NULL) {
+ if (s_count != 0) {
+ // Non-empty stack has been clicked: select or shift-move it
m_selected_item = new ItemSpec(s);
- if(button == 1) // right
- m_selected_amount = (s_count + 1) / 2;
- else if(button == 2) // middle
- m_selected_amount = MYMIN(s_count, 10);
+ u32 count;
+ if (button == 1) // right
+ count = (s_count + 1) / 2;
+ else if (button == 2) // middle
+ count = MYMIN(s_count, 10);
else // left
- m_selected_amount = s_count;
+ count = s_count;
- m_selected_dragging = true;
+ if (!event.MouseInput.Shift) {
+ // no shift: select item
+ m_selected_amount = count;
+ m_selected_dragging = true;
+ m_rmouse_auto_place = false;
+ } else {
+ // shift pressed: move item
+ if (button != 1)
+ shift_move_amount = count;
+ else // count of 1 at left click like after drag & drop
+ shift_move_amount = 1;
+ }
}
- }
- else { // m_selected_item != NULL
+ } else { // m_selected_item != NULL
assert(m_selected_amount >= 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;
}
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 "<<button<<" released at p=("
// <<p.X<<","<<p.Y<<")"<<std::endl;
- if(m_selected_item != NULL && m_selected_dragging && s.isValid()) {
- if(!identical) {
+ if (m_selected_item != NULL && m_selected_dragging && s.isValid()) {
+ if (!identical) {
// Dragged to different slot: move all selected
move_amount = m_selected_amount;
}
- }
- else if(m_selected_item != NULL && m_selected_dragging &&
- !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
+ } else if (m_selected_item != NULL && m_selected_dragging &&
+ !(getAbsoluteClippingRect().isPointInside(m_pointer))) {
// Dragged outside of window: drop all selected
drop_amount = m_selected_amount;
}
m_selected_dragging = false;
+ // Keep count of how many times right mouse button has been
+ // clicked. One click is drag without dropping. Click + release
+ // + click changes to drop one item when moved mode
+ if (button == 1 && m_selected_item != NULL)
+ m_rmouse_auto_place = !m_rmouse_auto_place;
+ } else if (updown == -1) {
+ // Mouse has been moved and rmb is down and mouse pointer just
+ // entered a new inventory field (checked in the entry-if, this
+ // is the only action here that is generated by mouse movement)
+ if (m_selected_item != NULL && s.isValid()) {
+ // Move 1 item
+ // TODO: middle mouse to move 10 items might be handy
+ if (m_rmouse_auto_place) {
+ // Only move an item if the destination slot is empty
+ // or contains the same item type as what is going to be
+ // moved
+ InventoryList *list_from = inv_selected->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());
assert(inv_selected && inv_s);
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
- InventoryList *list_to = inv_s->getList(s.listname);
+ InventoryList *list_to = list_s;
assert(list_from && list_to);
ItemStack stack_from = list_from->getItem(m_selected_item->i);
ItemStack stack_to = list_to->getItem(s.i);
// Check how many items can be moved
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
- ItemStack leftover = stack_to.addItem(stack_from, m_gamedef->idef());
+ ItemStack leftover = stack_to.addItem(stack_from, m_client->idef());
// If source stack cannot be added to destination stack at all,
// they are swapped
if ((leftover.count == stack_from.count) &&
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
}
m_selected_content_guess = ItemStack(); // Clear
}
- infostream<<"Handing IACTION_MOVE to manager"<<std::endl;
+ infostream << "Handing IACTION_MOVE to manager" << std::endl;
IMoveAction *a = new IMoveAction();
a->count = move_amount;
a->from_inv = m_selected_item->inventoryloc;
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
assert(drop_amount > 0 && drop_amount <= m_selected_amount);
m_selected_amount -= drop_amount;
- infostream<<"Handing IACTION_DROP to manager"<<std::endl;
+ infostream << "Handing IACTION_DROP to manager" << std::endl;
IDropAction *a = new IDropAction();
a->count = drop_amount;
a->from_inv = m_selected_item->inventoryloc;
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
assert(s.isValid());
assert(inv_s);
- infostream<<"Handing IACTION_CRAFT to manager"<<std::endl;
+ infostream << "Handing IACTION_CRAFT to manager" << std::endl;
ICraftAction *a = new ICraftAction();
a->count = craft_amount;
a->craft_inv = s.inventoryloc;
}
// 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;
m_selected_dragging = false;
m_selected_content_guess = ItemStack();
}
+ 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; i<m_fields.size(); i++) {
+ for (unsigned int i=0; i<m_fields.size(); i++) {
FieldSpec &s = m_fields[i];
if ((s.ftype == f_TabHeader) &&
(s.fid == event.GUIEvent.Caller->getID())) {
}
}
}
- 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."
<<std::endl;
// Returning true disables focus change
return true;
}
}
- if((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
+ if ((event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) ||
(event.GUIEvent.EventType == gui::EGET_CHECKBOX_CHANGED) ||
(event.GUIEvent.EventType == gui::EGET_COMBO_BOX_CHANGED) ||
(event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED)) {
quitMenu();
} else {
acceptInput();
- m_text_dst->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; i<m_fields.size(); i++) {
+ 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.ftype == f_Button) || (s.ftype == f_CheckBox)) &&
(s.fid == event.GUIEvent.Caller->getID())) {
s.send = true;
- if(s.is_exit) {
+ if (s.is_exit) {
if (m_allowclose) {
acceptInput(quit_mode_accept);
quitMenu();
} else {
- m_text_dst->gotText(narrow_to_wide("ExitButton"));
+ m_text_dst->gotText(L"ExitButton");
}
return true;
} else {
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; i<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s2 = m_fields[i];
if (s2.ftype == f_DropDown) {
s2.send = false;
// revert configuration to make sure dropdowns are sent on
// regular button click
- for(u32 i=0; i<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s2 = m_fields[i];
if (s2.ftype == f_DropDown) {
s2.send = true;
}
}
return true;
- }
- else if ((s.ftype == f_ScrollBar) &&
- (s.fid == event.GUIEvent.Caller->getID()))
- {
+ } else if ((s.ftype == f_ScrollBar) &&
+ (s.fid == event.GUIEvent.Caller->getID())) {
s.fdefault = L"Changed";
acceptInput(quit_mode_no);
s.fdefault = L"";
}
}
- if(event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
- if(event.GUIEvent.Caller->getID() > 257) {
+ if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
+ if (event.GUIEvent.Caller->getID() > 257) {
+ bool close_on_enter = true;
+ for (u32 i = 0; i < m_fields.size(); i++) {
+ FieldSpec &s = m_fields[i];
+ if (s.ftype == f_Unknown &&
+ s.fid == event.GUIEvent.Caller->getID()) {
+ current_field_enter_pending = s.fname;
+ UNORDERED_MAP<std::string, bool>::const_iterator it =
+ field_close_on_enter.find(s.fname);
+ if (it != field_close_on_enter.end())
+ close_on_enter = (*it).second;
+
+ break;
+ }
+ }
- if (m_allowclose) {
+ if (m_allowclose && close_on_enter) {
+ current_keys_pending.key_enter = true;
acceptInput(quit_mode_accept);
quitMenu();
} else {
}
}
- 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<m_fields.size(); i++) {
+ for (u32 i = 0; i < m_fields.size(); i++) {
FieldSpec &s = m_fields[i];
// if it's a table, set the send field
// so lua knows which table was changed
* @param id of element
* @return name string or empty string
*/
-std::wstring GUIFormSpecMenu::getNameByID(s32 id)
+std::string GUIFormSpecMenu::getNameByID(s32 id)
{
for(std::vector<FieldSpec>::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 "";
}
/**
std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
{
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
- iter != m_fields.end(); iter++) {
+ iter != m_fields.end(); ++iter) {
if (iter->fid == id) {
return iter->flabel;
}
}
return L"";
}
-
-bool GUIFormSpecMenu::parseColor(const std::string &value, video::SColor &color,
- bool quiet)
-{
- const char *hexpattern = NULL;
- if (value[0] == '#') {
- if (value.size() == 9)
- hexpattern = "#RRGGBBAA";
- else if (value.size() == 7)
- hexpattern = "#RRGGBB";
- else if (value.size() == 5)
- hexpattern = "#RGBA";
- else if (value.size() == 4)
- hexpattern = "#RGB";
- }
-
- if (hexpattern) {
- assert(strlen(hexpattern) == value.size());
- video::SColor outcolor(255, 255, 255, 255);
- for (size_t pos = 0; pos < value.size(); ++pos) {
- // '#' in the pattern means skip that character
- if (hexpattern[pos] == '#')
- continue;
-
- // Else assume hexpattern[pos] is one of 'R' 'G' 'B' 'A'
- // Read one or two digits, depending on hexpattern
- unsigned char c1, c2;
- if (hexpattern[pos+1] == hexpattern[pos]) {
- // Two digits, e.g. hexpattern == "#RRGGBB"
- if (!hex_digit_decode(value[pos], c1) ||
- !hex_digit_decode(value[pos+1], c2))
- goto fail;
- ++pos;
- }
- else {
- // One digit, e.g. hexpattern == "#RGB"
- if (!hex_digit_decode(value[pos], c1))
- goto fail;
- c2 = c1;
- }
- u32 colorpart = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
-
- // Update outcolor with newly read color part
- if (hexpattern[pos] == 'R')
- outcolor.setRed(colorpart);
- else if (hexpattern[pos] == 'G')
- outcolor.setGreen(colorpart);
- else if (hexpattern[pos] == 'B')
- outcolor.setBlue(colorpart);
- else if (hexpattern[pos] == 'A')
- outcolor.setAlpha(colorpart);
- }
-
- color = outcolor;
- return true;
- }
-
- // Optionally, named colors could be implemented here
-
-fail:
- if (!quiet)
- errorstream<<"Invalid color: \""<<value<<"\""<<std::endl;
- return false;
-}