Formspec: Create a new class for inventorylists (#9287)
authorDS <vorunbekannt75@web.de>
Sat, 1 Feb 2020 12:55:13 +0000 (13:55 +0100)
committerGitHub <noreply@github.com>
Sat, 1 Feb 2020 12:55:13 +0000 (13:55 +0100)
build/android/jni/Android.mk
doc/lua_api.txt
src/gui/CMakeLists.txt
src/gui/guiFormSpecMenu.cpp
src/gui/guiFormSpecMenu.h
src/gui/guiInventoryList.cpp [new file with mode: 0644]
src/gui/guiInventoryList.h [new file with mode: 0644]
util/travis/clang-format-whitelist.txt

index 60070f9e8ba6c9154cdcce1627cb6f5028887ee0..6d21544bfbc8154be3bf81cd24fc3d03956817c6 100644 (file)
@@ -189,6 +189,7 @@ LOCAL_SRC_FILES := \
                jni/src/gui/guiEngine.cpp                 \
                jni/src/gui/guiFormSpecMenu.cpp           \
                jni/src/gui/guiHyperText.cpp              \
+               jni/src/gui/guiInventoryList.cpp          \
                jni/src/gui/guiItemImage.cpp              \
                jni/src/gui/guiKeyChangeMenu.cpp          \
                jni/src/gui/guiPasswordChange.cpp         \
index a4af821c32175c2560bc835940000ff20d1d3899..80b694ee97c142af4435360f65fb4dc299b072a8 100644 (file)
@@ -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
 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
 reserved to pass key press events to formspec!
@@ -2058,7 +2056,6 @@ Elements
   be shown if the inventory list is of size 0.
 * **Note**: With the new coordinate system, the spacing between inventory
   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>]`
 
@@ -2066,7 +2063,6 @@ Elements
   be shown if the inventory list is of size 0.
 * **Note**: With the new coordinate system, the spacing between inventory
   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>]`
 
index a2ac0b19198f87c85536c7df9cd4ad26d5d92346..a9df7848de85d33a13dade023f04ee9eeff605f1 100644 (file)
@@ -9,6 +9,7 @@ set(gui_SRCS
        ${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiFormSpecMenu.cpp
+       ${CMAKE_CURRENT_SOURCE_DIR}/guiInventoryList.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiItemImage.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiKeyChangeMenu.cpp
        ${CMAKE_CURRENT_SOURCE_DIR}/guiPasswordChange.cpp
index d03ce451657ead46c12d211d22da0d08c6c1b96e..98f4368f451316a864d8e534fe0ebeb629198180 100644 (file)
@@ -61,6 +61,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "guiButtonImage.h"
 #include "guiButtonItemImage.h"
 #include "guiEditBoxWithScrollbar.h"
+#include "guiInventoryList.h"
 #include "guiItemImage.h"
 #include "guiScrollBar.h"
 #include "guiTable.h"
@@ -130,7 +131,7 @@ GUIFormSpecMenu::~GUIFormSpecMenu()
        for (auto &table_it : m_tables)
                table_it.second->drop();
        for (auto &inventorylist_it : m_inventorylists)
-               inventorylist_it.e->drop();
+               inventorylist_it->drop();
        for (auto &checkbox_it : m_checkboxes)
                checkbox_it.second->drop();
        for (auto &scrollbar_it : m_scrollbars)
@@ -347,7 +348,7 @@ void GUIFormSpecMenu::parseContainerEnd(parserData* data)
        }
 }
 
-void GUIFormSpecMenu::parseList(parserDatadata, const std::string &element)
+void GUIFormSpecMenu::parseList(parserData *data, const std::string &element)
 {
        if (m_client == 0) {
                warningstream<<"invalid use of 'list' with m_client==0"<<std::endl;
@@ -429,36 +430,28 @@ void GUIFormSpecMenu::parseList(parserData* data, const std::string &element)
                        3
                );
 
-               v2s32 pos;
-               core::rect<s32> rect;
+               v2f32 slot_spacing = data->real_coordinates ?
+                               v2f32(imgsize.X * 1.25f, imgsize.Y * 1.25f) : spacing;
 
-               if (data->real_coordinates) {
-                       pos = getRealCoordinateBasePos(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);
-               }
+               v2s32 pos = data->real_coordinates ? getRealCoordinateBasePos(v_pos)
+                               : getElementBasePos(&v_pos);
 
-               gui::IGUIElement *e = new gui::IGUIElement(EGUIET_ELEMENT, Environment,
-                               this, spec.fid, rect);
+               core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y,
+                               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
-               e->setVisible(false);
+               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);
 
-               m_inventorylists.emplace_back(loc, listname, e, geom, start_i,
-                               data->real_coordinates);
+               m_inventorylists.push_back(e);
                m_fields.push_back(spec);
                return;
        }
        errorstream<< "Invalid list element(" << parts.size() << "): '" << element << "'"  << std::endl;
 }
 
-void GUIFormSpecMenu::parseListRing(parserDatadata, const std::string &element)
+void GUIFormSpecMenu::parseListRing(parserData *data, const std::string &element)
 {
        if (m_client == 0) {
                errorstream << "WARNING: invalid use of 'listring' with m_client==0" << std::endl;
@@ -485,10 +478,10 @@ void GUIFormSpecMenu::parseListRing(parserData* data, const std::string &element
        if (element.empty() && m_inventorylists.size() > 1) {
                size_t siz = m_inventorylists.size();
                // insert the last two inv list elements into the list ring
-               const ListDrawSpec &spa = m_inventorylists[siz - 2];
-               const ListDrawSpec &spb = m_inventorylists[siz - 1];
-               m_inventory_rings.emplace_back(spa.inventoryloc, spa.listname);
-               m_inventory_rings.emplace_back(spb.inventoryloc, spb.listname);
+               const GUIInventoryList *spa = m_inventorylists[siz - 2];
+               const GUIInventoryList *spb = m_inventorylists[siz - 1];
+               m_inventory_rings.emplace_back(spa->getInventoryloc(), spa->getListname());
+               m_inventory_rings.emplace_back(spb->getInventoryloc(), spb->getListname());
                return;
        }
 
@@ -2171,12 +2164,13 @@ void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &eleme
        if (((parts.size() == 2) || (parts.size() == 3) || (parts.size() == 5)) ||
                ((parts.size() > 5) && (m_formspec_version > FORMSPEC_API_VERSION)))
        {
-               parseColorString(parts[0], m_slotbg_n, false);
-               parseColorString(parts[1], m_slotbg_h, false);
+               parseColorString(parts[0], data->inventorylist_options.slotbg_n, false);
+               parseColorString(parts[1], data->inventorylist_options.slotbg_h, false);
 
                if (parts.size() >= 3) {
-                       if (parseColorString(parts[2], m_slotbordercolor, false)) {
-                               m_slotborder = true;
+                       if (parseColorString(parts[2], data->inventorylist_options.slotbordercolor,
+                                       false)) {
+                               data->inventorylist_options.slotborder = true;
                        }
                }
                if (parts.size() == 5) {
@@ -2187,6 +2181,14 @@ void GUIFormSpecMenu::parseListColors(parserData* data, const std::string &eleme
                        if (parseColorString(parts[4], tmp_color, false))
                                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;
        }
        errorstream<< "Invalid listcolors element(" << parts.size() << "): '" << element << "'"  << std::endl;
@@ -2673,7 +2675,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        for (auto &table_it : m_tables)
                table_it.second->drop();
        for (auto &inventorylist_it : m_inventorylists)
-               inventorylist_it.e->drop();
+               inventorylist_it->drop();
        for (auto &checkbox_it : m_checkboxes)
                checkbox_it.second->drop();
        for (auto &scrollbar_it : m_scrollbars)
@@ -2692,8 +2694,6 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
        // Base position of contents of form
        mydata.basepos = getBasePos();
 
-       /* Convert m_init_draw_spec to m_inventorylists */
-
        m_inventorylists.clear();
        m_backgrounds.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_color = video::SColor(255,255,255,255);
 
-       m_slotbordercolor = video::SColor(200,0,0,0);
-       m_slotborder = false;
-
        // Add tooltip
        {
                assert(!m_tooltip_element);
@@ -3073,7 +3067,7 @@ bool GUIFormSpecMenu::getAndroidUIInput()
        std::string fieldname = m_jni_field_name;
        m_jni_field_name.clear();
 
-       for(std::vector<FieldSpec>::iterator iter =  m_fields.begin();
+       for (std::vector<FieldSpec>::iterator iter =  m_fields.begin();
                        iter != m_fields.end(); ++iter) {
 
                if (iter->fname != fieldname) {
@@ -3097,140 +3091,18 @@ bool GUIFormSpecMenu::getAndroidUIInput()
 }
 #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);
 
-       for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
-               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;
-
-                       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);
-}
-
-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;
+       for (const GUIInventoryList *e : m_inventorylists) {
+               s32 item_index = e->getItemIndexAtPos(p);
+               if (item_index != -1)
+                       return GUIInventoryList::ItemSpec(e->getInventoryloc(), e->getListname(),
+                                       item_index);
        }
-       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);
-                               }
-                       }
-               }
-       }
+       return GUIInventoryList::ItemSpec(InventoryLocation(), "", -1);
 }
 
 void GUIFormSpecMenu::drawSelectedItem()
@@ -3273,6 +3145,8 @@ void GUIFormSpecMenu::drawMenu()
        gui::IGUIFont *old_font = skin->getFont();
        skin->setFont(m_font);
 
+       m_hovered_item_tooltips.clear();
+
        updateSelectedItem();
 
        video::IVideoDriver* driver = Environment->getVideoDriver();
@@ -3315,21 +3189,17 @@ void GUIFormSpecMenu::drawMenu()
 
        /*
                Call base class
+               (This is where all the drawing happens.)
        */
        gui::IGUIElement::draw();
 
-       /*
-               Draw items
-               Layer 0: Item slot rectangles
-               Layer 1: Item images; prepare tooltip
-       */
-       bool item_hovered = false;
-       for (int layer = 0; layer < 2; layer++) {
-               for (const GUIFormSpecMenu::ListDrawSpec &spec : m_inventorylists) {
-                       drawList(spec, layer, item_hovered);
-               }
+       // Draw hovered item tooltips
+       for (const std::string &tooltip : m_hovered_item_tooltips) {
+               showTooltip(utf8_to_wide(tooltip), m_default_tooltip_color,
+                               m_default_tooltip_bgcolor);
        }
-       if (!item_hovered) {
+
+       if (m_hovered_item_tooltips.empty()) {
                // reset rotation time
                drawItemStack(driver, m_font, ItemStack(),
                        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 (!m_selected_item) {
-               for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) {
-                       if (s.listname != "craftpreview")
+               for (const GUIInventoryList *e : m_inventorylists) {
+                       if (e->getListname() != "craftpreview")
                                continue;
 
-                       Inventory *inv = m_invmgr->getInventory(s.inventoryloc);
+                       Inventory *inv = m_invmgr->getInventory(e->getInventoryloc());
                        if (!inv)
                                continue;
 
@@ -3481,8 +3351,8 @@ void GUIFormSpecMenu::updateSelectedItem()
                                continue;
 
                        // Grab selected item from the crafting result list
-                       m_selected_item = new ItemSpec;
-                       m_selected_item->inventoryloc = s.inventoryloc;
+                       m_selected_item = new GUIInventoryList::ItemSpec;
+                       m_selected_item->inventoryloc = e->getInventoryloc();
                        m_selected_item->listname = "craftresult";
                        m_selected_item->i = 0;
                        m_selected_amount = item.count;
@@ -3503,16 +3373,12 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
        // If the selected stack has become smaller, adjust m_selected_amount.
        // Return the selected stack.
 
-       if(m_selected_item)
-       {
-               if(m_selected_item->isValid())
-               {
+       if (m_selected_item) {
+               if (m_selected_item->isValid()) {
                        Inventory *inv = m_invmgr->getInventory(m_selected_item->inventoryloc);
-                       if(inv)
-                       {
+                       if (inv) {
                                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);
                                        if (!m_selected_swap.empty()) {
                                                if (m_selected_swap.name == stack.name &&
@@ -3530,7 +3396,7 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
 
                // selection was not valid
                delete m_selected_item;
-               m_selected_item = NULL;
+               m_selected_item = nullptr;
                m_selected_amount = 0;
                m_selected_dragging = false;
        }
@@ -3670,9 +3536,9 @@ 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) {
                        return true;
                }
@@ -3709,8 +3575,8 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
        }
 
        // Fix Esc/Return key being eaten by checkboxen and tables
-       if(event.EventType==EET_KEY_INPUT_EVENT) {
-               KeyPress kp(event.KeyInput);
+       if (event.EventType == EET_KEY_INPUT_EVENT) {
+                       KeyPress kp(event.KeyInput);
                if (kp == EscapeKey || kp == CancelKey
                                || kp == getKeySetting("keymap_inventory")
                                || event.KeyInput.Key==KEY_RETURN) {
@@ -3749,7 +3615,7 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event)
                if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) {
                        m_old_tooltip_id = -1;
                }
-               if (!isChild(hovered,this)) {
+               if (!isChild(hovered, this)) {
                        if (DoubleClickDetection(event)) {
                                return true;
                        }
@@ -3920,7 +3786,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
 
                m_old_tooltip_id = -1;
                updateSelectedItem();
-               ItemSpec s = getItemAtPos(m_pointer);
+               GUIInventoryList::ItemSpec s = getItemAtPos(m_pointer);
 
                Inventory *inv_selected = NULL;
                Inventory *inv_s = NULL;
@@ -4022,8 +3888,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                case BET_DOWN:
                        // Some mouse button has been pressed
 
-                       //infostream<<"Mouse button "<<button<<" pressed at p=("
-                       //      <<p.X<<","<<p.Y<<")"<<std::endl;
+                       //infostream << "Mouse button " << button << " pressed at p=("
+                       //      << event.MouseInput.X << "," << event.MouseInput.Y << ")"
+                       //      << std::endl;
 
                        m_selected_dragging = false;
 
@@ -4033,7 +3900,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                        } else if (!m_selected_item) {
                                if (s_count && button != BET_WHEEL_UP) {
                                        // 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;
                                        if (button == BET_RIGHT)
@@ -4261,7 +4128,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
 
                        // if there are no items selected or the selected item
                        // 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") {
 
                                assert(inv_s);
@@ -4279,7 +4146,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
                if (m_selected_amount == 0) {
                        m_selected_swap.clear();
                        delete m_selected_item;
-                       m_selected_item = NULL;
+                       m_selected_item = nullptr;
                        m_selected_amount = 0;
                        m_selected_dragging = false;
                }
index 155081e085d46e75dec3e65f7fb73b010b135860..67be4268a89a6b6701f54762b121d7e161173b44 100644 (file)
@@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
 #include "irrlichttypes_extrabloated.h"
 #include "inventorymanager.h"
 #include "modalMenu.h"
+#include "guiInventoryList.h"
 #include "guiTable.h"
 #include "network/networkprotocol.h"
 #include "client/joystick_controller.h"
@@ -78,51 +79,6 @@ public:
 
 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
        {
                ListRingSpec() = default;
@@ -186,35 +142,6 @@ class GUIFormSpecMenu : public GUIModalMenu
                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:
        GUIFormSpecMenu(JoystickController *joystick,
                        gui::IGUIElement* parent, s32 id,
@@ -283,13 +210,37 @@ public:
                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
        */
        void regenerateGui(v2u32 screensize);
 
-       ItemSpec getItemAtPos(v2s32 p) const;
-       void drawList(const ListDrawSpec &s, int layer, bool &item_hovered);
+       GUIInventoryList::ItemSpec getItemAtPos(v2s32 p) const;
        void drawSelectedItem();
        void drawMenu();
        void updateSelectedItem();
@@ -342,7 +293,7 @@ protected:
        std::string m_formspec_prepend;
        InventoryLocation m_current_inventory_location;
 
-       std::vector<ListDrawSpec> m_inventorylists;
+       std::vector<GUIInventoryList *> m_inventorylists;
        std::vector<ListRingSpec> m_inventory_rings;
        std::vector<gui::IGUIElement *> m_backgrounds;
        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, std::vector<std::string>>> m_dropdowns;
 
-       ItemSpec *m_selected_item = nullptr;
+       GUIInventoryList::ItemSpec *m_selected_item = nullptr;
        u16 m_selected_amount = 0;
        bool m_selected_dragging = false;
        ItemStack m_selected_swap;
@@ -374,12 +325,8 @@ protected:
 
        bool m_bgnonfullscreen;
        bool m_bgfullscreen;
-       bool m_slotborder;
        video::SColor m_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_color;
 
@@ -406,6 +353,8 @@ private:
                GUITable::TableOptions table_options;
                GUITable::TableColumns table_columns;
 
+               GUIInventoryList::Options inventorylist_options;
+
                struct {
                        s32 max = 1000;
                        s32 min = 0;
@@ -428,6 +377,7 @@ private:
 
        fs_key_pendig current_keys_pending;
        std::string current_field_enter_pending = "";
+       std::vector<std::string> m_hovered_item_tooltips;
 
        void parseElement(parserData* data, const std::string &element);
 
diff --git a/src/gui/guiInventoryList.cpp b/src/gui/guiInventoryList.cpp
new file mode 100644 (file)
index 0000000..ae7ec05
--- /dev/null
@@ -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;
+}
diff --git a/src/gui/guiInventoryList.h b/src/gui/guiInventoryList.h
new file mode 100644 (file)
index 0000000..fd2c360
--- /dev/null
@@ -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;
+};
index b945a6db3a39b965f8c1364f75221927a257a5cd..a2559194ae4208504e3133d9e1a3135712002410 100644 (file)
@@ -177,6 +177,8 @@ src/gui/guiFormSpecMenu.h
 src/gui/guiKeyChangeMenu.cpp
 src/gui/guiHyperText.cpp
 src/gui/guiHyperText.h
+src/gui/guiInventoryList.cpp
+src/gui/guiInventoryList.h
 src/gui/guiItemImage.cpp
 src/gui/guiItemImage.h
 src/gui/guiMainMenu.h