view Overlay.lua @ 294:30c9bdaad7a3 stable

merge 1.1 beta 10 to stable
author Flick
date Fri, 05 Aug 2011 16:28:13 -0700
parents 36a29870bf34
children
line wrap: on
line source
local _, ns = ...
local ReAction               = ns.ReAction
local L                      = ReAction.L
local LKB                    = ReAction.LKB
local CreateFrame            = CreateFrame
local InCombatLockdown       = InCombatLockdown
local floor                  = math.floor
local min                    = math.min
local format                 = string.format
local GameTooltip            = GameTooltip
local Bar                    = ReAction.Bar
local GetSize                = Bar.GetSize
local SetSize                = Bar.SetSize
local GetButtonSize          = Bar.GetButtonSize
local GetButtonGrid          = Bar.GetButtonGrid
local SetButtonSize          = Bar.SetButtonSize
local SetButtonGrid          = Bar.SetButtonGrid
local ApplyAnchor            = Bar.ApplyAnchor
local GameTooltipTextRight1  = GameTooltipTextRight1
local GameTooltipTextRight2  = GameTooltipTextRight2
local GameTooltipTextRight3  = GameTooltipTextRight3

--
-- Wrap some of the bar manipulators to make them state-aware
--
local function SetAnchor( bar, point, frame, relPoint, x, y )
  local state = bar:GetSecureState()
  if state then
    local anchorstate = bar:GetStateProperty(state, "anchorEnable")
    if anchorstate then
      bar:SetStateProperty(state, "anchorFrame", frame)
      bar:SetStateProperty(state, "anchorPoint", point)
      bar:SetStateProperty(state, "anchorRelPoint", relPoint)
      bar:SetStateProperty(state, "anchorX", x or 0)
      bar:SetStateProperty(state, "anchorY", y or 0)
      bar:SetAnchor(bar:GetAnchor())
      return
    end
  end
  bar:SetAnchor(point, frame, relPoint, x, y)
end

local function GetStateScale( bar )
  local state = bar:GetSecureState()
  if state and bar:GetStateProperty(state, "enableScale") then
    return bar:GetStateProperty(state, "scale")
  end
end

local function SetStateScale( bar, scale )
  local state = bar:GetSecureState()
  if state and bar:GetStateProperty(state, "enableScale") then
    bar:SetStateProperty(state, "scale", scale)
  end
end


--
-- Bar config overlay
--

local function GetNormalTextColor()
  return 1.0, 1.0, 1.0, 1.0
end

local function GetAnchoredTextColor()
  return 1.0, 1.0, 1.0, 1.0
end

local function GetNormalBgColor()
  return 0.7, 0.7, 1.0, 0.3
end

local function GetAnchoredBgColor()
  return 0.9, 0.2, 0.7, 0.3
end

local function StoreSize(bar)
  local f = bar:GetFrame()
  SetSize( bar, f:GetWidth(), f:GetHeight() )
end

local function StoreExtents(bar)
  local f = bar:GetFrame()
  local p, fr, rp, x, y = f:GetPoint(1)
  fr = fr and fr:GetName() or "UIParent"
  SetAnchor( bar, p, fr, rp, x, y )
  SetSize( bar, f:GetWidth(), f:GetHeight() )
end

local function RecomputeButtonSize(bar)
  local w, h = GetSize(bar)
  local bw, bh = GetButtonSize(bar)
  local r, c, s = GetButtonGrid(bar)

  local scaleW = (floor(w/c) - s) / bw
  local scaleH = (floor(h/r) - s) / bh
  local scale = min(scaleW, scaleH)

  SetButtonSize(bar, scale * bw, scale * bh, s)
end

local function ComputeBarScale(bar, overlay)
  local w, h = overlay:GetWidth() - 8, overlay:GetHeight() - 8
  local bw, bh = GetButtonSize(bar)
  local r, c, s = GetButtonGrid(bar)

  local scaleW = w / (c*(bw+s))
  local scaleH = h / (r*(bh+s))
  local scale = min(scaleW, scaleH)

  if scale > 2.5 then
    scale = 2.5
  elseif scale < 0.25 then
    scale = 0.25
  end

  return scale
end

local function RecomputeButtonSpacing(bar)
  local w, h = GetSize(bar)
  local bw, bh = GetButtonSize(bar)
  local r, c, s = GetButtonGrid(bar)

  SetButtonGrid(bar,r,c,min(floor(w/c) - bw, floor(h/r) - bh))
end

local function RecomputeGrid(bar)
  local w, h = GetSize(bar)
  local bw, bh = GetButtonSize(bar)
  local r, c, s = GetButtonGrid(bar)

  SetButtonGrid(bar, floor(h/(bh+s)), floor(w/(bw+s)), s)
end

local function ClampToButtons(bar)
  local bw, bh = GetButtonSize(bar)
  local r, c, s = GetButtonGrid(bar)
  SetSize(bar, (bw+s)*c + 1, (bh+s)*r + 1)
end

local function HideGameTooltip()
  GameTooltip:Hide()
end

local anchorInside  = { inside = true }
local anchorOutside = { outside = true }
local edges = { "BOTTOM", "TOP", "LEFT", "RIGHT" }
local oppositeEdges = {
  TOP = "BOTTOM",
  BOTTOM = "TOP",
  LEFT = "RIGHT",
  RIGHT = "LEFT"
}
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 snapPoints = {
  [anchorOutside] = {
    BOTTOMLEFT  = {"BOTTOMRIGHT","TOPLEFT","TOPRIGHT"},
    BOTTOM      = {"TOP"},
    BOTTOMRIGHT = {"BOTTOMLEFT","TOPRIGHT","TOPLEFT"},
    RIGHT       = {"LEFT"},
    TOPRIGHT    = {"TOPLEFT","BOTTOMRIGHT","BOTTOMLEFT"},
    TOP         = {"BOTTOM"},
    TOPLEFT     = {"TOPRIGHT","BOTTOMLEFT","BOTTOMRIGHT"},
    LEFT        = {"RIGHT"},
    CENTER      = {"CENTER"}
  },
  [anchorInside] = {
    BOTTOMLEFT  = {"BOTTOMLEFT"},
    BOTTOM      = {"BOTTOM"},
    BOTTOMRIGHT = {"BOTTOMRIGHT"},
    RIGHT       = {"RIGHT"},
    TOPRIGHT    = {"TOPRIGHT"},
    TOP         = {"TOP"},
    TOPLEFT     = {"TOPLEFT"},
    LEFT        = {"LEFT"},
    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 x, y 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 cornerTexCoords = {
              -- ULx, ULy, LLx, LLy, URx, URy, LRx, LRy
  TOPLEFT     = { 1,   1,   1,   0,   0,   1,   0,   0 },
  TOPRIGHT    = { 1,   0,   0,   0,   1,   1,   0,   1 },
  BOTTOMLEFT  = { 0,   1,   1,   1,   0,   0,   1,   0 },
  BOTTOMRIGHT = { 0,   0,   0,   1,   1,   0,   1,   1 },
}

-- 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)
    if not f then f = f2:GetParent() end
    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

local globalSnapTargets = { [UIParent] = anchorInside }

local function GetClosestFrameEdge(f1,f2,a)
  local dist, edge, opp
  if f2:IsVisible() and CheckAnchorable(f1,f2) then
    for _, e in pairs(edges) do
      local o = a.inside and e or oppositeEdges[e]
      if CheckEdgeOverlap(f1,f2,e) then
        local d = GetEdgeDistance(f1, f2, e, o)
        if not dist or (d < dist) then
          dist, edge, opp = d, e, o
        end
      end
    end
  end
  return dist, edge, opp
end

local function GetClosestVisibleEdge( f )
  local r, o, e1, e2
  local a = anchorOutside
  for _, b in ReAction:IterateBars() do
    local d, e, opp = GetClosestFrameEdge(f,b:GetFrame(),a)
    if d and (not r or d < r) then
      r, o, e1, e2 = d, b:GetFrame(), e, opp
    end
  end
  for f2, a2 in pairs(globalSnapTargets) do
    local d, e, opp = GetClosestFrameEdge(f,f2,a2)
    if d and (not r or d < r) then
      r, o, e1, e2, a = d, f2, e, opp, a2
    end
  end
  return o, e1, e2, a
end

local function GetClosestVisiblePoint(f1)
  local f2, e1, e2, a = GetClosestVisibleEdge(f1)
  if f2 then
    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
        for _, p2 in pairs(snapPoints[a][p1]) do
          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
    end
    return f2, p, rp, x, y
  end
end

local function GetClosestPointSnapped(f1, rx, ry, xOff, yOff)
  local o, p, rp, x, y = GetClosestVisiblePoint(f1)
  local s = false

  local insideOffsetFunc = p and insidePointOffsetFuncs[p]
  local coordFunc = p and pointCoordFuncs[p]
  if not insideOffsetFunc or not coordFunc then
    return
  end
  
  local sx, sy = insideOffsetFunc(xOff or 0, yOff or 0)
  local xx, yy = coordFunc(f1)
  if xx and yy then
    if math.abs(x) <= rx then
      if math.abs(y) <= ry then
        x = sx
        y = sy
        s = true
      elseif CheckEdgeOverlap(f1,o,"LEFT") then
        x = sx
        s = true
      end
    elseif math.abs(y) <= ry and CheckEdgeOverlap(f1,o,"TOP") then
      y = sy
      s = true
    end
  elseif xx then
    if math.abs(x) <= rx then
      x = sx
      s = true
      if math.abs(y) <= ry then
        y = sy
      end
    end
  elseif yy then
    if math.abs(y) <= ry then
      y = sy
      s = true
      if math.abs(x) <= rx then
        x = sx
      end
    end
  end

  -- correct for some Lua oddities with doubles
  if x == -0 then x = 0 end
  if y == -0 then y = 0 end
  
  if s then
    return o, p, rp, math.floor(x), math.floor(y)
  end
end

local function CreateSnapIndicator()
  local si = CreateFrame("Frame",nil,UIParent)
  si:SetFrameStrata("HIGH")
  si:SetHeight(16)
  si:SetWidth(16)
  local tex = si:CreateTexture()
  tex:SetAllPoints()
  tex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock")
  tex:SetBlendMode("ADD")
  tex:SetDrawLayer("OVERLAY")
  return si
end

local si1 = CreateSnapIndicator()
local si2 = CreateSnapIndicator()

local function DisplaySnapIndicator( f, rx, ry, xOff, yOff )
  local o, p, rp, x, y, snap = GetClosestPointSnapped(f, rx, ry, xOff, yOff)
  if o then
    si1:ClearAllPoints()
    si2:ClearAllPoints()
    si1:SetPoint("CENTER", f, p, 0, 0)
    local xx, yy = pointCoordFuncs[rp](o)
    x = math.abs(x) <=rx and xx and 0 or x
    y = math.abs(y) <=ry and yy and 0 or y
    si2:SetPoint("CENTER", o, rp, x, y)
    si1:Show()
    si2:Show()
  else
    if si1:IsVisible() then
      si1:Hide()
      si2:Hide()
    end
  end
  return o, p
end

local function HideSnapIndicator()
  if si1:IsVisible() then
    si1:Hide()
    si2:Hide()
  end
end

local function UpdateLabelString(overlay)
  local label = overlay.labelString
  if label then
    local name = overlay.labelName
    if name and overlay.labelSubtext then
      name = format("%s (%s)", name, overlay.labelSubtext)
    end
    label:SetText(name or "")
  end
end

local function CreateControls(bar)
  local f = bar:GetFrame()

  f:SetMovable(true)
  f:SetResizable(true)

  -- child of UIParent so that alpha and scale doesn't propagate to it
  local overlay = CreateFrame("Button", nil, UIParent)
  overlay:EnableMouse(true)
  overlay:SetFrameLevel(10) -- set it above the buttons
  overlay:SetPoint("TOPLEFT", f, "TOPLEFT", -4, 4)
  overlay:SetPoint("BOTTOMRIGHT", f, "BOTTOMRIGHT", 4, -4)
  overlay:SetBackdrop({
    edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
    tile = true,
    tileSize = 16,
    edgeSize = 16,
    insets = { left = 0, right = 0, top = 0, bottom = 0 },
  })

  -- textures
  local bgTex = overlay:CreateTexture(nil,"BACKGROUND")
  bgTex:SetTexture(0.7,0.7,1.0,0.2)
  bgTex:SetPoint("TOPLEFT",4,-4)
  bgTex:SetPoint("BOTTOMRIGHT",-4,4)
  local hTex = overlay:CreateTexture(nil,"HIGHLIGHT")
  hTex:SetTexture(0.7,0.7,1.0,0.2)
  hTex:SetPoint("TOPLEFT",4,-4)
  hTex:SetPoint("BOTTOMRIGHT",-4,4)
  hTex:SetBlendMode("ADD")
  local aTex = overlay:CreateTexture(nil,"ARTWORK")
  aTex:SetTexture("Interface\\AddOns\\ReAction\\img\\lock")
  aTex:SetWidth(16)
  aTex:SetHeight(16)
  aTex:Hide()

  -- label
  local label = overlay:CreateFontString(nil,"OVERLAY","GameFontNormalLarge")
  label:SetAllPoints()
  label:SetJustifyH("CENTER")
  label:SetShadowColor(0,0,0,1)
  label:SetShadowOffset(3,-3)
  label:SetTextColor(GetNormalTextColor())
  label:SetText(bar:GetName())
  label:Show()
  overlay.labelString = label
  overlay.labelName = bar:GetName()

  local function UpdateAnchorDecoration()
    local point, anchor, relPoint, x, y = f:GetPoint(1)
    if point then
      local ofsx, ofsy = insidePointOffsetFuncs[point](x,y)
      if (anchor and anchor ~= UIParent) or (ofsx == 0 and ofsy == 0) then
        bgTex:SetTexture( GetAnchoredBgColor() )
        hTex:SetTexture( GetAnchoredBgColor() )
        label:SetTextColor( GetAnchoredTextColor() )
        aTex:ClearAllPoints()
        aTex:SetPoint(point)
        aTex:Show()
        return
      end
    end
    bgTex:SetTexture( GetNormalBgColor() )
    hTex:SetTexture( GetNormalBgColor() )
    label:SetTextColor( GetNormalTextColor() )
    aTex:Hide()
  end

  local function StopResize()
    f:StopMovingOrSizing()
    f.isMoving = false
    f:SetScript("OnUpdate",nil)
    StoreSize(bar)
    ClampToButtons(bar)
    ReAction:RefreshEditor()
  end

  local function CornerUpdate()
    local bw, bh = GetButtonSize(bar)
    local r, c, s = GetButtonGrid(bar)
    local ss = GetStateScale(bar)
    if IsShiftKeyDown() then
      if ss then
        f:SetMinResize( ((s+bw)*c*0.25)/ss, ((s+bh)*r*0.25)/ss )
        f:SetMaxResize( ((s+bw)*c*2.5 + 1)/ss, ((s+bh)*r*2.5 + 1)/ss )
        scale = ComputeBarScale(bar, overlay)
      else
        f:SetMinResize( (s+12)*c+1, (s+12)*r+1 )
        f:SetMaxResize( (s+128)*c+1, (s+128)*r+1 )
        RecomputeButtonSize(bar)
      end
    elseif not ss and IsAltKeyDown() then
      f:SetMinResize( bw*c, bh*r )
      f:SetMaxResize( 2*bw*c, 2*bh*r )
      RecomputeButtonSpacing(bar)
    else
      f:SetMinResize( bw+s+1, bh+s+1 )
      f:SetMaxResize( 50*(bw+s)+1, 50*(bh+s)+1 )
      RecomputeGrid(bar)
    end
    GameTooltipTextRight2:SetText(format("%d x %d",r,c))

    local ss = GetStateScale(bar)
    if ss then
      GameTooltipTextRight4:SetText(format("%d%%", scale*100))
    else
      local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh)
      GameTooltipTextRight3:SetText(size)
      GameTooltipTextRight4:SetText(tostring(s))
    end
  end

  -- corner drag handles
  for _, point in pairs({"BOTTOMLEFT","TOPLEFT","BOTTOMRIGHT","TOPRIGHT"}) do
    local corner = CreateFrame("Frame",nil,overlay)
    corner:EnableMouse(true)
    corner:SetWidth(16)
    corner:SetHeight(16)
    corner:SetPoint(point)

    local tex = corner:CreateTexture(nil,"HIGHLIGHT")
    tex:SetTexture("Interface\\AddOns\\ReAction\\img\\corner")
    tex:SetTexCoord(unpack(cornerTexCoords[point]))
    tex:SetBlendMode("ADD")
    tex:SetAlpha(0.6)
    tex:SetAllPoints()
    
    corner:SetScript("OnMouseDown",
      function(_,btn)
        f:SetScript("OnUpdate", CornerUpdate)
        f:StartSizing(point)
      end
    )
    corner:SetScript("OnMouseUp",
      function()
        local ss = GetStateScale(bar)
        if ss then
          SetStateScale(bar, ComputeBarScale(bar, overlay))
        end
        StopResize()
      end)
    corner:SetScript("OnEnter",
      function()
        local bw, bh = GetButtonSize(bar)
        local r, c, s = bar:GetButtonGrid()
        local size = (bw == bh) and tostring(bw) or format("%d x %d",bw,bh)
        local ss = GetStateScale(bar)
        local state = bar:GetSecureState()
        GameTooltip:SetOwner(f, "ANCHOR_"..point)
        if ss then
          GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state))
        else
          GameTooltip:AddLine(bar:GetName())
        end
        GameTooltip:AddDoubleLine(format("|cffcccccc%s|r %s",L["Drag"],L["to add/remove buttons:"]), format("%d x %d",r,c))
        if ss then
          GameTooltip:AddLine(L["State Scale Override"])
          GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to change scale:"]), format("%d%%", bar:GetStateProperty(state,"scale")*100))
        else
          GameTooltip:AddDoubleLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to resize buttons:"]), tostring(floor(size)))
          GameTooltip:AddDoubleLine(format("|cff0033cc%s|r %s",L["Hold Alt"],  L["to change spacing:"]), tostring(floor(s)))
        end
        GameTooltip:Show()
      end
    )
    corner:SetScript("OnLeave", 
      function()
        GameTooltip:Hide()
        f:SetScript("OnUpdate",nil)
      end
    )
  end

  overlay:RegisterForDrag("LeftButton")
  overlay:RegisterForClicks("RightButtonUp")
  
  overlay:SetScript("OnDragStart",
    function()
      f:StartMoving()
      f.isMoving = true
      local w,h = bar:GetButtonSize()
      f:ClearAllPoints()
      UpdateAnchorDecoration()
      f:SetScript("OnUpdate", function()
          if IsShiftKeyDown() then
            local f, p = DisplaySnapIndicator(f,w,h)
          else
            HideSnapIndicator()
          end
        end)
    end
  )

  local function UpdateDragTooltip()
    GameTooltip:SetOwner(f, "ANCHOR_TOPRIGHT")
    local ss = GetStateScale(bar)
    local state = bar:GetSecureState()
    if ss then
      GameTooltip:AddLine(format("%s (%s: %s)", bar:GetName(), L["State"], state))
    else
      GameTooltip:AddLine(bar:GetName())
    end
    GameTooltip:AddLine(format("|cffcccccc%s|r %s",L["Drag"],L["to move"]))
    GameTooltip:AddLine(format("|cff00ff00%s|r %s",L["Hold Shift"],L["to anchor to nearby frames"]))
    GameTooltip:AddLine(format("|cff00cccc%s|r %s",L["Right-click"],L["for options..."]))
    local point, frame, relpoint, x, y = bar:GetFrame():GetPoint(1)
    if point then
      local ofsx, ofsy = insidePointOffsetFuncs[point](x,y)
      if (frame and frame ~= UIParent) or (ofsx == 0 and ofsy == 0) then
        frame = frame or UIParent
        GameTooltip:AddLine(format("%s <%s>",L["Currently anchored to"],frame:GetName()))
      end
    end
    GameTooltip:Show()
  end

  overlay:SetScript("OnDragStop",
    function()
      f:StopMovingOrSizing()
      f.isMoving = false
      f:SetScript("OnUpdate",nil)

      if IsShiftKeyDown() then
        local w, h = bar:GetButtonSize()
        local a, p, rp, x, y = GetClosestPointSnapped(f,w,h)
        if a then
          f:ClearAllPoints()
          f:SetPoint(p,a,rp,x,y)
        end
        HideSnapIndicator()
      end

      StoreExtents(bar)
      ReAction:RefreshEditor()
      UpdateDragTooltip()
      UpdateAnchorDecoration()
    end
  )

  overlay:SetScript("OnEnter",
    function()
      UpdateDragTooltip()
    end
  )

  overlay:SetScript("OnLeave", HideGameTooltip)

  overlay:SetScript("OnClick",
    function()
      ReAction:ShowEditor(bar)
    end
  )

  function overlay:RefreshControls()
    UpdateAnchorDecoration()
  end

  overlay:SetScript("OnShow", overlay.RefreshControls)

  if ReAction:GetKeybindMode() then
    overlay:SetFrameLevel(1)
  end

  UpdateLabelString(overlay)
  UpdateAnchorDecoration()

  return overlay
end


-- export methods to the Bar prototype
Bar.Overlay = { }
local proto = {__index = Bar.Overlay}

function Bar.Overlay:New( bar )
  return setmetatable( {frame = CreateControls(bar)}, proto )
end

function Bar.Overlay:SetLabel(name)
  self.frame.labelName = name
  UpdateLabelString(self.frame)
end

function Bar.Overlay:SetLabelSubtext(text)
  self.frame.labelSubtext = text
  UpdateLabelString(self.frame)
end

function Bar.Overlay:Show()
  self.frame:Show()
end

function Bar.Overlay:Hide()
  self.frame:Hide()
end

function Bar.Overlay:IsShown()
  return self.frame:IsShown()
end

function Bar.Overlay:RefreshControls()
  self.frame:RefreshControls()
end