View Single Post
06-09-17, 09:49 AM   #10
Resike
A Pyroguard Emberseer
AddOn Author - Click to view addons
Join Date: Mar 2010
Posts: 1,290
You can however thats a bit more complex, since you can never know if the relative frame is not positioned on fraction pixels, for that you have to get the relative frames (GetLeft|GetBottom|GetTop|GetRight) based on anchor points and substract those values from the actual frame's (GetLeft|GetBottom|GetTop|GetRight), and it even gets more complicated since every different anchor points need different calculations, however i have a function for that already, called GetRelativePoint:

Lua Code:
  1. math.round = function(self)
  2.     return math.floor(self + 0.5)
  3. end
  4.  
  5. local function GetRelativePoint(points, frame)
  6.     if not points then
  7.         points = {"BOTTOMLEFT", UIParent, "BOTTOMLEFT"}
  8.     end
  9.  
  10.     local rel = points[2]
  11.     if not rel then
  12.         rel = UIParent
  13.     end
  14.  
  15.     if type(rel) == "string" then
  16.         rel = _G[rel]
  17.     end
  18.  
  19.     if not rel then
  20.         return
  21.     end
  22.  
  23.     local point = string.upper(points[1])
  24.     local relPoint = string.upper(points[3])
  25.     local rX, rY, pX, pY
  26.  
  27.     if rel:GetLeft() then
  28.         if relPoint == "TOPRIGHT" then
  29.             rY = rel:GetTop()
  30.             rX = rel:GetRight()
  31.         elseif relPoint == "TOPLEFT" then
  32.             rY = rel:GetTop()
  33.             rX = rel:GetLeft()
  34.         elseif relPoint == "TOP" then
  35.             rY = rel:GetTop()
  36.             rX = (rel:GetRight() + rel:GetLeft()) / 2
  37.         elseif relPoint == "BOTTOMRIGHT" then
  38.             rY = rel:GetBottom()
  39.             rX = rel:GetRight()
  40.         elseif relPoint == "BOTTOMLEFT" then
  41.             rY = rel:GetBottom()
  42.             rX = rel:GetLeft()
  43.         elseif relPoint == "BOTTOM" then
  44.             rY = rel:GetBottom()
  45.             rX = (rel:GetRight() + rel:GetLeft()) / 2
  46.         elseif relPoint == "CENTER" then
  47.             rY = (rel:GetTop() + rel:GetBottom()) / 2
  48.             rX = (rel:GetRight() + rel:GetLeft()) / 2
  49.         elseif relPoint == "LEFT" then
  50.             rY = (rel:GetTop() + rel:GetBottom()) / 2
  51.             rX = rel:GetLeft()
  52.         elseif relPoint == "RIGHT" then
  53.             rY = (rel:GetTop() + rel:GetBottom()) / 2
  54.             rX = rel:GetRight()
  55.         else
  56.             return
  57.         end
  58.         if rel.GetEffectiveScale then
  59.             rY = rY * rel:GetEffectiveScale()
  60.             rX = rX * rel:GetEffectiveScale()
  61.         else
  62.             rY = rY * UIParent:GetEffectiveScale()
  63.             rX = rX * UIParent:GetEffectiveScale()
  64.         end
  65.     end
  66.  
  67.     if frame:GetLeft() then
  68.         if point == "TOPRIGHT" then
  69.             pY = frame:GetTop()
  70.             pX = frame:GetRight()
  71.         elseif point == "TOPLEFT" then
  72.             pY = frame:GetTop()
  73.             pX = frame:GetLeft()
  74.         elseif point == "TOP" then
  75.             pY = frame:GetTop()
  76.             pX = (frame:GetRight() + frame:GetLeft()) / 2
  77.         elseif point == "BOTTOMRIGHT" then
  78.             pY = frame:GetBottom()
  79.             pX = frame:GetRight()
  80.         elseif point == "BOTTOMLEFT" then
  81.             pY = frame:GetBottom()
  82.             pX = frame:GetLeft()
  83.         elseif point == "BOTTOM" then
  84.             pY = frame:GetBottom()
  85.             pX = (frame:GetRight() + frame:GetLeft()) / 2
  86.         elseif point == "CENTER" then
  87.             pY = (frame:GetTop() + frame:GetBottom()) / 2
  88.             pX = (frame:GetRight() + frame:GetLeft()) / 2
  89.         elseif point == "LEFT" then
  90.             pY = (frame:GetTop() + frame:GetBottom()) / 2
  91.             pX = frame:GetLeft()
  92.         elseif point == "RIGHT" then
  93.             pY = (frame:GetTop() + frame:GetBottom()) / 2
  94.             pX = frame:GetRight()
  95.         else
  96.             return
  97.         end
  98.         if frame.GetEffectiveScale then
  99.             pY = pY * frame:GetEffectiveScale()
  100.             pX = pX * frame:GetEffectiveScale()
  101.         else
  102.             pY = pY * UIParent:GetEffectiveScale()
  103.             pX = pX * UIParent:GetEffectiveScale()
  104.         end
  105.     end
  106.  
  107.     if rY and rX and pY and pX then
  108.         rX = pX - rX
  109.         rY = pY - rY
  110.         if frame.GetEffectiveScale then
  111.             rY = rY / frame:GetEffectiveScale()
  112.             rX = rX / frame:GetEffectiveScale()
  113.         else
  114.             rY = rY / UIParent:GetEffectiveScale()
  115.             rX = rX / UIParent:GetEffectiveScale()
  116.         end
  117.     else
  118.         rX = 0
  119.         rY = 0
  120.     end
  121.  
  122.     --print(point, rel:GetName(), relPoint, rX, rY)
  123.     return {point, rel:GetName(), relPoint, rX, rY}
  124. end
  125.  
  126. local frame = CreateFrame("Frame", "TestFrame", UIParent)
  127. frame:SetSize(400, 200)
  128. frame:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", 0, -50)
  129. frame:SetClampedToScreen(true)
  130.  
  131. frame.backdrop = {
  132.     bgFile = "Interface\\Buttons\\WHITE8X8",
  133.     edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
  134.     tile = true,
  135.     tileSize = 16,
  136.     edgeSize = 14,
  137.     insets = {left = 3, right = 3, top = 3, bottom = 3},
  138. }
  139.  
  140. frame:SetBackdrop(frame.backdrop)
  141. frame:SetBackdropColor(0.0, 0.0, 0.0, 0.5)
  142. frame:SetBackdropBorderColor(0.3, 0.3, 0.3, 1.0)
  143.  
  144. -- Frame Hooks
  145. hooksecurefunc(frame, "SetPoint", function(self)
  146.     if self.moving then
  147.         return
  148.     end
  149.  
  150.     self.moving = true
  151.     self:SetMovable(true)
  152.  
  153.     print("SetPoint")
  154.  
  155.     local point, relativeTo, relativePoint, xOffset, yOffset = self:GetPoint(1)
  156.  
  157.     local pos = GetRelativePoint({"TOPLEFT", PlayerFrame, "BOTTOMLEFT"}, self)
  158.  
  159.     local x = math.round(pos[4])
  160.     local y = math.round(pos[5])
  161.  
  162.     self:ClearAllPoints()
  163.     self:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", x, y)
  164.  
  165.     self:SetMovable(false)
  166.     self.moving = nil
  167. end)
  168.  
  169. hooksecurefunc(frame, "StopMovingOrSizing", function(self)
  170.     self:SetMovable(true)
  171.  
  172.     print("StopMovingOrSizing")
  173.  
  174.     local point, relativeTo, relativePoint, xOffset, yOffset = self:GetPoint(1)
  175.  
  176.     local pos = GetRelativePoint({"TOPLEFT", PlayerFrame, "BOTTOMLEFT"}, self)
  177.  
  178.     local x = math.round(pos[4])
  179.     local y = math.round(pos[5])
  180.  
  181.     self:ClearAllPoints()
  182.     self:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", x, y)
  183.  
  184.     self:SetMovable(false)
  185. end)
  186.  
  187. frame:HookScript("OnSizeChanged", function(self)
  188.     self:SetSize(math.round(self:GetWidth()), math.round(self:GetHeight()))
  189. end)
  190.  
  191. -- Parent Hooks
  192. local point, relativeTo, relativePoint, xOffset, yOffset = frame:GetPoint(1)
  193.  
  194. hooksecurefunc(relativeTo, "SetPoint", function(self)
  195.     if self.moving then
  196.         return
  197.     end
  198.  
  199.     self.moving = true
  200.     self:SetMovable(true)
  201.  
  202.     print("Parent: SetPoint")
  203.  
  204.     local point, relativeTo, relativePoint, xOffset, yOffset = self:GetPoint(1)
  205.  
  206.     local pos = GetRelativePoint({"TOPLEFT", PlayerFrame, "BOTTOMLEFT"}, self)
  207.  
  208.     local x = math.round(pos[4])
  209.     local y = math.round(pos[5])
  210.  
  211.     self:ClearAllPoints()
  212.     self:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", x, y)
  213.  
  214.     self:SetMovable(false)
  215.     self.moving = nil
  216. end)
  217.  
  218. hooksecurefunc(relativeTo, "StopMovingOrSizing", function(self)
  219.     self:SetMovable(true)
  220.  
  221.     print("Parent: StopMovingOrSizing")
  222.  
  223.     local point, relativeTo, relativePoint, xOffset, yOffset = self:GetPoint(1)
  224.  
  225.     local pos = GetRelativePoint({"TOPLEFT", PlayerFrame, "BOTTOMLEFT"}, self)
  226.  
  227.     local x = math.round(pos[4])
  228.     local y = math.round(pos[5])
  229.  
  230.     self:ClearAllPoints()
  231.     self:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", x, y)
  232.  
  233.     self:SetMovable(false)
  234. end)
  235.  
  236. relativeTo:HookScript("OnSizeChanged", function(self)
  237.     local point, relativeTo, relativePoint, xOffset, yOffset = self:GetPoint(1)
  238.  
  239.     local pos = GetRelativePoint({"TOPLEFT", PlayerFrame, "BOTTOMLEFT"}, self)
  240.  
  241.     local x = math.round(pos[4])
  242.     local y = math.round(pos[5])
  243.  
  244.     self:ClearAllPoints()
  245.     self:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", x, y)
  246. end)
  247.  
  248. -- Moving and Resizing functions
  249. frame:EnableMouse(true)
  250. --frame:SetMaxResize(800, 400)
  251. frame:SetMinResize(40, 40)
  252.  
  253. frame:SetScript("OnMouseDown", function(self, button)
  254.     if button == "LeftButton" then
  255.         self:SetMovable(true)
  256.         self:StartMoving()
  257.     end
  258. end)
  259. frame:SetScript("OnMouseUp", function(self, button)
  260.     if button == "LeftButton" then
  261.         self:StopMovingOrSizing()
  262.         self:SetMovable(false)
  263.  
  264.         local point, relativeTo, relativePoint, xOffset, yOffset = self:GetPoint(1)
  265.  
  266.         local pos = GetRelativePoint({"TOPLEFT", PlayerFrame, "BOTTOMLEFT"}, self)
  267.  
  268.         local x = math.round(pos[4])
  269.         local y = math.round(pos[5])
  270.  
  271.         self:ClearAllPoints()
  272.         self:SetPoint("TOPLEFT", PlayerFrame, "BOTTOMLEFT", x, y)
  273.     end
  274. end)
  275.  
  276. frame.br = CreateFrame("Frame", nil, frame)
  277. frame.br:SetPoint("TOPLEFT", frame, "BOTTOMRIGHT", -15, 15)
  278. frame.br:SetSize(12, 12)
  279. frame.br:EnableMouse(true)
  280.  
  281. frame.br.texture = frame.br:CreateTexture(nil, "OVERLAY")
  282. frame.br.texture:SetPoint("TOPLEFT", frame.br, "TOPLEFT", 0, 0)
  283. frame.br.texture:SetSize(12, 12)
  284. frame.br.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
  285.  
  286. frame.br:SetScript("OnEnter", function(self)
  287.     frame.br.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Highlight")
  288. end)
  289. frame.br:SetScript("OnLeave", function(self)
  290.     frame.br.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
  291. end)
  292.  
  293. frame.br:SetScript("OnMouseDown", function(self, button)
  294.     if button == "LeftButton" then
  295.         frame.br.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Down")
  296.  
  297.         frame:SetResizable(true)
  298.         frame:StartSizing("BottomRight")
  299.     end
  300. end)
  301. frame.br:SetScript("OnMouseUp", function(self, button)
  302.     frame:StopMovingOrSizing()
  303.     frame:SetResizable(false)
  304.  
  305.     frame.br.texture:SetTexture("Interface\\ChatFrame\\UI-ChatIM-SizeGrabber-Up")
  306. end)


This time the frame is anchored to the PlayerFrame, every time you move the PlayerFrame the frame moves with it, if you position the PlayerFrame on fraction of pixes it does not matter for the frame, the frame still will be positioned pixel perfectly every time. You can also move the frame related to the player frame and it will also keep the pixel perfection that way, and also every time to resize/rescale the PlayerFrame or resize the frame itself. HOWEVER it won't keep the pixel perfection if you scale the frame itself, thats requires even more work.

I don't think ElvUI has pixel perfection, at least not a "perfect" pixel perfection. They just using a hack to set you UI scale on a number which only makes the frames pixel perfect on a 1.0 scaling, however every other scale value will be still be ugly (mostly for frames with edges).

The perfect pixel perfect scaling/resizing on any value is a lot more harder to achieve, i have such a function for my frames, however it only works on frames that you know well and you build in a proper way, it can't be generalised to work on every frame easily.

Last edited by Resike : 06-09-17 at 01:36 PM.
  Reply With Quote