updated, added better sticky support changed some animations.

Pawkette [03-16-14 - 18:30]
updated, added better sticky support changed some animations.
Filename
PSBT.lua
PSBT.txt
PSBT.xml
PSBT_Auras.lua
PSBT_Combat.lua
PSBT_Experience.lua
PSBT_Fifo.lua
PSBT_Label.lua
PSBT_LowSomething.lua
PSBT_ScrollArea.lua
diff --git a/PSBT.lua b/PSBT.lua
index 8a2a56e..bed7633 100644
--- a/PSBT.lua
+++ b/PSBT.lua
@@ -41,6 +41,12 @@ end

 function PSBT:Initialize( control )
     self.control = control
+
+    self._areas[ PSBT_AREAS.INCOMING ]     = PSBT_ScrollArea:New( self.control, PSBT_AREAS.INCOMING,     BOTTOM )
+    self._areas[ PSBT_AREAS.OUTGOING ]     = PSBT_ScrollArea:New( self.control, PSBT_AREAS.OUTGOING,     TOP )
+    self._areas[ PSBT_AREAS.STATIC ]       = PSBT_ScrollArea:New( self.control, PSBT_AREAS.STATIC,       BOTTOM )
+    self._areas[ PSBT_AREAS.NOTIFICATION ] = PSBT_ScrollArea:New( self.control, PSBT_AREAS.NOTIFICATION, TOP )
+
     self.control:RegisterForEvent( EVENT_ADD_ON_LOADED, function( _, addon ) self:OnLoaded( addon ) end )
 end

@@ -50,34 +56,21 @@ function PSBT:OnLoaded( addon )
     end

     print( 'Loading PSBT' )
-
-
-    self._areas[ PSBT_AREAS.INCOMING ]     = self.control:GetNamedChild( PSBT_AREAS.INCOMING )
-    self._areas[ PSBT_AREAS.OUTGOING ]     = self.control:GetNamedChild( PSBT_AREAS.OUTGOING )
-    self._areas[ PSBT_AREAS.STATIC ]       = self.control:GetNamedChild( PSBT_AREAS.STATIC )
-    self._areas[ PSBT_AREAS.NOTIFICATION ] = self.control:GetNamedChild( PSBT_AREAS.NOTIFICATION )
-
-    print( self._areas )
-    for k,v in pairs( self._areas ) do
-        print( tostring( k ) .. ' = ' ..tostring( v ) )
-    end
-
-
     CBM:FireCallbacks( PSBT_EVENTS.LOADED, self )

     self.control:SetHandler( 'OnUpdate', function( _, frameTime ) self:OnUpdate( frameTime ) end )
 end

 function PSBT:OnUpdate( frameTime )
-    for k,v in pairs( self._modules ) do
-        v:OnUpdate( frameTime )
-    end
-
     for k,label in pairs( self:GetActiveObjects() ) do
-        if ( not label:IsVisible() ) then
+        if ( label:IsExpired( frameTime ) ) then
             self:ReleaseObject( k )
         end
     end
+
+    for k,v in pairs( self._modules ) do
+        v:OnUpdate( frameTime )
+    end
 end

 function PSBT:CreateLabel()
@@ -125,33 +118,16 @@ end

 function PSBT:NewEvent( scrollArea, sticky, icon, text )
     local entry = self:AcquireObject()
-
     local area = self._areas[ scrollArea ]
-
-    local height        = area:GetHeight()
-    local duration      = 1
-    local relativePoint = nil
-    if ( scrollArea == PSBT_AREAS.NOTIFICATION ) then
-        relativePoint = TOP
-        duration = height * 20
-    elseif ( scrollArea == PSBT_AREAS.INCOMING ) then
-        relativePoint = BOTTOM
-        duration = height * 10
-        height = height * -1
-    elseif ( scrollArea == PSBT_AREAS.OUTGOING ) then
-        relativePoint = TOP
-        duration = height * 10
-    elseif ( scrollArea == PSBT_AREAS.STATIC ) then
-        relativePoint = BOTTOM
-        duration = height * 50
-        height = height * -1
+    if ( not area ) then
+        return
     end

-    entry.control:SetAnchor( CENTER, area, relativePoint )
-
+    entry:SetExpire( -1 ) --pending
     entry:SetText( text )
     entry:SetTexture( icon )
-    entry:Play( height, duration )
+
+    area:Push( entry, sticky )
 end

 -- LEAVE ME THE FUARK ALONE
diff --git a/PSBT.txt b/PSBT.txt
index 12472f7..cbec478 100644
--- a/PSBT.txt
+++ b/PSBT.txt
@@ -23,6 +23,8 @@ PSBT_Experience.lua
 PSBT_LowSomething.lua

 ## core
+PSBT_Fifo.lua
 PSBT_Label.lua
+PSBT_ScrollArea.lua
 PSBT.lua
 PSBT.xml
\ No newline at end of file
diff --git a/PSBT.xml b/PSBT.xml
index b312e86..12f499a 100644
--- a/PSBT.xml
+++ b/PSBT.xml
@@ -4,16 +4,13 @@
             <Dimensions x="300" y="24" />

             <Controls>
-                <Texture name="$(parent)_Icon" layer="OVERLAY">
-                    <Dimensions x="24" />
-                    <Anchor point="TOPLEFT" relativeTo="$(parent)" relativePoint="TOPLEFT" />
-                    <Anchor point="BOTTOMLEFT" relativeTo="$(parent)" relativePoint="BOTTOMLEFT" />
-                </Texture>
-
-                <Label name="$(parent)_Name" font="ZoFontGameBold" horizontalAlignment="LEFT" verticalAlignment="CENTER" wrapMode="ELLIPSIS">
-                    <Anchor point="TOPLEFT" relativeTo="$(parent)_Icon" relativePoint="TOPRIGHT" offsetX="2" offsetY="2" />
-                    <Anchor point="BOTTOMRIGHT" relativeTo="$(parent)" relativePoint="BOTTOMRIGHT" offsetX="-2" offsetY="-2" />
+                <Label name="$(parent)_Name" font="ZoFontGameBold" horizontalAlignment="CENTER" verticalAlignment="CENTER" wrapMode="ELLIPSIS">
+                    <AnchorFill/>
                 </Label>
+
+                <Texture name="$(parent)_Icon" layer="OVERLAY" visible="false">
+                    <Dimensions x="24" y="24" />
+                </Texture>
             </Controls>
         </Control>

@@ -27,6 +24,7 @@
                     <Dimensions x="500" y="60" />
                     <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER" offsetX="0" offsetY="450" />
                     <Controls>
+                        <!--<Texture name="$(parent)BG" inherits="ZO_ThinListBgStrip" />-->
                     </Controls>
                 </Control>

@@ -34,6 +32,7 @@
                     <Dimensions x="500" y="60" />
                     <Anchor point="CENTER" relativeTo="$(parent)" relativePoint="CENTER" offsetX="0" offsetY="-300" />
                     <Controls>
+                        <!--<Texture name="$(parent)BG" inherits="ZO_ThinListBgStrip" />-->
                     </Controls>
                 </Control>

@@ -41,6 +40,7 @@
                     <Dimensions x="300" y="450" />
                     <Anchor point="RIGHT" relativeTo="$(parent)" relativePoint="CENTER" offsetX="-300" offsetY="150" />
                     <Controls>
+                        <!--<Texture name="$(parent)BG" inherits="ZO_ThinListBgStrip" />-->
                     </Controls>
                 </Control>

@@ -48,6 +48,7 @@
                     <Dimensions x="300" y="450" />
                     <Anchor point="LEFT" relativeTo="$(parent)" relativePoint="CENTER" offsetX="300" offsetY="150" />
                     <Controls>
+                        <!--<Texture name="$(parent)BG" inherits="ZO_ThinListBgStrip" />-->
                     </Controls>
                 </Control>
             </Controls>
diff --git a/PSBT_Auras.lua b/PSBT_Auras.lua
index 5e2e269..625864e 100644
--- a/PSBT_Auras.lua
+++ b/PSBT_Auras.lua
@@ -22,11 +22,11 @@ function PSBT_Auras:OnEffectChanged( changeType, effectSlot, effectName, unitTag
 end

 function PSBT_Auras:Add( name, iconName )
-    self:NewEvent( PSBT_AREAS.NOTIFICATION, false, iconName, name .. ' Gained' )
+    self:NewEvent( PSBT_AREAS.NOTIFICATION, true, iconName, name .. ' Gained' )
 end

 function PSBT_Auras:Remove( name, iconName )
-    self:NewEvent( PSBT_AREAS.NOTIFICATION, false, iconName, name .. ' Fades' )
+    self:NewEvent( PSBT_AREAS.NOTIFICATION, true, iconName, name .. ' Fades' )
 end

 CBM:RegisterCallback( PSBT_EVENTS.LOADED,
diff --git a/PSBT_Combat.lua b/PSBT_Combat.lua
index b3197e2..5882676 100644
--- a/PSBT_Combat.lua
+++ b/PSBT_Combat.lua
@@ -319,9 +319,9 @@ local combat_events =
 function PSBT_Combat:Initialize( ... )
     PSBT_Module.Initialize( self, ... )

-    self._buffer = ZO_CircularBuffer:New( DEFAULT_MAX_BUFFERED_EVENTS )
-    self._index = 1
-    self._free = nil
+    self._buffer    = ZO_CircularBuffer:New( DEFAULT_MAX_BUFFERED_EVENTS )
+    self._index     = 1
+    self._free      = nil

     for i=1,GetNumAbilities() do
         local name, icon = GetAbilityInfoByIndex( i )
@@ -339,15 +339,13 @@ function PSBT_Combat:OnCombatEvent( ... )

     -- did we get hit or do something?
     if ( IsPlayer( select( 7, ... ), select( 6, ... ) ) or IsPlayer( select( 9, ... ), select( 8, ... ) ) ) then
-
         if ( self._free ) then
             local argCount = select( "#", ... )
-            for i = 1,argCount do
+            for i = 1, argCount do
                 self._free[ i ] = select( i, ... )
             end

-            --clear any additional arguments
-            for i=#self._free,argCount+1,-1 do
+            for i = #self._free,argCount + 1, -1 do
                 self._free[ i ] = nil
             end

@@ -364,18 +362,20 @@ function PSBT_Combat:OnUpdate( frameTime )
     if ( self._index <= 0 ) then
         self._index = 1
     end
-
-    local size = self._buffer:Size()
-    local stop = zo_min( self._index + MAX_EVENTS, size )
-    for i=self._index,stop do
-        local iterator = self._buffer:At( i )
-        if ( iterator ) then
-            self:DispatchEvent( unpack( iterator ) )
+
+    local bufferSize = self._buffer:Size()
+    local endPoint = zo_min( self._index + MAX_EVENTS, bufferSize )
+    for i = self._index, endPoint do
+        local entry = self._buffer:At( i )
+        if ( entry ) then
+            self:DispatchEvent( unpack( entry ) )
         end
-    end
+    end

-    if ( stop <= size ) then
-        self._index = stop + 1
+    if ( endPoint >= bufferSize ) then
+        self._buffer:Clear()
+    else
+        self._index = endPoint + 1
     end
 end

diff --git a/PSBT_Experience.lua b/PSBT_Experience.lua
index 0b80ce9..ca46f19 100644
--- a/PSBT_Experience.lua
+++ b/PSBT_Experience.lua
@@ -25,7 +25,7 @@ function PSBT_Experience:OnXPUpdated( tag, exp, maxExp, reason  )
     self._currentExperience = xp

     if ( gain <= 0 ) then return end
-    self:NewEvent( PSBT_AREAS.INCOMING, false, nil, '+' .. tostring( gain ) .. ' XP' )
+    self:NewEvent( PSBT_AREAS.NOTIFICATION, true, nil, '+' .. tostring( gain ) .. ' XP' )
 end


diff --git a/PSBT_Fifo.lua b/PSBT_Fifo.lua
new file mode 100644
index 0000000..702e7a9
--- /dev/null
+++ b/PSBT_Fifo.lua
@@ -0,0 +1,87 @@
+------
+-- First in First Out container
+-- reference:
+-- https://github.com/daurnimator/lomp2/blob/master/fifo.lua
+------
+
+local select , setmetatable = select , setmetatable
+
+PSBT_Fifo = {}
+local mt =
+{
+    __index = PSBT_Fifo,
+    __newindex = function( f, k, v )
+        if ( type( k ) == 'number' ) then
+            return rawset( f, k, v )
+        end
+    end,
+}
+
+function PSBT_Fifo.New( ... )
+    return setmetatable( { head = 1, tail = select( '#', ... ), ... }, mt )
+end
+
+function PSBT_Fifo:Size()
+    return self.tail - self.head + 1
+end
+
+function PSBT_Fifo:Peek()
+    return self[ self.head ]
+end
+
+function PSBT_Fifo:Push( value )
+    self.tail = self.tail + 1
+    self[ self.tail ] = value
+end
+
+function PSBT_Fifo:Pop()
+    local head, tail = self.head, self.tail
+    if ( head > tail ) then
+        return nil
+    end
+
+    local value = self[ head ]
+    self[ head ] = nil
+    self.head = head + 1
+    return value
+end
+
+function PSBT_Fifo:Remove( index )
+    local head, tail = self.head, self.tail
+
+    if head + index > tail then
+        return
+    end
+
+    local position  = head + index - 1
+    local value     = self[ position ]
+
+    if ( position <= (head + tail) * 0.5 ) then
+        for i = position, head, -1 do
+            self[ i ] = self[ i - 1 ]
+        end
+        self.head = head + 1
+    else
+        for i = position, tail do
+            self[ i ] = self[ i + 1 ]
+        end
+        self.tail = tail - 1
+    end
+
+    return value
+end
+
+local iterator = function( fifo, previous )
+    local i = fifo.head + previous
+    if ( i > fifo.tail ) then
+        return nil
+    end
+
+    return previous + 1, fifo[ i ]
+end
+
+function PSBT_Fifo:Iterator()
+    return iterator, self, 0
+end
+
+mt.__len = PSBT_Fifo.Size
\ No newline at end of file
diff --git a/PSBT_Label.lua b/PSBT_Label.lua
index 2c943d3..7ffd91c 100644
--- a/PSBT_Label.lua
+++ b/PSBT_Label.lua
@@ -14,45 +14,59 @@ function PSBT_Label:Initialize( objectPool )
     self.control = CreateControlFromVirtual( 'PSBT_Label', self.objectPool.control, 'PSBT_Label', self.objectPool:GetNextControlId() )
     self.label   = self.control:GetNamedChild( '_Name' )
     self.icon    = self.control:GetNamedChild( '_Icon' )
+    self.expire  = 0
+    self.moving  = false
+
+    self.control:SetAlpha( 0.0 )
 end

-function PSBT_Label:Finalize()
-    self.label:SetText( '' )
-    self.icon:SetTexture( 0 )
-    self.control:SetHidden( true )
+function PSBT_Label:SetMoving( set )
+    self.moving = set
 end

-function PSBT_Label:SetText( text )
-    self.label:SetText( text )
+function PSBT_Label:IsMoving()
+    return self.moving
 end

-function PSBT_Label:SetTexture( texture )
-    if ( type( texture ) == 'string' ) then
-        self.icon:SetWidth( self.control:GetHeight() )
-        self.icon:SetTexture( texture )
-    else
-        self.icon:SetWidth( 0 )
+function PSBT_Label:SetExpire( expire )
+    self.expire = expire
+end
+
+function PSBT_Label:WillExpire( frameTime )
+    return frameTime > self.expire
+end
+
+function PSBT_Label:IsExpired( frameTime )
+    if ( self.expire == -1 ) then
+        return false
     end
+
+    if ( self.moving ) then
+        return false
+    end
+
+    return frameTime > self.expire
 end

-function PSBT_Label:IsVisible()
-    return self.control:GetAlpha() > 0.001
+function PSBT_Label:Finalize()
+    self:SetText( '' )
+    self:SetTexture( 0 )
+    self:SetExpire( 0 )
+    self:SetMoving( false )
 end

-function PSBT_Label:Play( height, duration )
-    self.control:SetHidden( false )
-    self.control:SetAlpha( 0.01 )
+function PSBT_Label:SetText( text )
+    self.label:SetText( text )

-    local enter = LibAnim:New( self.control )
-    enter:AlphaTo( 1.0, 500, nil, nil, ZO_LinearEase )
-    enter:InsertCallback( function() self:OnEnterComplete( height, duration ) end, 500 )
-    enter:Play()
+    local textWidth = self.label:GetTextDimensions()
+    self.icon:SetAnchor( CENTER, self.control, CENTER, ( textWidth * -0.45 ) - self.icon:GetWidth(), 0 )
 end

-function PSBT_Label:OnEnterComplete( height, duration )
-    local leave = LibAnim:New( self.control )
-    leave:AlphaTo( 0.0, duration, nil, nil, ZO_EaseOutCubic )
-    leave:TranslateTo( 0, height, duration, nil, nil, ZO_EaseOutCubic )
-
-    leave:Play()
+function PSBT_Label:SetTexture( texture )
+    if ( type( texture ) == 'string' ) then
+        self.icon:SetHidden( false )
+        self.icon:SetTexture( texture )
+    else
+        self.icon:SetHidden( true )
+    end
 end
\ No newline at end of file
diff --git a/PSBT_LowSomething.lua b/PSBT_LowSomething.lua
index 4946719..f5e76a6 100644
--- a/PSBT_LowSomething.lua
+++ b/PSBT_LowSomething.lua
@@ -51,7 +51,7 @@ function PSBT_LowSomething:OnPowerUpdate( unit, powerPoolIndex, powerType, power
         string = 'Mount Stamina Low! (|c0CF2B9' .. powerPool .. '|r)'
     end

-    self:NewEvent( PSBT_AREAS.STATIC, false, nil, string )
+    self:NewEvent( PSBT_AREAS.STATIC, true, nil, string )
 end

 CBM:RegisterCallback( PSBT_EVENTS.LOADED,
diff --git a/PSBT_ScrollArea.lua b/PSBT_ScrollArea.lua
new file mode 100644
index 0000000..9fcb65b
--- /dev/null
+++ b/PSBT_ScrollArea.lua
@@ -0,0 +1,140 @@
+local tinsert = table.insert
+local tremove = table.remove
+
+PSBT_ScrollArea                 = ZO_Object:Subclass()
+
+local LibAnim = LibStub( 'LibAnimation-1.0' )
+if ( not LibAnim ) then return end
+
+local NUM_STICKY = 4
+
+function PSBT_ScrollArea:New( ... )
+    local result = ZO_Object.New( self )
+    result:Initialize( ... )
+    return result
+end
+
+function PSBT_ScrollArea:Initialize( super, areaName, anchor )
+    self.control        = super:GetNamedChild( areaName )
+    self._anchor        = anchor
+    self._animHeight    = nil
+    self._newSticky     = false
+    self._sticky        = PSBT_Fifo.New()
+    self._pendingSticky = PSBT_Fifo.New()
+    self._normal        = {}
+    self._pendingNormal = PSBT_Fifo.New()
+
+    if ( anchor == TOP ) then
+        self._animHeight = self.control:GetHeight()
+    else
+        self._animHeight = -1 * self.control:GetHeight()
+    end
+
+    self.control:SetHandler( 'OnUpdate', function( event, ... ) self:OnUpdate( ... ) end )
+end
+
+function PSBT_ScrollArea:Push( entry, sticky )
+    if ( sticky ) then
+        entry.control:SetAnchor( CENTER, self.control, CENTER, 0, self.control:GetHeight() )
+        self._pendingSticky:Push( entry )
+        return
+    end
+
+    entry.control:SetAnchor( CENTER, self.control, self._anchor, 0, 0 )
+    self._pendingNormal:Push( entry )
+end
+
+function PSBT_ScrollArea:OnUpdate( frameTime )
+    while ( self._sticky:Size() > NUM_STICKY ) do
+        local old = self._sticky:Pop()
+        local anim = LibAnim:New( old.control )
+        anim:AlphaTo( 0.0, 200 )
+        anim:Play()
+
+        old:SetMoving( false )
+
+        old:SetExpire( frameTime + 2 )
+        self._newSticky = true
+    end
+
+    repeat
+        local entry = self._sticky:Peek()
+        if ( entry and entry:WillExpire( frameTime + 2 ) ) then
+            local anim = LibAnim:New( entry.control )
+            anim:AlphaTo( 0.0, 200 )
+            anim:TranslateTo( 0, -200, 200 )
+            anim:Play()
+
+            entry:SetMoving( false )
+
+            self._sticky:Pop()
+            self._newSticky = true
+        end
+    until( not entry or not entry:WillExpire( frameTime + 2 )  )
+
+    if ( self._pendingNormal:Size() ) then
+        local newEntry = self._pendingNormal:Pop()
+        if ( newEntry ) then
+            newEntry:SetExpire( frameTime + 5 )
+
+            local anim = LibAnim:New( newEntry.control )
+            anim:AlphaTo( 1.0, 200 )
+            anim:Play()
+
+            tinsert( self._normal, newEntry )
+        end
+    end
+
+    if ( self._pendingSticky:Size() ) then
+        local newEntry = self._pendingSticky:Pop()
+        if ( newEntry ) then
+            newEntry:SetExpire( frameTime + 5 )
+
+            local anim = LibAnim:New( newEntry.control )
+            anim:AlphaTo( 1.0, 200 )
+            anim:Play()
+
+            self._sticky:Push( newEntry )
+            self._newSticky = true
+        end
+    end
+
+    local i = 1
+    while ( i <= #self._normal ) do
+        local entry = self._normal[ i ]
+
+        if ( entry:WillExpire( frameTime + 2 ) ) then
+            local anim = LibAnim:New( entry.control )
+            anim:AlphaTo( 0.0, 200 )
+            anim:Play()
+
+            entry:SetMoving( false )
+
+            tremove( self._normal, i )
+        else
+            if ( not entry:IsMoving() ) then
+                local anim = LibAnim:New( entry.control )
+                anim:TranslateTo( 0, self._animHeight, 3000 )
+                anim:Play()
+
+                entry:SetMoving( true )
+            end
+            i = i + 1
+        end
+    end
+
+    local top = 0
+    for i, entry in self._sticky:Iterator() do
+        if ( self._newSticky ) then
+            local anim = LibAnim:New( entry.control )
+            anim:TranslateTo( 0, top, 200 )
+            anim:Play()
+
+            entry:SetMoving( true )
+
+            top = top + entry.control:GetHeight()
+        end
+    end
+
+    self._newSticky = false
+end
\ No newline at end of file