mirror of
https://github.com/luanti-org/luanti.git
synced 2025-06-27 16:36:03 +00:00
Add scroll_container formspec element (redo) (#9101)
New formspec elements: - `scroll_container[<X>,<Y>;<W>,<H>;<scrollbar name>;<orientation>;<scroll factor>]` - `scroll_container_end[]` Other elements can be embedded in this element. Scrollbar must be placed manually.
This commit is contained in:
parent
6cf15cf872
commit
0ac999ded7
11 changed files with 411 additions and 50 deletions
|
@ -16,6 +16,7 @@ set(gui_SRCS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiPathSelectMenu.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp
|
||||
|
|
|
@ -65,6 +65,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "guiInventoryList.h"
|
||||
#include "guiItemImage.h"
|
||||
#include "guiScrollBar.h"
|
||||
#include "guiScrollContainer.h"
|
||||
#include "guiTable.h"
|
||||
#include "intlGUIEditBox.h"
|
||||
#include "guiHyperText.h"
|
||||
|
@ -143,6 +144,8 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
|
|||
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;
|
||||
|
@ -351,6 +354,102 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data)
|
|||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
|
@ -443,9 +542,9 @@ void GUIFormSpecMenu::parseList(parserData *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, 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);
|
||||
|
@ -550,8 +649,8 @@ void GUIFormSpecMenu::parseCheckbox(parserData* data, const std::string &element
|
|||
|
||||
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));
|
||||
|
@ -610,8 +709,8 @@ void GUIFormSpecMenu::parseScrollBar(parserData* data, const std::string &elemen
|
|||
|
||||
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));
|
||||
|
@ -737,7 +836,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
|
|||
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);
|
||||
|
@ -774,8 +874,8 @@ void GUIFormSpecMenu::parseImage(parserData* data, const std::string &element)
|
|||
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);
|
||||
|
@ -886,7 +986,7 @@ void GUIFormSpecMenu::parseItemImage(parserData* data, const std::string &elemen
|
|||
);
|
||||
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));
|
||||
|
@ -949,8 +1049,8 @@ void GUIFormSpecMenu::parseButton(parserData* data, const std::string &element,
|
|||
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);
|
||||
|
@ -1141,7 +1241,8 @@ void GUIFormSpecMenu::parseTable(parserData* data, const std::string &element)
|
|||
}
|
||||
|
||||
//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);
|
||||
|
@ -1217,7 +1318,8 @@ void GUIFormSpecMenu::parseTextList(parserData* data, const std::string &element
|
|||
}
|
||||
|
||||
//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);
|
||||
|
@ -1293,7 +1395,8 @@ void GUIFormSpecMenu::parseDropDown(parserData* data, const std::string &element
|
|||
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);
|
||||
|
@ -1379,7 +1482,8 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
|
|||
);
|
||||
|
||||
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);
|
||||
|
@ -1390,7 +1494,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
|
|||
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'*');
|
||||
|
@ -1425,7 +1529,7 @@ void GUIFormSpecMenu::createTextField(parserData *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,
|
||||
this, 0);
|
||||
data->current_parent, 0);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1443,14 +1547,14 @@ void GUIFormSpecMenu::createTextField(parserData *data, FieldSpec &spec,
|
|||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1491,7 +1595,7 @@ void GUIFormSpecMenu::createTextField(parserData *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, this, 0);
|
||||
rect, false, true, data->current_parent, 0);
|
||||
|
||||
if (t)
|
||||
t->setNotClipped(style.getBool(StyleSpec::NOCLIP, false));
|
||||
|
@ -1671,8 +1775,8 @@ void GUIFormSpecMenu::parseHyperText(parserData *data, const std::string &elemen
|
|||
);
|
||||
|
||||
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);
|
||||
|
@ -1750,7 +1854,8 @@ void GUIFormSpecMenu::parseLabel(parserData* data, const std::string &element)
|
|||
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);
|
||||
|
@ -1830,7 +1935,7 @@ void GUIFormSpecMenu::parseVertLabel(parserData* data, const std::string &elemen
|
|||
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");
|
||||
|
@ -1904,7 +2009,7 @@ void GUIFormSpecMenu::parseImageButton(parserData* data, const std::string &elem
|
|||
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);
|
||||
|
@ -2010,8 +2115,8 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
|
|||
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);
|
||||
|
@ -2046,7 +2151,6 @@ void GUIFormSpecMenu::parseTabHeader(parserData* data, const std::string &elemen
|
|||
|
||||
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;
|
||||
|
@ -2106,7 +2210,7 @@ void GUIFormSpecMenu::parseItemImageButton(parserData* data, const std::string &
|
|||
);
|
||||
|
||||
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");
|
||||
|
@ -2164,7 +2268,8 @@ void GUIFormSpecMenu::parseBox(parserData* data, const std::string &element)
|
|||
|
||||
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));
|
||||
|
@ -2316,7 +2421,7 @@ void GUIFormSpecMenu::parseTooltip(parserData* data, const std::string &element)
|
|||
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);
|
||||
|
@ -2775,6 +2880,16 @@ void GUIFormSpecMenu::parseElement(parserData* data, const std::string &element)
|
|||
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;
|
||||
|
@ -2831,8 +2946,10 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||
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);
|
||||
|
@ -2841,6 +2958,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||
// 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();
|
||||
|
@ -2851,6 +2971,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||
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();
|
||||
|
@ -3117,11 +3238,24 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||
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) {
|
||||
|
@ -4418,6 +4552,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
|
|
@ -39,6 +39,7 @@ class ISimpleTextureSource;
|
|||
class Client;
|
||||
class GUIScrollBar;
|
||||
class TexturePool;
|
||||
class GUIScrollContainer;
|
||||
|
||||
typedef enum {
|
||||
f_Button,
|
||||
|
@ -310,6 +311,7 @@ protected:
|
|||
std::vector<std::pair<FieldSpec, GUIScrollBar *>> m_scrollbars;
|
||||
std::vector<std::pair<FieldSpec, std::vector<std::string>>> m_dropdowns;
|
||||
std::vector<gui::IGUIElement *> m_clickthrough_elements;
|
||||
std::vector<std::pair<std::string, GUIScrollContainer *>> m_scroll_containers;
|
||||
|
||||
GUIInventoryList::ItemSpec *m_selected_item = nullptr;
|
||||
u16 m_selected_amount = 0;
|
||||
|
@ -359,6 +361,7 @@ private:
|
|||
std::string focused_fieldname;
|
||||
GUITable::TableOptions table_options;
|
||||
GUITable::TableColumns table_columns;
|
||||
gui::IGUIElement *current_parent = nullptr;
|
||||
|
||||
GUIInventoryList::Options inventorylist_options;
|
||||
|
||||
|
@ -391,6 +394,8 @@ private:
|
|||
void parseSize(parserData* data, const std::string &element);
|
||||
void parseContainer(parserData* data, const std::string &element);
|
||||
void parseContainerEnd(parserData* data);
|
||||
void parseScrollContainer(parserData *data, const std::string &element);
|
||||
void parseScrollContainerEnd(parserData *data);
|
||||
void parseList(parserData* data, const std::string &element);
|
||||
void parseListRing(parserData* data, const std::string &element);
|
||||
void parseCheckbox(parserData* data, const std::string &element);
|
||||
|
|
|
@ -917,20 +917,20 @@ void TextDrawer::place(const core::rect<s32> &dest_rect)
|
|||
|
||||
// Draw text in a rectangle with a given offset. Items are actually placed in
|
||||
// relative (to upper left corner) coordinates.
|
||||
void TextDrawer::draw(const core::rect<s32> &dest_rect,
|
||||
void TextDrawer::draw(const core::rect<s32> &clip_rect,
|
||||
const core::position2d<s32> &dest_offset)
|
||||
{
|
||||
irr::video::IVideoDriver *driver = m_environment->getVideoDriver();
|
||||
core::position2d<s32> offset = dest_rect.UpperLeftCorner + dest_offset;
|
||||
core::position2d<s32> offset = dest_offset;
|
||||
offset.Y += m_voffset;
|
||||
|
||||
if (m_text.background_type == ParsedText::BACKGROUND_COLOR)
|
||||
driver->draw2DRectangle(m_text.background_color, dest_rect);
|
||||
driver->draw2DRectangle(m_text.background_color, clip_rect);
|
||||
|
||||
for (auto &p : m_text.m_paragraphs) {
|
||||
for (auto &el : p.elements) {
|
||||
core::rect<s32> rect(el.pos + offset, el.dim);
|
||||
if (!rect.isRectCollided(dest_rect))
|
||||
if (!rect.isRectCollided(clip_rect))
|
||||
continue;
|
||||
|
||||
switch (el.type) {
|
||||
|
@ -947,7 +947,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect,
|
|||
|
||||
if (el.type == ParsedText::ELEMENT_TEXT)
|
||||
el.font->draw(el.text, rect, color, false, true,
|
||||
&dest_rect);
|
||||
&clip_rect);
|
||||
|
||||
if (el.underline && el.drawwidth) {
|
||||
s32 linepos = el.pos.Y + offset.Y +
|
||||
|
@ -958,7 +958,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect,
|
|||
el.pos.X + offset.X + el.drawwidth,
|
||||
linepos + (el.baseline >> 3));
|
||||
|
||||
driver->draw2DRectangle(color, linerect, &dest_rect);
|
||||
driver->draw2DRectangle(color, linerect, &clip_rect);
|
||||
}
|
||||
} break;
|
||||
|
||||
|
@ -972,7 +972,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect,
|
|||
irr::core::rect<s32>(
|
||||
core::position2d<s32>(0, 0),
|
||||
texture->getOriginalSize()),
|
||||
&dest_rect, 0, true);
|
||||
&clip_rect, 0, true);
|
||||
} break;
|
||||
|
||||
case ParsedText::ELEMENT_ITEM: {
|
||||
|
@ -982,7 +982,7 @@ void TextDrawer::draw(const core::rect<s32> &dest_rect,
|
|||
|
||||
drawItemStack(
|
||||
m_environment->getVideoDriver(),
|
||||
g_fontengine->getFont(), item, rect, &dest_rect,
|
||||
g_fontengine->getFont(), item, rect, &clip_rect,
|
||||
m_client, IT_ROT_OTHER, el.angle, el.rotation
|
||||
);
|
||||
} break;
|
||||
|
@ -1094,6 +1094,7 @@ bool GUIHyperText::OnEvent(const SEvent &event)
|
|||
m_text_scrollpos.Y = -m_vscrollbar->getPos();
|
||||
m_drawer.draw(m_display_text_rect, m_text_scrollpos);
|
||||
checkHover(event.MouseInput.X, event.MouseInput.Y);
|
||||
return true;
|
||||
|
||||
} else if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
|
||||
ParsedText::Element *element = getElementAt(
|
||||
|
@ -1151,7 +1152,8 @@ void GUIHyperText::draw()
|
|||
m_vscrollbar->setPos(0);
|
||||
m_vscrollbar->setVisible(false);
|
||||
}
|
||||
m_drawer.draw(m_display_text_rect, m_text_scrollpos);
|
||||
m_drawer.draw(AbsoluteClippingRect,
|
||||
m_display_text_rect.UpperLeftCorner + m_text_scrollpos);
|
||||
|
||||
// draw children
|
||||
IGUIElement::draw();
|
||||
|
|
|
@ -174,7 +174,7 @@ public:
|
|||
|
||||
void place(const core::rect<s32> &dest_rect);
|
||||
inline s32 getHeight() { return m_height; };
|
||||
void draw(const core::rect<s32> &dest_rect,
|
||||
void draw(const core::rect<s32> &clip_rect,
|
||||
const core::position2d<s32> &dest_offset);
|
||||
ParsedText::Element *getElementAt(core::position2d<s32> pos);
|
||||
ParsedText::Tag *m_hovertag;
|
||||
|
|
70
src/gui/guiScrollContainer.cpp
Normal file
70
src/gui/guiScrollContainer.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2020 DS
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "guiScrollContainer.h"
|
||||
|
||||
GUIScrollContainer::GUIScrollContainer(gui::IGUIEnvironment *env,
|
||||
gui::IGUIElement *parent, s32 id, const core::rect<s32> &rectangle,
|
||||
const std::string &orientation, f32 scrollfactor) :
|
||||
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
|
||||
m_scrollbar(nullptr), m_scrollfactor(scrollfactor)
|
||||
{
|
||||
if (orientation == "vertical")
|
||||
m_orientation = VERTICAL;
|
||||
else if (orientation == "horizontal")
|
||||
m_orientation = HORIZONTAL;
|
||||
else
|
||||
m_orientation = UNDEFINED;
|
||||
}
|
||||
|
||||
bool GUIScrollContainer::OnEvent(const SEvent &event)
|
||||
{
|
||||
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
|
||||
event.MouseInput.Event == EMIE_MOUSE_WHEEL &&
|
||||
!event.MouseInput.isLeftPressed() && m_scrollbar) {
|
||||
Environment->setFocus(m_scrollbar);
|
||||
bool retval = m_scrollbar->OnEvent(event);
|
||||
|
||||
// a hacky fix for updating the hovering and co.
|
||||
IGUIElement *hovered_elem = getElementFromPoint(core::position2d<s32>(
|
||||
event.MouseInput.X, event.MouseInput.Y));
|
||||
SEvent mov_event = event;
|
||||
mov_event.MouseInput.Event = EMIE_MOUSE_MOVED;
|
||||
Environment->postEventFromUser(mov_event);
|
||||
if (hovered_elem)
|
||||
hovered_elem->OnEvent(mov_event);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
return IGUIElement::OnEvent(event);
|
||||
}
|
||||
|
||||
void GUIScrollContainer::updateScrolling()
|
||||
{
|
||||
s32 pos = m_scrollbar->getPos();
|
||||
core::rect<s32> rect = getRelativePosition();
|
||||
|
||||
if (m_orientation == VERTICAL)
|
||||
rect.UpperLeftCorner.Y = pos * m_scrollfactor;
|
||||
else if (m_orientation == HORIZONTAL)
|
||||
rect.UpperLeftCorner.X = pos * m_scrollfactor;
|
||||
|
||||
setRelativePosition(rect);
|
||||
}
|
56
src/gui/guiScrollContainer.h
Normal file
56
src/gui/guiScrollContainer.h
Normal file
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2020 DS
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "irrlichttypes_extrabloated.h"
|
||||
#include "util/string.h"
|
||||
#include "guiScrollBar.h"
|
||||
|
||||
class GUIScrollContainer : public gui::IGUIElement
|
||||
{
|
||||
public:
|
||||
GUIScrollContainer(gui::IGUIEnvironment *env, gui::IGUIElement *parent, s32 id,
|
||||
const core::rect<s32> &rectangle, const std::string &orientation,
|
||||
f32 scrollfactor);
|
||||
|
||||
virtual bool OnEvent(const SEvent &event) override;
|
||||
|
||||
inline void onScrollEvent(gui::IGUIElement *caller)
|
||||
{
|
||||
if (caller == m_scrollbar)
|
||||
updateScrolling();
|
||||
}
|
||||
|
||||
inline void setScrollBar(GUIScrollBar *scrollbar) { m_scrollbar = scrollbar; }
|
||||
|
||||
private:
|
||||
enum OrientationEnum
|
||||
{
|
||||
VERTICAL,
|
||||
HORIZONTAL,
|
||||
UNDEFINED
|
||||
};
|
||||
|
||||
GUIScrollBar *m_scrollbar;
|
||||
OrientationEnum m_orientation;
|
||||
f32 m_scrollfactor;
|
||||
|
||||
void updateScrolling();
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue