Formspec: Create a new class for inventorylists (#9287)

This commit is contained in:
DS 2020-02-01 13:55:13 +01:00 committed by GitHub
parent 908e762479
commit 1116918dbb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 448 additions and 291 deletions

@ -189,6 +189,7 @@ LOCAL_SRC_FILES := \
jni/src/gui/guiEngine.cpp \ jni/src/gui/guiEngine.cpp \
jni/src/gui/guiFormSpecMenu.cpp \ jni/src/gui/guiFormSpecMenu.cpp \
jni/src/gui/guiHyperText.cpp \ jni/src/gui/guiHyperText.cpp \
jni/src/gui/guiInventoryList.cpp \
jni/src/gui/guiItemImage.cpp \ jni/src/gui/guiItemImage.cpp \
jni/src/gui/guiKeyChangeMenu.cpp \ jni/src/gui/guiKeyChangeMenu.cpp \
jni/src/gui/guiPasswordChange.cpp \ jni/src/gui/guiPasswordChange.cpp \

@ -1946,8 +1946,6 @@ For coloured text you can use `minetest.colorize`.
Since formspec version 3, elements drawn in the order they are defined. All Since formspec version 3, elements drawn in the order they are defined. All
background elements are drawn before all other elements. background elements are drawn before all other elements.
`list` elements are an exception here. They are drawn last. This, however, might
be changed at any time.
**WARNING**: do _not_ use a element name starting with `key_`; those names are **WARNING**: do _not_ use a element name starting with `key_`; those names are
reserved to pass key press events to formspec! reserved to pass key press events to formspec!
@ -2058,7 +2056,6 @@ Elements
be shown if the inventory list is of size 0. be shown if the inventory list is of size 0.
* **Note**: With the new coordinate system, the spacing between inventory * **Note**: With the new coordinate system, the spacing between inventory
slots is one-fourth the size of an inventory slot. slots is one-fourth the size of an inventory slot.
* **Note**: Lists are drawn after every other element. This might change at any time.
### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]` ### `list[<inventory location>;<list name>;<X>,<Y>;<W>,<H>;<starting item index>]`
@ -2066,7 +2063,6 @@ Elements
be shown if the inventory list is of size 0. be shown if the inventory list is of size 0.
* **Note**: With the new coordinate system, the spacing between inventory * **Note**: With the new coordinate system, the spacing between inventory
slots is one-fourth the size of an inventory slot. slots is one-fourth the size of an inventory slot.
* **Note**: Lists are drawn after every other element. This might change at any time.
### `listring[<inventory location>;<list name>]` ### `listring[<inventory location>;<list name>]`

@ -9,6 +9,7 @@ set(gui_SRCS
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiInventoryList.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiItemImage.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiItemImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp

@ -61,6 +61,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "guiButtonImage.h" #include "guiButtonImage.h"
#include "guiButtonItemImage.h" #include "guiButtonItemImage.h"
#include "guiEditBoxWithScrollbar.h" #include "guiEditBoxWithScrollbar.h"
#include "guiInventoryList.h"
#include "guiItemImage.h" #include "guiItemImage.h"
#include "guiScrollBar.h" #include "guiScrollBar.h"
#include "guiTable.h" #include "guiTable.h"
@ -130,7 +131,7 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
for (auto &table_it : m_tables) for (auto &table_it : m_tables)
table_it.second->drop(); table_it.second->drop();
for (auto &inventorylist_it : m_inventorylists) for (auto &inventorylist_it : m_inventorylists)
inventorylist_it.e->drop(); inventorylist_it->drop();
for (auto &checkbox_it : m_checkboxes) for (auto &checkbox_it : m_checkboxes)
checkbox_it.second->drop(); checkbox_it.second->drop();
for (auto &scrollbar_it : m_scrollbars) for (auto &scrollbar_it : m_scrollbars)
@ -429,29 +430,21 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
3 3
); );
v2s32 pos; v2f32 slot_spacing = data->real_coordinates ?
core::rect<s32> rect; v2f32(imgsize.X * 1.25f, imgsize.Y * 1.25f) : spacing;
if (data->real_coordinates) { v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos)
pos = getRealCoordinateBasePos(v_pos); : getElementBasePos(&v_pos);
rect = core::rect<s32>(pos.X, pos.Y,
pos.X + (geom.X - 1) * (imgsize.X * 1.25) + imgsize.X,
pos.Y + (geom.Y - 1) * (imgsize.Y * 1.25) + imgsize.Y);
} else {
pos = getElementBasePos(&v_pos);
rect = core::rect<s32>(pos.X, pos.Y,
pos.X + (geom.X - 1) * spacing.X + imgsize.X,
pos.Y + (geom.Y - 1) * spacing.Y + imgsize.Y);
}
gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment, core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
this, spec.fid, rect); pos.X + (geom.X - 1) * slot_spacing.X + imgsize.X,
pos.Y + (geom.Y - 1) * slot_spacing.Y + imgsize.Y);
// the element the list is bound to should not block mouse-clicks GUIInventoryList *e = new GUIInventoryList(Environment, this, spec.fid,
e->setVisible(false); rect, m_invmgr, loc, listname, geom, start_i, imgsize, slot_spacing,
this, data->inventorylist_options, m_font);
m_inventorylists.emplace_back(loc, listname, e, geom, start_i, m_inventorylists.push_back(e);
data->real_coordinates);
m_fields.push_back(spec); m_fields.push_back(spec);
return; return;
} }
@ -485,10 +478,10 @@ void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element
if (element.empty() && m_inventorylists.size() > 1) { if (element.empty() && m_inventorylists.size() > 1) {
size_t siz = m_inventorylists.size(); size_t siz = m_inventorylists.size();
// insert the last two inv list elements into the list ring // insert the last two inv list elements into the list ring
const ListDrawSpec &spa = m_inventorylists[siz - 2]; const GUIInventoryList *spa = m_inventorylists[siz - 2];
const ListDrawSpec &spb = m_inventorylists[siz - 1]; const GUIInventoryList *spb = m_inventorylists[siz - 1];
m_inventory_rings.emplace_back(spa.inventoryloc, spa.listname); m_inventory_rings.emplace_back(spa->getInventoryloc(), spa->getListname());
m_inventory_rings.emplace_back(spb.inventoryloc, spb.listname); m_inventory_rings.emplace_back(spb->getInventoryloc(), spb->getListname());
return; return;
} }
@ -2171,12 +2164,13 @@ void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &eleme
if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) || if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION))) ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
{ {
parseColorString(parts[0], m_slotbg_n, false); parseColorString(parts[0], data->inventorylist_options.slotbg_n, false);
parseColorString(parts[1], m_slotbg_h, false); parseColorString(parts[1], data->inventorylist_options.slotbg_h, false);
if (parts.size() >= 3) { if (parts.size() >= 3) {
if (parseColorString(parts[2], m_slotbordercolor, false)) { if (parseColorString(parts[2], data->inventorylist_options.slotbordercolor,
m_slotborder = true; false)) {
data->inventorylist_options.slotborder = true;
} }
} }
if (parts.size() == 5) { if (parts.size() == 5) {
@ -2187,6 +2181,14 @@ void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &eleme
if (parseColorString(parts[4], tmp_color, false)) if (parseColorString(parts[4], tmp_color, false))
m_default_tooltip_color = tmp_color; m_default_tooltip_color = tmp_color;
} }
// update all already parsed inventorylists
for (GUIInventoryList *e : m_inventorylists) {
e->setSlotBGColors(data->inventorylist_options.slotbg_n,
data->inventorylist_options.slotbg_h);
e->setSlotBorders(data->inventorylist_options.slotborder,
data->inventorylist_options.slotbordercolor);
}
return; return;
} }
errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl; errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'" << std::endl;
@ -2673,7 +2675,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
for (auto &table_it : m_tables) for (auto &table_it : m_tables)
table_it.second->drop(); table_it.second->drop();
for (auto &inventorylist_it : m_inventorylists) for (auto &inventorylist_it : m_inventorylists)
inventorylist_it.e->drop(); inventorylist_it->drop();
for (auto &checkbox_it : m_checkboxes) for (auto &checkbox_it : m_checkboxes)
checkbox_it.second->drop(); checkbox_it.second->drop();
for (auto &scrollbar_it : m_scrollbars) for (auto &scrollbar_it : m_scrollbars)
@ -2692,8 +2694,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
// Base position of contents of form // Base position of contents of form
mydata.basepos = getBasePos(); mydata.basepos = getBasePos();
/* Convert m_init_draw_spec to m_inventorylists */
m_inventorylists.clear(); m_inventorylists.clear();
m_backgrounds.clear(); m_backgrounds.clear();
m_tables.clear(); m_tables.clear();
@ -2732,15 +2732,9 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
); );
} }
m_slotbg_n = video::SColor(255,128,128,128);
m_slotbg_h = video::SColor(255,192,192,192);
m_default_tooltip_bgcolor = video::SColor(255,110,130,60); m_default_tooltip_bgcolor = video::SColor(255,110,130,60);
m_default_tooltip_color = video::SColor(255,255,255,255); m_default_tooltip_color = video::SColor(255,255,255,255);
m_slotbordercolor = video::SColor(200,0,0,0);
m_slotborder = false;
// Add tooltip // Add tooltip
{ {
assert(!m_tooltip_element); assert(!m_tooltip_element);
@ -3097,140 +3091,18 @@ bool GUIFormSpecMenu::getAndroidUIInput()
} }
#endif #endif
GUIFormSpecMenu::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const GUIInventoryList::ItemSpec GUIFormSpecMenu::getItemAtPos(v2s32 p) const
{ {
core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y); core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y);
for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) { for (const GUIInventoryList *e : m_inventorylists) {
core::rect<s32> clipping_rect = s.e->getAbsoluteClippingRect(); s32 item_index = e->getItemIndexAtPos(p);
v2s32 base_pos = s.e->getAbsolutePosition().UpperLeftCorner; if (item_index != -1)
for(s32 i=0; i<s.geom.X*s.geom.Y; i++) { return GUIInventoryList::ItemSpec(e->getInventoryloc(), e->getListname(),
s32 item_i = i + s.start_item_i; item_index);
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 + base_pos + p0;
rect.clipAgainst(clipping_rect);
if (rect.getArea() > 0 && rect.isPointInside(p))
return ItemSpec(s.inventoryloc, s.listname, item_i);
}
} }
return ItemSpec(InventoryLocation(), "", -1); return GUIInventoryList::ItemSpec(InventoryLocation(), "", -1);
}
void GUIFormSpecMenu::drawList(const ListDrawSpec &s, int layer,
bool &item_hovered)
{
video::IVideoDriver* driver = Environment->getVideoDriver();
Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
if (!inv) {
warningstream<<"GUIFormSpecMenu::drawList(): "
<< "The inventory location "
<< "\"" << s.inventoryloc.dump() << "\" doesn't exist anymore"
<< std::endl;
return;
}
InventoryList *ilist = inv->getList(s.listname);
if (!ilist) {
warningstream << "GUIFormSpecMenu::drawList(): "
<< "The inventory list \"" << s.listname << "\" @ \""
<< s.inventoryloc.dump() << "\" doesn't exist anymore"
<< std::endl;
return;
}
core::rect<s32> imgrect(0, 0, imgsize.X, imgsize.Y);
core::rect<s32> clipping_rect = s.e->getAbsoluteClippingRect();
v2s32 base_pos = s.e->getAbsolutePosition().UpperLeftCorner;
for (s32 i = 0; i < s.geom.X * s.geom.Y; i++) {
s32 item_i = i + s.start_item_i;
if (item_i >= (s32)ilist->getSize())
break;
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 + base_pos + p;
ItemStack item = ilist->getItem(item_i);
bool selected = m_selected_item
&& m_invmgr->getInventory(m_selected_item->inventoryloc) == inv
&& m_selected_item->listname == s.listname
&& m_selected_item->i == item_i;
core::rect<s32> clipped_rect(rect);
clipped_rect.clipAgainst(clipping_rect);
bool hovering = clipped_rect.getArea() > 0 &&
clipped_rect.isPointInside(m_pointer);
ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
(hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
if (layer == 0) {
if (hovering) {
item_hovered = true;
driver->draw2DRectangle(m_slotbg_h, rect, &clipping_rect);
} else {
driver->draw2DRectangle(m_slotbg_n, rect, &clipping_rect);
}
}
//Draw inv slot borders
if (m_slotborder) {
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
s32 border = 1;
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y1 - border),
v2s32(x2 + border, y1)), &clipping_rect);
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y2),
v2s32(x2 + border, y2 + border)), &clipping_rect);
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y1),
v2s32(x1, y2)), &clipping_rect);
driver->draw2DRectangle(m_slotbordercolor,
core::rect<s32>(v2s32(x2, y1),
v2s32(x2 + border, y2)), &clipping_rect);
}
if (layer == 1) {
if (selected)
item.takeItem(m_selected_amount);
if (!item.empty()) {
// Draw item stack
drawItemStack(driver, m_font, item,
rect, &clipping_rect, m_client, rotation_kind);
// 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);
}
}
}
}
} }
void GUIFormSpecMenu::drawSelectedItem() void GUIFormSpecMenu::drawSelectedItem()
@ -3273,6 +3145,8 @@ void GUIFormSpecMenu::drawMenu()
gui::IGUIFont *old_font = skin->getFont(); gui::IGUIFont *old_font = skin->getFont();
skin->setFont(m_font); skin->setFont(m_font);
m_hovered_item_tooltips.clear();
updateSelectedItem(); updateSelectedItem();
video::IVideoDriver* driver = Environment->getVideoDriver(); video::IVideoDriver* driver = Environment->getVideoDriver();
@ -3315,21 +3189,17 @@ void GUIFormSpecMenu::drawMenu()
/* /*
Call base class Call base class
(This is where all the drawing happens.)
*/ */
gui::IGUIElement::draw(); gui::IGUIElement::draw();
/* // Draw hovered item tooltips
Draw items for (const std::string &tooltip : m_hovered_item_tooltips) {
Layer 0: Item slot rectangles showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color,
Layer 1: Item images; prepare tooltip m_default_tooltip_bgcolor);
*/
bool item_hovered = false;
for (int layer = 0; layer < 2; layer++) {
for (const GUIFormSpecMenu::ListDrawSpec &spec : m_inventorylists) {
drawList(spec, layer, item_hovered);
} }
}
if (!item_hovered) { if (m_hovered_item_tooltips.empty()) {
// reset rotation time // reset rotation time
drawItemStack(driver, m_font, ItemStack(), drawItemStack(driver, m_font, ItemStack(),
core::rect<s32>(v2s32(0, 0), v2s32(0, 0)), core::rect<s32>(v2s32(0, 0), v2s32(0, 0)),
@ -3463,11 +3333,11 @@ void GUIFormSpecMenu::updateSelectedItem()
// If craftresult is nonempty and nothing else is selected, select it now. // If craftresult is nonempty and nothing else is selected, select it now.
if (!m_selected_item) { if (!m_selected_item) {
for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) { for (const GUIInventoryList *e : m_inventorylists) {
if (s.listname != "craftpreview") if (e->getListname() != "craftpreview")
continue; continue;
Inventory *inv = m_invmgr->getInventory(s.inventoryloc); Inventory *inv = m_invmgr->getInventory(e->getInventoryloc());
if (!inv) if (!inv)
continue; continue;
@ -3481,8 +3351,8 @@ void GUIFormSpecMenu::updateSelectedItem()
continue; continue;
// Grab selected item from the crafting result list // Grab selected item from the crafting result list
m_selected_item = new ItemSpec; m_selected_item = new GUIInventoryList::ItemSpec;
m_selected_item->inventoryloc = s.inventoryloc; m_selected_item->inventoryloc = e->getInventoryloc();
m_selected_item->listname = "craftresult"; m_selected_item->listname = "craftresult";
m_selected_item->i = 0; m_selected_item->i = 0;
m_selected_amount = item.count; m_selected_amount = item.count;
@ -3503,16 +3373,12 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
// If the selected stack has become smaller, adjust m_selected_amount. // If the selected stack has become smaller, adjust m_selected_amount.
// Return the selected stack. // Return the selected stack.
if(m_selected_item) if (m_selected_item) {
{ if (m_selected_item->isValid()) {
if(m_selected_item->isValid())
{
Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc); Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
if(inv) if (inv) {
{
InventoryList *list = inv->getList(m_selected_item->listname); InventoryList *list = inv->getList(m_selected_item->listname);
if(list && (u32) m_selected_item->i < list->getSize()) if (list && (u32) m_selected_item->i < list->getSize()) {
{
ItemStack stack = list->getItem(m_selected_item->i); ItemStack stack = list->getItem(m_selected_item->i);
if (!m_selected_swap.empty()) { if (!m_selected_swap.empty()) {
if (m_selected_swap.name == stack.name && if (m_selected_swap.name == stack.name &&
@ -3530,7 +3396,7 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
// selection was not valid // selection was not valid
delete m_selected_item; delete m_selected_item;
m_selected_item = NULL; m_selected_item = nullptr;
m_selected_amount = 0; m_selected_amount = 0;
m_selected_dragging = false; m_selected_dragging = false;
} }
@ -3672,7 +3538,7 @@ void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode=quit_mode_no)
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
{ {
while(tocheck != NULL) { while (tocheck) {
if (tocheck == parent) { if (tocheck == parent) {
return true; return true;
} }
@ -3920,7 +3786,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_old_tooltip_id = -1; m_old_tooltip_id = -1;
updateSelectedItem(); updateSelectedItem();
ItemSpec s = getItemAtPos(m_pointer); GUIInventoryList::ItemSpec s = getItemAtPos(m_pointer);
Inventory *inv_selected = NULL; Inventory *inv_selected = NULL;
Inventory *inv_s = NULL; Inventory *inv_s = NULL;
@ -4023,7 +3889,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Some mouse button has been pressed // Some mouse button has been pressed
//infostream << "Mouse button " << button << " pressed at p=(" //infostream << "Mouse button " << button << " pressed at p=("
// <<p.X<<","<<p.Y<<")"<<std::endl; // << event.MouseInput.X << "," << event.MouseInput.Y << ")"
// << std::endl;
m_selected_dragging = false; m_selected_dragging = false;
@ -4033,7 +3900,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
} else if (!m_selected_item) { } else if (!m_selected_item) {
if (s_count && button != BET_WHEEL_UP) { if (s_count && button != BET_WHEEL_UP) {
// Non-empty stack has been clicked: select or shift-move it // Non-empty stack has been clicked: select or shift-move it
m_selected_item = new ItemSpec(s); m_selected_item = new GUIInventoryList::ItemSpec(s);
u32 count; u32 count;
if (button == BET_RIGHT) if (button == BET_RIGHT)
@ -4261,7 +4128,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// if there are no items selected or the selected item // if there are no items selected or the selected item
// belongs to craftresult list, proceed with crafting // belongs to craftresult list, proceed with crafting
if (m_selected_item == NULL || if (!m_selected_item ||
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") { !m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
assert(inv_s); assert(inv_s);
@ -4279,7 +4146,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
if (m_selected_amount == 0) { if (m_selected_amount == 0) {
m_selected_swap.clear(); m_selected_swap.clear();
delete m_selected_item; delete m_selected_item;
m_selected_item = NULL; m_selected_item = nullptr;
m_selected_amount = 0; m_selected_amount = 0;
m_selected_dragging = false; m_selected_dragging = false;
} }

@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
#include "irrlichttypes_extrabloated.h" #include "irrlichttypes_extrabloated.h"
#include "inventorymanager.h" #include "inventorymanager.h"
#include "modalMenu.h" #include "modalMenu.h"
#include "guiInventoryList.h"
#include "guiTable.h" #include "guiTable.h"
#include "network/networkprotocol.h" #include "network/networkprotocol.h"
#include "client/joystick_controller.h" #include "client/joystick_controller.h"
@ -78,51 +79,6 @@ public:
class GUIFormSpecMenu : public GUIModalMenu class GUIFormSpecMenu : public GUIModalMenu
{ {
struct ItemSpec
{
ItemSpec() = default;
ItemSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
s32 a_i) :
inventoryloc(a_inventoryloc),
listname(a_listname),
i(a_i)
{
}
bool isValid() const { return i != -1; }
InventoryLocation inventoryloc;
std::string listname;
s32 i = -1;
};
struct ListDrawSpec
{
ListDrawSpec() = default;
ListDrawSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
IGUIElement *elem, v2s32 a_geom, s32 a_start_item_i,
bool a_real_coordinates):
inventoryloc(a_inventoryloc),
listname(a_listname),
e(elem),
geom(a_geom),
start_item_i(a_start_item_i),
real_coordinates(a_real_coordinates)
{
}
InventoryLocation inventoryloc;
std::string listname;
IGUIElement *e;
v2s32 geom;
s32 start_item_i;
bool real_coordinates;
};
struct ListRingSpec struct ListRingSpec
{ {
ListRingSpec() = default; ListRingSpec() = default;
@ -186,35 +142,6 @@ class GUIFormSpecMenu : public GUIModalMenu
irr::video::SColor color; irr::video::SColor color;
}; };
struct StaticTextSpec
{
StaticTextSpec():
parent_button(NULL)
{
}
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect):
text(a_text),
rect(a_rect),
parent_button(NULL)
{
}
StaticTextSpec(const std::wstring &a_text,
const core::rect<s32> &a_rect,
gui::IGUIButton *a_parent_button):
text(a_text),
rect(a_rect),
parent_button(a_parent_button)
{
}
std::wstring text;
core::rect<s32> rect;
gui::IGUIButton *parent_button;
};
public: public:
GUIFormSpecMenu(JoystickController *joystick, GUIFormSpecMenu(JoystickController *joystick,
gui::IGUIElement* parent, s32 id, gui::IGUIElement* parent, s32 id,
@ -283,13 +210,37 @@ public:
m_focused_element = elementname; m_focused_element = elementname;
} }
Client *getClient() const
{
return m_client;
}
const GUIInventoryList::ItemSpec *getSelectedItem() const
{
return m_selected_item;
}
const u16 getSelectedAmount() const
{
return m_selected_amount;
}
bool doTooltipAppendItemname() const
{
return m_tooltip_append_itemname;
}
void addHoveredItemTooltip(const std::string &name)
{
m_hovered_item_tooltips.emplace_back(name);
}
/* /*
Remove and re-add (or reposition) stuff Remove and re-add (or reposition) stuff
*/ */
void regenerateGui(v2u32 screensize); void regenerateGui(v2u32 screensize);
ItemSpec getItemAtPos(v2s32 p) const; GUIInventoryList::ItemSpec getItemAtPos(v2s32 p) const;
void drawList(const ListDrawSpec &s, int layer, bool &item_hovered);
void drawSelectedItem(); void drawSelectedItem();
void drawMenu(); void drawMenu();
void updateSelectedItem(); void updateSelectedItem();
@ -342,7 +293,7 @@ protected:
std::string m_formspec_prepend; std::string m_formspec_prepend;
InventoryLocation m_current_inventory_location; InventoryLocation m_current_inventory_location;
std::vector<ListDrawSpec> m_inventorylists; std::vector<GUIInventoryList *> m_inventorylists;
std::vector<ListRingSpec> m_inventory_rings; std::vector<ListRingSpec> m_inventory_rings;
std::vector<gui::IGUIElement *> m_backgrounds; std::vector<gui::IGUIElement *> m_backgrounds;
std::unordered_map<std::string, bool> field_close_on_enter; std::unordered_map<std::string, bool> field_close_on_enter;
@ -354,7 +305,7 @@ protected:
std::vector<std::pair<FieldSpec, GUIScrollBar *>> m_scrollbars; std::vector<std::pair<FieldSpec, GUIScrollBar *>> m_scrollbars;
std::vector<std::pair<FieldSpec, std::vector<std::string>>> m_dropdowns; std::vector<std::pair<FieldSpec, std::vector<std::string>>> m_dropdowns;
ItemSpec *m_selected_item = nullptr; GUIInventoryList::ItemSpec *m_selected_item = nullptr;
u16 m_selected_amount = 0; u16 m_selected_amount = 0;
bool m_selected_dragging = false; bool m_selected_dragging = false;
ItemStack m_selected_swap; ItemStack m_selected_swap;
@ -374,12 +325,8 @@ protected:
bool m_bgnonfullscreen; bool m_bgnonfullscreen;
bool m_bgfullscreen; bool m_bgfullscreen;
bool m_slotborder;
video::SColor m_bgcolor; video::SColor m_bgcolor;
video::SColor m_fullscreen_bgcolor; video::SColor m_fullscreen_bgcolor;
video::SColor m_slotbg_n;
video::SColor m_slotbg_h;
video::SColor m_slotbordercolor;
video::SColor m_default_tooltip_bgcolor; video::SColor m_default_tooltip_bgcolor;
video::SColor m_default_tooltip_color; video::SColor m_default_tooltip_color;
@ -406,6 +353,8 @@ private:
GUITable::TableOptions table_options; GUITable::TableOptions table_options;
GUITable::TableColumns table_columns; GUITable::TableColumns table_columns;
GUIInventoryList::Options inventorylist_options;
struct { struct {
s32 max = 1000; s32 max = 1000;
s32 min = 0; s32 min = 0;
@ -428,6 +377,7 @@ private:
fs_key_pendig current_keys_pending; fs_key_pendig current_keys_pending;
std::string current_field_enter_pending = ""; std::string current_field_enter_pending = "";
std::vector<std::string> m_hovered_item_tooltips;
void parseElement(parserData* data, const std::string &element); void parseElement(parserData* data, const std::string &element);

@ -0,0 +1,210 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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 "guiInventoryList.h"
#include "guiFormSpecMenu.h"
#include "client/hud.h"
#include "client/client.h"
GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env,
gui::IGUIElement *parent,
s32 id,
const core::rect<s32> &rectangle,
InventoryManager *invmgr,
const InventoryLocation &inventoryloc,
const std::string &listname,
const v2s32 &geom,
const s32 start_item_i,
const v2s32 &slot_size,
const v2f32 &slot_spacing,
GUIFormSpecMenu *fs_menu,
const Options &options,
gui::IGUIFont *font) :
gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
m_invmgr(invmgr),
m_inventoryloc(inventoryloc),
m_listname(listname),
m_geom(geom),
m_start_item_i(start_item_i),
m_slot_size(slot_size),
m_slot_spacing(slot_spacing),
m_fs_menu(fs_menu),
m_options(options),
m_font(font),
m_hovered_i(-1)
{
}
void GUIInventoryList::draw()
{
if (!IsVisible)
return;
Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
if (!inv) {
warningstream << "GUIInventoryList::draw(): "
<< "The inventory location "
<< "\"" << m_inventoryloc.dump() << "\" doesn't exist anymore"
<< std::endl;
return;
}
InventoryList *ilist = inv->getList(m_listname);
if (!ilist) {
warningstream << "GUIInventoryList::draw(): "
<< "The inventory list \"" << m_listname << "\" @ \""
<< m_inventoryloc.dump() << "\" doesn't exist anymore"
<< std::endl;
return;
}
video::IVideoDriver *driver = Environment->getVideoDriver();
Client *client = m_fs_menu->getClient();
const ItemSpec *selected_item = m_fs_menu->getSelectedItem();
core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
for (s32 i = 0; i < m_geom.X * m_geom.Y; i++) {
s32 item_i = i + m_start_item_i;
if (item_i >= (s32)ilist->getSize())
break;
v2s32 p((i % m_geom.X) * m_slot_spacing.X,
(i / m_geom.X) * m_slot_spacing.Y);
core::rect<s32> rect = imgrect + base_pos + p;
ItemStack item = ilist->getItem(item_i);
bool selected = selected_item
&& m_invmgr->getInventory(selected_item->inventoryloc) == inv
&& selected_item->listname == m_listname
&& selected_item->i == item_i;
core::rect<s32> clipped_rect(rect);
clipped_rect.clipAgainst(AbsoluteClippingRect);
bool hovering = m_hovered_i == item_i;
ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
(hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
// layer 0
if (hovering) {
driver->draw2DRectangle(m_options.slotbg_h, rect, &AbsoluteClippingRect);
} else {
driver->draw2DRectangle(m_options.slotbg_n, rect, &AbsoluteClippingRect);
}
// Draw inv slot borders
if (m_options.slotborder) {
s32 x1 = rect.UpperLeftCorner.X;
s32 y1 = rect.UpperLeftCorner.Y;
s32 x2 = rect.LowerRightCorner.X;
s32 y2 = rect.LowerRightCorner.Y;
s32 border = 1;
core::rect<s32> clipping_rect = Parent ? Parent->getAbsoluteClippingRect()
: core::rect<s32>();
core::rect<s32> *clipping_rect_ptr = Parent ? &clipping_rect : nullptr;
driver->draw2DRectangle(m_options.slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y1 - border),
v2s32(x2 + border, y1)), clipping_rect_ptr);
driver->draw2DRectangle(m_options.slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y2),
v2s32(x2 + border, y2 + border)), clipping_rect_ptr);
driver->draw2DRectangle(m_options.slotbordercolor,
core::rect<s32>(v2s32(x1 - border, y1),
v2s32(x1, y2)), clipping_rect_ptr);
driver->draw2DRectangle(m_options.slotbordercolor,
core::rect<s32>(v2s32(x2, y1),
v2s32(x2 + border, y2)), clipping_rect_ptr);
}
// layer 1
if (selected)
item.takeItem(m_fs_menu->getSelectedAmount());
if (!item.empty()) {
// Draw item stack
drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
client, rotation_kind);
// Add hovering tooltip
if (hovering && !selected_item) {
std::string tooltip = item.getDescription(client->idef());
if (m_fs_menu->doTooltipAppendItemname())
tooltip += "\n[" + item.name + "]";
m_fs_menu->addHoveredItemTooltip(tooltip);
}
}
}
IGUIElement::draw();
}
bool GUIInventoryList::OnEvent(const SEvent &event)
{
if (event.EventType != EET_MOUSE_INPUT_EVENT) {
if (event.EventType == EET_GUI_EVENT &&
event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
// element is no longer hovered
m_hovered_i = -1;
}
return IGUIElement::OnEvent(event);
}
m_hovered_i = getItemIndexAtPos(v2s32(event.MouseInput.X, event.MouseInput.Y));
if (m_hovered_i != -1)
return IGUIElement::OnEvent(event);
// no item slot at pos of mouse event => allow clicking through
// find the element that would be hovered if this inventorylist was invisible
bool was_visible = IsVisible;
IsVisible = false;
IGUIElement *hovered =
Environment->getRootGUIElement()->getElementFromPoint(
core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
bool ret = hovered && hovered->OnEvent(event);
IsVisible = was_visible;
return ret;
}
s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const
{
if (!IsVisible || AbsoluteClippingRect.getArea() <= 0 ||
!AbsoluteClippingRect.isPointInside(p))
return -1;
core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
// instead of looping through each slot, we look where p would be in the grid
s32 i = (p.X - base_pos.X) / (s32)m_slot_spacing.X
+ m_geom.X * ((p.Y - base_pos.Y) / (s32)m_slot_spacing.Y);
v2s32 p0((i % m_geom.X) * m_slot_spacing.X,
(i / m_geom.X) * m_slot_spacing.Y);
core::rect<s32> rect = imgrect + base_pos + p0;
rect.clipAgainst(AbsoluteClippingRect);
if (rect.getArea() > 0 && rect.isPointInside(p))
return i + m_start_item_i;
return -1;
}

130
src/gui/guiInventoryList.h Normal file

@ -0,0 +1,130 @@
/*
Minetest
Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
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 "inventorymanager.h"
#include "irrlichttypes_extrabloated.h"
#include "util/string.h"
class GUIFormSpecMenu;
class GUIInventoryList : public gui::IGUIElement
{
public:
struct ItemSpec
{
ItemSpec() = default;
ItemSpec(const InventoryLocation &a_inventoryloc,
const std::string &a_listname,
s32 a_i) :
inventoryloc(a_inventoryloc),
listname(a_listname),
i(a_i)
{
}
bool isValid() const { return i != -1; }
InventoryLocation inventoryloc;
std::string listname;
s32 i = -1;
};
// options for inventorylists that are setable with the lua api
struct Options {
// whether a one-pixel border for the slots should be drawn and its color
bool slotborder = false;
video::SColor slotbordercolor = video::SColor(200, 0, 0, 0);
// colors for normal and highlighted slot background
video::SColor slotbg_n = video::SColor(255, 128, 128, 128);
video::SColor slotbg_h = video::SColor(255, 192, 192, 192);
};
GUIInventoryList(gui::IGUIEnvironment *env,
gui::IGUIElement *parent,
s32 id,
const core::rect<s32> &rectangle,
InventoryManager *invmgr,
const InventoryLocation &inventoryloc,
const std::string &listname,
const v2s32 &geom,
const s32 start_item_i,
const v2s32 &slot_size,
const v2f32 &slot_spacing,
GUIFormSpecMenu *fs_menu,
const Options &options,
gui::IGUIFont *font);
virtual void draw() override;
virtual bool OnEvent(const SEvent &event) override;
const InventoryLocation &getInventoryloc() const
{
return m_inventoryloc;
}
const std::string &getListname() const
{
return m_listname;
}
void setSlotBGColors(const video::SColor &slotbg_n, const video::SColor &slotbg_h)
{
m_options.slotbg_n = slotbg_n;
m_options.slotbg_h = slotbg_h;
}
void setSlotBorders(bool slotborder, const video::SColor &slotbordercolor)
{
m_options.slotborder = slotborder;
m_options.slotbordercolor = slotbordercolor;
}
// returns -1 if not item is at pos p
s32 getItemIndexAtPos(v2s32 p) const;
private:
InventoryManager *m_invmgr;
const InventoryLocation m_inventoryloc;
const std::string m_listname;
// specifies the width and height of the inventorylist in itemslots
const v2s32 m_geom;
// the first item's index in inventory
const s32 m_start_item_i;
// specifies how large the slot rects are
const v2s32 m_slot_size;
// specifies how large the space between slots is (space between is spacing-size)
const v2f32 m_slot_spacing;
// the GUIFormSpecMenu can have an item selected and co.
GUIFormSpecMenu *m_fs_menu;
Options m_options;
// the font
gui::IGUIFont *m_font;
// the index of the hovered item; -1 if no item is hovered
s32 m_hovered_i;
};

@ -177,6 +177,8 @@ src/gui/guiFormSpecMenu.h
src/gui/guiKeyChangeMenu.cpp src/gui/guiKeyChangeMenu.cpp
src/gui/guiHyperText.cpp src/gui/guiHyperText.cpp
src/gui/guiHyperText.h src/gui/guiHyperText.h
src/gui/guiInventoryList.cpp
src/gui/guiInventoryList.h
src/gui/guiItemImage.cpp src/gui/guiItemImage.cpp
src/gui/guiItemImage.h src/gui/guiItemImage.h
src/gui/guiMainMenu.h src/gui/guiMainMenu.h