summaryrefslogtreecommitdiff
path: root/src/gui/guiFormSpecMenu.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/guiFormSpecMenu.cpp')
-rw-r--r--src/gui/guiFormSpecMenu.cpp864
1 files changed, 671 insertions, 193 deletions
diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp
index 6c404728f..aee7da869 100644
--- a/src/gui/guiFormSpecMenu.cpp
+++ b/src/gui/guiFormSpecMenu.cpp
@@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include <iterator>
#include <sstream>
#include <limits>
+#include "guiButton.h"
#include "guiFormSpecMenu.h"
#include "guiTable.h"
#include "constants.h"
@@ -66,8 +67,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#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; \
}
/*
@@ -86,7 +87,7 @@ inline u32 clamp_u8(s32 value)
GUIFormSpecMenu::GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement *parent, s32 id, IMenuManager *menumgr,
Client *client, ISimpleTextureSource *tsrc, IFormSource *fsrc, TextDest *tdst,
- std::string formspecPrepend,
+ const std::string &formspecPrepend,
bool remap_dbl_click):
GUIModalMenu(RenderingEngine::get_gui_env(), parent, id, menumgr),
m_invmgr(client),
@@ -270,6 +271,25 @@ v2s32 GUIFormSpecMenu::getElementBasePos(bool absolute,
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,',');
@@ -306,8 +326,8 @@ void GUIFormSpecMenu::parseContainer(parserData* data, const std::string &elemen
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;
@@ -348,13 +368,19 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
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 = getElementBasePos(true, &v_pos);
+ 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]);
@@ -369,7 +395,7 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
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;
@@ -430,20 +456,37 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
MY_CHECKPOS("checkbox",0);
- v2s32 pos = getElementBasePos(false, &v_pos);
-
bool fselected = false;
if (selected == "true")
fselected = true;
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
- s32 spacing = Environment->getSkin()->getSize(gui::EGDS_CHECK_BOX_WIDTH) + 7;
+ 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;
- 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 + spacing,
- pos.Y + ((imgsize.Y / 2) + m_btn_height));
+ 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,
@@ -457,6 +500,9 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
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);
}
@@ -474,24 +520,25 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
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 = getElementBasePos(false, &v_pos);
+ 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]) * spacing.X;
- dim.Y = stof(v_dim[1]) * spacing.Y;
-
core::rect<s32> rect =
core::rect<s32>(pos.X, pos.Y, pos.X + dim.X, pos.Y + dim.Y);
@@ -512,6 +559,9 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
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]));
@@ -539,10 +589,17 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
MY_CHECKPOS("image", 0);
MY_CHECKGEOM("image", 1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ 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;
@@ -580,10 +637,17 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen
MY_CHECKPOS("itemimage",0);
MY_CHECKGEOM("itemimage",1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ 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;
@@ -609,14 +673,23 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
MY_CHECKPOS("button",0);
MY_CHECKGEOM("button",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * 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;
@@ -632,8 +705,38 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
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);
@@ -649,9 +752,8 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme
{
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]);
@@ -659,25 +761,58 @@ void GUIFormSpecMenu::parseBackground(parserData* data, const std::string &eleme
MY_CHECKPOS("background",0);
MY_CHECKGEOM("background",1);
- v2s32 pos = getElementBasePos(true, &v_pos);
- pos.X -= (spacing.X - (float)imgsize.X) / 2;
- pos.Y -= (spacing.Y - (float)imgsize.Y) / 2;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * spacing.Y;
+
+ 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;
}
+ 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, clip);
+ m_backgrounds.emplace_back(name, pos, geom, middle, clip);
return;
}
@@ -736,10 +871,17 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
MY_CHECKPOS("table",0);
MY_CHECKGEOM("table",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * 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);
@@ -773,6 +915,9 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
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;
@@ -803,11 +948,17 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
MY_CHECKPOS("textlist",0);
MY_CHECKGEOM("textlist",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * 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);
@@ -841,6 +992,9 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
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;
@@ -864,12 +1018,29 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
MY_CHECKPOS("dropdown",0);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
+ v2s32 geom;
+ core::rect<s32> rect;
+
+ if (data->real_coordinates) {
+ std::vector<std::string> v_geom = split(parts[1],',');
+
+ if (v_geom.size() == 1)
+ v_geom.emplace_back("1");
+
+ MY_CHECKGEOM("dropdown",1);
- s32 width = stof(parts[1]) * spacing.Y;
+ 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;
- core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
- pos.X + width, pos.Y + (m_btn_height * 2));
+ rect = core::rect<s32>(pos.X, pos.Y,
+ pos.X + width, pos.Y + (m_btn_height * 2));
+ }
FieldSpec spec(
name,
@@ -896,6 +1067,9 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
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>());
@@ -934,15 +1108,22 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
MY_CHECKPOS("pwdfield",0);
MY_CHECKGEOM("pwdfield",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
- pos -= padding;
-
+ v2s32 pos;
v2s32 geom;
- 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;
+ 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);
@@ -972,6 +1153,11 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
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;
@@ -1022,12 +1208,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
true, Environment, this, spec.fid, rect, is_editable, is_multiline);
e->drop();
} else {
- if (is_multiline)
+ if (is_multiline) {
e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true,
Environment, this, spec.fid, rect, is_editable, true);
- else if (is_editable)
+ e->drop();
+ } else if (is_editable) {
e = Environment->addEditBox(spec.fdefault.c_str(), rect, true,
this, spec.fid);
+ }
}
if (e) {
@@ -1048,6 +1236,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
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()) {
@@ -1117,23 +1313,29 @@ void GUIFormSpecMenu::parseTextArea(parserData* data, std::vector<std::string>&
MY_CHECKPOS(type,0);
MY_CHECKGEOM(type,1);
- v2s32 pos = getElementBasePos(false, &v_pos);
- pos -= padding;
-
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * 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);
@@ -1197,46 +1399,79 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
MY_CHECKPOS("label",0);
- 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;
-
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_colors = translate_string(
+ utf8_to_wide(unescape_string(lines[i])));
+ // Without color escapes to get the font dimensions
+ std::wstring wlabel_plain = unescape_enriched(wlabel_colors);
+
+ 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_plain.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_plain.c_str()).Width,
+ pos.Y + m_btn_height);
+ }
+
FieldSpec spec(
"",
- wlabel,
+ wlabel_colors,
L"",
258+m_fields.size()
);
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)
@@ -1252,15 +1487,35 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
MY_CHECKPOS("vertlabel",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ 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;
+
+ // 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));
- core::rect<s32> rect = core::rect<s32>(
- pos.X, pos.Y+((imgsize.Y/2)- m_btn_height),
+ } 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;
@@ -1278,9 +1533,14 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
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;
}
@@ -1304,11 +1564,6 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
MY_CHECKPOS("imagebutton",0);
MY_CHECKGEOM("imagebutton",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
- v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
- geom.Y = (stof(v_geom[1]) * spacing.Y) - (spacing.Y - imgsize.Y);
-
bool noclip = false;
bool drawborder = true;
std::string pressed_image_name;
@@ -1324,9 +1579,22 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
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);
@@ -1341,7 +1609,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
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;
@@ -1358,14 +1626,21 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
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;
@@ -1376,25 +1651,43 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
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;
}
@@ -1408,15 +1701,26 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
spec.ftype = f_TabHeader;
v2s32 pos;
- {
+ v2s32 geom;
+
+ 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();
}
- v2s32 geom;
- 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,
pos.Y+geom.Y);
@@ -1425,17 +1729,22 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
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) &&
@@ -1476,10 +1785,17 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
MY_CHECKPOS("itemimagebutton",0);
MY_CHECKGEOM("itemimagebutton",1);
- v2s32 pos = getElementBasePos(false, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = (stof(v_geom[0]) * spacing.X) - (spacing.X - imgsize.X);
- geom.Y = (stof(v_geom[1]) * 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);
@@ -1504,6 +1820,10 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
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);
}
@@ -1513,7 +1833,11 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
spec.rect=rect;
m_fields.push_back(spec);
- pos = getElementBasePos(true, &v_pos);
+ 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;
@@ -1534,10 +1858,17 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
MY_CHECKPOS("box",0);
MY_CHECKGEOM("box",1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * 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;
@@ -1640,13 +1971,20 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
std::vector<std::string> v_pos = split(parts[0], ',');
std::vector<std::string> v_geom = split(parts[1], ',');
- MY_CHECKPOS("tooltip", 0);
+ MY_CHECKPOS("tooltip", 0);
MY_CHECKGEOM("tooltip", 1);
- v2s32 pos = getElementBasePos(true, &v_pos);
+ v2s32 pos;
v2s32 geom;
- geom.X = stof(v_geom[0]) * spacing.X;
- geom.Y = stof(v_geom[1]) * 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;
+ }
irr::core::rect<s32> rect(pos, pos + geom);
m_tooltip_rects.emplace_back(rect, spec);
@@ -1772,12 +2110,70 @@ void GUIFormSpecMenu::parseAnchor(parserData *data, const std::string &element)
<< "'" << 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
@@ -1837,8 +2233,8 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
return;
}
- if (type == "background") {
- parseBackground(data,description);
+ if (type == "background" || type == "background9") {
+ parseBackground(data, description);
return;
}
@@ -1932,6 +2328,21 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
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;
@@ -2002,6 +2413,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
m_inventory_rings.clear();
m_static_texts.clear();
m_dropdowns.clear();
+ theme_by_name.clear();
+ theme_by_type.clear();
m_bgfullscreen = false;
@@ -2096,6 +2509,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
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
if (m_lock) {
@@ -2186,10 +2610,18 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
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,
@@ -2221,9 +2653,17 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
pos_offset = v2f32();
if (enable_prepends) {
+ // Backup the coordinates so that prepends can use the coordinates of choice.
+ bool rc_backup = mydata.real_coordinates;
+ u16 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++) {
@@ -2313,8 +2753,16 @@ GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
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))
@@ -2356,8 +2804,15 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
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 = ilist->getItem(item_i);
@@ -2401,37 +2856,23 @@ void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
}
if (layer == 1) {
- // Draw item stack
if (selected)
item.takeItem(m_selected_amount);
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);
- else if (m_tooltip_append_itemname)
- 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 += "\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);
- }
}
}
}
@@ -2510,6 +2951,8 @@ void GUIFormSpecMenu::drawMenu()
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();
@@ -2519,12 +2962,23 @@ void GUIFormSpecMenu::drawMenu()
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;
@@ -3406,7 +3860,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// 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 && 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) {
@@ -3740,3 +4194,27 @@ std::wstring GUIFormSpecMenu::getLabelByID(s32 id)
}
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;
+}