local color = minetest.colorize\r
\r
+local hypertext = minetest.formspec_escape([[\r
+ <global halign=justify valign=center background=#CCF color=#444 actioncolor=darkblue margin=10>\r
+ <center><bigger>Furnace</bigger></center>\r
+ <style color=black>Furnaces</style> are <action name="crafting">crafted</action> and used by the player for the purpose of cooking food and <action name="smelting">smelting</action> various items.\r
+ <item name=default:furnace float=right width=128 rotate=yes>\r
+ <style color=black>Type:</style> Solid block\r
+ <style color=black>Drops:</style> Itself\r
+ <style color=black>Physics:</style> No\r
+ <style color=black>Luminance:</style> Inactive:No Active:Yes (8)\r
+ <style color=black>Flammable:</style> No\r
+ <style color=black>Generated:</style> No\r
+ <style color=black>Renewable:</style> Yes\r
+ <style color=black>Stackable:</style> Yes (99)\r
+ <style color=black>Itemstring:</style> default:furnace default:furnace_active\r
+ <big>Usage</big>\r
+ The furnace menu can be accessed by <action name="using">using</action> the furnace.\r
+ The furnace has 3 <action name="inventory">inventories</action>: An input slot, a fuel slot and 4 output slots. The fire in the furnace will automatically start when there is a smeltable item in the input slot and a fuel in the fuel slot.\r
+ As long as the fire is on, the furnace will smelt any smeltable item in the input slot, one by one, until it is empty. When the fire goes off, it will smelt the next item until there are no smeltable items and no fuel items left.\r
+ The current stage of cooking can be seen by <action name="pointing">pointing</action> the furnace or by viewing the furnace menu. In the furnace menu, the flame symbol roughly shows the remaining burning time. The arrow symbol shows the progress of the current smelting process.\r
+ <big>Renewing</big>\r
+ Furnaces can be crafted from e.g. <action name="default:cobblestone">cobblestone</action>, a renewable resource.\r
+ <big>Crafting</big>\r
+ Sorry no way to display crafting yet in formspec pages.\r
+ <big>Fuel</big>\r
+ See <action name="smelting">Smelting</action> for a list of furnace fuels.\r
+ <big>Recipes</big>\r
+ See the <action name="smelting">Smelting</action> page.\r
+]])\r
+\r
+\r
local clip_fs = [[\r
style_type[label,button,image_button,item_image_button,\r
tabheader,scrollbar,table,animated_image\r
animated_image[3,4.25;1,1;;test_animation.png;4;0;3]\r
animated_image[5.5,0.5;5,2;;test_animation.png;4;100]\r
animated_image[5.5,2.75;5,2;;test_animation.jpg;4;100]\r
- ]]\r
+ ]],\r
+\r
+ "formspec_version[3]"..\r
+ "size[12,12]"..\r
+ "button[8.5,1;1,1;bla;Bla]"..\r
+ "box[1,1;8,6;#00aa]"..\r
+ "scroll_container[1,1;8,6;scrbar;vertical]"..\r
+ "button[0,1;1,1;lorem;Lorem]"..\r
+ "button[0,10;1,1;ipsum;Ipsum]"..\r
+ "pwdfield[2,2;1,1;lorem2;Lorem]"..\r
+ "list[current_player;main;4,4;1,5;]"..\r
+ "box[2,5;3,2;#ffff00]"..\r
+ "image[1,10;3,2;bubble.png]"..\r
+ "image[3,1;bubble.png]"..\r
+ "item_image[2,6;3,2;default:mese]"..\r
+ "label[2,15;bla Bli\nfoo bar]"..\r
+ "item_image_button[2,3;1,1;default:dirt_with_grass;itemimagebutton;ItemImageButton]"..\r
+ "tooltip[0,11;3,2;Buz;#f00;#000]"..\r
+ "box[0,11;3,2;#00ff00]"..\r
+ "hypertext[3,13;3,3;;" .. hypertext .. "]" ..\r
+ "container[0,18]"..\r
+ "box[1,2;3,2;#0a0a]"..\r
+ "scroll_container[1,2;3,2;scrbar2;horizontal;0.06]"..\r
+ "button[0,0;6,1;butnest;Nest]"..\r
+ "label[10,0.5;nest]"..\r
+ "scroll_container_end[]"..\r
+ "scrollbar[1,0;3.5,0.3;horizontal;scrbar2;0]"..\r
+ "container_end[]"..\r
+ "dropdown[0,6;2;hmdrpdwn;apple,bulb;1]"..\r
+ "image_button[0,4;2,2;bubble.png;bubblebutton;bbbbtt;false;true;heart.png]"..\r
+ "box[1,22.5;4,1;#a00a]"..\r
+ "scroll_container_end[]"..\r
+ "scrollbaroptions[max=170]".. -- lowest seen pos is: 0.1*170+6=23 (factor*max+height)\r
+ "scrollbar[7.5,0;0.3,4;vertical;scrbar;0]"..\r
+ "scrollbar[8,0;0.3,4;vertical;scrbarhmmm;0]"..\r
+ "dropdown[0,6;2;hmdrpdwnnn;apple,bulb;1]",\r
}\r
\r
local function show_test_formspec(pname, page_id)\r
page_id = page_id or 2\r
\r
- local fs = pages[page_id] .. "tabheader[0,0;6,0.65;maintabs;Real Coord,Styles,Noclip,MiscEle;" .. page_id .. ";false;false]"\r
+ local fs = pages[page_id] .. "tabheader[0,0;8,0.65;maintabs;Real Coord,Styles,Noclip,MiscEle,Scroll Container;" .. page_id .. ";false;false]"\r
\r
minetest.show_formspec(pname, "test:formspec", fs)\r
end\r
#include "guiInventoryList.h"
#include "guiItemImage.h"
#include "guiScrollBar.h"
+#include "guiScrollContainer.h"
#include "guiTable.h"
#include "intlGUIEditBox.h"
#include "guiHyperText.h"
tooltip_rect_it.first->drop();
for (auto &clickthrough_it : m_clickthrough_elements)
clickthrough_it->drop();
+ for (auto &scroll_container_it : m_scroll_containers)
+ scroll_container_it.second->drop();
delete m_selected_item;
delete m_form_src;
}
}
+void GUIFormSpecMenu::parseScrollContainer(parserData *data, const std::string &element)
+{
+ std::vector<std::string> parts = split(element, ';');
+
+ if (parts.size() < 4 ||
+ (parts.size() > 5 && m_formspec_version <= FORMSPEC_API_VERSION)) {
+ errorstream << "Invalid scroll_container start element (" << parts.size()
+ << "): '" << element << "'" << std::endl;
+ return;
+ }
+
+ std::vector<std::string> v_pos = split(parts[0], ',');
+ std::vector<std::string> v_geom = split(parts[1], ',');
+ std::string scrollbar_name = parts[2];
+ std::string orientation = parts[3];
+ f32 scroll_factor = 0.1f;
+ if (parts.size() >= 5 && !parts[4].empty())
+ scroll_factor = stof(parts[4]);
+
+ MY_CHECKPOS("scroll_container", 0);
+ MY_CHECKGEOM("scroll_container", 1);
+
+ v2s32 pos = getRealCoordinateBasePos(v_pos);
+ v2s32 geom = getRealCoordinateGeometry(v_geom);
+
+ if (orientation == "vertical")
+ scroll_factor *= -imgsize.Y;
+ else if (orientation == "horizontal")
+ scroll_factor *= -imgsize.X;
+ else
+ warningstream << "GUIFormSpecMenu::parseScrollContainer(): "
+ << "Invalid scroll_container orientation: " << orientation
+ << std::endl;
+
+ // old parent (at first: this)
+ // ^ is parent of clipper
+ // ^ is parent of mover
+ // ^ is parent of other elements
+
+ // make clipper
+ 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);
+
+ // make mover
+ FieldSpec spec_mover(
+ "",
+ L"",
+ L"",
+ 258 + m_fields.size()
+ );
+
+ core::rect<s32> rect_mover = core::rect<s32>(0, 0, geom.X, geom.Y);
+
+ GUIScrollContainer *mover = new GUIScrollContainer(Environment,
+ clipper, spec_mover.fid, rect_mover, orientation, scroll_factor);
+
+ data->current_parent = mover;
+
+ m_scroll_containers.emplace_back(scrollbar_name, mover);
+
+ m_fields.push_back(spec_mover);
+
+ clipper->drop();
+
+ // remove interferring offset of normal containers
+ container_stack.push(pos_offset);
+ pos_offset.X = 0.0f;
+ pos_offset.Y = 0.0f;
+}
+
+void GUIFormSpecMenu::parseScrollContainerEnd(parserData *data)
+{
+ if (data->current_parent == this || data->current_parent->getParent() == this ||
+ container_stack.empty()) {
+ errorstream << "Invalid scroll_container end element, "
+ << "no matching scroll_container start element" << std::endl;
+ return;
+ }
+
+ if (pos_offset.getLengthSQ() != 0.0f) {
+ // pos_offset is only set by containers and scroll_containers.
+ // scroll_containers always set it to 0,0 which means that if it is
+ // not 0,0, it is a normal container that was opened last, not a
+ // scroll_container
+ errorstream << "Invalid scroll_container end element, "
+ << "an inner container was left open" << std::endl;
+ return;
+ }
+
+ data->current_parent = data->current_parent->getParent()->getParent();
+ pos_offset = container_stack.top();
+ container_stack.pop();
+}
+
void GUIFormSpecMenu::parseList(parserData *data, const std::string &element)
{
if (m_client == 0) {
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, this, spec.fid,
- rect, m_invmgr, loc, listname, geom, start_i, imgsize, slot_spacing,
- this, data->inventorylist_options, m_font);
+ GUIInventoryList *e = new GUIInventoryList(Environment, data->current_parent,
+ spec.fid, rect, m_invmgr, loc, listname, geom, start_i, imgsize,
+ slot_spacing, this, data->inventorylist_options, m_font);
m_inventorylists.push_back(e);
m_fields.push_back(spec);
spec.ftype = f_CheckBox;
- gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect, this,
- spec.fid, spec.flabel.c_str());
+ gui::IGUICheckBox *e = Environment->addCheckBox(fselected, rect,
+ data->current_parent, spec.fid, spec.flabel.c_str());
auto style = getDefaultStyleForElement("checkbox", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
spec.ftype = f_ScrollBar;
spec.send = true;
- GUIScrollBar *e = new GUIScrollBar(Environment, this, spec.fid, rect,
- is_horizontal, true);
+ GUIScrollBar *e = new GUIScrollBar(Environment, data->current_parent,
+ spec.fid, rect, is_horizontal, true);
auto style = getDefaultStyleForElement("scrollbar", name);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
1
);
core::rect<s32> rect(pos, pos + geom);
- gui::IGUIImage *e = Environment->addImage(rect, this, spec.fid, 0, true);
+ gui::IGUIImage *e = Environment->addImage(rect, data->current_parent,
+ spec.fid, 0, true);
e->setImage(texture);
e->setScaleImage(true);
auto style = getDefaultStyleForElement("image", spec.fname);
L"",
258 + m_fields.size()
);
- gui::IGUIImage *e = Environment->addImage(texture, pos, true, this,
- spec.fid, 0);
+ gui::IGUIImage *e = Environment->addImage(texture, pos, true,
+ data->current_parent, spec.fid, 0);
auto style = getDefaultStyleForElement("image", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
m_fields.push_back(spec);
);
spec.ftype = f_ItemImage;
- GUIItemImage *e = new GUIItemImage(Environment, this, spec.fid,
+ GUIItemImage *e = new GUIItemImage(Environment, data->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));
if(type == "button_exit")
spec.is_exit = true;
- GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc, this,
- spec.fid, spec.flabel.c_str());
+ GUIButton *e = GUIButton::addButton(Environment, rect, m_tsrc,
+ data->current_parent, spec.fid, spec.flabel.c_str());
auto style = getStyleForElement(type, name, (type != "button") ? "button" : "");
e->setStyles(style);
}
//now really show table
- GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc);
+ GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
+ rect, m_tsrc);
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
}
//now really show list
- GUITable *e = new GUITable(Environment, this, spec.fid, rect, m_tsrc);
+ GUITable *e = new GUITable(Environment, data->current_parent, spec.fid,
+ rect, m_tsrc);
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
spec.send = true;
//now really show list
- gui::IGUIComboBox *e = Environment->addComboBox(rect, this, spec.fid);
+ gui::IGUIComboBox *e = Environment->addComboBox(rect, data->current_parent,
+ spec.fid);
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
);
spec.send = true;
- gui::IGUIEditBox * e = Environment->addEditBox(0, rect, true, this, spec.fid);
+ gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
+ data->current_parent, spec.fid);
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
rect.UpperLeftCorner.Y -= font_height;
rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + font_height;
gui::StaticText::add(Environment, spec.flabel.c_str(), rect, false, true,
- this, 0);
+ data->current_parent, 0);
}
e->setPasswordBox(true,L'*');
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,
- this, 0);
+ data->current_parent, 0);
return;
}
if (use_intl_edit_box && g_settings->getBool("freetype")) {
e = new gui::intlGUIEditBox(spec.fdefault.c_str(), true, Environment,
- this, spec.fid, rect, is_editable, is_multiline);
+ data->current_parent, spec.fid, rect, is_editable, is_multiline);
} else {
if (is_multiline) {
- e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true,
- Environment, this, spec.fid, rect, is_editable, true);
+ e = new GUIEditBoxWithScrollBar(spec.fdefault.c_str(), true, Environment,
+ data->current_parent, spec.fid, rect, is_editable, true);
} else if (is_editable) {
- e = Environment->addEditBox(spec.fdefault.c_str(), rect, true, this,
- spec.fid);
+ e = Environment->addEditBox(spec.fdefault.c_str(), rect, true,
+ data->current_parent, spec.fid);
e->grab();
}
}
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, this, 0);
+ rect, false, true, data->current_parent, 0);
if (t)
t->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
);
spec.ftype = f_HyperText;
- GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment, this,
- spec.fid, rect, m_client, m_tsrc);
+ GUIHyperText *e = new GUIHyperText(spec.flabel.c_str(), Environment,
+ data->current_parent, spec.fid, rect, m_client, m_tsrc);
e->drop();
m_fields.push_back(spec);
4
);
gui::IGUIStaticText *e = gui::StaticText::add(Environment,
- spec.flabel.c_str(), rect, false, false, this, spec.fid);
+ spec.flabel.c_str(), rect, false, false, data->current_parent,
+ spec.fid);
e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
auto style = getDefaultStyleForElement("label", spec.fname);
258 + m_fields.size()
);
gui::IGUIStaticText *e = gui::StaticText::add(Environment, spec.flabel.c_str(),
- rect, false, false, this, spec.fid);
+ rect, false, false, data->current_parent, spec.fid);
e->setTextAlignment(gui::EGUIA_CENTER, gui::EGUIA_CENTER);
auto style = getDefaultStyleForElement("vertlabel", spec.fname, "label");
spec.is_exit = true;
GUIButtonImage *e = GUIButtonImage::addButton(Environment, rect, m_tsrc,
- this, spec.fid, spec.flabel.c_str());
+ data->current_parent, spec.fid, spec.flabel.c_str());
if (spec.fname == data->focused_fieldname) {
Environment->setFocus(e);
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X,
pos.Y+geom.Y);
- gui::IGUITabControl *e = Environment->addTabControl(rect, this,
- show_background, show_border, spec.fid);
+ gui::IGUITabControl *e = Environment->addTabControl(rect,
+ data->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);
void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &element)
{
-
if (m_client == 0) {
warningstream << "invalid use of item_image_button with m_client==0"
<< std::endl;
);
GUIButtonItemImage *e_btn = GUIButtonItemImage::addButton(Environment,
- rect, m_tsrc, this, spec_btn.fid, spec_btn.flabel.c_str(),
+ rect, m_tsrc, data->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");
core::rect<s32> rect(pos, pos + geom);
- GUIBox *e = new GUIBox(Environment, this, spec.fid, rect, tmp_color);
+ GUIBox *e = new GUIBox(Environment, data->current_parent, spec.fid,
+ rect, tmp_color);
auto style = getDefaultStyleForElement("box", spec.fname);
e->setNotClipped(style.getBool(StyleSpec::NOCLIP, m_formspec_version < 3));
core::rect<s32> rect(pos, pos + geom);
gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment,
- this, fieldspec.fid, rect);
+ data->current_parent, fieldspec.fid, rect);
// the element the rect tooltip is bound to should not block mouse-clicks
e->setVisible(false);
return;
}
+ if (type == "scroll_container") {
+ parseScrollContainer(data, description);
+ return;
+ }
+
+ if (type == "scroll_container_end") {
+ parseScrollContainerEnd(data);
+ return;
+ }
+
// Ignore others
infostream << "Unknown DrawSpec: type=" << type << ", data=\"" << description << "\""
<< std::endl;
tooltip_rect_it.first->drop();
for (auto &clickthrough_it : m_clickthrough_elements)
clickthrough_it->drop();
+ for (auto &scroll_container_it : m_scroll_containers)
+ scroll_container_it.second->drop();
- mydata.size= v2s32(100,100);
+ mydata.size = v2s32(100, 100);
mydata.screensize = screensize;
mydata.offset = v2f32(0.5f, 0.5f);
mydata.anchor = v2f32(0.5f, 0.5f);
// Base position of contents of form
mydata.basepos = getBasePos();
+ // the parent for the parsed elements
+ mydata.current_parent = this;
+
m_inventorylists.clear();
m_backgrounds.clear();
m_tables.clear();
m_tooltip_rects.clear();
m_inventory_rings.clear();
m_dropdowns.clear();
+ m_scroll_containers.clear();
theme_by_name.clear();
theme_by_type.clear();
m_clickthrough_elements.clear();
parseElement(&mydata, elements[i]);
}
- if (!container_stack.empty()) {
+ if (mydata.current_parent != this) {
+ errorstream << "Invalid formspec string: scroll_container was never closed!"
+ << std::endl;
+ } else if (!container_stack.empty()) {
errorstream << "Invalid formspec string: container was never closed!"
<< std::endl;
}
+ // get the scrollbar elements for scroll_containers
+ for (const std::pair<std::string, GUIScrollContainer *> &c : m_scroll_containers) {
+ for (const std::pair<FieldSpec, GUIScrollBar *> &b : m_scrollbars) {
+ if (c.first == b.first.fname) {
+ c.second->setScrollBar(b.second);
+ break;
+ }
+ }
+ }
+
// If there are fields without explicit size[], add a "Proceed"
// button and adjust size to fit all the fields.
if (mydata.simple_field_count > 0 && !mydata.explicit_size) {
}
}
+ if (event.GUIEvent.EventType == gui::EGET_SCROLL_BAR_CHANGED) {
+ // move scroll_containers
+ for (const std::pair<std::string, GUIScrollContainer *> &c : m_scroll_containers)
+ c.second->onScrollEvent(event.GUIEvent.Caller);
+ }
+
if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
if (event.GUIEvent.Caller->getID() > 257) {
bool close_on_enter = true;