From 252c79d53a10a28d08cb46d45a39ba9fd6343ed0 Mon Sep 17 00:00:00 2001 From: OgelGames Date: Mon, 5 Jun 2023 20:00:32 +1000 Subject: [PATCH] Inventory mouse shortcut improvements (#13146) Co-authored-by: Muhammad Rifqi Priyo Susanto --- src/client/client.cpp | 2 +- src/client/client.h | 3 + src/gui/guiFormSpecMenu.cpp | 642 +++++++++++++++++++++++++++--------- src/gui/guiFormSpecMenu.h | 24 +- src/gui/guiInventoryList.h | 6 + src/inventory.cpp | 7 + src/inventory.h | 4 + 7 files changed, 520 insertions(+), 168 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 0cb587301..5a9112cf0 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -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); diff --git a/src/client/client.h b/src/client/client.h index 585af2fd3..eb965b994 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -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(); diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index e48f13d25..826c0dfd3 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -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 "<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; diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 02d4f1a19..d11651266 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -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> 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; diff --git a/src/gui/guiInventoryList.h b/src/gui/guiInventoryList.h index 5c06fed08..07ca93c3f 100644 --- a/src/gui/guiInventoryList.h +++ b/src/gui/guiInventoryList.h @@ -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; diff --git a/src/inventory.cpp b/src/inventory.cpp index 7c9e6da1a..9d42cee97 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -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) diff --git a/src/inventory.h b/src/inventory.h index 9ec15d3aa..3109246d4 100644 --- a/src/inventory.h +++ b/src/inventory.h @@ -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.