]> git.lizzy.rs Git - dragonfireclient.git/blob - src/gui/guiInventoryList.cpp
Improved Tracers
[dragonfireclient.git] / src / gui / guiInventoryList.cpp
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
17 #include "guiInventoryList.h"
18 #include "guiFormSpecMenu.h"
19 #include "client/hud.h"
20 #include "client/client.h"
21
22 GUIInventoryList::GUIInventoryList(gui::IGUIEnvironment *env,
23         gui::IGUIElement *parent,
24         s32 id,
25         const core::rect<s32> &rectangle,
26         InventoryManager *invmgr,
27         const InventoryLocation &inventoryloc,
28         const std::string &listname,
29         const v2s32 &geom,
30         const s32 start_item_i,
31         const v2s32 &slot_size,
32         const v2f32 &slot_spacing,
33         GUIFormSpecMenu *fs_menu,
34         const Options &options,
35         gui::IGUIFont *font) :
36         gui::IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, rectangle),
37         m_invmgr(invmgr),
38         m_inventoryloc(inventoryloc),
39         m_listname(listname),
40         m_geom(geom),
41         m_start_item_i(start_item_i),
42         m_slot_size(slot_size),
43         m_slot_spacing(slot_spacing),
44         m_fs_menu(fs_menu),
45         m_options(options),
46         m_font(font),
47         m_hovered_i(-1),
48         m_already_warned(false)
49 {
50 }
51
52 void GUIInventoryList::draw()
53 {
54         if (!IsVisible)
55                 return;
56
57         Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
58         if (!inv) {
59                 if (!m_already_warned) {
60                         warningstream << "GUIInventoryList::draw(): "
61                                         << "The inventory location "
62                                         << "\"" << m_inventoryloc.dump() << "\" doesn't exist"
63                                         << std::endl;
64                         m_already_warned = true;
65                 }
66                 return;
67         }
68         InventoryList *ilist = inv->getList(m_listname);
69         if (!ilist) {
70                 if (!m_already_warned) {
71                         warningstream << "GUIInventoryList::draw(): "
72                                         << "The inventory list \"" << m_listname << "\" @ \""
73                                         << m_inventoryloc.dump() << "\" doesn't exist"
74                                         << std::endl;
75                         m_already_warned = true;
76                 }
77                 return;
78         }
79         m_already_warned = false;
80
81         video::IVideoDriver *driver = Environment->getVideoDriver();
82         Client *client = m_fs_menu->getClient();
83         const ItemSpec *selected_item = m_fs_menu->getSelectedItem();
84
85         core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
86         v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
87
88         const s32 list_size = (s32)ilist->getSize();
89
90         for (s32 i = 0; i < m_geom.X * m_geom.Y; i++) {
91                 s32 item_i = i + m_start_item_i;
92                 if (item_i >= list_size)
93                         break;
94
95                 v2s32 p((i % m_geom.X) * m_slot_spacing.X,
96                                 (i / m_geom.X) * m_slot_spacing.Y);
97                 core::rect<s32> rect = imgrect + base_pos + p;
98                 ItemStack item = ilist->getItem(item_i);
99
100                 bool selected = selected_item
101                         && m_invmgr->getInventory(selected_item->inventoryloc) == inv
102                         && selected_item->listname == m_listname
103                         && selected_item->i == item_i;
104                 core::rect<s32> clipped_rect(rect);
105                 clipped_rect.clipAgainst(AbsoluteClippingRect);
106                 bool hovering = m_hovered_i == item_i;
107                 ItemRotationKind rotation_kind = selected ? IT_ROT_SELECTED :
108                         (hovering ? IT_ROT_HOVERED : IT_ROT_NONE);
109
110                 // layer 0
111                 if (hovering) {
112                         driver->draw2DRectangle(m_options.slotbg_h, rect, &AbsoluteClippingRect);
113                 } else {
114                         driver->draw2DRectangle(m_options.slotbg_n, rect, &AbsoluteClippingRect);
115                 }
116
117                 // Draw inv slot borders
118                 if (m_options.slotborder) {
119                         s32 x1 = rect.UpperLeftCorner.X;
120                         s32 y1 = rect.UpperLeftCorner.Y;
121                         s32 x2 = rect.LowerRightCorner.X;
122                         s32 y2 = rect.LowerRightCorner.Y;
123                         s32 border = 1;
124                         core::rect<s32> clipping_rect = Parent ? Parent->getAbsoluteClippingRect()
125                                         : core::rect<s32>();
126                         core::rect<s32> *clipping_rect_ptr = Parent ? &clipping_rect : nullptr;
127                         driver->draw2DRectangle(m_options.slotbordercolor,
128                                 core::rect<s32>(v2s32(x1 - border, y1 - border),
129                                                                 v2s32(x2 + border, y1)), clipping_rect_ptr);
130                         driver->draw2DRectangle(m_options.slotbordercolor,
131                                 core::rect<s32>(v2s32(x1 - border, y2),
132                                                                 v2s32(x2 + border, y2 + border)), clipping_rect_ptr);
133                         driver->draw2DRectangle(m_options.slotbordercolor,
134                                 core::rect<s32>(v2s32(x1 - border, y1),
135                                                                 v2s32(x1, y2)), clipping_rect_ptr);
136                         driver->draw2DRectangle(m_options.slotbordercolor,
137                                 core::rect<s32>(v2s32(x2, y1),
138                                                                 v2s32(x2 + border, y2)), clipping_rect_ptr);
139                 }
140
141                 // layer 1
142                 if (selected)
143                         item.takeItem(m_fs_menu->getSelectedAmount());
144
145                 if (!item.empty()) {
146                         // Draw item stack
147                         drawItemStack(driver, m_font, item, rect, &AbsoluteClippingRect,
148                                         client, rotation_kind);
149                         // Add hovering tooltip
150                         if (hovering && !selected_item) {
151                                 std::string tooltip = item.getDescription(client->idef());
152                                 if (m_fs_menu->doTooltipAppendItemname())
153                                         tooltip += "\n[" + item.name + "]";
154                                 m_fs_menu->addHoveredItemTooltip(tooltip);
155                         }
156                 }
157         }
158
159         IGUIElement::draw();
160 }
161
162 bool GUIInventoryList::OnEvent(const SEvent &event)
163 {
164         if (event.EventType != EET_MOUSE_INPUT_EVENT) {
165                 if (event.EventType == EET_GUI_EVENT &&
166                                 event.GUIEvent.EventType == EGET_ELEMENT_LEFT) {
167                         // element is no longer hovered
168                         m_hovered_i = -1;
169                 }
170                 return IGUIElement::OnEvent(event);
171         }
172
173         m_hovered_i = getItemIndexAtPos(v2s32(event.MouseInput.X, event.MouseInput.Y));
174
175         if (m_hovered_i != -1)
176                 return IGUIElement::OnEvent(event);
177
178         // no item slot at pos of mouse event => allow clicking through
179         // find the element that would be hovered if this inventorylist was invisible
180         bool was_visible = IsVisible;
181         IsVisible = false;
182         IGUIElement *hovered =
183                 Environment->getRootGUIElement()->getElementFromPoint(
184                         core::position2d<s32>(event.MouseInput.X, event.MouseInput.Y));
185
186         // if the player clicks outside of the formspec window, hovered is not
187         // m_fs_menu, but some other weird element (with ID -1). we do however need
188         // hovered to be m_fs_menu as item dropping when clicking outside of the
189         // formspec window is handled in its OnEvent callback
190         if (!hovered || hovered->getID() == -1)
191                 hovered = m_fs_menu;
192
193         bool ret = hovered->OnEvent(event);
194
195         IsVisible = was_visible;
196
197         return ret;
198 }
199
200 s32 GUIInventoryList::getItemIndexAtPos(v2s32 p) const
201 {
202         // no item if no gui element at pointer
203         if (!IsVisible || AbsoluteClippingRect.getArea() <= 0 ||
204                         !AbsoluteClippingRect.isPointInside(p))
205                 return -1;
206
207         // there can not be an item if the inventory or the inventorylist does not exist
208         Inventory *inv = m_invmgr->getInventory(m_inventoryloc);
209         if (!inv)
210                 return -1;
211         InventoryList *ilist = inv->getList(m_listname);
212         if (!ilist)
213                 return -1;
214
215         core::rect<s32> imgrect(0, 0, m_slot_size.X, m_slot_size.Y);
216         v2s32 base_pos = AbsoluteRect.UpperLeftCorner;
217
218         // instead of looping through each slot, we look where p would be in the grid
219         s32 i = (p.X - base_pos.X) / (s32)m_slot_spacing.X
220                         + m_geom.X * ((p.Y - base_pos.Y) / (s32)m_slot_spacing.Y);
221
222         v2s32 p0((i % m_geom.X) * m_slot_spacing.X,
223                         (i / m_geom.X) * m_slot_spacing.Y);
224
225         core::rect<s32> rect = imgrect + base_pos + p0;
226
227         rect.clipAgainst(AbsoluteClippingRect);
228
229         if (rect.getArea() > 0 && rect.isPointInside(p) &&
230                         i + m_start_item_i < (s32)ilist->getSize())
231                 return i + m_start_item_i;
232
233         return -1;
234 }