From 93ad42385cb5e38659796e3f49562b334a5245de Mon Sep 17 00:00:00 2001 From: Hendrik Leppkes Date: Fri, 5 Sep 2008 10:53:13 +0200 Subject: [PATCH] Added simple Bar-snapping support, more to come --- Bar.lua | 19 +-- Bartender4.toc | 1 + libs/SimpleSticky.lua | 273 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 286 insertions(+), 7 deletions(-) create mode 100644 libs/SimpleSticky.lua 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