Thread Tools Display Modes
02-13-12, 06:33 PM   #1
Rainrider
A Firelord
AddOn Author - Click to view addons
Join Date: Nov 2008
Posts: 454
Sorting auras in oUF 1.6.0

Hello,

I'm porting my layout to oUF 1.6.0 and have some issues with sorting auras by remaining duration. Auras are not sorted correctly and gaps appear between them. My code works with the stable oUF version. Here is a stripped down code snippet from what I use for buffs:

lua Code:
  1. local FormatTime = function(seconds)
  2.     local day, hour, minute = 86400, 3600, 60
  3.     if (seconds >= day) then
  4.         return format("%dd", floor(seconds/day + 0.5)), seconds % day
  5.     elseif (seconds >= hour) then
  6.         return format("%dh", floor(seconds/hour + 0.5)), seconds % hour
  7.     elseif (seconds >= minute) then
  8.         if (seconds <= minute * 5) then
  9.             return format("%d:%02d", floor(seconds/60), seconds % minute), seconds - floor(seconds)
  10.         end
  11.         return format("%dm", floor(seconds/minute + 0.5)), seconds % minute
  12.     elseif (seconds >= minute / 12) then
  13.         return floor(seconds + 0.5), (seconds * 100 - floor(seconds * 100)) / 100
  14.     end
  15.     return format("%.1f", seconds), (seconds * 100 - floor(seconds * 100)) / 100
  16. end
  17.  
  18. local CreateAuraTimer = function(self, elapsed)
  19.     if (self.timeLeft) then
  20.         self.elapsed = (self.elapsed or 0) + elapsed
  21.         if (self.elapsed >= 0.1) then
  22.             if (not self.first) then
  23.                 self.timeLeft = self.timeLeft - self.elapsed
  24.             else
  25.                 self.timeLeft = self.timeLeft - GetTime()
  26.                 self.first = false
  27.             end
  28.             if (self.timeLeft > 0) then
  29.                 local time = FormatTime(self.timeLeft)
  30.                     self.remaining:SetText(time)
  31.                 if (self.timeLeft < 5) then
  32.                     self.remaining:SetTextColor(0.69, 0.31, 0.31)
  33.                 else
  34.                     self.remaining:SetTextColor(0.84, 0.75, 0.65)
  35.                 end
  36.             else
  37.                 self.remaining:Hide()
  38.                 self:SetScript("OnUpdate", nil)
  39.             end
  40.             self.elapsed = 0
  41.         end
  42.     end
  43. end
  44.  
  45. local SortAuras = function(a, b)
  46.     if (a and b and a.timeLeft and b.timeLeft) then
  47.         return a.timeLeft > b.timeLeft
  48.     end
  49. end
  50.  
  51. local PostCreateIcon = function(Icons, icon)
  52.     -- remove OmniCC and CooldownCount timers
  53.     icon.cd.noOCC = true
  54.     icon.cd.noCooldownCount = true
  55.    
  56.     icon.count:SetPoint("BOTTOMRIGHT", 1, 1.5)
  57.     icon.count:SetFont(ns.media.FONT, 8, "OUTLINE")
  58.     icon.count:SetTextColor(0.84, 0.75, 0.65)
  59.     icon.count:SetJustifyH("RIGHT")
  60.    
  61.     icon.icon:SetTexCoord(0.1, 0.9, 0.1, 0.9)
  62.  
  63.     icon.overlay:SetTexture(ns.media.BTNTEXTURE)
  64.     icon.overlay:SetPoint("TOPLEFT", -3.5, 3.5)
  65.     icon.overlay:SetPoint("BOTTOMRIGHT", 3.5, -3.5)
  66.     icon.overlay:SetTexCoord(0, 1, 0, 1)
  67.    
  68.     icon.remaining = PutFontString(icon, ns.media.FONT, 8, "OUTLINE", "LEFT")
  69.     icon.remaining:SetPoint("TOP", 0, 1)
  70. end
  71.  
  72. local PreSetPosition = function(Auras)
  73.     table.sort(Auras, SortAuras)
  74. end
  75.  
  76. local PostUpdateIcon
  77. do
  78.     local playerUnits = {
  79.         player = true,
  80.         pet = true,
  81.         vehicle = true,
  82.     }
  83.  
  84.     PostUpdateIcon = function(icons, unit, icon, index, offset)
  85.         local _, _, _, _, _, duration, expirationTime, caster, _ = UnitAura(unit, index, icon.filter)
  86.        
  87.         if (not playerUnits[caster]) then
  88.             if ((UnitCanAttack("player", unit) and icon.isDebuff)
  89.                     or (UnitIsFriend("player", unit) and not icon.isDebuff)) then
  90.                 icon.icon:SetDesaturated(true)
  91.                 icon.overlay:SetVertexColor(0.5, 0.5, 0.5)
  92.             end
  93.         end
  94.        
  95.         if (duration and duration > 0) then
  96.             icon.remaining:Show()
  97.             icon.timeLeft = expirationTime
  98.             icon:SetScript("OnUpdate", CreateAuraTimer)
  99.         else
  100.             icon.remaining:Hide()
  101.             icon.timeLeft = math.huge
  102.             icon:SetScript("OnUpdate", nil)
  103.         end
  104.  
  105.         icon.first = true
  106.     end
  107. end
  108.  
  109. local AddBuffs = function(self, unit)
  110.     self.Buffs = CreateFrame("Frame", self:GetName().."_Buffs", self)
  111.     self.Buffs.spacing = 6
  112.     self.Buffs.size = (230 - 9 * self.Buffs.spacing) / 10
  113.     self.Buffs.disableCooldown = true
  114.     self.Buffs.showType = true
  115.     self.Buffs.onlyShowPlayer = ns.cfg.onlyShowPlayerBuffs
  116.     self.Buffs.PreSetPosition = PreSetPosition  -- sort auras by time remaining
  117.     self.Buffs.PostCreateIcon = PostCreateIcon  -- set overlay, cd, count, timer font
  118.     self.Buffs.PostUpdateIcon = PostUpdateIcon  -- set icon saturation and update timer
  119.    
  120.     if (unit == "player" or unit == "target") then
  121.         self.Buffs:SetSize(self.Buffs.size * 10 + 9 * self.Buffs.spacing, 4 * self.Buffs.size + 3 * self.Buffs.spacing)
  122.         self.Buffs["growth-y"] = "DOWN"
  123.        
  124.         if (unit == "player") then
  125.             self.Buffs:SetPoint("TOPRIGHT", self, "TOPLEFT", -9, -1)
  126.             self.Buffs.initialAnchor = "TOPRIGHT"
  127.             self.Buffs["growth-x"] = "LEFT"
  128.         else
  129.             self.Buffs:SetPoint("TOPLEFT", self, "TOPRIGHT", 9, -1)
  130.             self.Buffs.initialAnchor = "TOPLEFT"
  131.             self.Buffs["growth-x"] = "RIGHT"
  132.         end
  133.     end
  134.    
  135.     if (unit == "pet") then
  136.         self.Buffs:SetPoint("RIGHT", self.Debuffs, "LEFT", -5, 0)
  137.         self.Buffs.num = 6
  138.         self.Buffs:SetSize(self.Buffs.num * self.Buffs.size + (self.Buffs.num - 1) * self.Buffs.spacing, self.Buffs.size)
  139.         self.Buffs.initialAnchor = "RIGHT"
  140.         self.Buffs["growth-x"] = "LEFT"
  141.     end
  142.    
  143.     if (unit:match("boss%d")) then
  144.         self.Buffs:SetPoint("TOPLEFT", self, "TOPRIGHT", 15, 0)
  145.         self.Buffs.num = 6
  146.         self.Buffs:SetSize(self.Buffs.num * self.Buffs.size + (self.Buffs.num - 1) * self.Buffs.spacing, self.Buffs.size)
  147.         self.Buffs.initialAnchor = "LEFT"
  148.         self.Buffs["growth-x"] = "RIGHT"
  149.     end
  150. end
  151. ns.AddBuffs = AddBuffs

Full code can be found at https://github.com/Rainrider/oUF_Rai..._functions.lua

Here a screen of what happens:


Green area is for buffs, red for debuffs. The upper part is the result of buffing myself with Mark of the Wild and Thorns and switching to bear form. The lower part is after Thorns ticked out. The standart buff display is showing the buffs sorted by index.

Is this an error on my part or is it a bug in oUF?
  Reply With Quote
02-14-12, 02:02 AM   #2
haste
Featured Artist
 
haste's Avatar
Premium Member
Featured
Join Date: Dec 2005
Posts: 1,027
Looks like the issue on oUF's side. I'll going to push a fix for this and a small API change when I get home later today.

The change will be that :PreSetPosition() can return two values, which tell oUF what range of auras it needs to re-position. It currently attempts to re-position them on every try, but it uses the default range.
__________________
「貴方は1人じゃないよ」
  Reply With Quote
02-14-12, 08:38 PM   #3
Rainrider
A Firelord
AddOn Author - Click to view addons
Join Date: Nov 2008
Posts: 454
It works now. I return 0 and #auras. Haven't been able to test whether there is any difference between say #buffs and buffs.visibleBuffs, but there is a problem if the layout returns toRange greater than the current number of auras (tested this with returning the max number of auras that you pass to PreSetPosition. Maybe you could safe guard this by breaking the loop in SetPosition if not button). You also forgot to make the change for buffs and debuffs.

And another one I wasn't able to understand: Your range in SetPosition is from 0 (anchoredIcons) to the number of created icons. In Update you call SetPosition with a range form 1 to createdIcons. SetPosition would then start to anchor from the second aura and leave a gap of 1 button at the start, wouldn't it?

Last edited by Rainrider : 02-14-12 at 08:46 PM.
  Reply With Quote
02-17-12, 02:47 PM   #4
haste
Featured Artist
 
haste's Avatar
Premium Member
Featured
Join Date: Dec 2005
Posts: 1,027
Originally Posted by Rainrider View Post
It works now. I return 0 and #auras. Haven't been able to test whether there is any difference between say #buffs and buffs.visibleBuffs,
You probably meant buffs.createdIcons and #buffs, those two should never be different, but there isn't too much hassle keeping the variable around. It also saves Lua from having to traverse the table to figure out the number of elements.

Originally Posted by Rainrider View Post
but there is a problem if the layout returns toRange greater than the current number of auras (tested this with returning the max number of auras that you pass to PreSetPosition. Maybe you could safe guard this by breaking the loop in SetPosition if not button). You also forgot to make the change for buffs and debuffs.
I'll make :SetPosition bail once it doesn't find a button. Also added the missing :PreSetPosition returns. Thanks

Originally Posted by Rainrider View Post
And another one I wasn't able to understand: Your range in SetPosition is from 0 (anchoredIcons) to the number of created icons. In Update you call SetPosition with a range form 1 to createdIcons. SetPosition would then start to anchor from the second aura and leave a gap of 1 button at the start, wouldn't it?
That's a bug. The code path is only used when someone wants to force the aura element to re-position the icons. I should probably change :SetPosition to work from 1 and up instead, it makes a lot more sense from a common Lua perspective. Thanks again
__________________
「貴方は1人じゃないよ」
  Reply With Quote
02-18-12, 10:53 AM   #5
Rainrider
A Firelord
AddOn Author - Click to view addons
Join Date: Nov 2008
Posts: 454
Originally Posted by haste View Post
You probably meant buffs.createdIcons and #buffs, those two should never be different, but there isn't too much hassle keeping the variable around. It also saves Lua from having to traverse the table to figure out the number of elements.
I want to return the lowest possible range in order to spare table traversion and reanchoring, which means I would only want to sort and reanchor what's visible. If I read this right, you increment createdIcons in createAuraIcon before calling customFilter, meaning createdIcons >= visible{Auras|Buffs|Debuffs}. If #{auras|buffs|debuffs} == createdIcons, then I return a higher range than I need to and thus do SetPosition on icons I don't see. So I don't quite get it why we run SetPosition up to createdIcons and not visible{Auras|Buffs|Debuffs}. And because there are no gaps after SetPosition I believe the function only does this for the visible icons and not the created ones (I have to admit I haven't tested this with CustomFilter yet). Or I just messed the whole thing up and don't understand your code properly.
  Reply With Quote
02-19-12, 05:52 AM   #6
haste
Featured Artist
 
haste's Avatar
Premium Member
Featured
Join Date: Dec 2005
Posts: 1,027
I changed the default :SetPosition() to only anchor icons that it hasn't anchored before. This works great as long as you don't sort the auras, which is why :PreSetPosition() needs to return ranges to :SetPosition().

This does mean that visible{Auras,{B,Deb}uffs is equal to createdIcons when oUF runs :SetPosition() for a certain range, which also is equal to #icons.

If we have two units, one with 1 buff and one with 5 buffs. If I target the one with 1 buff first, oUF will run :SetPosition() for the range 1 to 1. If I then target the unit with 5 buffs, oUF will run :SetPostion() for the range 2 to 5. If I target the unit with 1 buff again, oUF won't run :SetPosition().

The above is true as long as :PreSetPosition() doesn't return a range.

:SetPosition() is currently 0-based, which it really shouldn't be. I honestly don't know why I ended up writing it that way, but it's something I'd like to fix.
__________________
「貴方は1人じゃないよ」
  Reply With Quote

WoWInterface » Featured Projects » oUF (Otravi Unit Frames) » Sorting auras in oUF 1.6.0


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off