Inventory mouse shortcut improvements (#13146)

Co-authored-by: Muhammad Rifqi Priyo Susanto <muhammadrifqipriyosusanto@gmail.com>
This commit is contained in:
OgelGames 2023-06-05 20:00:32 +10:00 committed by GitHub
parent 23f7aab354
commit 252c79d53a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 520 additions and 168 deletions

@ -682,7 +682,7 @@ void Client::step(float dtime)
the local inventory (so the player notices the lag problem
and knows something is wrong).
*/
if (m_inventory_from_server) {
if (m_inventory_from_server && !inhibit_inventory_revert) {
float interval = 10.0f;
float count_before = std::floor(m_inventory_from_server_age / interval);

@ -437,11 +437,14 @@ public:
ModChannel *getModChannel(const std::string &channel) override;
const std::string &getFormspecPrepend() const;
inline MeshGrid getMeshGrid()
{
return m_mesh_grid;
}
bool inhibit_inventory_revert = false;
private:
void loadMods();

@ -3746,10 +3746,15 @@ void GUIFormSpecMenu::showTooltip(const std::wstring &text,
void GUIFormSpecMenu::updateSelectedItem()
{
// Don't update when dragging an item
if (m_selected_item && (m_selected_dragging || m_left_dragging))
return;
verifySelectedItem();
// If craftresult is nonempty and nothing else is selected, select it now.
if (!m_selected_item) {
// If craftresult is not empty and nothing else is selected,
// try to move it somewhere or select it now
if (!m_selected_item || m_shift_move_after_craft) {
for (const GUIInventoryList *e : m_inventorylists) {
if (e->getListname() != "craftpreview")
continue;
@ -3767,14 +3772,48 @@ void GUIFormSpecMenu::updateSelectedItem()
if (item.empty())
continue;
// Grab selected item from the crafting result list
m_selected_item = new GUIInventoryList::ItemSpec;
m_selected_item->inventoryloc = e->getInventoryloc();
m_selected_item->listname = "craftresult";
m_selected_item->i = 0;
m_selected_item->slotsize = e->getSlotSize();
m_selected_amount = item.count;
m_selected_dragging = false;
GUIInventoryList::ItemSpec s = GUIInventoryList::ItemSpec();
s.inventoryloc = e->getInventoryloc();
s.listname = "craftresult";
s.i = 0;
s.slotsize = e->getSlotSize();
if (m_shift_move_after_craft) {
// Try to shift-move the crafted item to the next list in the ring after the "craft" list.
// We don't look for the "craftresult" list because it's a hidden list,
// and shouldn't be part of the formspec, thus it won't be in the list ring.
do {
s16 r = getNextInventoryRing(s.inventoryloc, "craft");
if (r < 0) // Not found
break;
const ListRingSpec &to_ring = m_inventory_rings[r];
Inventory *inv_to = m_invmgr->getInventory(to_ring.inventoryloc);
if (!inv_to)
break;
InventoryList *list_to = inv_to->getList(to_ring.listname);
if (!list_to)
break;
IMoveAction *a = new IMoveAction();
a->count = item.count;
a->from_inv = s.inventoryloc;
a->from_list = s.listname;
a->from_i = s.i;
a->to_inv = to_ring.inventoryloc;
a->to_list = to_ring.listname;
a->move_somewhere = true;
m_invmgr->inventoryAction(a);
} while (0);
m_shift_move_after_craft = false;
} else {
// Grab selected item from the crafting result list
m_selected_item = new GUIInventoryList::ItemSpec(s);
m_selected_amount = item.count;
m_selected_dragging = false;
}
break;
}
}
@ -3821,6 +3860,25 @@ ItemStack GUIFormSpecMenu::verifySelectedItem()
return ItemStack();
}
s16 GUIFormSpecMenu::getNextInventoryRing(
const InventoryLocation &inventoryloc, const std::string &listname)
{
u16 rings = m_inventory_rings.size();
if (rings < 2)
return -1;
// Look for the source ring
s16 index = -1;
for (u16 i = 0; i < rings; i++) {
ListRingSpec &lr = m_inventory_rings[i];
if (lr.inventoryloc == inventoryloc && lr.listname == listname) {
// Set the index to the next ring
index = (i + 1) % rings;
break;
}
}
return index;
}
void GUIFormSpecMenu::acceptInput(FormspecQuitMode quitmode)
{
if(m_text_dst)
@ -4052,19 +4110,6 @@ void GUIFormSpecMenu::tryClose()
}
}
enum ButtonEventType : u8
{
BET_LEFT,
BET_RIGHT,
BET_MIDDLE,
BET_WHEEL_UP,
BET_WHEEL_DOWN,
BET_UP,
BET_DOWN,
BET_MOVE,
BET_OTHER
};
bool GUIFormSpecMenu::OnEvent(const SEvent& event)
{
if (event.EventType==EET_KEY_INPUT_EVENT) {
@ -4117,13 +4162,14 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
/* Mouse event other than movement, or crossing the border of inventory
field while holding right mouse button
field while holding left, right, or middle mouse button
*/
if (event.EventType == EET_MOUSE_INPUT_EVENT &&
(event.MouseInput.Event != EMIE_MOUSE_MOVED ||
(event.MouseInput.Event == EMIE_MOUSE_MOVED &&
event.MouseInput.isRightPressed() &&
getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) {
(event.MouseInput.Event != EMIE_MOUSE_MOVED ||
((event.MouseInput.isLeftPressed() ||
event.MouseInput.isRightPressed() ||
event.MouseInput.isMiddlePressed()) &&
getItemAtPos(m_pointer).i != getItemAtPos(m_old_pointer).i))) {
// Get selected item and hovered/clicked item (s)
@ -4132,13 +4178,15 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
GUIInventoryList::ItemSpec s = getItemAtPos(m_pointer);
Inventory *inv_selected = NULL;
InventoryList *list_selected = NULL;
Inventory *inv_s = NULL;
InventoryList *list_s = NULL;
if (m_selected_item) {
inv_selected = m_invmgr->getInventory(m_selected_item->inventoryloc);
sanity_check(inv_selected);
sanity_check(inv_selected->getList(m_selected_item->listname) != NULL);
list_selected = inv_selected->getList(m_selected_item->listname);
sanity_check(list_selected);
}
u32 s_count = 0;
@ -4174,12 +4222,21 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
s_count = list_s->getItem(s.i).count;
} while(0);
bool identical = m_selected_item && s.isValid() &&
(inv_selected == inv_s) &&
(m_selected_item->listname == s.listname) &&
(m_selected_item->i == s.i);
// True if the hovered slot is the selected slot
bool identical = m_selected_item && s.isValid() && (*m_selected_item == s);
ButtonEventType button = BET_LEFT;
// True if the hovered slot is empty
bool empty = s.isValid() && list_s->getItem(s.i).empty();
// True if the hovered item would stack with the selected item
bool matching = false;
if (m_selected_item && s.isValid()) {
ItemStack a = list_selected->getItem(m_selected_item->i);
ItemStack b = list_s->getItem(s.i);
matching = a.stacksWith(b);
}
ButtonEventType button = BET_OTHER;
ButtonEventType updown = BET_OTHER;
switch (event.MouseInput.Event) {
case EMIE_LMOUSE_PRESSED_DOWN:
@ -4220,6 +4277,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// from s to the next inventory ring.
u32 shift_move_amount = 0;
// Set this number to a positive value to generate a move action
// from s to m_selected_item.
u32 pickup_amount = 0;
// Set this number to a positive value to generate a drop action
// from m_selected_item.
u32 drop_amount = 0;
@ -4228,92 +4289,154 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
u32 craft_amount = 0;
switch (updown) {
case BET_DOWN:
case BET_DOWN: {
// Some mouse button has been pressed
//infostream << "Mouse button " << button << " pressed at p=("
// << event.MouseInput.X << "," << event.MouseInput.Y << ")"
// << std::endl;
if (m_held_mouse_button != BET_OTHER)
break;
if (button == BET_LEFT || button == BET_RIGHT || button == BET_MIDDLE)
m_held_mouse_button = button;
m_selected_dragging = false;
if (s.isValid() && s.listname == "craftpreview") {
// Craft preview has been clicked: craft
craft_amount = (button == BET_MIDDLE ? 10 : 1);
} 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 GUIInventoryList::ItemSpec(s);
u32 count;
if (button == BET_RIGHT)
count = (s_count + 1) / 2;
else if (button == BET_MIDDLE)
count = MYMIN(s_count, 10);
else if (button == BET_WHEEL_DOWN)
count = 1;
else // left
count = s_count;
if (!event.MouseInput.Shift) {
// no shift: select item
m_selected_amount = count;
m_selected_dragging = button != BET_WHEEL_DOWN;
m_auto_place = false;
} else {
// shift pressed: move item, right click moves 1
shift_move_amount = button == BET_RIGHT ? 1 : count;
}
}
} else { // m_selected_item != NULL
assert(m_selected_amount >= 1);
if (s.isValid()) {
// Clicked a slot: move
if (button == BET_RIGHT || button == BET_WHEEL_UP)
move_amount = 1;
else if (button == BET_MIDDLE)
move_amount = MYMIN(m_selected_amount, 10);
else if (button == BET_LEFT)
move_amount = m_selected_amount;
// else wheeldown
if (identical) {
if (button == BET_WHEEL_DOWN) {
if (m_selected_amount < s_count)
++m_selected_amount;
} else {
if (move_amount >= m_selected_amount)
m_selected_amount = 0;
else
m_selected_amount -= move_amount;
move_amount = 0;
}
}
} else if (!getAbsoluteClippingRect().isPointInside(m_pointer)
&& button != BET_WHEEL_DOWN) {
if (!s.isValid()) {
if (m_selected_item && !getAbsoluteClippingRect().isPointInside(m_pointer)) {
// Clicked outside of the window: drop
if (button == BET_RIGHT || button == BET_WHEEL_UP)
drop_amount = 1;
else if (button == BET_MIDDLE)
drop_amount = MYMIN(m_selected_amount, 10);
else // left
else if (button == BET_LEFT)
drop_amount = m_selected_amount;
}
break;
}
break;
case BET_UP:
if (s.listname == "craftpreview") {
// Craft preview has been clicked: craft
if (button == BET_MIDDLE)
craft_amount = 10;
else if (event.MouseInput.Shift && button == BET_LEFT)
// TODO: We should craft everything with shift-left-click,
// but the slow crafting code limits us, so we only craft one
craft_amount = 1;
//craft_amount = list_s->getItem(s.i).getStackMax(m_client->idef());
else
craft_amount = 1;
// Holding shift moves the crafted item to the inventory
m_shift_move_after_craft = event.MouseInput.Shift;
} else if (!m_selected_item && button != BET_WHEEL_UP && !empty) {
// Non-empty stack has been clicked: select or shift-move it
u32 count = 0;
if (button == BET_RIGHT)
count = (s_count + 1) / 2;
else if (button == BET_MIDDLE)
count = MYMIN(s_count, 10);
else if (button == BET_WHEEL_DOWN)
count = 1;
else if (button == BET_LEFT)
count = s_count;
if (event.MouseInput.Shift) {
// Shift pressed: move item, right click moves 1
shift_move_amount = button == BET_RIGHT ? 1 : count;
} else {
// No shift: select item
m_selected_item = new GUIInventoryList::ItemSpec(s);
m_selected_amount = count;
m_selected_dragging = button != BET_WHEEL_DOWN;
}
} else if (m_selected_item) {
// Clicked a slot: move
if (button == BET_RIGHT || button == BET_WHEEL_UP)
move_amount = 1;
else if (button == BET_WHEEL_DOWN)
pickup_amount = MYMIN(s_count, 1);
else if (button == BET_MIDDLE)
move_amount = MYMIN(m_selected_amount, 10);
else if (button == BET_LEFT)
move_amount = m_selected_amount;
if (event.MouseInput.Shift && !identical && matching) {
// Shift-move all items the same as the selected item to the next list
move_amount = 0;
// Try to find somewhere to move the items to
s16 r = getNextInventoryRing(s.inventoryloc, s.listname);
if (r < 0) // Not found
break;
const ListRingSpec &to_ring = m_inventory_rings[r];
Inventory *inv_to = m_invmgr->getInventory(to_ring.inventoryloc);
if (!inv_to)
break;
InventoryList *list_to = inv_to->getList(to_ring.listname);
if (!list_to)
break;
ItemStack slct = list_selected->getItem(m_selected_item->i);
for (s32 i = 0; i < list_s->getSize(); i++) {
// Skip the selected slot
if (i == m_selected_item->i)
continue;
ItemStack item = list_s->getItem(i);
if (slct.stacksWith(item)) {
IMoveAction *a = new IMoveAction();
a->count = item.count;
a->from_inv = s.inventoryloc;
a->from_list = s.listname;
a->from_i = i;
a->to_inv = to_ring.inventoryloc;
a->to_list = to_ring.listname;
a->move_somewhere = true;
m_invmgr->inventoryAction(a);
}
}
} else if (button == BET_LEFT && (empty || matching)) {
// We don't know if the user is left-dragging or just moving
// the item, so assume that they are left-dragging,
// and wait for the next event before moving the item
m_left_dragging = true;
m_client->inhibit_inventory_revert = true;
m_left_drag_stack = list_selected->getItem(m_selected_item->i);
m_left_drag_amount = m_selected_amount;
m_left_drag_stacks.emplace_back(s, list_s->getItem(s.i));
move_amount = 0;
} else if (identical) {
// Change the selected amount instead of moving
if (button == BET_WHEEL_DOWN) {
if (m_selected_amount < s_count)
++m_selected_amount;
} else if (button == BET_WHEEL_UP) {
if (m_selected_amount > 0)
--m_selected_amount;
} else {
if (move_amount >= m_selected_amount)
m_selected_amount = 0;
else
m_selected_amount -= move_amount;
}
move_amount = 0;
pickup_amount = 0;
}
}
break;
}
case BET_UP: {
// Some mouse button has been released
//infostream<<"Mouse button "<<button<<" released at p=("
// <<p.X<<","<<p.Y<<")"<<std::endl;
if (m_held_mouse_button != BET_OTHER && m_held_mouse_button != button)
break;
m_held_mouse_button = BET_OTHER;
if (m_selected_dragging && m_selected_item) {
if (s.isValid()) {
if (!identical) {
// Dragged to different slot: move all selected
move_amount = m_selected_amount;
}
if (s.isValid() && !identical && (empty || matching)) {
// Dragged to different slot: move all selected
move_amount = m_selected_amount;
} else if (!getAbsoluteClippingRect().isPointInside(m_pointer)) {
// Dragged outside of window: drop all selected
drop_amount = m_selected_amount;
@ -4321,60 +4444,215 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
m_selected_dragging = false;
// Keep track of whether the mouse button be released
// One click is drag without dropping. Click + release
// + click changes to drop item when moved mode
if (m_selected_item)
m_auto_place = true;
break;
case BET_MOVE:
// Mouse has been moved and rmb is down and mouse pointer just
// entered a new inventory field (checked in the entry-if, this
// is the only action here that is generated by mouse movement)
if (m_selected_item && s.isValid() && s.listname != "craftpreview") {
// Move 1 item
// TODO: middle mouse to move 10 items might be handy
if (m_auto_place) {
// Only move an item if the destination slot is empty
// or contains the same item type as what is going to be
// moved
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
InventoryList *list_to = list_s;
assert(list_from && list_to);
ItemStack stack_from = list_from->getItem(m_selected_item->i);
ItemStack stack_to = list_to->getItem(s.i);
if (stack_to.empty() || stack_to.name == stack_from.name)
move_amount = 1;
if (m_left_dragging && button == BET_LEFT) {
m_left_dragging = false;
m_client->inhibit_inventory_revert = false;
if (m_left_drag_stacks.size() > 1) {
// Finalize the left-dragging
for (auto &ds : m_left_drag_stacks) {
// Check how many items we should move to this slot,
// it may be less than the full split
Inventory *inv_to = m_invmgr->getInventory(ds.first.inventoryloc);
InventoryList *list_to = inv_to->getList(ds.first.listname);
ItemStack stack_to = list_to->getItem(ds.first.i);
u16 amount = stack_to.count - ds.second.count;
IMoveAction *a = new IMoveAction();
a->count = amount;
a->from_inv = m_selected_item->inventoryloc;
a->from_list = m_selected_item->listname;
a->from_i = m_selected_item->i;
a->to_inv = ds.first.inventoryloc;
a->to_list = ds.first.listname;
a->to_i = ds.first.i;
m_invmgr->inventoryAction(a);
}
} else if (identical) {
// Put the selected item back where it came from
m_selected_amount = 0;
} else if (s.isValid()) {
// Move the selected item
move_amount = m_selected_amount;
}
m_left_drag_stacks.clear();
}
break;
}
case BET_MOVE: {
// Mouse button is down and mouse pointer entered a new inventory field
if (!s.isValid() || s.listname == "craftpreview")
break;
if (!m_selected_item && event.MouseInput.Shift) {
// Shift-move items while dragging
if (m_held_mouse_button == BET_RIGHT)
shift_move_amount = 1;
else if (m_held_mouse_button == BET_MIDDLE)
shift_move_amount = MYMIN(s_count, 10);
else if (m_held_mouse_button == BET_LEFT)
shift_move_amount = s_count;
} else if (m_selected_item) {
if (m_held_mouse_button != BET_LEFT) {
// Move items if the destination slot is empty
// or contains the same item type as what is going to be moved
if (!m_selected_dragging && (empty || matching)) {
if (m_held_mouse_button == BET_RIGHT)
move_amount = 1;
else if (m_held_mouse_button == BET_MIDDLE)
move_amount = MYMIN(m_selected_amount, 10);
}
} else if (m_left_dragging && (empty || matching) &&
m_left_drag_amount > m_left_drag_stacks.size()) {
// Add the slot to the left-drag list if it doesn't exist
bool found = false;
for (auto &ds : m_left_drag_stacks) {
if (s == ds.first) {
found = true;
break;
}
}
if (!found) {
m_left_drag_stacks.emplace_back(s, list_s->getItem(s.i));
}
} else if (m_selected_dragging && matching && !identical) {
// Pickup items of the same type while dragging
pickup_amount = s_count;
}
} else if (m_held_mouse_button == BET_LEFT) {
// Start picking up items
m_selected_item = new GUIInventoryList::ItemSpec(s);
m_selected_amount = s_count;
m_selected_dragging = true;
}
break;
}
case BET_OTHER: {
// Some other mouse event has occured
// Currently only left-double-click should trigger this
if (!s.isValid() || event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK)
break;
// Abort left-dragging
m_left_dragging = false;
m_client->inhibit_inventory_revert = false;
m_left_drag_stacks.clear();
// Both the selected item and the hovered item need to be checked
// because we don't know exactly when the double-click happened
ItemStack slct;
if (!m_selected_item && !empty)
slct = list_s->getItem(s.i);
else if (m_selected_item && (identical || empty))
slct = list_selected->getItem(m_selected_item->i);
// Pickup all of the item from the list
if (slct.count > 0) {
for (s32 i = 0; i < list_s->getSize(); i++) {
// Skip the selected slot
if (i == s.i)
continue;
ItemStack item = list_s->getItem(i);
if (slct.stacksWith(item)) {
// Found a match, check if we can pick it up
bool full = false;
u16 amount = item.count;
ItemStack leftover = slct.addItem(item, m_client->idef());
if (!leftover.empty()) {
amount -= leftover.count;
full = true;
}
if (amount > 0) {
IMoveAction *a = new IMoveAction();
a->count = amount;
a->from_inv = s.inventoryloc;
a->from_list = s.listname;
a->from_i = i;
a->to_inv = s.inventoryloc;
a->to_list = s.listname;
a->to_i = s.i;
m_invmgr->inventoryAction(a);
if (m_selected_item)
m_selected_amount += amount;
}
if (full) // Stack is full, stop
break;
}
}
}
break;
break;
}
default:
break;
}
// Update left-dragged slots
if (m_left_dragging && m_left_drag_stacks.size() > 1) {
// The split amount will always at least one, because the number
// of slots will never be greater than the selected amount
u16 split_amount = m_left_drag_amount / m_left_drag_stacks.size();
ItemStack stack_from = m_left_drag_stack;
m_selected_amount = m_left_drag_amount;
for (auto &ds : m_left_drag_stacks) {
Inventory *inv_to = m_invmgr->getInventory(ds.first.inventoryloc);
InventoryList *list_to = inv_to->getList(ds.first.listname);
if (ds.first == *m_selected_item) {
// Adding to the source stack, just change the selected amount
m_selected_amount -= split_amount;
} else {
// Reset the stack to its original state
list_to->changeItem(ds.first.i, ds.second);
// Add the new split to the stack
ItemStack add_stack = stack_from;
add_stack.count = split_amount;
ItemStack leftover = list_to->addItem(ds.first.i, add_stack);
// Remove the split items from the source stack
u16 moved = split_amount - leftover.count;
m_selected_amount -= moved;
stack_from.count -= moved;
}
}
// Save the adjusted source stack
list_selected->changeItem(m_selected_item->i, stack_from);
}
// Possibly send inventory action to server
if (move_amount > 0) {
// Send IAction::Move
assert(m_selected_item && m_selected_item->isValid());
assert(s.isValid());
assert(list_selected && list_s);
assert(inv_selected && inv_s);
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
InventoryList *list_to = list_s;
assert(list_from && list_to);
ItemStack stack_from = list_from->getItem(m_selected_item->i);
ItemStack stack_to = list_to->getItem(s.i);
ItemStack stack_from = list_selected->getItem(m_selected_item->i);
ItemStack stack_to = list_s->getItem(s.i);
// Check how many items can be moved
move_amount = stack_from.count = MYMIN(move_amount, stack_from.count);
ItemStack leftover = stack_to.addItem(stack_from, m_client->idef());
bool move = true;
// If source stack cannot be added to destination stack at all,
// they are swapped
if (leftover.count == stack_from.count &&
leftover.name == stack_from.name) {
if (leftover.count == stack_from.count && leftover.name == stack_from.name) {
if (m_selected_swap.empty()) {
m_selected_amount = stack_to.count;
m_selected_dragging = false;
@ -4383,7 +4661,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
// Skip next validation checks due async inventory calls
m_selected_swap = stack_to;
} else {
move = false;
move_amount = 0;
}
}
// Source stack goes fully into destination stack
@ -4396,7 +4674,7 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
m_selected_amount -= move_amount;
}
if (move) {
if (move_amount > 0) {
infostream << "Handing IAction::Move to manager" << std::endl;
IMoveAction *a = new IMoveAction();
a->count = move_amount;
@ -4408,31 +4686,64 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->to_i = s.i;
m_invmgr->inventoryAction(a);
}
} else if (shift_move_amount > 0) {
u32 mis = m_inventory_rings.size();
u32 i = 0;
for (; i < mis; i++) {
const ListRingSpec &sp = m_inventory_rings[i];
if (sp.inventoryloc == s.inventoryloc
&& sp.listname == s.listname)
break;
} else if (pickup_amount > 0) {
// Send IAction::Move
assert(m_selected_item && m_selected_item->isValid());
assert(s.isValid());
assert(list_selected && list_s);
ItemStack stack_from = list_s->getItem(s.i);
ItemStack stack_to = list_selected->getItem(m_selected_item->i);
// Only move if the items are exactly the same,
// we shouldn't attempt to pickup different items
if (matching) {
// Check how many items can be moved
pickup_amount = stack_from.count = MYMIN(pickup_amount, stack_from.count);
ItemStack leftover = stack_to.addItem(stack_from, m_client->idef());
pickup_amount -= leftover.count;
} else {
pickup_amount = 0;
}
if (pickup_amount > 0) {
m_selected_amount += pickup_amount;
infostream << "Handing IAction::Move to manager" << std::endl;
IMoveAction *a = new IMoveAction();
a->count = pickup_amount;
a->from_inv = s.inventoryloc;
a->from_list = s.listname;
a->from_i = s.i;
a->to_inv = m_selected_item->inventoryloc;
a->to_list = m_selected_item->listname;
a->to_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
}
} else if (shift_move_amount > 0) {
// Try to shift-move the item
do {
if (i >= mis) // if not found
s16 r = getNextInventoryRing(s.inventoryloc, s.listname);
if (r < 0) // Not found
break;
u32 to_inv_ind = (i + 1) % mis;
const ListRingSpec &to_inv_sp = m_inventory_rings[to_inv_ind];
const ListRingSpec &to_ring = m_inventory_rings[r];
InventoryList *list_from = list_s;
if (!s.isValid())
break;
Inventory *inv_to = m_invmgr->getInventory(to_inv_sp.inventoryloc);
Inventory *inv_to = m_invmgr->getInventory(to_ring.inventoryloc);
if (!inv_to)
break;
InventoryList *list_to = inv_to->getList(to_inv_sp.listname);
InventoryList *list_to = inv_to->getList(to_ring.listname);
if (!list_to)
break;
// Check how many items can be moved
ItemStack stack_from = list_from->getItem(s.i);
assert(shift_move_amount <= stack_from.count);
shift_move_amount = MYMIN(shift_move_amount, stack_from.count);
if (shift_move_amount == 0)
break;
infostream << "Handing IAction::Move to manager" << std::endl;
IMoveAction *a = new IMoveAction();
@ -4440,19 +4751,18 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_inv = s.inventoryloc;
a->from_list = s.listname;
a->from_i = s.i;
a->to_inv = to_inv_sp.inventoryloc;
a->to_list = to_inv_sp.listname;
a->to_inv = to_ring.inventoryloc;
a->to_list = to_ring.listname;
a->move_somewhere = true;
m_invmgr->inventoryAction(a);
} while (0);
} else if (drop_amount > 0) {
// Send IAction::Drop
assert(m_selected_item && m_selected_item->isValid());
assert(inv_selected);
InventoryList *list_from = inv_selected->getList(m_selected_item->listname);
assert(list_from);
ItemStack stack_from = list_from->getItem(m_selected_item->i);
assert(list_selected);
ItemStack stack_from = list_selected->getItem(m_selected_item->i);
// Check how many items can be dropped
drop_amount = stack_from.count = MYMIN(drop_amount, stack_from.count);
@ -4466,10 +4776,11 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
a->from_list = m_selected_item->listname;
a->from_i = m_selected_item->i;
m_invmgr->inventoryAction(a);
} else if (craft_amount > 0) {
assert(s.isValid());
// if there are no items selected or the selected item
// If there are no items selected or the selected item
// belongs to craftresult list, proceed with crafting
if (!m_selected_item ||
!m_selected_item->isValid() || m_selected_item->listname == "craftresult") {
@ -4485,8 +4796,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event)
}
}
// If m_selected_amount has been decreased to zero, deselect
if (m_selected_amount == 0) {
// If m_selected_amount has been decreased to zero,
// and we are not left-dragging, deselect
if (m_selected_amount == 0 && !m_left_dragging) {
m_selected_swap.clear();
delete m_selected_item;
m_selected_item = nullptr;

@ -64,6 +64,19 @@ enum FormspecQuitMode {
quit_mode_cancel
};
enum ButtonEventType : u8
{
BET_LEFT,
BET_RIGHT,
BET_MIDDLE,
BET_WHEEL_UP,
BET_WHEEL_DOWN,
BET_UP,
BET_DOWN,
BET_MOVE,
BET_OTHER
};
struct TextDest
{
virtual ~TextDest() = default;
@ -258,6 +271,8 @@ public:
void updateSelectedItem();
ItemStack verifySelectedItem();
s16 getNextInventoryRing(const InventoryLocation &inventoryloc, const std::string &listname);
void acceptInput(FormspecQuitMode quitmode=quit_mode_no);
bool preprocessEvent(const SEvent& event);
bool OnEvent(const SEvent& event);
@ -332,6 +347,13 @@ protected:
u16 m_selected_amount = 0;
bool m_selected_dragging = false;
ItemStack m_selected_swap;
ButtonEventType m_held_mouse_button = BET_OTHER;
bool m_shift_move_after_craft = false;
u16 m_left_drag_amount = 0;
ItemStack m_left_drag_stack;
std::vector<std::pair<GUIInventoryList::ItemSpec, ItemStack>> m_left_drag_stacks;
bool m_left_dragging = false;
gui::IGUIStaticText *m_tooltip_element = nullptr;
@ -340,8 +362,6 @@ protected:
u64 m_hovered_time = 0;
s32 m_old_tooltip_id = -1;
bool m_auto_place = false;
bool m_allowclose = true;
bool m_lock = false;
v2u32 m_lockscreensize;

@ -43,6 +43,12 @@ public:
{
}
bool operator==(const ItemSpec& other)
{
return inventoryloc == other.inventoryloc &&
listname == other.listname && i == other.i;
}
bool isValid() const { return i != -1; }
InventoryLocation inventoryloc;

@ -392,6 +392,13 @@ bool ItemStack::itemFits(ItemStack newitem,
return newitem.empty();
}
bool ItemStack::stacksWith(ItemStack other) const
{
return (this->name == other.name &&
this->wear == other.wear &&
this->metadata == other.metadata);
}
ItemStack ItemStack::takeItem(u32 takecount)
{
if(takecount == 0 || count == 0)

@ -162,6 +162,10 @@ struct ItemStack
ItemStack *restitem, // may be NULL
IItemDefManager *itemdef) const;
// Checks if another itemstack would stack with this one.
// Does not check if the item actually fits in the stack.
bool stacksWith(ItemStack other) const;
// Takes some items.
// If there are not enough, takes as many as it can.
// Returns empty item if couldn't take any.