Formspec: allow lists to change size and existence while the formspec is open (#9700)

Fixes #9640.
This commit is contained in:
DS 2020-04-18 17:21:10 +02:00 committed by GitHub
parent 241bf44260
commit 4fb6b6afa7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 36 additions and 41 deletions

@ -493,34 +493,6 @@ void GUIFormSpecMenu::parseList(parserData *data, const std::string &element)
return; return;
} }
// check for the existence of inventory and list
Inventory *inv = m_invmgr->getInventory(loc);
if (!inv) {
warningstream << "GUIFormSpecMenu::parseList(): "
<< "The inventory location "
<< "\"" << loc.dump() << "\" doesn't exist"
<< std::endl;
return;
}
InventoryList *ilist = inv->getList(listname);
if (!ilist) {
warningstream << "GUIFormSpecMenu::parseList(): "
<< "The inventory list \"" << listname << "\" "
<< "@ \"" << loc.dump() << "\" doesn't exist"
<< std::endl;
return;
}
// trim geom if it is larger than the actual inventory size
s32 list_size = (s32)ilist->getSize();
if (list_size < geom.X * geom.Y + start_i) {
list_size -= MYMAX(start_i, 0);
geom.Y = list_size / geom.X;
geom.Y += list_size % geom.X > 0 ? 1 : 0;
if (geom.Y <= 1)
geom.X = list_size;
}
if (!data->explicit_size) if (!data->explicit_size)
warningstream << "invalid use of list without a size[] element" << std::endl; warningstream << "invalid use of list without a size[] element" << std::endl;

@ -47,7 +47,8 @@ GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env,
m_fs_menu(fs_menu), m_fs_menu(fs_menu),
m_options(options), m_options(options),
m_font(font), m_font(font),
m_hovered_i(-1) m_hovered_i(-1),
m_already_warned(false)
{ {
} }
@ -58,20 +59,27 @@ void GUIInventoryList::draw()
Inventory *inv = m_invmgr->getInventory(m_inventoryloc); Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
if (!inv) { if (!inv) {
if (!m_already_warned) {
warningstream << "GUIInventoryList::draw(): " warningstream << "GUIInventoryList::draw(): "
<< "The inventory location " << "The inventory location "
<< "\"" << m_inventoryloc.dump() << "\" doesn't exist anymore" << "\"" << m_inventoryloc.dump() << "\" doesn't exist"
<< std::endl; << std::endl;
m_already_warned = true;
}
return; return;
} }
InventoryList *ilist = inv->getList(m_listname); InventoryList *ilist = inv->getList(m_listname);
if (!ilist) { if (!ilist) {
if (!m_already_warned) {
warningstream << "GUIInventoryList::draw(): " warningstream << "GUIInventoryList::draw(): "
<< "The inventory list \"" << m_listname << "\" @ \"" << "The inventory list \"" << m_listname << "\" @ \""
<< m_inventoryloc.dump() << "\" doesn't exist anymore" << m_inventoryloc.dump() << "\" doesn't exist"
<< std::endl; << std::endl;
m_already_warned = true;
}
return; return;
} }
m_already_warned = false;
video::IVideoDriver *driver = Environment->getVideoDriver(); video::IVideoDriver *driver = Environment->getVideoDriver();
Client *client = m_fs_menu->getClient(); Client *client = m_fs_menu->getClient();
@ -80,9 +88,11 @@ void GUIInventoryList::draw()
core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y); core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
v2s32 base_pos = AbsoluteRect.UpperLeftCorner; v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
const s32 list_size = (s32)ilist->getSize();
for (s32 i = 0; i < m_geom.X * m_geom.Y; i++) { for (s32 i = 0; i < m_geom.X * m_geom.Y; i++) {
s32 item_i = i + m_start_item_i; s32 item_i = i + m_start_item_i;
if (item_i >= (s32)ilist->getSize()) if (item_i >= list_size)
break; break;
v2s32 p((i % m_geom.X) * m_slot_spacing.X, v2s32 p((i % m_geom.X) * m_slot_spacing.X,
@ -192,10 +202,19 @@ bool GUIInventoryList::OnEvent(const SEvent &event)
s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const
{ {
// no item if no gui element at pointer
if (!IsVisible || AbsoluteClippingRect.getArea() <= 0 || if (!IsVisible || AbsoluteClippingRect.getArea() <= 0 ||
!AbsoluteClippingRect.isPointInside(p)) !AbsoluteClippingRect.isPointInside(p))
return -1; return -1;
// there can not be an item if the inventory or the inventorylist does not exist
Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
if (!inv)
return -1;
InventoryList *ilist = inv->getList(m_listname);
if (!ilist)
return -1;
core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y); core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
v2s32 base_pos = AbsoluteRect.UpperLeftCorner; v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
@ -210,7 +229,8 @@ s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const
rect.clipAgainst(AbsoluteClippingRect); rect.clipAgainst(AbsoluteClippingRect);
if (rect.getArea() > 0 && rect.isPointInside(p)) if (rect.getArea() > 0 && rect.isPointInside(p) &&
i + m_start_item_i < (s32)ilist->getSize())
return i + m_start_item_i; return i + m_start_item_i;
return -1; return -1;

@ -107,7 +107,7 @@ private:
const InventoryLocation m_inventoryloc; const InventoryLocation m_inventoryloc;
const std::string m_listname; const std::string m_listname;
// specifies the width and height of the inventorylist in itemslots // the specified width and height of the shown inventorylist in itemslots
const v2s32 m_geom; const v2s32 m_geom;
// the first item's index in inventory // the first item's index in inventory
const s32 m_start_item_i; const s32 m_start_item_i;
@ -127,4 +127,7 @@ private:
// the index of the hovered item; -1 if no item is hovered // the index of the hovered item; -1 if no item is hovered
s32 m_hovered_i; s32 m_hovered_i;
// we do not want to write a warning on every draw
bool m_already_warned;
}; };