# HG changeset patch # User Flick # Date 1175299650 0 # Node ID a805e4464237d88369f1b08bb782c459da71af5a # Parent 639282f3a0e0dcd4845329e8eb48923797a43456 AceLibrary'd ReAnchor diff -r 639282f3a0e0 -r a805e4464237 ReAction.toc --- a/ReAction.toc Fri Mar 23 19:28:30 2007 +0000 +++ b/ReAction.toc Sat Mar 31 00:07:30 2007 +0000 @@ -23,11 +23,10 @@ libs\Tablet-2.0\Tablet-2.0.lua libs\FuBarPlugin-2.0\FuBarPlugin-2.0.lua libs\ReBound-1.0\ReBound-1.0.lua +libs\ReAnchor-1.0\ReAnchor-1.0.lua locale-enUS.lua -classes\ReAnchor.xml -classes\ReAnchor.lua classes\ReBar.xml classes\ReBar.lua diff -r 639282f3a0e0 -r a805e4464237 classes/ReAnchor.lua --- a/classes/ReAnchor.lua Fri Mar 23 19:28:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,314 +0,0 @@ --- --- ReAnchor.lua --- --- Provides drag-placement facilities for frames. --- - --- local constants -local AceOO = AceLibrary("AceOO-2.0") - -local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } - -local pointsOnEdge = { - BOTTOM = { "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", }, - TOP = { "TOP", "TOPLEFT", "TOPRIGHT", }, - RIGHT = { "RIGHT", "BOTTOMRIGHT", "TOPRIGHT", }, - LEFT = { "LEFT", "BOTTOMLEFT", "TOPLEFT", }, -} - -local edgeSelector = { - BOTTOM = 1, -- select x of x,y - TOP = 1, -- select x of x,y - LEFT = 2, -- select y of x,y - RIGHT = 2, -- select y of x,y -} - -local oppositePoints = { - BOTTOMLEFT = "TOPRIGHT", - BOTTOM = "TOP", - BOTTOMRIGHT = "TOPLEFT", - RIGHT = "LEFT", - TOPRIGHT = "BOTTOMLEFT", - TOP = "BOTTOM", - TOPLEFT = "BOTTOMRIGHT", - LEFT = "RIGHT", - CENTER = "CENTER", -} - -local insidePointOffsetFuncs = { - BOTTOMLEFT = function(x, y) return x, y end, - BOTTOM = function(x, y) return 0, y end, - BOTTOMRIGHT = function(x, y) return -x, y end, - RIGHT = function(x, y) return -x, 0 end, - TOPRIGHT = function(x, y) return -x, -y end, - TOP = function(x, y) return 0, -y end, - TOPLEFT = function(x, y) return x, -y end, - LEFT = function(x, y) return x, 0 end, - CENTER = function(x, y) return 0, 0 end, -} - -local pointCoordFuncs = { - BOTTOMLEFT = function(f) return f:GetLeft(), f:GetBottom() end, - BOTTOM = function(f) return nil, f:GetBottom() end, - BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end, - RIGHT = function(f) return f:GetRight(), nil end, - TOPRIGHT = function(f) return f:GetRight(), f:GetTop() end, - TOP = function(f) return nil, f:GetTop() end, - TOPLEFT = function(f) return f:GetLeft(), f:GetTop() end, - LEFT = function(f) return f:GetLeft(), nil end, - CENTER = function(f) return f:GetCenter() end, -} - -local edgeBoundsFuncs = { - BOTTOM = function(f) return f:GetLeft(), f:GetRight() end, - LEFT = function(f) return f:GetBottom(), f:GetTop() end -} -edgeBoundsFuncs.TOP = edgeBoundsFuncs.BOTTOM -edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT - - --- local utility functions - --- Returns absolute coordinates x,y of the named point 'p' of frame 'f' -local function GetPointCoords( f, p ) - local x, y = pointCoordFuncs[p](f) - if not(x and y) then - local cx, cy = f:GetCenter() - x = x or cx - y = y or cy - end - return x, y -end - - --- Returns true if frame 'f1' can be anchored to frame 'f2' -local function CheckAnchorable( f1, f2 ) - -- can't anchor a frame to itself or to nil - if f1 == f2 or f2 == nil then - return false - end - - -- can always anchor to UIParent - if f2 == UIParent then - return true - end - - -- also can't do circular anchoring of frames - -- walk the anchor chain, which generally shouldn't be that expensive - -- (who nests draggables that deep anyway?) - for i = 1, f2:GetNumPoints() do - local _, f = f2:GetPoint(i) - return CheckAnchorable(f1,f) - end - - return true -end - --- Returns true if frames f1 and f2 specified edges overlap -local function CheckEdgeOverlap( f1, f2, e ) - local l1, u1 = edgeBoundsFuncs[e](f1) - local l2, u2 = edgeBoundsFuncs[e](f2) - return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2 -end - --- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2 -local function CheckPointEdgeOverlap( f1, p1, f2, e2 ) - local l, u = edgeBoundsFuncs[e2](f2) - local x, y = GetPointCoords(f1,p1) - x = select(edgeSelector[e2], x, y) - return l <= x and x <= u -end - --- Returns the distance between corresponding edges. It is --- assumed that the passed in edges e1 and e2 are the same or opposites -local function GetEdgeDistance( f1, f2, e1, e2 ) - local x1, y1 = pointCoordFuncs[e1](f1) - local x2, y2 = pointCoordFuncs[e2](f2) - return math.abs((x1 or y1) - (x2 or y2)) -end - --- Returns interior offsets (specified absolutely) from a point -local function GetInteriorOffsetsToPoint(p, x, y) - return insidePointOffsetFuncs[p](x,y) -end - - - - - --- ReAnchor is a Mixin which provides some anchoring and --- placement methods for frames. --- An object with the ReAnchor mixin must support the --- IAnchorable interface (implicitly or explicitly). --- The mixin methods also require arguments to support --- that interface. - --- In the method prototypes, 'IRObjs' is used to refer to a --- table of objects which support the IAnchorable interface. - - -ReAnchor = AceOO.Mixin { - "GetClosestVisibleEdge", - "GetClosestVisiblePoint", - "GetClosestPointSnapped", - "DisplaySnapIndicator", - "HideSnapIndicator", -} - ---------------------------------------------------------- --- Constants and classes that are not exported via mixin ---------------------------------------------------------- -ReAnchor.IAnchorable = AceOO.Interface { - GetFrame = "function", - GetAnchorage = "function", -- return ReAnchor.anchorInside or .anchorOutside -} - -ReAnchor.anchorInside = { inside = true } -ReAnchor.anchorOutside = { outside = true } - -ReAnchor.snapIndicator1 = CreateFrame("Frame",nil,UIParent,"ReAnchorSnapIndicatorTemplate") -ReAnchor.snapIndicator2 = CreateFrame("Frame",nil,UIParent,"ReAnchorSnapIndicatorTemplate") - - - - --------------------- --- Mixin methods --------------------- - --- returns: --- (1) o : the closest IRObj --- (2) e1 : the point (edge) on self:GetFrame() --- (3) e2 : the point (edge) on o:GetFrame() -function ReAnchor:GetClosestVisibleEdge( IRObjs ) - local f1 = self:GetFrame() - local r, o, e1, e2 - for _, o2 in pairs(IRObjs) do - local f2 = o2:GetFrame() - local a = o2:GetAnchorage() - if f2:IsVisible() and CheckAnchorable(f1,f2) then - for _, e in pairs(edges) do - local opp = a.inside and e or oppositePoints[e] - if CheckEdgeOverlap(f1,f2,e) then - local d = GetEdgeDistance(f1, f2, e, opp) - if not r or d < r then - r, o, e1, e2 = d, o2, e, opp - end - end - end - end - end - return o, e1, e2 -end - --- returns: --- (1) o: the closest IRObj --- (1) p: the point on self:GetFrame() --- (2) rp: the relativePoint on o:GetFrame() --- (3) x: x offset --- (4) y: y offset --- such that self:GetFrame():SetPoint(p,o:GetFrame(),rp,x,y) preserves the current location -function ReAnchor:GetClosestVisiblePoint( IRObjs ) - local f1 = self:GetFrame() - local o, e1, e2 = self:GetClosestVisibleEdge( IRObjs ) - local f2 = o:GetFrame() - local rsq, p, rp, x, y - -- iterate pointsOnEdge in order and use < to prefer edge centers to corners - for _, p1 in ipairs(pointsOnEdge[e1]) do - if CheckPointEdgeOverlap(f1,p1,f2,e2) then - local p2 = o:GetAnchorage().outside and oppositePoints[p1] or p1 - local x1, y1 = GetPointCoords(f1,p1) - local x2, y2 = GetPointCoords(f2,p2) - local dx = x1 - x2 - local dy = y1 - y2 - local rsq2 = dx*dx + dy*dy - if not rsq or rsq2 < rsq then - rsq, p, rp, x, y = rsq2, p1, p2, dx, dy - end - end - end - return o, p, rp, x, y -end - - --- Calls self:GetClosestVisiblePoint() and then snaps to the specified --- offsets if within the given range in x and y (as appropriate) --- Return semantic is the same as GetClosestVisiblePoint(). Returns nil --- if no snap can be done. -function ReAnchor:GetClosestPointSnapped(IRObjs, r, xOff, yOff) - local f1 = self:GetFrame() - local o, p, rp, x, y = self:GetClosestVisiblePoint(IRObjs) - local s = false - - if r then - local sx, sy = GetInteriorOffsetsToPoint(p, xOff or 0, yOff or 0) - local xx, yy = pointCoordFuncs[p](f1) - if xx and yy then - if math.abs(x) <= r then - x = sx - s = true - end - if math.abs(y) <= r then - y = sy - s = true - end - elseif xx then - if math.abs(x) <= r then - x = sx - s = true - if math.abs(y) <= r then - y = sy - end - end - elseif yy then - if math.abs(y) <= r then - y = sy - s = true - if math.abs(x) <= r then - x = sx - end - end - end - end - - if s then - return o, p, rp, x, y - end -end - - - --- shows anchor-indicators on the associated frame and the target frame --- when a snap is warranted. -function ReAnchor:DisplaySnapIndicator( IRObjs, r, xOff, yOff ) - local o, p, rp, x, y, snap = self:GetClosestPointSnapped(IRObjs, r, xOff, yOff) - local si1 = ReAnchor.snapIndicator1 - local si2 = ReAnchor.snapIndicator2 - if o then - si1:ClearAllPoints() - si2:ClearAllPoints() - si1:SetPoint("CENTER", self:GetFrame(), p, 0, 0) - local xx, yy = pointCoordFuncs[rp](o:GetFrame()) - x = math.abs(x) <=r and xx and 0 or x - y = math.abs(y) <=r and yy and 0 or y - si2:SetPoint("CENTER", o:GetFrame(), rp, x, y) - si1:Show() - si2:Show() - else - if si1:IsVisible() then - si1:Hide() - si2:Hide() - end - end -end - - -function ReAnchor:HideSnapIndicator() - local si1 = ReAnchor.snapIndicator1 - local si2 = ReAnchor.snapIndicator2 - if si1:IsVisible() then - si1:Hide() - si2:Hide() - end -end - diff -r 639282f3a0e0 -r a805e4464237 classes/ReAnchor.xml --- a/classes/ReAnchor.xml Fri Mar 23 19:28:30 2007 +0000 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - diff -r 639282f3a0e0 -r a805e4464237 classes/ReBar.lua --- a/classes/ReBar.lua Fri Mar 23 19:28:30 2007 +0000 +++ b/classes/ReBar.lua Sat Mar 31 00:07:30 2007 +0000 @@ -81,6 +81,7 @@ end local AceOO = AceLibrary("AceOO-2.0") +local ReAnchor = AceLibrary("ReAnchor-1.0") -- ReBar is an Ace 2 class prototype object. ReBar = AceOO.Class("AceEvent-2.0", ReAnchor, ReAnchor.IAnchorable) diff -r 639282f3a0e0 -r a805e4464237 libs/ReAnchor-1.0/ReAnchor-1.0.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libs/ReAnchor-1.0/ReAnchor-1.0.lua Sat Mar 31 00:07:30 2007 +0000 @@ -0,0 +1,355 @@ +--[[ +Name: ReAnchor-1.0 +Revision: $Rev: 1 $ +Author: Flick +Website: +Documentation: +SVN: +Description: Provides drag-placement facilities for frames +License: MIT +Dependencies: AceLibrary, AceOO-2.0 +]] + + +local version_major, version_minor = "ReAnchor-1.0", "$Rev: 1 $" + +if not AceLibrary then error(version_major .. " requires AceLibrary.") end +if not AceLibrary:IsNewVersion(version_major, version_minor) then return end +if not AceLibrary:HasInstance("AceOO-2.0") then error(version_major .. " requires AceOO-2.0") end + +-- local constants +local AceOO = AceLibrary("AceOO-2.0") + +local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" } + +local pointsOnEdge = { + BOTTOM = { "BOTTOM", "BOTTOMLEFT", "BOTTOMRIGHT", }, + TOP = { "TOP", "TOPLEFT", "TOPRIGHT", }, + RIGHT = { "RIGHT", "BOTTOMRIGHT", "TOPRIGHT", }, + LEFT = { "LEFT", "BOTTOMLEFT", "TOPLEFT", }, +} + +local edgeSelector = { + BOTTOM = 1, -- select x of x,y + TOP = 1, -- select x of x,y + LEFT = 2, -- select y of x,y + RIGHT = 2, -- select y of x,y +} + +local oppositePoints = { + BOTTOMLEFT = "TOPRIGHT", + BOTTOM = "TOP", + BOTTOMRIGHT = "TOPLEFT", + RIGHT = "LEFT", + TOPRIGHT = "BOTTOMLEFT", + TOP = "BOTTOM", + TOPLEFT = "BOTTOMRIGHT", + LEFT = "RIGHT", + CENTER = "CENTER", +} + +local insidePointOffsetFuncs = { + BOTTOMLEFT = function(x, y) return x, y end, + BOTTOM = function(x, y) return 0, y end, + BOTTOMRIGHT = function(x, y) return -x, y end, + RIGHT = function(x, y) return -x, 0 end, + TOPRIGHT = function(x, y) return -x, -y end, + TOP = function(x, y) return 0, -y end, + TOPLEFT = function(x, y) return x, -y end, + LEFT = function(x, y) return x, 0 end, + CENTER = function(x, y) return 0, 0 end, +} + +local pointCoordFuncs = { + BOTTOMLEFT = function(f) return f:GetLeft(), f:GetBottom() end, + BOTTOM = function(f) return nil, f:GetBottom() end, + BOTTOMRIGHT = function(f) return f:GetRight(), f:GetBottom() end, + RIGHT = function(f) return f:GetRight(), nil end, + TOPRIGHT = function(f) return f:GetRight(), f:GetTop() end, + TOP = function(f) return nil, f:GetTop() end, + TOPLEFT = function(f) return f:GetLeft(), f:GetTop() end, + LEFT = function(f) return f:GetLeft(), nil end, + CENTER = function(f) return f:GetCenter() end, +} + +local edgeBoundsFuncs = { + BOTTOM = function(f) return f:GetLeft(), f:GetRight() end, + LEFT = function(f) return f:GetBottom(), f:GetTop() end +} +edgeBoundsFuncs.TOP = edgeBoundsFuncs.BOTTOM +edgeBoundsFuncs.RIGHT = edgeBoundsFuncs.LEFT + + +-- local utility functions + +-- Returns absolute coordinates x,y of the named point 'p' of frame 'f' +local function GetPointCoords( f, p ) + local x, y = pointCoordFuncs[p](f) + if not(x and y) then + local cx, cy = f:GetCenter() + x = x or cx + y = y or cy + end + return x, y +end + + +-- Returns true if frame 'f1' can be anchored to frame 'f2' +local function CheckAnchorable( f1, f2 ) + -- can't anchor a frame to itself or to nil + if f1 == f2 or f2 == nil then + return false + end + + -- can always anchor to UIParent + if f2 == UIParent then + return true + end + + -- also can't do circular anchoring of frames + -- walk the anchor chain, which generally shouldn't be that expensive + -- (who nests draggables that deep anyway?) + for i = 1, f2:GetNumPoints() do + local _, f = f2:GetPoint(i) + return CheckAnchorable(f1,f) + end + + return true +end + +-- Returns true if frames f1 and f2 specified edges overlap +local function CheckEdgeOverlap( f1, f2, e ) + local l1, u1 = edgeBoundsFuncs[e](f1) + local l2, u2 = edgeBoundsFuncs[e](f2) + return l1 <= l2 and l2 <= u1 or l2 <= l1 and l1 <= u2 +end + +-- Returns true if point p1 on frame f1 overlaps edge e2 on frame f2 +local function CheckPointEdgeOverlap( f1, p1, f2, e2 ) + local l, u = edgeBoundsFuncs[e2](f2) + local x, y = GetPointCoords(f1,p1) + x = select(edgeSelector[e2], x, y) + return l <= x and x <= u +end + +-- Returns the distance between corresponding edges. It is +-- assumed that the passed in edges e1 and e2 are the same or opposites +local function GetEdgeDistance( f1, f2, e1, e2 ) + local x1, y1 = pointCoordFuncs[e1](f1) + local x2, y2 = pointCoordFuncs[e2](f2) + return math.abs((x1 or y1) - (x2 or y2)) +end + +-- Returns interior offsets (specified absolutely) from a point +local function GetInteriorOffsetsToPoint(p, x, y) + return insidePointOffsetFuncs[p](x,y) +end + +-- creates a snap indicator square +local function createSnapIndicator() + local f = CreateFrame("Frame",nil,UIParent) + f:SetFrameStrata("HIGH") + local t = f:CreateTexture(nil,"OVERLAY") + t:SetBlendMode("ADD") + t:SetTexture(1,0.82,0,0.8) + t:SetPoint("CENTER") + f:SetWidth(8) + f:SetHeight(8) + f:Show() + return f +end + +-- local objects +local snapIndicator1 +local snapIndicator2 + + + + + + +-- ReAnchor is a Mixin which provides some anchoring and +-- placement methods for frames. +-- An object with the ReAnchor mixin must support the +-- IAnchorable interface (implicitly or explicitly). +-- The mixin methods also require arguments to support +-- that interface. + +-- In the method prototypes, 'IRObjs' is used to refer to a +-- table of objects which support the IAnchorable interface. + + +local ReAnchor = AceOO.Mixin { + "GetClosestVisibleEdge", + "GetClosestVisiblePoint", + "GetClosestPointSnapped", + "DisplaySnapIndicator", + "HideSnapIndicator", +} + +--------------------------------------------------------- +-- Constants and classes that are not exported via mixin +--------------------------------------------------------- +ReAnchor.IAnchorable = AceOO.Interface { + GetFrame = "function", + GetAnchorage = "function", -- return ReAnchor.anchorInside or .anchorOutside +} + +ReAnchor.anchorInside = { inside = true } +ReAnchor.anchorOutside = { outside = true } + + + +-------------------- +-- Mixin methods +-------------------- + +-- returns: +-- (1) o : the closest IRObj +-- (2) e1 : the point (edge) on self:GetFrame() +-- (3) e2 : the point (edge) on o:GetFrame() +function ReAnchor:GetClosestVisibleEdge( IRObjs ) + local f1 = self:GetFrame() + local r, o, e1, e2 + for _, o2 in pairs(IRObjs) do + local f2 = o2:GetFrame() + local a = o2:GetAnchorage() + if f2:IsVisible() and CheckAnchorable(f1,f2) then + for _, e in pairs(edges) do + local opp = a.inside and e or oppositePoints[e] + if CheckEdgeOverlap(f1,f2,e) then + local d = GetEdgeDistance(f1, f2, e, opp) + if not r or d < r then + r, o, e1, e2 = d, o2, e, opp + end + end + end + end + end + return o, e1, e2 +end + +-- returns: +-- (1) o: the closest IRObj +-- (1) p: the point on self:GetFrame() +-- (2) rp: the relativePoint on o:GetFrame() +-- (3) x: x offset +-- (4) y: y offset +-- such that self:GetFrame():SetPoint(p,o:GetFrame(),rp,x,y) preserves the current location +function ReAnchor:GetClosestVisiblePoint( IRObjs ) + local f1 = self:GetFrame() + local o, e1, e2 = self:GetClosestVisibleEdge( IRObjs ) + local f2 = o:GetFrame() + local rsq, p, rp, x, y + -- iterate pointsOnEdge in order and use < to prefer edge centers to corners + for _, p1 in ipairs(pointsOnEdge[e1]) do + if CheckPointEdgeOverlap(f1,p1,f2,e2) then + local p2 = o:GetAnchorage().outside and oppositePoints[p1] or p1 + local x1, y1 = GetPointCoords(f1,p1) + local x2, y2 = GetPointCoords(f2,p2) + local dx = x1 - x2 + local dy = y1 - y2 + local rsq2 = dx*dx + dy*dy + if not rsq or rsq2 < rsq then + rsq, p, rp, x, y = rsq2, p1, p2, dx, dy + end + end + end + return o, p, rp, x, y +end + + +-- Calls self:GetClosestVisiblePoint() and then snaps to the specified +-- offsets if within the given range in x and y (as appropriate) +-- Return semantic is the same as GetClosestVisiblePoint(). Returns nil +-- if no snap can be done. +function ReAnchor:GetClosestPointSnapped(IRObjs, r, xOff, yOff) + local f1 = self:GetFrame() + local o, p, rp, x, y = self:GetClosestVisiblePoint(IRObjs) + local s = false + + if r then + local sx, sy = GetInteriorOffsetsToPoint(p, xOff or 0, yOff or 0) + local xx, yy = pointCoordFuncs[p](f1) + if xx and yy then + if math.abs(x) <= r then + x = sx + s = true + end + if math.abs(y) <= r then + y = sy + s = true + end + elseif xx then + if math.abs(x) <= r then + x = sx + s = true + if math.abs(y) <= r then + y = sy + end + end + elseif yy then + if math.abs(y) <= r then + y = sy + s = true + if math.abs(x) <= r then + x = sx + end + end + end + end + + if s then + return o, p, rp, x, y + end +end + + + +-- shows anchor-indicators on the associated frame and the target frame +-- when a snap is warranted. +function ReAnchor:DisplaySnapIndicator( IRObjs, r, xOff, yOff ) + local o, p, rp, x, y, snap = self:GetClosestPointSnapped(IRObjs, r, xOff, yOff) + local si1 = snapIndicator1 + local si2 = snapIndicator2 + if o then + si1:ClearAllPoints() + si2:ClearAllPoints() + si1:SetPoint("CENTER", self:GetFrame(), p, 0, 0) + local xx, yy = pointCoordFuncs[rp](o:GetFrame()) + x = math.abs(x) <=r and xx and 0 or x + y = math.abs(y) <=r and yy and 0 or y + si2:SetPoint("CENTER", o:GetFrame(), rp, x, y) + si1:Show() + si2:Show() + else + if si1:IsVisible() then + si1:Hide() + si2:Hide() + end + end +end + + +function ReAnchor:HideSnapIndicator() + if snapIndicator1:IsVisible() then + snapIndicator1:Hide() + snapIndicator2:Hide() + end +end + + + +-- library setup + +local function activate( self, oldLib, oldDeactivate ) + snapIndicator1 = createSnapIndicator() + snapIndicator2 = createSnapIndicator() + + if oldDeactivate then + oldDeactivate(oldLib) + end +end + +AceLibrary:Register(ReAnchor, version_major, version_minor, activate) +ReAnchor = nil