diff --git a/Changelog.md b/Changelog.md index 8031135..5da5d14 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,6 +2,10 @@ ## Unreleased +### Fixed + +- The whole game froze while dragging the Bags or Bank window (seconds at a time with the Bank open): the game re-positioned every item button inside the window on each mouse movement. A light outline now follows the mouse while dragging, and the window moves once, when you drop it. + ### Added - `/bs cpu` (hidden command): with ClassicAPI and `/console scriptProfile 1`, shows where CPU time went since the last reset: the busiest addons and Bagshui's busiest functions. `/bs cpu reset` starts a new measurement, and `/bs cpu clamp on|off` switches the windows' keep-on-screen clamp until /reload (for testing drag freezes). diff --git a/Components/Inventory.Ui.lua b/Components/Inventory.Ui.lua index 0ea89ac..4020399 100644 --- a/Components/Inventory.Ui.lua +++ b/Components/Inventory.Ui.lua @@ -1141,6 +1141,7 @@ Bagshui:AddComponent(function() --- - If there's a frame docked to this one, close it. function Inventory:UiFrame_OnHide() self.uiFrame:StopMovingOrSizing() + self:ReleaseDragOutline() -- If the mouse button was released while the game hitched, OnDragStop may never have fired. -- A stuck flag would make FixWindowPosition() (and window rescue) skip this window forever. self.dragInProgress = false @@ -1158,6 +1159,55 @@ Bagshui:AddComponent(function() end end + -- Outline that follows the mouse while an inventory window is dragged. + -- Moving the real window makes the game re-position every item button, texture and cooldown + -- inside it on each mouse movement, which froze the whole game for seconds with the Bags and + -- Bank open. The outline is a single empty frame, and the real window moves once, on drop. + local dragOutline + + local function GetDragOutline() + if not dragOutline then + dragOutline = _G.CreateFrame("Frame", "BagshuiDragOutline", _G.UIParent) + dragOutline:SetFrameStrata("FULLSCREEN_DIALOG") + dragOutline:SetMovable(true) + dragOutline:EnableMouse(true) + if dragOutline.SetClampedToScreen then + dragOutline:SetClampedToScreen(true) + end + dragOutline:SetBackdrop({ + bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", + edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", + tile = true, + tileSize = 16, + edgeSize = 16, + insets = { left = 4, right = 4, top = 4, bottom = 4 }, + }) + dragOutline:SetBackdropColor(0, 0, 0, 0.3) + dragOutline:SetBackdropBorderColor(1, 1, 1, 0.9) + -- If the drop was ever missed, clicking the outline finishes the move. + dragOutline:SetScript("OnMouseUp", function() + if dragOutline.owner then + dragOutline.owner:UiFrame_OnDragStop() + end + end) + dragOutline:Hide() + end + return dragOutline + end + + --- Stop and hide the drag outline if this window owns it. + ---@return number? left, number? top Where the outline was dropped (window coordinates). + function Inventory:ReleaseDragOutline() + if not dragOutline or dragOutline.owner ~= self then + return + end + dragOutline:StopMovingOrSizing() + local left, top = dragOutline:GetLeft(), dragOutline:GetTop() + dragOutline:Hide() + dragOutline.owner = nil + return left, top + end + --- Handle dragging (start). function Inventory:UiFrame_OnDragStart() if self.dockTo then @@ -1166,7 +1216,23 @@ Bagshui:AddComponent(function() -- Has to be self instead of "this" because it can be called from docked windows. self.dragInProgress = true self.dragStartedAt = _G.GetTime() - self.uiFrame:StartMoving() + + local frame = self.uiFrame + local left, top = frame:GetLeft(), frame:GetTop() + if left and top then + -- Same parent and scale as the window, so both use the same coordinates. + local outline = GetDragOutline() + outline.owner = self + outline:SetScale(frame:GetScale()) + outline:SetWidth(frame:GetWidth()) + outline:SetHeight(frame:GetHeight()) + outline:ClearAllPoints() + outline:SetPoint("TOPLEFT", _G.UIParent, "BOTTOMLEFT", left, top) + outline:Show() + outline:StartMoving() + else + frame:StartMoving() + end end end @@ -1176,8 +1242,18 @@ Bagshui:AddComponent(function() Bagshui.components[self.dockTo]:UiFrame_OnDragStop() elseif not self.settings.windowLocked then -- Has to be self instead of "this" because it can be called from docked windows. + if not self.dragInProgress then + return + end self.dragInProgress = false - self.uiFrame:StopMovingOrSizing() + local left, top = self:ReleaseDragOutline() + if left and top then + -- Move the real window once, to where the outline was dropped. + self.uiFrame:ClearAllPoints() + self.uiFrame:SetPoint("TOPLEFT", _G.UIParent, "BOTTOMLEFT", left, top) + else + self.uiFrame:StopMovingOrSizing() + end self:FixSettingsMenuPosition() self:SaveWindowPosition() -- If the window was dropped (nearly) off the screen, bring it back right away