170 changes: 113 additions & 57 deletions src/gui/guiFormSpecMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -313,25 +313,33 @@ v2s32 GUIFormSpecMenu::getOldElementPosition(const v2f32 &pos) const
return v2s32(pos_f.X, pos_f.Y);
}

void GUIFormSpecMenu::createButton(ParserState *state, ElementSpec *espec)
void GUIFormSpecMenu::createButton(ElementSpec *espec)
{
core::recti rect;

if (state->real_coordinates) {
rect = getElementRect(espec->getVector2df("pos"), espec->getVector2df("size"));
v2f32 pos = espec->getVector2df("pos");
v2f32 size = espec->getVector2df("size");

bool is_image = espec->has("image");

if (!espec->getBool("old_coord")) {
rect = getElementRect(pos, size);
} else {
v2s32 pos = getOldElementPosition(espec->getVector2df("pos"));
v2f32 size = espec->getVector2df("size");
size.X = (size.X * spacing.X) - (spacing.X - imgsize.X);
pos.Y += (size.Y * imgsize.Y) / 2;
if (is_image) {
size.X = (size.X * spacing.X) - (spacing.X - imgsize.X);
size.Y = (size.X * spacing.Y) - (spacing.Y - imgsize.Y);
rect = core::recti(getOldElementPosition(pos),
core::dimension2d<s32>(size.X, size.Y));
} else {
v2s32 pos = getOldElementPosition(espec->getVector2df("pos"));
size.X = (size.X * spacing.X) - (spacing.X - imgsize.X);
pos.Y += (size.Y * imgsize.Y) / 2;

rect = core::recti(pos.X, pos.Y - m_btn_height,
pos.X + size.X, pos.Y + m_btn_height);
rect = core::recti(pos.X, pos.Y - m_btn_height,
pos.X + size.X, pos.Y + m_btn_height);
}
}

if (!state->explicit_size) // TODO: Remove
warningstream << "Invalid use of button without a size[] element" << std::endl;

bool is_exit = espec->getBool("exit");

FieldSpec fspec(
Expand All @@ -340,17 +348,52 @@ void GUIFormSpecMenu::createButton(ParserState *state, ElementSpec *espec)
L"",
258 + m_fields.size()
);

fspec.ftype = f_Button;
fspec.is_exit = is_exit;

GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc,
state->current_parent, fspec.fid, fspec.flabel.c_str());

e->setStyles(getStyleForElement(is_exit ? "button_exit" : "button",
fspec.fname, is_exit ? "button" : ""));
irr_ptr<GUIButton> e(nullptr);
if (is_image) {
e = std::move(irr_ptr<GUIButton>(static_cast<GUIButton *>(
GUIButtonImage::addButton(Environment, rect, m_tsrc,
m_current_parent, fspec.fid, fspec.flabel.c_str()))));
e->setScaleImage(true);
} else {
e = std::move(irr_ptr<GUIButton>(GUIButton::addButton(Environment, rect, m_tsrc,
m_current_parent, fspec.fid, fspec.flabel.c_str())));
}
e->grab();

if (espec->getBool("focused"))
Environment->setFocus(e);
Environment->setFocus(e.get());

// Temporary styling
std::string type;
std::string inherit = "";
if (is_image)
type = "image_button";
else
type = "button";

if (is_exit) {
inherit = type;
type += "_exit";
}

auto style = getStyleForElement(type, fspec.fname, inherit);

// Override style properties with values specified directly in the element
if (espec->has("image"))
style[StyleSpec::STATE_DEFAULT].set(StyleSpec::FGIMG, espec->getString("image"));

if (espec->has("pressed_image"))
style[StyleSpec::STATE_PRESSED].set(StyleSpec::FGIMG, espec->getString("pressed_image"));

style[StyleSpec::STATE_DEFAULT].set(StyleSpec::NOCLIP, espec->getBool("noclip") ? "true" : "false");
style[StyleSpec::STATE_DEFAULT].set(StyleSpec::BORDER, espec->getBool("border", true) ? "true" : "false");

e->setStyles(style);
// End temporary styling

m_fields.push_back(fspec);
}
Expand Down Expand Up @@ -451,7 +494,7 @@ void GUIFormSpecMenu::parseScrollContainer(ParserState *data, const std::string
core::rect<s32> rect_clipper = core::rect<s32>(pos, pos + geom);

gui::IGUIElement *clipper = new gui::IGUIElement(EGUIET_ELEMENT, Environment,
data->current_parent, 0, rect_clipper);
m_current_parent, 0, rect_clipper);

// make mover
FieldSpec spec_mover(
Expand All @@ -466,7 +509,7 @@ void GUIFormSpecMenu::parseScrollContainer(ParserState *data, const std::string
GUIScrollContainer *mover = new GUIScrollContainer(Environment,
clipper, spec_mover.fid, rect_mover, orientation, scroll_factor);

data->current_parent = mover;
m_current_parent = mover;

m_scroll_containers.emplace_back(scrollbar_name, mover);

Expand All @@ -482,7 +525,7 @@ void GUIFormSpecMenu::parseScrollContainer(ParserState *data, const std::string

void GUIFormSpecMenu::parseScrollContainerEnd(ParserState *data)
{
if (data->current_parent == this || data->current_parent->getParent() == this ||
if (m_current_parent == this || m_current_parent->getParent() == this ||
container_stack.empty()) {
errorstream << "Invalid scroll_container end element, "
<< "no matching scroll_container start element" << std::endl;
Expand All @@ -499,7 +542,7 @@ void GUIFormSpecMenu::parseScrollContainerEnd(ParserState *data)
return;
}

data->current_parent = data->current_parent->getParent()->getParent();
m_current_parent = m_current_parent->getParent()->getParent();
pos_offset = container_stack.top();
container_stack.pop();
}
Expand Down Expand Up @@ -568,7 +611,7 @@ void GUIFormSpecMenu::parseList(ParserState *data, const std::string &element)
pos.X + (geom.X - 1) * slot_spacing.X + imgsize.X,
pos.Y + (geom.Y - 1) * slot_spacing.Y + imgsize.Y);

GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent,
GUIInventoryList *e = new GUIInventoryList(Environment, m_current_parent,
spec.fid, rect, m_invmgr, loc, listname, geom, start_i, imgsize,
slot_spacing, this, data->inventorylist_options, m_font);

Expand Down Expand Up @@ -676,7 +719,7 @@ void GUIFormSpecMenu::parseCheckbox(ParserState* data, const std::string &elemen
spec.ftype = f_CheckBox;

gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect,
data->current_parent, spec.fid, spec.flabel.c_str());
m_current_parent, spec.fid, spec.flabel.c_str());

auto style = getDefaultStyleForElement("checkbox", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
Expand Down Expand Up @@ -735,7 +778,7 @@ void GUIFormSpecMenu::parseScrollBar(ParserState* data, const std::string &eleme

spec.ftype = f_ScrollBar;
spec.send = true;
GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent,
GUIScrollBar *e = new GUIScrollBar(Environment, m_current_parent,
spec.fid, rect, is_horizontal, true);

auto style = getDefaultStyleForElement("scrollbar", name);
Expand Down Expand Up @@ -866,7 +909,7 @@ void GUIFormSpecMenu::parseImage(ParserState* data, const std::string &element)
1
);
core::rect<s32> rect(pos, pos + geom);
gui::IGUIImage *e = Environment->addImage(rect, data->current_parent,
gui::IGUIImage *e = Environment->addImage(rect, m_current_parent,
spec.fid, 0, true);
e->setImage(texture);
e->setScaleImage(true);
Expand Down Expand Up @@ -905,7 +948,7 @@ void GUIFormSpecMenu::parseImage(ParserState* data, const std::string &element)
258 + m_fields.size()
);
gui::IGUIImage *e = Environment->addImage(texture, pos, true,
data->current_parent, spec.fid, 0);
m_current_parent, spec.fid, 0);
auto style = getDefaultStyleForElement("image", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, data->formspec_version < 3));
m_fields.push_back(spec);
Expand Down Expand Up @@ -1018,7 +1061,7 @@ void GUIFormSpecMenu::parseItemImage(ParserState* data, const std::string &eleme
);
spec.ftype = f_ItemImage;

GUIItemImage *e = new GUIItemImage(Environment, data->current_parent, spec.fid,
GUIItemImage *e = new GUIItemImage(Environment, m_current_parent, spec.fid,
core::rect<s32>(pos, pos + geom), name, m_font, m_client);
auto style = getDefaultStyleForElement("item_image", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
Expand Down Expand Up @@ -1208,7 +1251,7 @@ void GUIFormSpecMenu::parseTable(ParserState* data, const std::string &element)
}

//now really show table
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
GUITable *e = new GUITable(Environment, m_current_parent, spec.fid,
rect, m_tsrc);

if (spec.fname == data->focused_element) {
Expand Down Expand Up @@ -1286,7 +1329,7 @@ void GUIFormSpecMenu::parseTextList(ParserState* data, const std::string &elemen
}

//now really show list
GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
GUITable *e = new GUITable(Environment, m_current_parent, spec.fid,
rect, m_tsrc);

if (spec.fname == data->focused_element) {
Expand Down Expand Up @@ -1365,7 +1408,7 @@ void GUIFormSpecMenu::parseDropDown(ParserState* data, const std::string &elemen
spec.send = true;

//now really show list
gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent,
gui::IGUIComboBox *e = Environment->addComboBox(rect, m_current_parent,
spec.fid);

if (spec.fname == data->focused_element) {
Expand Down Expand Up @@ -1453,7 +1496,7 @@ void GUIFormSpecMenu::parsePwdField(ParserState* data, const std::string &elemen

spec.send = true;
gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
data->current_parent, spec.fid);
m_current_parent, spec.fid);

if (spec.fname == data->focused_element) {
Environment->setFocus(e);
Expand All @@ -1464,7 +1507,7 @@ void GUIFormSpecMenu::parsePwdField(ParserState* data, const std::string &elemen
rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
data->current_parent, 0);
m_current_parent, 0);
}

e->setPasswordBox(true,L'*');
Expand Down Expand Up @@ -1500,7 +1543,7 @@ void GUIFormSpecMenu::createTextField(ParserState *data, FieldSpec &spec,
if (!is_editable && !is_multiline) {
// spec field id to 0, this stops submit searching for a value that isn't there
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
data->current_parent, 0);
m_current_parent, 0);
return;
}

Expand All @@ -1518,14 +1561,14 @@ void GUIFormSpecMenu::createTextField(ParserState *data, FieldSpec &spec,

if (use_intl_edit_box && g_settings->getBool("freetype")) {
e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment,
data->current_parent, spec.fid, rect, is_editable, is_multiline);
m_current_parent, spec.fid, rect, is_editable, is_multiline);
} else {
if (is_multiline) {
e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment,
data->current_parent, spec.fid, rect, is_editable, true);
m_current_parent, spec.fid, rect, is_editable, true);
} else if (is_editable) {
e = Environment->addEditBox(spec.fdefault.c_str(), rect, true,
data->current_parent, spec.fid);
m_current_parent, spec.fid);
e->grab();
}
}
Expand Down Expand Up @@ -1567,7 +1610,7 @@ void GUIFormSpecMenu::createTextField(ParserState *data, FieldSpec &spec,
rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
IGUIElement *t = gui::StaticText::add(Environment, spec.flabel.c_str(),
rect, false, true, data->current_parent, 0);
rect, false, true, m_current_parent, 0);

if (t)
t->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
Expand Down Expand Up @@ -1748,7 +1791,7 @@ void GUIFormSpecMenu::parseHyperText(ParserState *data, const std::string &eleme

spec.ftype = f_HyperText;
GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment,
data->current_parent, spec.fid, rect, m_client, m_tsrc);
m_current_parent, spec.fid, rect, m_client, m_tsrc);
e->drop();

m_fields.push_back(spec);
Expand Down Expand Up @@ -1831,7 +1874,7 @@ void GUIFormSpecMenu::parseLabel(ParserState* data, const std::string &element)
4
);
gui::IGUIStaticText *e = gui::StaticText::add(Environment,
spec.flabel.c_str(), rect, false, false, data->current_parent,
spec.flabel.c_str(), rect, false, false, m_current_parent,
spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);

Expand Down Expand Up @@ -1917,7 +1960,7 @@ void GUIFormSpecMenu::parseVertLabel(ParserState* data, const std::string &eleme
258 + m_fields.size()
);
gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(),
rect, false, false, data->current_parent, spec.fid);
rect, false, false, m_current_parent, spec.fid);
e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);

e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
Expand Down Expand Up @@ -1991,7 +2034,7 @@ void GUIFormSpecMenu::parseImageButton(ParserState* data, const std::string &ele
spec.is_exit = true;

GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc,
data->current_parent, spec.fid, spec.flabel.c_str());
m_current_parent, spec.fid, spec.flabel.c_str());

if (spec.fname == data->focused_element) {
Environment->setFocus(e);
Expand Down Expand Up @@ -2101,7 +2144,7 @@ void GUIFormSpecMenu::parseTabHeader(ParserState* data, const std::string &eleme
pos.Y+geom.Y);

gui::IGUITabControl *e = Environment->addTabControl(rect,
data->current_parent, show_background, show_border, spec.fid);
m_current_parent, 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(geom.Y);
Expand Down Expand Up @@ -2191,7 +2234,7 @@ void GUIFormSpecMenu::parseItemImageButton(ParserState* data, const std::string
);

GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment,
rect, m_tsrc, data->current_parent, spec_btn.fid, spec_btn.flabel.c_str(),
rect, m_tsrc, m_current_parent, spec_btn.fid, spec_btn.flabel.c_str(),
item_name, m_client);

auto style = getStyleForElement("item_image_button", spec_btn.fname, "image_button");
Expand Down Expand Up @@ -2262,7 +2305,7 @@ void GUIFormSpecMenu::parseBox(ParserState* data, const std::string &element)

core::rect<s32> rect(pos, pos + geom);

GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid, rect,
GUIBox *e = new GUIBox(Environment, m_current_parent, spec.fid, rect,
colors, bordercolors, borderwidths);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, data->formspec_version < 3));
e->drop();
Expand Down Expand Up @@ -2409,7 +2452,7 @@ void GUIFormSpecMenu::parseTooltip(ParserState* data, const std::string &element
core::rect<s32> rect(pos, pos + geom);

gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment,
data->current_parent, fieldspec.fid, rect);
m_current_parent, fieldspec.fid, rect);

// the element the rect tooltip is bound to should not block mouse-clicks
e->setVisible(false);
Expand Down Expand Up @@ -2698,6 +2741,27 @@ void GUIFormSpecMenu::parseSetFocus(ParserState *state, const std::string &eleme

void GUIFormSpecMenu::parseElement(ParserState* data, const std::string &element)
{
FormSpecParser parser;
std::unique_ptr<ElementSpec> espec = parser.parseElement(data, element);
if (espec == nullptr)
goto old_elements;

{
// This array must be kept in sync with the ElementType enum
constexpr void (GUIFormSpecMenu::*creators[])(ElementSpec *) = {
nullptr, // Not implemented yet
nullptr,
nullptr,
&GUIFormSpecMenu::createButton
};

(this->*creators[espec->getElementType()])(espec.get());
return;
}

// Only temporary, of course
old_elements:

//some prechecks
if (element.empty())
return;
Expand Down Expand Up @@ -2752,14 +2816,6 @@ void GUIFormSpecMenu::parseElement(ParserState* data, const std::string &element
return;
}

if (type == "button" || type == "button_exit") {
FormSpecParser parser;
std::unique_ptr<ElementSpec> espec = parser.parseElement(data, element);
if (espec != nullptr)
createButton(data, espec.get());
return;
}

if (type == "background" || type == "background9") {
parseBackground(data, description);
return;
Expand Down Expand Up @@ -2964,8 +3020,8 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// Base position of contents of form
mydata.basepos = getBasePos();

// the parent for the parsed elements
mydata.current_parent = this;
// the parent for the created elements
m_current_parent = this;

m_inventorylists.clear();
m_backgrounds.clear();
Expand Down Expand Up @@ -3244,7 +3300,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
parseElement(&mydata, elements[i]);
}

if (mydata.current_parent != this) {
if (m_current_parent != this) {
errorstream << "Invalid formspec string: scroll_container was never closed!"
<< std::endl;
} else if (!container_stack.empty()) {
Expand Down
11 changes: 9 additions & 2 deletions src/gui/guiFormSpecMenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ with this program; if not, write to the Free Software Foundation, Inc.,

#pragma once

#include <array>
#include <memory>
#include <utility>
#include <stack>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>

#include "irrlichttypes_extrabloated.h"
#include "irr_ptr.h"
#include "inventorymanager.h"
#include "modalMenu.h"
#include "guiInventoryList.h"
Expand Down Expand Up @@ -324,6 +329,8 @@ class GUIFormSpecMenu : public GUIModalMenu
std::vector<gui::IGUIElement *> m_clickthrough_elements;
std::vector<std::pair<std::string, GUIScrollContainer *>> m_scroll_containers;

gui::IGUIElement *m_current_parent;

GUIInventoryList::ItemSpec *m_selected_item = nullptr;
u16 m_selected_amount = 0;
bool m_selected_dragging = false;
Expand Down Expand Up @@ -369,7 +376,7 @@ class GUIFormSpecMenu : public GUIModalMenu

void parseElement(ParserState* data, const std::string &element);

void createButton(ParserState *state, ElementSpec *espec);
void createButton(ElementSpec *espec);

void parseSize(ParserState* data, const std::string &element);
void parseContainer(ParserState* data, const std::string &element);
Expand Down