guiInventoryList.cpp 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. Minetest
  3. Copyright (C) 2013 celeron55, Perttu Ahola <celeron55@gmail.com>
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU Lesser General Public License as published by
  6. the Free Software Foundation; either version 2.1 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU Lesser General Public License for more details.
  12. You should have received a copy of the GNU Lesser General Public License along
  13. with this program; if not, write to the Free Software Foundation, Inc.,
  14. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  15. */
  16. #include "guiInventoryList.h"
  17. #include "guiFormSpecMenu.h"
  18. #include "client/hud.h"
  19. #include "client/client.h"
  20. GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env,
  21. gui::IGUIElement *parent,
  22. s32 id,
  23. const core::rect<s32> &rectangle,
  24. InventoryManager *invmgr,
  25. const InventoryLocation &inventoryloc,
  26. const std::string &listname,
  27. const v2s32 &geom,
  28. const s32 start_item_i,
  29. const v2s32 &slot_size,
  30. const v2f32 &slot_spacing,
  31. GUIFormSpecMenu *fs_menu,
  32. const Options &options,
  33. gui::IGUIFont *font) :
  34. gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
  35. m_invmgr(invmgr),
  36. m_inventoryloc(inventoryloc),
  37. m_listname(listname),
  38. m_geom(geom),
  39. m_start_item_i(start_item_i),
  40. m_slot_size(slot_size),
  41. m_slot_spacing(slot_spacing),
  42. m_fs_menu(fs_menu),
  43. m_options(options),
  44. m_font(font),
  45. m_hovered_i(-1),
  46. m_already_warned(false)
  47. {
  48. }
  49. void GUIInventoryList::draw()
  50. {
  51. if (!IsVisible)
  52. return;
  53. Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
  54. if (!inv) {
  55. if (!m_already_warned) {
  56. warningstream << "GUIInventoryList::draw(): "
  57. << "The inventory location "
  58. << "\"" << m_inventoryloc.dump() << "\" doesn't exist"
  59. << std::endl;
  60. m_already_warned = true;
  61. }
  62. return;
  63. }
  64. InventoryList *ilist = inv->getList(m_listname);
  65. if (!ilist) {
  66. if (!m_already_warned) {
  67. warningstream << "GUIInventoryList::draw(): "
  68. << "The inventory list \"" << m_listname << "\" @ \""
  69. << m_inventoryloc.dump() << "\" doesn't exist"
  70. << std::endl;
  71. m_already_warned = true;
  72. }
  73. return;
  74. }
  75. m_already_warned = false;
  76. video::IVideoDriver *driver = Environment->getVideoDriver();
  77. Client *client = m_fs_menu->getClient();
  78. const ItemSpec *selected_item = m_fs_menu->getSelectedItem();
  79. core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
  80. v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
  81. const s32 list_size = (s32)ilist->getSize();
  82. for (s32 i = 0; i < m_geom.X * m_geom.Y; i++) {
  83. s32 item_i = i + m_start_item_i;
  84. if (item_i >= list_size)
  85. break;
  86. v2s32 p((i % m_geom.X) * m_slot_spacing.X,
  87. (i / m_geom.X) * m_slot_spacing.Y);
  88. core::rect<s32> rect = imgrect + base_pos + p;
  89. ItemStack item = ilist->getItem(item_i);
  90. bool selected = selected_item
  91. && m_invmgr->getInventory(selected_item->inventoryloc) == inv
  92. && selected_item->listname == m_listname
  93. && selected_item->i == item_i;
  94. bool hovering = m_hovered_i == item_i;
  95. ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
  96. (hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
  97. // layer 0
  98. if (hovering) {
  99. driver->draw2DRectangle(m_options.slotbg_h, rect, &AbsoluteClippingRect);
  100. } else {
  101. driver->draw2DRectangle(m_options.slotbg_n, rect, &AbsoluteClippingRect);
  102. }
  103. // Draw inv slot borders
  104. if (m_options.slotborder) {
  105. s32 x1 = rect.UpperLeftCorner.X;
  106. s32 y1 = rect.UpperLeftCorner.Y;
  107. s32 x2 = rect.LowerRightCorner.X;
  108. s32 y2 = rect.LowerRightCorner.Y;
  109. s32 border = 1;
  110. core::rect<s32> clipping_rect = Parent ? Parent->getAbsoluteClippingRect()
  111. : core::rect<s32>();
  112. core::rect<s32> *clipping_rect_ptr = Parent ? &clipping_rect : nullptr;
  113. driver->draw2DRectangle(m_options.slotbordercolor,
  114. core::rect<s32>(v2s32(x1 - border, y1 - border),
  115. v2s32(x2 + border, y1)), clipping_rect_ptr);
  116. driver->draw2DRectangle(m_options.slotbordercolor,
  117. core::rect<s32>(v2s32(x1 - border, y2),
  118. v2s32(x2 + border, y2 + border)), clipping_rect_ptr);
  119. driver->draw2DRectangle(m_options.slotbordercolor,
  120. core::rect<s32>(v2s32(x1 - border, y1),
  121. v2s32(x1, y2)), clipping_rect_ptr);
  122. driver->draw2DRectangle(m_options.slotbordercolor,
  123. core::rect<s32>(v2s32(x2, y1),
  124. v2s32(x2 + border, y2)), clipping_rect_ptr);
  125. }
  126. // layer 1
  127. if (selected)
  128. item.takeItem(m_fs_menu->getSelectedAmount());
  129. if (!item.empty()) {
  130. // Draw item stack
  131. drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
  132. client, rotation_kind);
  133. // Add hovering tooltip
  134. if (hovering && !selected_item) {
  135. std::string tooltip = item.getDescription(client->idef());
  136. if (m_fs_menu->doTooltipAppendItemname())
  137. tooltip += "\n[" + item.name + "]";
  138. m_fs_menu->addHoveredItemTooltip(tooltip);
  139. }
  140. }
  141. }
  142. IGUIElement::draw();
  143. }
  144. bool GUIInventoryList::OnEvent(const SEvent &event)
  145. {
  146. if (event.EventType != EET_MOUSE_INPUT_EVENT) {
  147. if (event.EventType == EET_GUI_EVENT &&
  148. event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
  149. // element is no longer hovered
  150. m_hovered_i = -1;
  151. }
  152. return IGUIElement::OnEvent(event);
  153. }
  154. m_hovered_i = getItemIndexAtPos(v2s32(event.MouseInput.X, event.MouseInput.Y));
  155. if (m_hovered_i != -1)
  156. return IGUIElement::OnEvent(event);
  157. // no item slot at pos of mouse event => allow clicking through
  158. // find the element that would be hovered if this inventorylist was invisible
  159. bool was_visible = IsVisible;
  160. IsVisible = false;
  161. IGUIElement *hovered =
  162. Environment->getRootGUIElement()->getElementFromPoint(
  163. core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
  164. // if the player clicks outside of the formspec window, hovered is not
  165. // m_fs_menu, but some other weird element (with ID -1). we do however need
  166. // hovered to be m_fs_menu as item dropping when clicking outside of the
  167. // formspec window is handled in its OnEvent callback
  168. if (!hovered || hovered->getID() == -1)
  169. hovered = m_fs_menu;
  170. bool ret = hovered->OnEvent(event);
  171. IsVisible = was_visible;
  172. return ret;
  173. }
  174. s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const
  175. {
  176. // no item if no gui element at pointer
  177. if (!IsVisible || AbsoluteClippingRect.getArea() <= 0 ||
  178. !AbsoluteClippingRect.isPointInside(p))
  179. return -1;
  180. // there can not be an item if the inventory or the inventorylist does not exist
  181. Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
  182. if (!inv)
  183. return -1;
  184. InventoryList *ilist = inv->getList(m_listname);
  185. if (!ilist)
  186. return -1;
  187. core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
  188. v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
  189. // instead of looping through each slot, we look where p would be in the grid
  190. s32 i = (p.X - base_pos.X) / (s32)m_slot_spacing.X
  191. + m_geom.X * ((p.Y - base_pos.Y) / (s32)m_slot_spacing.Y);
  192. v2s32 p0((i % m_geom.X) * m_slot_spacing.X,
  193. (i / m_geom.X) * m_slot_spacing.Y);
  194. core::rect<s32> rect = imgrect + base_pos + p0;
  195. rect.clipAgainst(AbsoluteClippingRect);
  196. if (rect.getArea() > 0 && rect.isPointInside(p) &&
  197. i + m_start_item_i < (s32)ilist->getSize())
  198. return i + m_start_item_i;
  199. return -1;
  200. }