diff --git a/build/android/jni/Android.mk b/build/android/jni/Android.mk index 60070f9e8..6d21544bf 100644 --- a/build/android/jni/Android.mk +++ b/build/android/jni/Android.mk @@ -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 \ diff --git a/doc/lua_api.txt b/doc/lua_api.txt index a4af821c3..80b694ee9 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -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[;;,;,;]` @@ -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[;]` diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index a2ac0b191..a9df7848d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -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 diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index d03ce4516..98f4368f4 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -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(parserData* data, 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"< 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(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(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 rect = core::rect(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(parserData* data, 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::iterator iter = m_fields.begin(); + for (std::vector::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 imgrect(0, 0, imgsize.X, imgsize.Y); - for (const GUIFormSpecMenu::ListDrawSpec &s : m_inventorylists) { - core::rect clipping_rect = s.e->getAbsoluteClippingRect(); - v2s32 base_pos = s.e->getAbsolutePosition().UpperLeftCorner; - for(s32 i=0; i rect = imgrect + base_pos + p0; - rect.clipAgainst(clipping_rect); - if (rect.getArea() > 0 && rect.isPointInside(p)) - return ItemSpec(s.inventoryloc, s.listname, item_i); - } + 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); } - 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; - } - 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 imgrect(0, 0, imgsize.X, imgsize.Y); - core::rect 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 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 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(v2s32(x1 - border, y1 - border), - v2s32(x2 + border, y1)), &clipping_rect); - driver->draw2DRectangle(m_slotbordercolor, - core::rect(v2s32(x1 - border, y2), - v2s32(x2 + border, y2 + border)), &clipping_rect); - driver->draw2DRectangle(m_slotbordercolor, - core::rect(v2s32(x1 - border, y1), - v2s32(x1, y2)), &clipping_rect); - driver->draw2DRectangle(m_slotbordercolor, - core::rect(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(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 "<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; } diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 155081e08..67be4268a 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -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 &a_rect): - text(a_text), - rect(a_rect), - parent_button(NULL) - { - } - - StaticTextSpec(const std::wstring &a_text, - const core::rect &a_rect, - gui::IGUIButton *a_parent_button): - text(a_text), - rect(a_rect), - parent_button(a_parent_button) - { - } - - std::wstring text; - core::rect 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 m_inventorylists; + std::vector m_inventorylists; std::vector m_inventory_rings; std::vector m_backgrounds; std::unordered_map field_close_on_enter; @@ -354,7 +305,7 @@ protected: std::vector> m_scrollbars; std::vector>> 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 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 index 000000000..ae7ec0539 --- /dev/null +++ b/src/gui/guiInventoryList.cpp @@ -0,0 +1,210 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +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 &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 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 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 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 clipping_rect = Parent ? Parent->getAbsoluteClippingRect() + : core::rect(); + core::rect *clipping_rect_ptr = Parent ? &clipping_rect : nullptr; + driver->draw2DRectangle(m_options.slotbordercolor, + core::rect(v2s32(x1 - border, y1 - border), + v2s32(x2 + border, y1)), clipping_rect_ptr); + driver->draw2DRectangle(m_options.slotbordercolor, + core::rect(v2s32(x1 - border, y2), + v2s32(x2 + border, y2 + border)), clipping_rect_ptr); + driver->draw2DRectangle(m_options.slotbordercolor, + core::rect(v2s32(x1 - border, y1), + v2s32(x1, y2)), clipping_rect_ptr); + driver->draw2DRectangle(m_options.slotbordercolor, + core::rect(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(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 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 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 index 000000000..fd2c3601b --- /dev/null +++ b/src/gui/guiInventoryList.h @@ -0,0 +1,130 @@ +/* +Minetest +Copyright (C) 2013 celeron55, Perttu Ahola + +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 &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; +}; diff --git a/util/travis/clang-format-whitelist.txt b/util/travis/clang-format-whitelist.txt index b945a6db3..a2559194a 100644 --- a/util/travis/clang-format-whitelist.txt +++ b/util/travis/clang-format-whitelist.txt @@ -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