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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019W3rwt5PZSpEDkXYzRuRhH
This commit is contained in:
2026-11-01 03:04:31 +00:00
co-authored by Claude Opus 5.5
parent 0c1b566cf6
commit aee35a3c8f
2 changed files with 82 additions and 2 deletions
+4
View File
@@ -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).
+78 -2
View File
@@ -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