diff --git a/Bar.lua b/Bar.lua
index bc2888d..5a6d477 100644
--- a/Bar.lua
+++ b/Bar.lua
@@ -24,6 +24,14 @@ local defaults = {
},
}
+local Sticky = LibStub("LibSimpleSticky-1.0")
+
+local barregistry = {}
+Bartender4.Bar = {}
+Bartender4.Bar.defaults = defaults
+Bartender4.Bar.prototype = Bar
+Bartender4.Bar.barregistry = barregistry
+
local barOnEnter, barOnLeave, barOnDragStart, barOnDragStop, barOnClick, barOnUpdateFunc
do
function barOnEnter(self)
@@ -36,7 +44,8 @@ do
function barOnDragStart(self)
local parent = self:GetParent()
- parent:StartMoving()
+ local offset = 8 - (parent.config.padding or 0)
+ Sticky:StartMoving(parent, barregistry, offset, offset, offset, offset)
self:SetBackdropBorderColor(0, 0, 0, 0)
parent.isMoving = true
end
@@ -44,7 +53,8 @@ do
function barOnDragStop(self)
local parent = self:GetParent()
if parent.isMoving then
- parent:StopMovingOrSizing()
+ local sticky, stickTo = Sticky:StopMoving(parent)
+ --Bartender4:Print(sticky, stickTo and stickTo:GetName() or nil)
parent:SavePosition()
end
end
@@ -63,11 +73,6 @@ do
end
end
-local barregistry = {}
-Bartender4.Bar = {}
-Bartender4.Bar.defaults = defaults
-Bartender4.Bar.prototype = Bar
-Bartender4.Bar.barregistry = barregistry
function Bartender4.Bar:Create(id, config, name)
id = tostring(id)
assert(not barregistry[id], "duplicated entry in barregistry.")
diff --git a/Bartender4.toc b/Bartender4.toc
index be20765..e90ca20 100644
--- a/Bartender4.toc
+++ b/Bartender4.toc
@@ -11,6 +11,7 @@
embeds.xml
libs\LibDataBroker-1.1.lua
+libs\SimpleSticky.lua
locale\locale.xml
diff --git a/libs/SimpleSticky.lua b/libs/SimpleSticky.lua
new file mode 100644
index 0000000..88388b0
--- /dev/null
+++ b/libs/SimpleSticky.lua
@@ -0,0 +1,273 @@
+--[[---------------------------------------------------------------------------------
+ General Library providing an alternate StartMoving() that allows you to
+ specify a number of frames to snap-to when moving the frame around
+
+ Example Usage:
+
+
+ this:RegisterForDrag("LeftButton")
+
+
+ StickyFrames:StartMoving(this, {WatchDogFrame_player, WatchDogFrame_target, WatchDogFrame_party1, WatchDogFrame_party2, WatchDogFrame_party3, WatchDogFrame_party4},3,3,3,3)
+
+
+ StickyFrames:StopMoving(this)
+ StickyFrames:AnchorFrame(this)
+
+
+------------------------------------------------------------------------------------
+This is a modified version by Nevcairiel for Bartender4
+------------------------------------------------------------------------------------]]
+
+local MAJOR, MINOR = "LibSimpleSticky-1.0", 1
+local StickyFrames, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+
+if not StickyFrames then return end
+
+--[[---------------------------------------------------------------------------------
+ Class declaration, along with a temporary table to hold any existing OnUpdate
+ scripts.
+------------------------------------------------------------------------------------]]
+
+StickyFrames.scripts = StickyFrames.scripts or {}
+StickyFrames.rangeX = 15
+StickyFrames.rangeY = 15
+StickyFrames.sticky = StickyFrames.sticky or {}
+
+--[[---------------------------------------------------------------------------------
+ StickyFrames:StartMoving() - Sets a custom OnUpdate for the frame so it follows
+ the mouse and snaps to the frames you specify
+
+ frame: The frame we want to move. Is typically "this"
+
+ frameList: A integer indexed list of frames that the given frame should try to
+ stick to. These don't have to have anything special done to them,
+ and they don't really even need to exist. You can inclue the
+ moving frame in this list, it will be ignored. This helps you
+ if you have a number of frames, just make ONE list to pass.
+
+ {WatchDogFrame_player, WatchDogFrame_party1, .. WatchDogFrame_party4}
+
+ left: If your frame has a tranparent border around the entire frame
+ (think backdrops with borders). This can be used to fine tune the
+ edges when you're stickying groups. Refers to any offset on the
+ LEFT edge of the frame being moved.
+
+ top: same
+ right: same
+ bottom: same
+------------------------------------------------------------------------------------]]
+
+function StickyFrames:StartMoving(frame, frameList, left, top, right, bottom)
+ local x,y = GetCursorPosition()
+ local aX,aY = frame:GetCenter()
+ local aS = frame:GetEffectiveScale()
+
+ aX,aY = aX*aS,aY*aS
+ local xoffset,yoffset = (aX - x),(aY - y)
+ self.scripts[frame] = frame:GetScript("OnUpdate")
+ frame:SetScript("OnUpdate", self:GetUpdateFunc(frame, frameList, xoffset, yoffset, left, top, right, bottom))
+end
+
+--[[---------------------------------------------------------------------------------
+ This stops the OnUpdate, leaving the frame at its last position. This will
+ leave it anchored to UIParent. You can call StickyFrames:AnchorFrame() to
+ anchor it back "TOPLEFT" , "TOPLEFT" to the parent.
+------------------------------------------------------------------------------------]]
+
+function StickyFrames:StopMoving(frame)
+ frame:SetScript("OnUpdate", self.scripts[frame])
+ self.scripts[frame] = nil
+
+ if StickyFrames.sticky[frame] then
+ local sticky = StickyFrames.sticky[frame]
+ StickyFrames.sticky[frame] = nil
+ return true, sticky
+ else
+ return false, nil
+ end
+end
+
+--[[---------------------------------------------------------------------------------
+ This can be called in conjunction with StickyFrames:StopMoving() to anchor the
+ frame right back to the parent, so you can manipulate its children as a group
+ (This is useful in WatchDog)
+------------------------------------------------------------------------------------]]
+
+function StickyFrames:AnchorFrame(frame)
+ local xA,yA = frame:GetCenter()
+ local parent = frame:GetParent() or UIParent
+ local xP,yP = parent:GetCenter()
+ local sA,sP = frame:GetEffectiveScale(), parent:GetEffectiveScale()
+
+ xP,yP = (xP*sP) / sA, (yP*sP) / sA
+
+ local xo,yo = (xP - xA)*-1, (yP - yA)*-1
+
+ frame:ClearAllPoints()
+ frame:SetPoint("CENTER", parent, "CENTER", xo, yo)
+end
+
+
+--[[---------------------------------------------------------------------------------
+ Internal Functions -- Do not call these.
+------------------------------------------------------------------------------------]]
+
+
+
+--[[---------------------------------------------------------------------------------
+ Returns an anonymous OnUpdate function for the frame in question. Need
+ to provide the frame, frameList along with the x and y offset (difference between
+ where the mouse picked up the frame, and the insets (left,top,right,bottom) in the
+ case of borders, etc.w
+------------------------------------------------------------------------------------]]
+
+function StickyFrames:GetUpdateFunc(frame, frameList, xoffset, yoffset, left, top, right, bottom)
+ return function()
+ local x,y = GetCursorPosition()
+ local s = frame:GetEffectiveScale()
+ local sticky = nil
+
+ x,y = x/s,y/s
+
+ frame:ClearAllPoints()
+ frame:SetPoint("CENTER", UIParent, "BOTTOMLEFT", x+xoffset, y+yoffset)
+
+ StickyFrames.sticky[frame] = nil
+ for k,v in pairs(frameList) do
+ if frame ~= v and not IsShiftKeyDown() and v:IsVisible() then
+ if self:SnapFrame(frame, v, left, top, right, bottom) then
+ StickyFrames.sticky[frame] = v
+ break
+ end
+ end
+ end
+ end
+end
+
+
+--[[---------------------------------------------------------------------------------
+ Internal debug function.
+------------------------------------------------------------------------------------]]
+
+function StickyFrames:debug(msg)
+ DEFAULT_CHAT_FRAME:AddMessage("|cffffff00StickyFrames: |r"..tostring(msg))
+end
+
+--[[---------------------------------------------------------------------------------
+ This is called when finding an overlap between two sticky frame. If frameA is near
+ a sticky edge of frameB, then it will snap to that edge and return true. If there
+ is no sticky edge collision, will return false so we can test other frames for
+ stickyness.
+------------------------------------------------------------------------------------]]
+function StickyFrames:SnapFrame(frameA, frameB, left, top, right, bottom)
+ local sA, sB = frameA:GetEffectiveScale(), frameB:GetEffectiveScale()
+ local xA, yA = frameA:GetCenter()
+ local xB, yB = frameB:GetCenter()
+ local hA, hB = frameA:GetHeight() / 2, ((frameB:GetHeight() * sB) / sA) / 2
+ local wA, wB = frameA:GetWidth() / 2, ((frameB:GetWidth() * sB) / sA) / 2
+
+ local newX, newY = xA, yA
+
+ if not left then left = 0 end
+ if not top then top = 0 end
+ if not right then right = 0 end
+ if not bottom then bottom = 0 end
+
+ -- Lets translate B's coords into A's scale
+ xB, yB = (xB*sB) / sA, (yB*sB) / sA
+
+ local stickyAx, stickyAy = wA * 0.75, hA * 0.75
+ local stickyBx, stickyBy = wB * 0.75, hB * 0.75
+
+ -- Grab the edges of each frame, for easier comparison
+
+ local lA, tA, rA, bA = frameA:GetLeft(), frameA:GetTop(), frameA:GetRight(), frameA:GetBottom()
+ local lB, tB, rB, bB = frameB:GetLeft(), frameB:GetTop(), frameB:GetRight(), frameB:GetBottom()
+ local snap = nil
+
+ -- Translate into A's scale
+ lB, tB, rB, bB = (lB * sB) / sA, (tB * sB) / sA, (rB * sB) / sA, (bB * sB) / sA
+
+ if (bA <= tB and bB <= tA) then
+
+ -- Horizontal Centers
+ if xA <= (xB + StickyFrames.rangeX) and xA >= (xB - StickyFrames.rangeX) then
+ newX = xB
+ snap = true
+ end
+
+ -- Interior Left
+ if lA <= (lB + StickyFrames.rangeX) and lA >= (lB - StickyFrames.rangeX) then
+ newX = lB + wA
+ if frameB == UIParent or frameB == WorldFrame then newX = newX - left/2 end
+ snap = true
+ end
+
+ -- Interior Right
+ if rA <= (rB + StickyFrames.rangeX) and rA >= (rB - StickyFrames.rangeX) then
+ newX = rB - wA
+ if frameB == UIParent or frameB == WorldFrame then newX = newX + right/2 end
+ snap = true
+ end
+
+ -- Exterior Left to Right
+ if lA <= (rB + StickyFrames.rangeX) and lA >= (rB - StickyFrames.rangeX) then
+ newX = rB + (wA - left)
+ if frameB == UIParent or frameB == WorldFrame then newX = newX + left/2 end
+ snap = true
+ end
+
+ -- Exterior Right to Left
+ if rA <= (lB + StickyFrames.rangeX) and rA >= (lB - StickyFrames.rangeX) then
+ newX = lB - (wA - right)
+ if frameB == UIParent or frameB == WorldFrame then newX = newX - right/2 end
+ snap = true
+ end
+
+ end
+
+ if (lA <= rB and lB <= rA) then
+
+ -- Vertical Centers
+ if yA <= (yB + StickyFrames.rangeY) and yA >= (yB - StickyFrames.rangeY) then
+ newY = yB
+ snap = true
+ end
+
+ -- Interior Top
+ if tA <= (tB + StickyFrames.rangeY) and tA >= (tB - StickyFrames.rangeY) then
+ newY = tB - hA
+ if frameB == UIParent or frameB == WorldFrame then newY = newY + top/2 end
+ snap = true
+ end
+
+ -- Interior Bottom
+ if bA <= (bB + StickyFrames.rangeY) and bA >= (bB - StickyFrames.rangeY) then
+ newY = bB + hA
+ if frameB == UIParent or frameB == WorldFrame then newY = newY - bottom/2 end
+ snap = true
+ end
+
+ -- Exterior Top to Bottom
+ if tA <= (bB + StickyFrames.rangeY + bottom) and tA >= (bB - StickyFrames.rangeY + bottom) then
+ newY = bB - (hA - top)
+ if frameB == UIParent or frameB == WorldFrame then newY = newY - top/2 end
+ snap = true
+ end
+
+ -- Exterior Bottom to Top
+ if bA <= (tB + StickyFrames.rangeY - top) and bA >= (tB - StickyFrames.rangeY - top) then
+ newY = tB + (hA - bottom)
+ if frameB == UIParent or frameB == WorldFrame then newY = newY + bottom/2 end
+ snap = true
+ end
+
+ end
+
+ if snap then
+ frameA:ClearAllPoints()
+ frameA:SetPoint("CENTER", UIParent, "BOTTOMLEFT", newX, newY)
+ return true
+ end
+end