#include <iterator>
#include <sstream>
#include <limits>
+#include "guiButton.h"
#include "guiFormSpecMenu.h"
#include "guiTable.h"
#include "constants.h"
#include "gamedef.h"
-#include "keycode.h"
+#include "client/keycode.h"
#include "util/strfnd.h"
#include <IGUICheckBox.h>
#include <IGUIEditBox.h>
#include "mainmenumanager.h"
#include "porting.h"
#include "settings.h"
-#include "client.h"
-#include "fontengine.h"
+#include "client/client.h"
+#include "client/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"
+#include "client/guiscalingfilter.h"
#include "guiEditBoxWithScrollbar.h"
#include "intlGUIEditBox.h"
#define MY_CHECKGEOM(a,b) \
if (v_geom.size() != 2) { \
- errorstream<< "Invalid pos for element " << a << "specified: \"" \
- << parts[b] << "\"" << std::endl; \
+ errorstream<< "Invalid geometry for element " << a << \
+ "specified: \"" << parts[b] << "\"" << std::endl; \
return; \
}
/*
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
- bool remap_dbl_click) :
+ const std::string &formspecPrepend,
+ bool remap_dbl_click):
GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
m_invmgr(client),
m_tsrc(tsrc),
m_client(client),
+ m_formspec_prepend(formspecPrepend),
m_form_src(fsrc),
m_text_dst(tdst),
m_joystick(joystick),
m_remap_dbl_click(remap_dbl_click)
-#ifdef __ANDROID__
- , m_JavaDialogFieldName("")
-#endif
{
current_keys_pending.key_down = false;
current_keys_pending.key_up = false;
}
void GUIFormSpecMenu::create(GUIFormSpecMenu *&cur_formspec, Client *client,
- JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest)
+ JoystickController *joystick, IFormSource *fs_src, TextDest *txt_dest,
+ const std::string &formspecPrepend)
{
if (cur_formspec == nullptr) {
cur_formspec = new GUIFormSpecMenu(joystick, guiroot, -1, &g_menumgr,
- client, client->getTextureSource(), fs_src, txt_dest);
+ client, client->getTextureSource(), fs_src, txt_dest, formspecPrepend);
cur_formspec->doPause = false;
/*
*/
} else {
+ cur_formspec->setFormspecPrepend(formspecPrepend);
cur_formspec->setFormSource(fs_src);
cur_formspec->setTextDest(txt_dest);
}
return NULL;
}
+v2s32 GUIFormSpecMenu::getElementBasePos(bool absolute,
+ const std::vector<std::string> *v_pos)
+{
+ v2s32 pos = padding;
+ if (absolute)
+ pos += AbsoluteRect.UpperLeftCorner;
+
+ v2f32 pos_f = v2f32(pos.X, pos.Y) + pos_offset * spacing;
+ if (v_pos) {
+ pos_f.X += stof((*v_pos)[0]) * spacing.X;
+ pos_f.Y += stof((*v_pos)[1]) * spacing.Y;
+ }
+ return v2s32(pos_f.X, pos_f.Y);
+}
+
+v2s32 GUIFormSpecMenu::getRealCoordinateBasePos(bool absolute,
+ const std::vector<std::string> &v_pos)
+{
+ v2f32 pos_f = v2f32(0.0f, 0.0f);
+
+ pos_f.X += stof(v_pos[0]) + pos_offset.X;
+ pos_f.Y += stof(v_pos[1]) + pos_offset.Y;
+
+ if (absolute)
+ return v2s32(pos_f.X * imgsize.X + AbsoluteRect.UpperLeftCorner.X,
+ pos_f.Y * imgsize.Y + AbsoluteRect.UpperLeftCorner.Y);
+ return v2s32(pos_f.X * imgsize.X, pos_f.Y * imgsize.Y);
+}
+
+v2s32 GUIFormSpecMenu::getRealCoordinateGeometry(const std::vector<std::string> &v_geom)
+{
+ return v2s32(stof(v_geom[0]) * imgsize.X, stof(v_geom[1]) * imgsize.Y);
+}
+
void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
{
std::vector<std::string> parts = split(element,',');
data->invsize.Y = MYMAX(0, stof(parts[1]));
lockSize(false);
+#ifndef __ANDROID__
if (parts.size() == 3) {
if (parts[2] == "true") {
lockSize(true,v2u32(800,600));
}
}
-
+#endif
data->explicit_size = true;
return;
}
parts[1] = parts[1].substr(0, parts[1].find(';'));
container_stack.push(pos_offset);
- pos_offset.X += MYMAX(0, stof(parts[0]));
- pos_offset.Y += MYMAX(0, stof(parts[1]));
+ pos_offset.X += stof(parts[0]);
+ pos_offset.Y += stof(parts[1]);
return;
}
errorstream<< "Invalid container start element (" << parts.size() << "): '" << element << "'" << std::endl;
InventoryLocation loc;
- if(location == "context" || location == "current_name")
+ if (location == "context" || location == "current_name")
loc = m_current_inventory_location;
else
loc.deSerialize(location);
- 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;
-
+ v2s32 pos;
v2s32 geom;
+
+ if (data->real_coordinates)
+ pos = getRealCoordinateBasePos(true, v_pos);
+ else
+ pos = getElementBasePos(true, &v_pos);
+
geom.X = stoi(v_geom[0]);
geom.Y = stoi(v_geom[1]);
if(!data->explicit_size)
warningstream<<"invalid use of list without a size[] element"<<std::endl;
- m_inventorylists.emplace_back(loc, listname, pos, geom, start_i);
+ m_inventorylists.emplace_back(loc, listname, pos, geom, start_i, data->real_coordinates);
return;
}
errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'" << std::endl;
MY_CHECKPOS("checkbox",0);
- v2s32 pos = padding + pos_offset * spacing;
- pos.X += stof(v_pos[0]) * (float) spacing.X;
- pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
bool fselected = false;
if (selected == "true")
fselected = true;
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
-
- core::rect<s32> rect = core::rect<s32>(
- pos.X, pos.Y + ((imgsize.Y/2) - m_btn_height),
- pos.X + m_font->getDimension(wlabel.c_str()).Width + 25, // text size + size of checkbox
- pos.Y + ((imgsize.Y/2) + m_btn_height));
+ const core::dimension2d<u32> label_size = m_font->getDimension(wlabel.c_str());
+ s32 cb_size = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH);
+ s32 y_center = (std::max(label_size.Height, (u32)cb_size) + 1) / 2;
+
+ v2s32 pos;
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+
+ rect = core::rect<s32>(
+ pos.X,
+ pos.Y - y_center,
+ pos.X + label_size.Width + cb_size + 7,
+ pos.Y + y_center
+ );
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ rect = core::rect<s32>(
+ pos.X,
+ pos.Y + imgsize.Y / 2 - y_center,
+ pos.X + label_size.Width + cb_size + 7,
+ pos.Y + imgsize.Y / 2 + y_center
+ );
+ }
FieldSpec spec(
name,
gui::IGUICheckBox* e = Environment->addCheckBox(fselected, rect, this,
spec.fid, spec.flabel.c_str());
+ auto style = getStyleForElement("checkbox", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
if (parts.size() >= 5) {
std::vector<std::string> v_pos = split(parts[0],',');
- std::vector<std::string> v_dim = split(parts[1],',');
+ std::vector<std::string> v_geom = split(parts[1],',');
std::string name = parts[3];
std::string value = parts[4];
MY_CHECKPOS("scrollbar",0);
+ MY_CHECKGEOM("scrollbar",1);
- 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 pos;
+ v2s32 dim;
- if (v_dim.size() != 2) {
- errorstream<< "Invalid size for element " << "scrollbar"
- << "specified: \"" << parts[1] << "\"" << std::endl;
- return;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ dim = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ dim.X = stof(v_geom[0]) * spacing.X;
+ dim.Y = stof(v_geom[1]) * spacing.Y;
}
- v2s32 dim;
- dim.X = stof(v_dim[0]) * (float) spacing.X;
- dim.Y = stof(v_dim[1]) * (float) spacing.Y;
-
core::rect<s32> rect =
core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
gui::IGUIScrollBar* e =
Environment->addScrollBar(is_horizontal,rect,this,spec.fid);
+ auto style = getStyleForElement("scrollbar", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
e->setMax(1000);
e->setMin(0);
e->setPos(stoi(parts[4]));
MY_CHECKPOS("image", 0);
MY_CHECKGEOM("image", 1);
- 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;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)imgsize.X;
- geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+ }
if (!data->explicit_size)
warningstream<<"invalid use of image without a size[] element"<<std::endl;
MY_CHECKPOS("image", 0);
- 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;
+ v2s32 pos = getElementBasePos(true, &v_pos);
if (!data->explicit_size)
warningstream<<"invalid use of image without a size[] element"<<std::endl;
MY_CHECKPOS("itemimage",0);
MY_CHECKGEOM("itemimage",1);
- 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;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)imgsize.X;
- geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * (float)imgsize.X;
+ geom.Y = stof(v_geom[1]) * (float)imgsize.Y;
+ }
if(!data->explicit_size)
warningstream<<"invalid use of item_image without a size[] element"<<std::endl;
MY_CHECKPOS("button",0);
MY_CHECKGEOM("button",1);
- 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 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
- pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ core::rect<s32> rect;
- core::rect<s32> rect =
- core::rect<s32>(pos.X, pos.Y - m_btn_height,
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
+ pos.Y+geom.Y);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+
+ rect = core::rect<s32>(pos.X, pos.Y - m_btn_height,
pos.X + geom.X, pos.Y + m_btn_height);
+ }
if(!data->explicit_size)
warningstream<<"invalid use of button without a size[] element"<<std::endl;
spec.ftype = f_Button;
if(type == "button_exit")
spec.is_exit = true;
- gui::IGUIButton* e = Environment->addButton(rect, this, spec.fid,
- spec.flabel.c_str());
+
+ GUIButton *e = GUIButton::addButton(Environment, rect, this, spec.fid, spec.flabel.c_str());
+
+ auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
+ if (style.isNotDefault(StyleSpec::BGCOLOR)) {
+ e->setColor(style.getColor(StyleSpec::BGCOLOR));
+ }
+ if (style.isNotDefault(StyleSpec::TEXTCOLOR)) {
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR));
+ }
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+
+ if (style.isNotDefault(StyleSpec::BGIMG)) {
+ std::string image_name = style.get(StyleSpec::BGIMG, "");
+ std::string pressed_image_name = style.get(StyleSpec::BGIMG_PRESSED, "");
+
+ video::ITexture *texture = 0;
+ video::ITexture *pressed_texture = 0;
+ texture = m_tsrc->getTexture(image_name);
+ if (!pressed_image_name.empty())
+ pressed_texture = m_tsrc->getTexture(pressed_image_name);
+ else
+ pressed_texture = texture;
+
+ e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
+ e->setImage(guiScalingImageButton(
+ Environment->getVideoDriver(), texture, geom.X, geom.Y));
+ e->setPressedImage(guiScalingImageButton(
+ Environment->getVideoDriver(), pressed_texture, geom.X, geom.Y));
+ e->setScaleImage(true);
+ }
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
{
std::vector<std::string> parts = split(element,';');
- if (((parts.size() == 3) || (parts.size() == 4)) ||
- ((parts.size() > 4) && (m_formspec_version > FORMSPEC_API_VERSION)))
- {
+ if ((parts.size() >= 3 && 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],',');
std::string name = unescape_string(parts[2]);
MY_CHECKPOS("background",0);
MY_CHECKGEOM("background",1);
- 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 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)spacing.X;
- geom.Y = stof(v_geom[1]) * (float)spacing.Y;
- if (!data->explicit_size)
- warningstream<<"invalid use of background without a size[] element"<<std::endl;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ pos.X -= (spacing.X - (float)imgsize.X) / 2;
+ pos.Y -= (spacing.Y - (float)imgsize.Y) / 2;
+
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
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
+ if (parts.size() >= 4 && is_yes(parts[3])) {
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos) * -1;
+ geom = v2s32(0, 0);
+ } else {
+ pos.X = stoi(v_pos[0]); //acts as offset
+ pos.Y = stoi(v_pos[1]);
+ }
clip = true;
}
- m_backgrounds.emplace_back(name, pos, geom, clip);
+
+ core::rect<s32> middle;
+ if (parts.size() >= 5) {
+ std::vector<std::string> v_middle = split(parts[4], ',');
+ if (v_middle.size() == 1) {
+ s32 x = stoi(v_middle[0]);
+ middle.UpperLeftCorner = core::vector2di(x, x);
+ middle.LowerRightCorner = core::vector2di(-x, -x);
+ } else if (v_middle.size() == 2) {
+ s32 x = stoi(v_middle[0]);
+ s32 y = stoi(v_middle[1]);
+ middle.UpperLeftCorner = core::vector2di(x, y);
+ middle.LowerRightCorner = core::vector2di(-x, -y);
+ // `-x` is interpreted as `w - x`
+ } else if (v_middle.size() == 4) {
+ middle.UpperLeftCorner = core::vector2di(stoi(v_middle[0]), stoi(v_middle[1]));
+ middle.LowerRightCorner = core::vector2di(stoi(v_middle[2]), stoi(v_middle[3]));
+ } else {
+ warningstream << "Invalid rectangle given to middle param of background[] element" << std::endl;
+ }
+ }
+
+ if (!data->explicit_size && !clip)
+ warningstream << "invalid use of unclipped background without a size[] element" << std::endl;
+
+ m_backgrounds.emplace_back(name, pos, geom, middle, clip);
return;
}
MY_CHECKPOS("table",0);
MY_CHECKGEOM("table",1);
- 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 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)spacing.X;
- geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
if (!str_initial_selection.empty() && str_initial_selection != "0")
e->setSelected(stoi(str_initial_selection));
+ auto style = getStyleForElement("table", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
return;
MY_CHECKPOS("textlist",0);
MY_CHECKGEOM("textlist",1);
- 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 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)spacing.X;
- geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
if (!str_initial_selection.empty() && str_initial_selection != "0")
e->setSelected(stoi(str_initial_selection));
+ auto style = getStyleForElement("textlist", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
m_tables.emplace_back(spec, e);
m_fields.push_back(spec);
return;
MY_CHECKPOS("dropdown",0);
- 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 pos;
+ v2s32 geom;
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ std::vector<std::string> v_geom = split(parts[1],',');
- s32 width = stof(parts[1]) * (float)spacing.Y;
+ if (v_geom.size() == 1)
+ v_geom.emplace_back("1");
- core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
- pos.X + width, pos.Y + (m_btn_height * 2));
+ MY_CHECKGEOM("dropdown",1);
+
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+
+ s32 width = stof(parts[1]) * spacing.Y;
+
+ rect = core::rect<s32>(pos.X, pos.Y,
+ pos.X + width, pos.Y + (m_btn_height * 2));
+ }
FieldSpec spec(
name,
if (!str_initial_selection.empty())
e->setSelected(stoi(str_initial_selection)-1);
+ auto style = getStyleForElement("dropdown", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+
m_fields.push_back(spec);
m_dropdowns.emplace_back(spec, std::vector<std::string>());
MY_CHECKPOS("pwdfield",0);
MY_CHECKGEOM("pwdfield",1);
- v2s32 pos = pos_offset * spacing;
- pos.X += stof(v_pos[0]) * (float)spacing.X;
- pos.Y += stof(v_pos[1]) * (float)spacing.Y;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
- pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
- pos.Y -= m_btn_height;
- geom.Y = m_btn_height*2;
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ pos -= padding;
+
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ pos.Y -= m_btn_height;
+ geom.Y = m_btn_height*2;
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
e->setPasswordBox(true,L'*');
+ auto style = getStyleForElement("pwdfield", name, "field");
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+
irr::SEvent evt;
evt.EventType = EET_KEY_INPUT_EVENT;
evt.KeyInput.Key = KEY_END;
}
if (e) {
- if (is_editable && spec.fname == data->focused_fieldname)
+ if (is_editable && spec.fname == data->focused_fieldname)
Environment->setFocus(e);
if (is_multiline) {
evt.KeyInput.PressedDown = true;
e->OnEvent(evt);
}
+
+ auto style = getStyleForElement(is_multiline ? "textarea" : "field", spec.fname);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+ if (style.get(StyleSpec::BGCOLOR, "") == "transparent") {
+ e->setDrawBackground(false);
+ }
}
if (!spec.flabel.empty()) {
if(data->explicit_size)
warningstream<<"invalid use of unpositioned \"field\" in inventory"<<std::endl;
- v2s32 pos = padding + AbsoluteRect.UpperLeftCorner + pos_offset * spacing;
+ v2s32 pos = getElementBasePos(false, nullptr);
pos.Y = ((m_fields.size()+2)*60);
v2s32 size = DesiredRect.getSize();
MY_CHECKPOS(type,0);
MY_CHECKGEOM(type,1);
- v2s32 pos = pos_offset * spacing;
- pos.X += stof(v_pos[0]) * (float) spacing.X;
- pos.Y += stof(v_pos[1]) * (float) spacing.Y;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ pos -= padding;
- if (type == "textarea")
- {
- geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
- pos.Y += m_btn_height;
- }
- else
- {
- pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
- pos.Y -= m_btn_height;
- geom.Y = m_btn_height*2;
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+
+ if (type == "textarea")
+ {
+ geom.Y = (stof(v_geom[1]) * (float)imgsize.Y) - (spacing.Y-imgsize.Y);
+ pos.Y += m_btn_height;
+ }
+ else
+ {
+ pos.Y += (stof(v_geom[1]) * (float)imgsize.Y)/2;
+ pos.Y -= m_btn_height;
+ geom.Y = m_btn_height*2;
+ }
}
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
MY_CHECKPOS("label",0);
- v2s32 pos = padding + pos_offset * spacing;
- pos.X += stof(v_pos[0]) * (float)spacing.X;
- pos.Y += (stof(v_pos[1]) + 7.0/30.0) * (float)spacing.Y;
-
if(!data->explicit_size)
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,
- posy + m_btn_height);
+ std::wstring wlabel = unescape_translate(unescape_string(
+ utf8_to_wide(lines[i])));
+
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ // Lines are spaced at the distance of 1/2 imgsize.
+ // This alows lines that line up with the new elements
+ // easily without sacrificing good line distance. If
+ // it was one whole imgsize, it would have too much
+ // spacing.
+ v2s32 pos = getRealCoordinateBasePos(false, v_pos);
+
+ // Labels are positioned by their center, not their top.
+ pos.Y += (((float) imgsize.Y) / -2) + (((float) imgsize.Y) * i / 2);
+
+ rect = core::rect<s32>(
+ pos.X, pos.Y,
+ pos.X + m_font->getDimension(wlabel.c_str()).Width,
+ pos.Y + imgsize.Y);
+
+ } else {
+ // 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.
+
+ v2s32 pos = getElementBasePos(false, nullptr);
+ pos.X += stof(v_pos[0]) * spacing.X;
+ pos.Y += (stof(v_pos[1]) + 7.0f / 30.0f) * spacing.Y;
+
+ pos.Y += ((float) i) * spacing.Y * 2.0 / 5.0;
+
+ rect = core::rect<s32>(
+ pos.X, pos.Y - m_btn_height,
+ pos.X + m_font->getDimension(wlabel.c_str()).Width,
+ pos.Y + m_btn_height);
+ }
+
FieldSpec spec(
"",
wlabel,
gui::IGUIStaticText *e = gui::StaticText::add(Environment,
spec.flabel.c_str(), rect, false, false, this, spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
+
+ auto style = getStyleForElement("label", spec.fname);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+
m_fields.push_back(spec);
}
return;
}
- errorstream<< "Invalid label element(" << parts.size() << "): '" << element << "'" << std::endl;
+ errorstream << "Invalid label element(" << parts.size() << "): '" << element
+ << "'" << std::endl;
}
void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &element)
MY_CHECKPOS("vertlabel",1);
- 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 pos;
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+
+ // Vertlabels are positioned by center, not left.
+ pos.X -= imgsize.X / 2;
- core::rect<s32> rect = core::rect<s32>(
- pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
+ // We use text.length + 1 because without it, the rect
+ // isn't quite tall enough and cuts off the text.
+ rect = core::rect<s32>(pos.X, pos.Y,
+ pos.X + imgsize.X,
+ pos.Y + font_line_height(m_font) *
+ (text.length() + 1));
+
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+
+ // As above, the length must be one longer. The width of
+ // the rect (15 pixels) seems rather arbitrary, but
+ // changing it might break something.
+ rect = core::rect<s32>(
+ pos.X, pos.Y+((imgsize.Y/2) - m_btn_height),
pos.X+15, pos.Y +
- 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
+ font_line_height(m_font) *
+ (text.length() + 1) +
+ ((imgsize.Y/2) - m_btn_height));
+ }
if(!data->explicit_size)
warningstream<<"invalid use of label without a size[] element"<<std::endl;
L"",
258+m_fields.size()
);
- gui::IGUIStaticText *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
+ gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(),
rect, false, false, this, spec.fid);
- t->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+ e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
+
+ auto style = getStyleForElement("vertlabel", spec.fname, "label");
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setOverrideColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
+
m_fields.push_back(spec);
return;
}
MY_CHECKPOS("imagebutton",0);
MY_CHECKGEOM("imagebutton",1);
- 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;
- geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
- geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
-
bool noclip = false;
bool drawborder = true;
std::string pressed_image_name;
pressed_image_name = parts[7];
}
- core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
+ v2s32 pos;
+ v2s32 geom;
- if(!data->explicit_size)
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
+ }
+
+ core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
+ pos.Y+geom.Y);
+
+ if (!data->explicit_size)
warningstream<<"invalid use of image_button without a size[] element"<<std::endl;
image_name = unescape_string(image_name);
258+m_fields.size()
);
spec.ftype = f_Button;
- if(type == "image_button_exit")
+ if (type == "image_button_exit")
spec.is_exit = true;
video::ITexture *texture = 0;
Environment->setFocus(e);
}
- e->setUseAlphaChannel(true);
+ auto style = getStyleForElement("image_button", spec.fname);
+
+ e->setUseAlphaChannel(style.getBool(StyleSpec::ALPHA, true));
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);
+ if (parts.size() >= 7) {
+ e->setNotClipped(noclip);
+ e->setDrawBorder(drawborder);
+ } else {
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+ }
m_fields.push_back(spec);
return;
void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &element)
{
- std::vector<std::string> parts = split(element,';');
+ std::vector<std::string> parts = split(element, ';');
- if (((parts.size() == 4) || (parts.size() == 6)) ||
- ((parts.size() > 6) && (m_formspec_version > FORMSPEC_API_VERSION)))
+ if (((parts.size() == 4) || (parts.size() == 6)) || (parts.size() == 7 &&
+ data->real_coordinates) || ((parts.size() > 6) &&
+ (m_formspec_version > FORMSPEC_API_VERSION)))
{
std::vector<std::string> v_pos = split(parts[0],',');
- std::string name = parts[1];
- std::vector<std::string> buttons = split(parts[2],',');
- std::string str_index = parts[3];
+
+ // If we're using real coordinates, add an extra field for height.
+ // Width is not here because tabs are the width of the text, and
+ // there's no reason to change that.
+ unsigned int i = 0;
+ std::vector<std::string> v_geom = {"1", "0.75"}; // Dummy width and default height
+ bool auto_width = true;
+ if (parts.size() == 7) {
+ i++;
+
+ v_geom = split(parts[1], ',');
+ if (v_geom.size() == 1)
+ v_geom.insert(v_geom.begin(), "1"); // Dummy value
+ else
+ auto_width = false;
+ }
+
+ std::string name = parts[i+1];
+ std::vector<std::string> buttons = split(parts[i+2], ',');
+ std::string str_index = parts[i+3];
bool show_background = true;
bool show_border = true;
- int tab_index = stoi(str_index) -1;
+ int tab_index = stoi(str_index) - 1;
- MY_CHECKPOS("tabheader",0);
+ MY_CHECKPOS("tabheader", 0);
- if (parts.size() == 6) {
- if (parts[4] == "true")
+ if (parts.size() == 6 + i) {
+ if (parts[4+i] == "true")
show_background = false;
- if (parts[5] == "false")
+ if (parts[5+i] == "false")
show_border = false;
}
spec.ftype = f_TabHeader;
- 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 pos;
v2s32 geom;
- geom.X = DesiredRect.getWidth();
- geom.Y = m_btn_height*2;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+
+ geom = getRealCoordinateGeometry(v_geom);
+ pos.Y -= geom.Y; // TabHeader base pos is the bottom, not the top.
+ if (auto_width)
+ geom.X = DesiredRect.getWidth(); // Set automatic width
+
+ MY_CHECKGEOM("tabheader", 1);
+ } else {
+ v2f32 pos_f = pos_offset * spacing;
+ pos_f.X += stof(v_pos[0]) * spacing.X;
+ pos_f.Y += stof(v_pos[1]) * spacing.Y - m_btn_height * 2;
+ pos = v2s32(pos_f.X, pos_f.Y);
+
+ geom.Y = m_btn_height * 2;
+ geom.X = DesiredRect.getWidth();
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
pos.Y+geom.Y);
show_background, show_border, spec.fid);
e->setAlignment(irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_UPPERLEFT,
irr::gui::EGUIA_UPPERLEFT, irr::gui::EGUIA_LOWERRIGHT);
- e->setTabHeight(m_btn_height*2);
+ e->setTabHeight(geom.Y);
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
- e->setNotClipped(true);
+ auto style = getStyleForElement("tabheader", name);
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, true));
for (const std::string &button : buttons) {
- e->addTab(unescape_translate(unescape_string(
+ auto tab = e->addTab(unescape_translate(unescape_string(
utf8_to_wide(button))).c_str(), -1);
+ if (style.isNotDefault(StyleSpec::BGCOLOR))
+ tab->setBackgroundColor(style.getColor(StyleSpec::BGCOLOR));
+
+ tab->setTextColor(style.getColor(StyleSpec::TEXTCOLOR, video::SColor(0xFFFFFFFF)));
}
if ((tab_index >= 0) &&
MY_CHECKPOS("itemimagebutton",0);
MY_CHECKGEOM("itemimagebutton",1);
- 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 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * (float)spacing.X)-(spacing.X-imgsize.X);
- geom.Y = (stof(v_geom[1]) * (float)spacing.Y)-(spacing.Y-imgsize.Y);
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(false, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(false, &v_pos);
+ geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
+ geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
+ }
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
gui::IGUIButton *e = Environment->addButton(rect, this, spec.fid, L"");
+ auto style = getStyleForElement("item_image_button", spec.fname, "image_button");
+ e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
+ e->setDrawBorder(style.getBool(StyleSpec::BORDER, true));
+
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
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;
+
+ if (data->real_coordinates)
+ pos = getRealCoordinateBasePos(true, v_pos);
+ else
+ pos = getElementBasePos(true, &v_pos);
+
m_itemimages.emplace_back("", item_name, e, pos, geom);
m_static_texts.emplace_back(utf8_to_wide(label), rect, e);
return;
MY_CHECKPOS("box",0);
MY_CHECKGEOM("box",1);
- 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;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * (float)spacing.X;
- geom.Y = stof(v_geom[1]) * (float)spacing.Y;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
+ }
video::SColor tmp_color;
- if (parseColorString(parts[2], tmp_color, false)) {
+ if (parseColorString(parts[2], tmp_color, false, 0x8C)) {
BoxDrawSpec spec(pos, geom, tmp_color);
m_boxes.push_back(spec);
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[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])),
- m_default_tooltip_bgcolor, m_default_tooltip_color);
+ if (parts.size() < 2) {
+ errorstream << "Invalid tooltip element(" << parts.size() << "): '"
+ << element << "'" << std::endl;
return;
}
- if (parts.size() == 4) {
- std::string name = parts[0];
- video::SColor tmp_color1, tmp_color2;
- if ( parseColorString(parts[2], tmp_color1, false) && parseColorString(parts[3], tmp_color2, false) ) {
- m_tooltips[name] = TooltipSpec(utf8_to_wide(unescape_string(parts[1])),
- tmp_color1, tmp_color2);
- return;
+ // Get mode and check size
+ bool rect_mode = parts[0].find(',') != std::string::npos;
+ size_t base_size = rect_mode ? 3 : 2;
+ if (parts.size() != base_size && parts.size() != base_size + 2) {
+ errorstream << "Invalid tooltip element(" << parts.size() << "): '"
+ << element << "'" << std::endl;
+ return;
+ }
+
+ // Read colors
+ video::SColor bgcolor = m_default_tooltip_bgcolor;
+ video::SColor color = m_default_tooltip_color;
+ if (parts.size() == base_size + 2 &&
+ (!parseColorString(parts[base_size], bgcolor, false) ||
+ !parseColorString(parts[base_size + 1], color, false))) {
+ errorstream << "Invalid color in tooltip element(" << parts.size()
+ << "): '" << element << "'" << std::endl;
+ return;
+ }
+
+ // Make tooltip spec
+ std::string text = unescape_string(parts[rect_mode ? 2 : 1]);
+ TooltipSpec spec(utf8_to_wide(text), bgcolor, color);
+
+ // Add tooltip
+ if (rect_mode) {
+ std::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
+
+ MY_CHECKPOS("tooltip", 0);
+ MY_CHECKGEOM("tooltip", 1);
+
+ v2s32 pos;
+ v2s32 geom;
+
+ if (data->real_coordinates) {
+ pos = getRealCoordinateBasePos(true, v_pos);
+ geom = getRealCoordinateGeometry(v_geom);
+ } else {
+ pos = getElementBasePos(true, &v_pos);
+ geom.X = stof(v_geom[0]) * spacing.X;
+ geom.Y = stof(v_geom[1]) * spacing.Y;
}
+
+ irr::core::rect<s32> rect(pos, pos + geom);
+ m_tooltip_rects.emplace_back(rect, spec);
+ } else {
+ m_tooltips[parts[0]] = spec;
}
- errorstream<< "Invalid tooltip element(" << parts.size() << "): '" << element << "'" << std::endl;
}
bool GUIFormSpecMenu::parseVersionDirect(const std::string &data)
<< "'" << std::endl;
}
+bool GUIFormSpecMenu::parseStyle(parserData *data, const std::string &element, bool style_type)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() < 2) {
+ errorstream << "Invalid style element (" << parts.size() << "): '" << element
+ << "'" << std::endl;
+ return false;
+ }
+
+ std::string selector = trim(parts[0]);
+ if (selector.empty()) {
+ errorstream << "Invalid style element (Selector required): '" << element
+ << "'" << std::endl;
+ return false;
+ }
+
+ StyleSpec spec;
+
+ for (size_t i = 1; i < parts.size(); i++) {
+ size_t equal_pos = parts[i].find('=');
+ if (equal_pos == std::string::npos) {
+ errorstream << "Invalid style element (Property missing value): '" << element
+ << "'" << std::endl;
+ return false;
+ }
+
+ std::string propname = trim(parts[i].substr(0, equal_pos));
+ std::string value = trim(unescape_string(parts[i].substr(equal_pos + 1)));
+
+ std::transform(propname.begin(), propname.end(), propname.begin(), ::tolower);
+
+ StyleSpec::Property prop = StyleSpec::GetPropertyByName(propname);
+ if (prop == StyleSpec::NONE) {
+ if (property_warned.find(propname) != property_warned.end()) {
+ warningstream << "Invalid style element (Unknown property " << propname << "): '"
+ << element
+ << "'" << std::endl;
+ property_warned.insert(propname);
+ }
+ return false;
+ }
+
+ spec.set(prop, value);
+ }
+
+ if (style_type) {
+ theme_by_type[selector] |= spec;
+ } else {
+ theme_by_name[selector] |= spec;
+ }
+
+ return true;
+}
+
void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
{
//some prechecks
if (element.empty())
return;
+ if (parseVersionDirect(element))
+ return;
+
std::vector<std::string> parts = split(element,'[');
// ugly workaround to keep compatibility
return;
}
+ if (type == "real_coordinates") {
+ data->real_coordinates = is_yes(description);
+ return;
+ }
+
+ if (type == "style") {
+ parseStyle(data, description, false);
+ return;
+ }
+
+ if (type == "style_type") {
+ parseStyle(data, description, true);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
m_fields.clear();
m_boxes.clear();
m_tooltips.clear();
+ m_tooltip_rects.clear();
m_inventory_rings.clear();
m_static_texts.clear();
m_dropdowns.clear();
+ theme_by_name.clear();
+ theme_by_type.clear();
m_bgfullscreen = false;
);
}
-
m_slotbg_n = video::SColor(255,128,128,128);
m_slotbg_h = video::SColor(255,192,192,192);
/* try to read version from first element only */
if (!elements.empty()) {
- if ( parseVersionDirect(elements[0]) ) {
+ if (parseVersionDirect(elements[0])) {
i++;
}
}
}
}
+ /* "no_prepend" element is always after "position" (or "size" element) if it used */
+ bool enable_prepends = true;
+ for (; i < elements.size(); i++) {
+ if (elements[i].empty())
+ break;
+
+ std::vector<std::string> parts = split(elements[i], '[');
+ if (trim(parts[0]) == "no_prepend")
+ enable_prepends = false;
+ else
+ break;
+ }
+
+ /* Copy of the "real_coordinates" element for after the form size. */
+ mydata.real_coordinates = m_formspec_version >= 2;
+ for (; i < elements.size(); i++) {
+ std::vector<std::string> parts = split(elements[i], '[');
+ std::string name = trim(parts[0]);
+ if (name != "real_coordinates" || parts.size() != 2)
+ break; // Invalid format
+
+ mydata.real_coordinates = is_yes(trim(parts[1]));
+ }
if (mydata.explicit_size) {
// compute scaling for specified form size
// 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;
+#ifdef __ANDROID__
+ // For mobile devices these magic numbers are
+ // different and forms should always use the
+ // maximum screen space available.
+ double prefer_imgsize = mydata.screensize.Y / 10 * gui_scaling;
+ double fitx_imgsize = mydata.screensize.X /
+ ((12.0 / 8.0) * (0.5 + mydata.invsize.X));
+ double fity_imgsize = mydata.screensize.Y /
+ ((15.0 / 11.0) * (0.85 + mydata.invsize.Y));
+ use_imgsize = MYMIN(prefer_imgsize,
+ MYMIN(fitx_imgsize, fity_imgsize));
+#else
+ double prefer_imgsize = mydata.screensize.Y / 15 * gui_scaling;
double fitx_imgsize = mydata.screensize.X /
- ((5.0/4.0) * (0.5 + mydata.invsize.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));
+ ((15.0 / 13.0) * (0.85 * mydata.invsize.Y));
double screen_dpi = RenderingEngine::getDisplayDensity() * 96;
double min_imgsize = 0.3 * screen_dpi * gui_scaling;
use_imgsize = MYMAX(min_imgsize, MYMIN(prefer_imgsize,
MYMIN(fitx_imgsize, fity_imgsize)));
+#endif
}
// Everything else is scaled in proportion to the
// 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);
+ spacing = v2f32(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
- );
+ if (mydata.real_coordinates) {
+ mydata.size = v2s32(
+ mydata.invsize.X*imgsize.X,
+ mydata.invsize.Y*imgsize.Y
+ );
+ } else {
+ 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,
mydata.basepos = getBasePos();
m_tooltip_element->setOverrideFont(m_font);
- gui::IGUISkin* skin = Environment->getSkin();
+ gui::IGUISkin *skin = Environment->getSkin();
sanity_check(skin);
gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font);
- pos_offset = v2s32();
+ pos_offset = v2f32();
+
+ if (enable_prepends) {
+ // Backup the coordinates so that prepends can use the coordinates of choice.
+ bool rc_backup = mydata.real_coordinates;
+ bool version_backup = m_formspec_version;
+ mydata.real_coordinates = false; // Old coordinates by default.
+
+ std::vector<std::string> prepend_elements = split(m_formspec_prepend, ']');
+ for (const auto &element : prepend_elements)
+ parseElement(&mydata, element);
+
+ m_formspec_version = version_backup;
+ mydata.real_coordinates = rc_backup; // Restore coordinates
+ }
+
for (; i< elements.size(); i++) {
parseElement(&mydata, elements[i]);
}
#ifdef __ANDROID__
bool GUIFormSpecMenu::getAndroidUIInput()
{
- /* no dialog shown */
- if (m_JavaDialogFieldName == "") {
+ if (!hasAndroidUIInput())
return false;
- }
- /* still waiting */
- if (porting::getInputDialogState() == -1) {
- return true;
- }
-
- std::string fieldname = m_JavaDialogFieldName;
- m_JavaDialogFieldName = "";
-
- /* no value abort dialog processing */
- if (porting::getInputDialogState() != 0) {
- return false;
- }
+ std::string fieldname = m_jni_field_name;
+ m_jni_field_name.clear();
for(std::vector<FieldSpec>::iterator iter = m_fields.begin();
iter != m_fields.end(); ++iter) {
std::string text = porting::getInputDialogValue();
- ((gui::IGUIEditBox*) tochange)->
- setText(utf8_to_wide(text).c_str());
+ ((gui::IGUIEditBox *)tochange)->setText(utf8_to_wide(text).c_str());
}
return false;
}
for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
for(s32 i=0; i<s.geom.X*s.geom.Y; i++) {
s32 item_i = i + s.start_item_i;
- s32 x = (i%s.geom.X) * spacing.X;
- s32 y = (i/s.geom.X) * spacing.Y;
+
+ s32 x;
+ s32 y;
+ if (s.real_coordinates) {
+ x = (i%s.geom.X) * (imgsize.X * 1.25);
+ y = (i/s.geom.X) * (imgsize.Y * 1.25);
+ } else {
+ x = (i%s.geom.X) * spacing.X;
+ y = (i/s.geom.X) * spacing.Y;
+ }
v2s32 p0(x,y);
core::rect<s32> rect = imgrect + s.pos + p0;
if(rect.isPointInside(p))
return ItemSpec(InventoryLocation(), "", -1);
}
-void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int phase,
+void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
bool &item_hovered)
{
video::IVideoDriver* driver = Environment->getVideoDriver();
core::rect<s32> imgrect(0,0,imgsize.X,imgsize.Y);
- for(s32 i=0; i<s.geom.X*s.geom.Y; i++)
- {
+ for (s32 i = 0; i < s.geom.X * s.geom.Y; i++) {
s32 item_i = i + s.start_item_i;
- if(item_i >= (s32) ilist->getSize())
+ if (item_i >= (s32)ilist->getSize())
break;
- s32 x = (i%s.geom.X) * spacing.X;
- s32 y = (i/s.geom.X) * spacing.Y;
+
+ s32 x;
+ s32 y;
+ if (s.real_coordinates) {
+ x = (i%s.geom.X) * (imgsize.X * 1.25);
+ y = (i/s.geom.X) * (imgsize.Y * 1.25);
+ } else {
+ x = (i%s.geom.X) * spacing.X;
+ y = (i/s.geom.X) * spacing.Y;
+ }
v2s32 p(x,y);
core::rect<s32> rect = imgrect + s.pos + p;
- ItemStack item;
- if(ilist)
- item = ilist->getItem(item_i);
+ ItemStack item = ilist->getItem(item_i);
bool selected = m_selected_item
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
(hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
- if (phase == 0) {
+ if (layer == 0) {
if (hovering) {
item_hovered = true;
driver->draw2DRectangle(m_slotbg_h, rect, &AbsoluteClippingRect);
v2s32(x2 + border, y2)), NULL);
}
- if(phase == 1)
- {
- // Draw item stack
- if(selected)
- {
+ if (layer == 1) {
+ if (selected)
item.takeItem(m_selected_amount);
- }
- if(!item.empty())
- {
+
+ if (!item.empty()) {
+ // Draw item stack
drawItemStack(driver, m_font, item,
rect, &AbsoluteClippingRect, m_client,
rotation_kind);
- }
-
- // Draw tooltip
- std::wstring tooltip_text;
- if (hovering && !m_selected_item) {
- const std::string &desc = item.metadata.getString("description");
- if (desc.empty())
- tooltip_text =
- utf8_to_wide(item.getDefinition(m_client->idef()).description);
- else
- tooltip_text = utf8_to_wide(desc);
-
- if (!item.name.empty()) {
- if (tooltip_text.empty())
- tooltip_text = utf8_to_wide(item.name);
+ // Draw tooltip
+ if (hovering && !m_selected_item) {
+ std::string tooltip = item.getDescription(m_client->idef());
if (m_tooltip_append_itemname)
- tooltip_text += utf8_to_wide(" [" + item.name + "]");
+ tooltip += "\n[" + item.name + "]";
+ showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color,
+ m_default_tooltip_bgcolor);
}
}
- if (!tooltip_text.empty()) {
- showTooltip(tooltip_text, m_default_tooltip_color,
- m_default_tooltip_bgcolor);
- }
}
}
}
m_tooltip_element->setVisible(false);
+ for (const auto &pair : m_tooltip_rects) {
+ if (pair.first.isPointInside(m_pointer)) {
+ const std::wstring &text = pair.second.tooltip;
+ if (!text.empty()) {
+ showTooltip(text, pair.second.color, pair.second.bgcolor);
+ break;
+ }
+ }
+ }
+
/*
Draw backgrounds
*/
core::rect<s32> imgrect(0, 0, spec.geom.X, spec.geom.Y);
// Image rectangle on screen
core::rect<s32> rect = imgrect + spec.pos;
+ // Middle rect for 9-slicing
+ core::rect<s32> middle = spec.middle;
if (spec.clip) {
core::dimension2d<s32> absrec_size = AbsoluteRect.getSize();
AbsoluteRect.UpperLeftCorner.Y + absrec_size.Height + spec.pos.Y);
}
- const video::SColor color(255,255,255,255);
- const video::SColor colors[] = {color,color,color,color};
- draw2DImageFilterScaled(driver, texture, rect,
- core::rect<s32>(core::position2d<s32>(0,0),
- core::dimension2di(texture->getOriginalSize())),
- NULL/*&AbsoluteClippingRect*/, colors, true);
+ if (middle.getArea() == 0) {
+ const video::SColor color(255, 255, 255, 255);
+ const video::SColor colors[] = {color, color, color, color};
+ draw2DImageFilterScaled(driver, texture, rect,
+ core::rect<s32>(core::position2d<s32>(0, 0),
+ core::dimension2di(texture->getOriginalSize())),
+ NULL/*&AbsoluteClippingRect*/, colors, true);
+ } else {
+ // `-x` is interpreted as `w - x`
+ if (middle.LowerRightCorner.X < 0) {
+ middle.LowerRightCorner.X += texture->getOriginalSize().Width;
+ }
+ if (middle.LowerRightCorner.Y < 0) {
+ middle.LowerRightCorner.Y += texture->getOriginalSize().Height;
+ }
+ draw2DImage9Slice(driver, texture, rect, middle);
+ }
} else {
errorstream << "GUIFormSpecMenu::drawMenu() Draw backgrounds unable to load texture:" << std::endl;
errorstream << "\t" << spec.name << std::endl;
for (const GUIFormSpecMenu::BoxDrawSpec &spec : m_boxes) {
irr::video::SColor todraw = spec.color;
- todraw.setAlpha(140);
-
core::rect<s32> rect(spec.pos.X,spec.pos.Y,
spec.pos.X + spec.geom.X,spec.pos.Y + spec.geom.Y);
/*
Draw items
- Phase 0: Item slot rectangles
- Phase 1: Item images; prepare tooltip
+ Layer 0: Item slot rectangles
+ Layer 1: Item images; prepare tooltip
*/
bool item_hovered = false;
- int start_phase = 0;
- for (int phase = start_phase; phase <= 1; phase++) {
+ for (int layer = 0; layer < 2; layer++) {
for (const GUIFormSpecMenu::ListDrawSpec &spec : m_inventorylists) {
- drawList(spec, phase, item_hovered);
+ drawList(spec, layer, item_hovered);
}
}
if (!item_hovered) {
void GUIFormSpecMenu::updateSelectedItem()
{
- // If the selected stack has become empty for some reason, deselect it.
- // If the selected stack has become inaccessible, deselect it.
- // If the selected stack has become smaller, adjust m_selected_amount.
- ItemStack selected = verifySelectedItem();
-
- // WARNING: BLACK MAGIC
- // See if there is a stack suited for our current guess.
- // If such stack does not exist, clear the guess.
- if (!m_selected_content_guess.name.empty() &&
- selected.name == m_selected_content_guess.name &&
- selected.count == m_selected_content_guess.count){
- // Selected item fits the guess. Skip the black magic.
- } else if (!m_selected_content_guess.name.empty()) {
- bool found = false;
- for(u32 i=0; i<m_inventorylists.size() && !found; i++){
- const ListDrawSpec &s = m_inventorylists[i];
+ verifySelectedItem();
+
+ // If craftresult is nonempty and nothing else is selected, select it now.
+ if (!m_selected_item) {
+ for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
+ if (s.listname != "craftpreview")
+ continue;
+
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
- if(!inv)
+ if (!inv)
continue;
- InventoryList *list = inv->getList(s.listname);
- if(!list)
+
+ InventoryList *list = inv->getList("craftresult");
+
+ if (!list || list->getSize() == 0)
continue;
- for(s32 i=0; i<s.geom.X*s.geom.Y && !found; i++){
- u32 item_i = i + s.start_item_i;
- if(item_i >= list->getSize())
- continue;
- ItemStack stack = list->getItem(item_i);
- if(stack.name == m_selected_content_guess.name &&
- stack.count == m_selected_content_guess.count){
- found = true;
- infostream<<"Client: Changing selected content guess to "
- <<s.inventoryloc.dump()<<" "<<s.listname
- <<" "<<item_i<<std::endl;
- delete m_selected_item;
- m_selected_item = new ItemSpec(s.inventoryloc, s.listname, item_i);
- m_selected_amount = stack.count;
- }
- }
- }
- if(!found){
- infostream<<"Client: Discarding selected content guess: "
- <<m_selected_content_guess.getItemString()<<std::endl;
- m_selected_content_guess.name = "";
- }
- }
- // If craftresult is nonempty and nothing else is selected, select it now.
- if(!m_selected_item)
- {
- for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
- if(s.listname == "craftpreview")
- {
- Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
- InventoryList *list = inv->getList("craftresult");
- if(list && list->getSize() >= 1 && !list->getItem(0).empty())
- {
- m_selected_item = new ItemSpec;
- m_selected_item->inventoryloc = s.inventoryloc;
- m_selected_item->listname = "craftresult";
- m_selected_item->i = 0;
- m_selected_amount = 0;
- m_selected_dragging = false;
- break;
- }
- }
+ const ItemStack &item = list->getItem(0);
+ if (item.empty())
+ continue;
+
+ // Grab selected item from the crafting result list
+ m_selected_item = new ItemSpec;
+ m_selected_item->inventoryloc = s.inventoryloc;
+ m_selected_item->listname = "craftresult";
+ m_selected_item->i = 0;
+ m_selected_amount = item.count;
+ m_selected_dragging = false;
+ break;
}
}
// If craftresult is selected, keep the whole stack selected
- if(m_selected_item && m_selected_item->listname == "craftresult")
- {
+ if (m_selected_item && m_selected_item->listname == "craftresult")
m_selected_amount = verifySelectedItem().count;
- }
}
ItemStack GUIFormSpecMenu::verifySelectedItem()
if(list && (u32) m_selected_item->i < list->getSize())
{
ItemStack stack = list->getItem(m_selected_item->i);
- if(m_selected_amount > stack.count)
- m_selected_amount = stack.count;
- if(!stack.empty())
+ if (!m_selected_swap.empty()) {
+ if (m_selected_swap.name == stack.name &&
+ m_selected_swap.count == stack.count)
+ m_selected_swap.clear();
+ } else {
+ m_selected_amount = std::min(m_selected_amount, stack.count);
+ }
+
+ if (!stack.empty())
return stack;
}
}
gui::IGUIElement *focused = Environment->getFocus();
if (focused && isMyChild(focused) &&
(focused->getType() == gui::EGUIET_LIST_BOX ||
- focused->getType() == gui::EGUIET_CHECK_BOX)) {
+ focused->getType() == gui::EGUIET_CHECK_BOX) &&
+ (focused->getParent()->getType() != gui::EGUIET_COMBO_BOX ||
+ event.KeyInput.Key != KEY_RETURN)) {
OnEvent(event);
return true;
}
}
}
- #ifdef __ANDROID__
- // display software keyboard when clicking edit boxes
- if (event.EventType == EET_MOUSE_INPUT_EVENT
- && event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
- gui::IGUIElement *hovered =
- Environment->getRootGUIElement()->getElementFromPoint(
- core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
- if ((hovered) && (hovered->getType() == irr::gui::EGUIET_EDIT_BOX)) {
- bool retval = hovered->OnEvent(event);
- if (retval) {
- Environment->setFocus(hovered);
- }
- m_JavaDialogFieldName = getNameByID(hovered->getID());
- std::string message = gettext("Enter ");
- std::string label = wide_to_utf8(getLabelByID(hovered->getID()));
- if (label == "") {
- label = "text";
- }
- message += gettext(label) + ":";
-
- /* single line text input */
- int type = 2;
-
- /* multi line text input */
- if (((gui::IGUIEditBox*) hovered)->isMultiLineEnabled()) {
- type = 1;
- }
-
- /* passwords are always single line */
- if (((gui::IGUIEditBox*) hovered)->isPasswordBox()) {
- type = 3;
- }
-
- porting::showInputDialog(gettext("ok"), "",
- wide_to_utf8(((gui::IGUIEditBox*) hovered)->getText()),
- type);
- return retval;
- }
- }
-
- if (event.EventType == EET_TOUCH_INPUT_EVENT)
- {
- SEvent translated;
- memset(&translated, 0, sizeof(SEvent));
- translated.EventType = EET_MOUSE_INPUT_EVENT;
- gui::IGUIElement* root = Environment->getRootGUIElement();
-
- if (!root) {
- errorstream
- << "GUIFormSpecMenu::preprocessEvent unable to get root element"
- << std::endl;
- return false;
- }
- gui::IGUIElement* hovered = root->getElementFromPoint(
- core::position2d<s32>(
- event.TouchInput.X,
- event.TouchInput.Y));
-
- translated.MouseInput.X = event.TouchInput.X;
- translated.MouseInput.Y = event.TouchInput.Y;
- translated.MouseInput.Control = false;
-
- bool dont_send_event = false;
-
- if (event.TouchInput.touchedCount == 1) {
- switch (event.TouchInput.Event) {
- case ETIE_PRESSED_DOWN:
- m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
- translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
- m_down_pos = m_pointer;
- break;
- case ETIE_MOVED:
- m_pointer = v2s32(event.TouchInput.X,event.TouchInput.Y);
- translated.MouseInput.Event = EMIE_MOUSE_MOVED;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
- break;
- case ETIE_LEFT_UP:
- translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
- translated.MouseInput.ButtonStates = 0;
- hovered = root->getElementFromPoint(m_down_pos);
- /* we don't have a valid pointer element use last
- * known pointer pos */
- translated.MouseInput.X = m_pointer.X;
- translated.MouseInput.Y = m_pointer.Y;
-
- /* reset down pos */
- m_down_pos = v2s32(0,0);
- break;
- default:
- dont_send_event = true;
- //this is not supposed to happen
- errorstream
- << "GUIFormSpecMenu::preprocessEvent unexpected usecase Event="
- << event.TouchInput.Event << std::endl;
- }
- } else if ( (event.TouchInput.touchedCount == 2) &&
- (event.TouchInput.Event == ETIE_PRESSED_DOWN) ) {
- hovered = root->getElementFromPoint(m_down_pos);
-
- translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
- translated.MouseInput.ButtonStates = EMBSM_LEFT | EMBSM_RIGHT;
- translated.MouseInput.X = m_pointer.X;
- translated.MouseInput.Y = m_pointer.Y;
-
- if (hovered) {
- hovered->OnEvent(translated);
- }
-
- translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
- translated.MouseInput.ButtonStates = EMBSM_LEFT;
-
-
- if (hovered) {
- hovered->OnEvent(translated);
- }
- dont_send_event = true;
- }
- /* ignore unhandled 2 touch events ... accidental moving for example */
- else if (event.TouchInput.touchedCount == 2) {
- dont_send_event = true;
- }
- else if (event.TouchInput.touchedCount > 2) {
- errorstream
- << "GUIFormSpecMenu::preprocessEvent to many multitouch events "
- << event.TouchInput.touchedCount << " ignoring them" << std::endl;
- }
-
- if (dont_send_event) {
- return true;
- }
-
- /* check if translated event needs to be preprocessed again */
- if (preprocessEvent(translated)) {
- return true;
- }
- if (hovered) {
- grab();
- bool retval = hovered->OnEvent(translated);
-
- if (event.TouchInput.Event == ETIE_LEFT_UP) {
- /* reset pointer */
- m_pointer = v2s32(0,0);
- }
- drop();
- return retval;
- }
- }
- #endif
-
if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
/* TODO add a check like:
if (event.JoystickEvent != joystick_we_listen_for)
return handled;
}
- return false;
+ return GUIModalMenu::preprocessEvent(event);
}
/******************************************************************************/
}
}
+enum ButtonEventType : u8
+{
+ BET_LEFT,
+ BET_RIGHT,
+ BET_MIDDLE,
+ BET_WHEEL_UP,
+ BET_WHEEL_DOWN,
+ BET_UP,
+ BET_DOWN,
+ BET_MOVE,
+ BET_OTHER
+};
+
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if (event.EventType==EET_KEY_INPUT_EVENT) {
s_count = list_s->getItem(s.i).count;
} while(0);
- bool identical = (m_selected_item != NULL) && s.isValid() &&
+ bool identical = m_selected_item && s.isValid() &&
(inv_selected == inv_s) &&
(m_selected_item->listname == s.listname) &&
(m_selected_item->i == s.i);
- // buttons: 0 = left, 1 = right, 2 = middle
- // up/down: 0 = down (press), 1 = up (release), 2 = unknown event, -1 movement
- int button = 0;
- int updown = 2;
- if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN)
- { button = 0; updown = 0; }
- else if (event.MouseInput.Event == EMIE_RMOUSE_PRESSED_DOWN)
- { button = 1; updown = 0; }
- else if (event.MouseInput.Event == EMIE_MMOUSE_PRESSED_DOWN)
- { button = 2; updown = 0; }
- else if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP)
- { button = 0; updown = 1; }
- else if (event.MouseInput.Event == EMIE_RMOUSE_LEFT_UP)
- { button = 1; updown = 1; }
- else if (event.MouseInput.Event == EMIE_MMOUSE_LEFT_UP)
- { button = 2; updown = 1; }
- else if (event.MouseInput.Event == EMIE_MOUSE_MOVED)
- { updown = -1;}
+ ButtonEventType button = BET_LEFT;
+ ButtonEventType updown = BET_OTHER;
+ switch (event.MouseInput.Event) {
+ case EMIE_LMOUSE_PRESSED_DOWN:
+ button = BET_LEFT; updown = BET_DOWN;
+ break;
+ case EMIE_RMOUSE_PRESSED_DOWN:
+ button = BET_RIGHT; updown = BET_DOWN;
+ break;
+ case EMIE_MMOUSE_PRESSED_DOWN:
+ button = BET_MIDDLE; updown = BET_DOWN;
+ break;
+ case EMIE_MOUSE_WHEEL:
+ button = (event.MouseInput.Wheel > 0) ?
+ BET_WHEEL_UP : BET_WHEEL_DOWN;
+ updown = BET_DOWN;
+ break;
+ case EMIE_LMOUSE_LEFT_UP:
+ button = BET_LEFT; updown = BET_UP;
+ break;
+ case EMIE_RMOUSE_LEFT_UP:
+ button = BET_RIGHT; updown = BET_UP;
+ break;
+ case EMIE_MMOUSE_LEFT_UP:
+ button = BET_MIDDLE; updown = BET_UP;
+ break;
+ case EMIE_MOUSE_MOVED:
+ updown = BET_MOVE;
+ break;
+ default:
+ break;
+ }
// Set this number to a positive value to generate a move action
// from m_selected_item to s.
// Set this number to a positive value to generate a craft action at s.
u32 craft_amount = 0;
- if (updown == 0) {
+ switch (updown) {
+ case BET_DOWN:
// Some mouse button has been pressed
//infostream<<"Mouse button "<<button<<" pressed at p=("
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) {
+ craft_amount = (button == BET_MIDDLE ? 10 : 1);
+ } else if (!m_selected_item) {
+ if (s_count && button != BET_WHEEL_UP) {
// Non-empty stack has been clicked: select or shift-move it
m_selected_item = new ItemSpec(s);
u32 count;
- if (button == 1) // right
+ if (button == BET_RIGHT)
count = (s_count + 1) / 2;
- else if (button == 2) // middle
+ else if (button == BET_MIDDLE)
count = MYMIN(s_count, 10);
+ else if (button == BET_WHEEL_DOWN)
+ count = 1;
else // left
count = s_count;
if (!event.MouseInput.Shift) {
// no shift: select item
m_selected_amount = count;
- m_selected_dragging = true;
+ m_selected_dragging = button != BET_WHEEL_DOWN;
m_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;
+ // shift pressed: move item, right click moves 1
+ shift_move_amount = button == BET_RIGHT ? 1 : count;
}
}
} else { // m_selected_item != NULL
if (s.isValid()) {
// Clicked a slot: move
- if (button == 1) // right
+ if (button == BET_RIGHT || button == BET_WHEEL_UP)
move_amount = 1;
- else if (button == 2) // middle
+ else if (button == BET_MIDDLE)
move_amount = MYMIN(m_selected_amount, 10);
- else // left
+ else if (button == BET_LEFT)
move_amount = m_selected_amount;
+ // else wheeldown
if (identical) {
- if (move_amount >= m_selected_amount)
- m_selected_amount = 0;
- else
- m_selected_amount -= move_amount;
- move_amount = 0;
+ if (button == BET_WHEEL_DOWN) {
+ if (m_selected_amount < s_count)
+ ++m_selected_amount;
+ } else {
+ if (move_amount >= m_selected_amount)
+ m_selected_amount = 0;
+ else
+ m_selected_amount -= move_amount;
+ move_amount = 0;
+ }
}
- }
- else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
+ } else if (!getAbsoluteClippingRect().isPointInside(m_pointer)
+ && button != BET_WHEEL_DOWN) {
// Clicked outside of the window: drop
- if (button == 1) // right
+ if (button == BET_RIGHT || button == BET_WHEEL_UP)
drop_amount = 1;
- else if (button == 2) // middle
+ else if (button == BET_MIDDLE)
drop_amount = MYMIN(m_selected_amount, 10);
else // left
drop_amount = m_selected_amount;
}
}
- }
- else if (updown == 1) {
+ break;
+ case BET_UP:
// 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) {
- // Dragged to different slot: move all selected
- move_amount = m_selected_amount;
+ if (m_selected_dragging && m_selected_item) {
+ if (s.isValid()) {
+ if (!identical) {
+ // Dragged to different slot: move all selected
+ move_amount = m_selected_amount;
+ }
+ } else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
+ // Dragged outside of window: drop all selected
+ drop_amount = m_selected_amount;
}
- } 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;
// + click changes to drop item when moved mode
if (m_selected_item)
m_auto_place = true;
- } else if (updown == -1) {
+ break;
+ case BET_MOVE:
// 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()) {
+ if (m_selected_item && s.isValid() && s.listname != "craftpreview") {
// Move 1 item
// TODO: middle mouse to move 10 items might be handy
if (m_auto_place) {
move_amount = 1;
}
}
+ break;
+ default:
+ break;
}
// Possibly send inventory action to server
// 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_client->idef());
+ bool move = true;
// If source stack cannot be added to destination stack at all,
// they are swapped
- if ((leftover.count == stack_from.count) &&
- (leftover.name == stack_from.name)) {
- m_selected_amount = stack_to.count;
- // In case the server doesn't directly swap them but instead
- // moves stack_to somewhere else, set this
- m_selected_content_guess = stack_to;
- m_selected_content_guess_inventory = s.inventoryloc;
+ if (leftover.count == stack_from.count &&
+ leftover.name == stack_from.name) {
+
+ if (m_selected_swap.empty()) {
+ m_selected_amount = stack_to.count;
+ m_selected_dragging = false;
+
+ // WARNING: BLACK MAGIC, BUT IN A REDUCED SET
+ // Skip next validation checks due async inventory calls
+ m_selected_swap = stack_to;
+ } else {
+ move = false;
+ }
}
// Source stack goes fully into destination stack
else if (leftover.empty()) {
m_selected_amount -= move_amount;
- m_selected_content_guess = ItemStack(); // Clear
}
// Source stack goes partly into destination stack
else {
move_amount -= leftover.count;
m_selected_amount -= move_amount;
- m_selected_content_guess = ItemStack(); // Clear
}
- infostream << "Handing IAction::Move to manager" << std::endl;
- IMoveAction *a = new IMoveAction();
- a->count = move_amount;
- a->from_inv = m_selected_item->inventoryloc;
- a->from_list = m_selected_item->listname;
- a->from_i = m_selected_item->i;
- a->to_inv = s.inventoryloc;
- a->to_list = s.listname;
- a->to_i = s.i;
- m_invmgr->inventoryAction(a);
+ if (move) {
+ infostream << "Handing IAction::Move to manager" << std::endl;
+ IMoveAction *a = new IMoveAction();
+ a->count = move_amount;
+ a->from_inv = m_selected_item->inventoryloc;
+ a->from_list = m_selected_item->listname;
+ a->from_i = m_selected_item->i;
+ a->to_inv = s.inventoryloc;
+ a->to_list = s.listname;
+ a->to_i = s.i;
+ m_invmgr->inventoryAction(a);
+ }
} else if (shift_move_amount > 0) {
u32 mis = m_inventory_rings.size();
u32 i = 0;
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;
- }
- }
- }
+
+ 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);
} while (0);
} else if (drop_amount > 0) {
- m_selected_content_guess = ItemStack(); // Clear
-
// Send IAction::Drop
assert(m_selected_item && m_selected_item->isValid());
if (m_selected_item == NULL ||
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
- m_selected_content_guess = ItemStack(); // Clear
-
assert(inv_s);
// Send IACTION_CRAFT
// If m_selected_amount has been decreased to zero, deselect
if (m_selected_amount == 0) {
+ m_selected_swap.clear();
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;
}
}
return L"";
}
+
+StyleSpec GUIFormSpecMenu::getStyleForElement(const std::string &type,
+ const std::string &name, const std::string &parent_type) {
+ StyleSpec ret;
+
+ if (!parent_type.empty()) {
+ auto it = theme_by_type.find(parent_type);
+ if (it != theme_by_type.end()) {
+ ret |= it->second;
+ }
+ }
+
+ auto it = theme_by_type.find(type);
+ if (it != theme_by_type.end()) {
+ ret |= it->second;
+ }
+
+ it = theme_by_name.find(name);
+ if (it != theme_by_name.end()) {
+ ret |= it->second;
+ }
+
+ return ret;
+}