From aee35a3c8f8ddf888b47c58bf72c8f44efdb65bd Mon Sep 17 00:00:00 2001 From: OEVG <950+oevg@noreply.octowow.st> Date: Sun, 1 Nov 2026 03:04:31 +0000 Subject: [PATCH] Drag inventory windows with an outline to stop drag freezes Moving the real window made the client re-lay out every item button, texture and cooldown model on each mouse movement, freezing the game for seconds with the Bags and Bank open (profiling showed almost no Lua time during the freezes). A single empty outline frame now follows the mouse, and the window is moved once on drop. Clicking the outline finishes a move whose drop was missed. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_019W3rwt5PZSpEDkXYzRuRhH --- Changelog.md | 4 ++ Components/Inventory.Ui.lua | 80 ++++++++++++++++++++++++++++++++++++- 2 files changed, 82 insertions(+), 2 deletions(-) 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