FCB_PUSH_DIRECTION_DOWN = 1 FCB_PUSH_DIRECTION_UP = -1 function ZO_FadingControlBuffer_GetEntryControl(entry) return entry._control end function ZO_FadingControlBuffer_GetHeaderControl(header) return header._control end function ZO_FadingControlBuffer_GetLineControl(line) return line._control end ZO_FadingControlBuffer = ZO_InitializingCallbackObject:Subclass() local HOLD_TIMES = { 6000, 4000, 2000 } local START_FULLY_OPAQUE = 1 local START_AT_BEGINNING = 2 local START_FADE_IMMEDIATELY = 3 local CONTROL_TYPE_ENTRY = 1 local CONTROL_TYPE_ITEM = 2 -- Note: maxHeight, maxDisplayedEntries, and maxLinesPerEntry can be nil, which means they will be ignored. maxLinesPerEntry does not include the entry header, if any. -- maxHeight can be nil, a number, or a function. If it's nil, it will be ignored. If it's a function, it will be evaluated before being used. -- If narrateAllEntries is true, every active entry will be narrated any time we add or update an entry. Otherwise, we will only narrate the most recent entry added/updated function ZO_FadingControlBuffer:Initialize(control, maxDisplayedEntries, maxHeight, maxLinesPerEntry, fadeAnimationName, translateAnimationName, anchor, narrateAllEntries) self.control = control self.entryPools = {} self.headerPools = {} self.linePools = {} self.queue = {} self.templates = {} self.activeEntries = {} self.maxDisplayedEntries = maxDisplayedEntries self.maxHeight = maxHeight self.maxLinesPerEntry = maxLinesPerEntry self.fadeAnimationName = fadeAnimationName self.translateAnimationName = translateAnimationName self.anchor = anchor or ZO_Anchor:New(TOP) self.currentNumDisplayedEntries = 0 self.currentlyMovingEntries = 0 self.currentlyFadingEntries = 0 self.translateDuration = 500 self.holdTimes = HOLD_TIMES self.fadesInImmediately = false self.additionalVerticalSpacing = 0 self.pushDirection = FCB_PUSH_DIRECTION_DOWN self.holdDisplayingEntries = false self.narrateAllEntries = narrateAllEntries SCREEN_NARRATION_MANAGER:RegisterFadingControlBuffer(self) end function ZO_FadingControlBuffer:SetTranslateDuration(translateDuration) self.translateDuration = translateDuration end function ZO_FadingControlBuffer:SetHoldTimes(...) self.holdTimes = { ... } end function ZO_FadingControlBuffer:SetHoldDisplayingEntries(holdEntries) self.holdDisplayingEntries = holdEntries if(not holdEntries) then if(self:HasQueuedEntry() and not self:HasEntries()) then self:DisplayNextQueuedEntry() end end end function ZO_FadingControlBuffer:SetAdditionalVerticalSpacing(additionalVerticalSpacing) self.additionalVerticalSpacing = additionalVerticalSpacing end function ZO_FadingControlBuffer:SetFadesInImmediately(fadesInImmediately) self.fadesInImmediately = fadesInImmediately end function ZO_FadingControlBuffer:SetPushDirection(pushDirection) self.pushDirection = pushDirection end function ZO_FadingControlBuffer:SetDisplayOlderEntriesFirst(displayOlderEntriesFirst) self.displayOlderEntriesFirst = displayOlderEntriesFirst end --[[ 'templateData' must be a table as follows: {setup = , equalityCheck = , equalitySetup = , headerTemplateName = , headerSetup = , headerEqualityCheck = }) where: setup = function(control, data) equalityCheck = function(oldLines, newLines) -> bool [optional] equalitySetup = function(fadingControlBuffer, oldEntry, newEntry) [optional, but required if equalityCheck is specified] where: fadingControlBuffer is this class object. oldEntry is the currently active entry. newEntry is the entry that has been deemed equal to oldEntry via the equalityCheck function. headerTemplateName [optional, but required if a header is present in an entry] headerSetup = function(control, data) [optional, but required if a header is present in an entry] headerEqualityCheck = function(fadingControlBuffer, oldHeader, newHeader) [optional] headerNarrationText = function(data) [optional, used when we want to specify special behavior for narrating header entries for this template] narrationText = function(data) [optional, used when we want to specify special behavior for narrating line entries for this template] --]] function ZO_FadingControlBuffer:AddTemplate(templateName, templateData) self.templates[templateName] = templateData end function ZO_FadingControlBuffer:HasTemplate(templateName) return self.templates[templateName] ~= nil end --[[ An 'entry' can consist of a header and one or more lines (either are optional). An 'item' is the term used for either a 'header' or a 'line'. 'entry' must be a table as follows: {header = , lines = } where: header contains a table that is passed through as 'data' to the headerSetup function. [optional] lines contains an array of tables that are each passed through as 'data' to the setup function. [optional] --]] function ZO_FadingControlBuffer:AddEntry(templateName, entry) if self.templates[templateName] then local templateData = self.templates[templateName] entry._templateName = templateName if not self:HasQueuedEntry() then if not self:TryHandlingExistingEntry(templateName, templateData, entry) then if self:CanDisplayEntry(templateName, entry) then self:DisplayEntry(templateName, entry) else self:EnqueueEntry(templateName, entry) end end else self:EnqueueEntry(templateName, entry) end end end function ZO_FadingControlBuffer:SetMaxHeight(maxHeight) self.maxHeight = maxHeight end function ZO_FadingControlBuffer:ClearAll() ZO_ClearNumericallyIndexedTable(self.queue) while #self.activeEntries > 0 do local entry = self.activeEntries[#self.activeEntries] entry.m_fadeTimeline:Stop() self:ReleaseControl(entry) end end function ZO_FadingControlBuffer:HasEntries() return #self.activeEntries > 0 end function ZO_FadingControlBuffer:FadeAll() ZO_ClearNumericallyIndexedTable(self.queue) for _, entry in ipairs(self.activeEntries) do self:UpdateFadeOutDelayAndPlayFromOffset(entry, START_FADE_IMMEDIATELY) end end --[[ Private API ]]-- function ZO_FadingControlBuffer:GetEntryIndex(entryControl) for i, entry in ipairs(self.activeEntries) do if entry == entryControl then return i end end end function ZO_FadingControlBuffer:GetLineIndex(entryControl, lineControl) for i, entry in ipairs(entryControl.activeLines) do if entry == lineControl then return i end end end function ZO_FadingControlBuffer:TryHandlingExistingEntry(templateName, templateData, entry) if not templateData.equalityCheck then return false end local handled = false for _, activeEntryControl in ipairs(self.activeEntries) do local activeEntry = activeEntryControl.entry if templateName == activeEntry._templateName then -- Are both headers equal? if activeEntry.header and entry.header and templateData.headerEqualityCheck and templateData.headerEqualityCheck(activeEntry.header, entry.header) then if entry.lines then -- Are all lines equal? if activeEntry.lines and templateData.equalityCheck(activeEntry.lines, entry.lines) then templateData.equalitySetup(self, activeEntry, entry) self.lastHandledEntry = activeEntry self:FireCallbacks("OnEntryUpdated") activeEntryControl.setupTime = GetFrameTimeMilliseconds() handled = true else -- The headers are equal, but the lines are not equal. Add the lines to the header if there is space. if self:CanDisplayEntry(templateName, entry, activeEntryControl) then self:AddLinesToExistingEntry(activeEntryControl, entry.lines, templateData.displayOlderLinesFirst) handled = true end end end -- Headers are either not equal or not present. Are all lines equal? elseif activeEntry.lines and entry.lines and templateData.equalityCheck(activeEntry.lines, entry.lines) then templateData.equalitySetup(self, activeEntry, entry) self.lastHandledEntry = activeEntry self:FireCallbacks("OnEntryUpdated") handled = true end if handled then -- Reset the fade timer. activeEntryControl.m_fadeTimeline:Stop() self:UpdateFadeOutDelayAndPlayFromOffset(activeEntryControl, START_FULLY_OPAQUE) return true end end end return false end function ZO_FadingControlBuffer:GetEntryHoldTime(alertEntry) local entryIndex = self:GetEntryIndex(alertEntry) return self.holdTimes[entryIndex] or self.holdTimes[#self.holdTimes] end function ZO_FadingControlBuffer:UpdateFadeOutDelayAndPlayFromOffset(alertEntry, adjustType) local fadeTimeline = alertEntry.m_fadeTimeline local fadeInAnimation = fadeTimeline:GetAnimation(1) local fadeInDelay = fadeTimeline:GetAnimationOffset(fadeInAnimation) local initialOffset if adjustType == START_FULLY_OPAQUE or adjustType == START_FADE_IMMEDIATELY then initialOffset = fadeInAnimation:GetDuration() elseif adjustType == START_AT_BEGINNING then initialOffset = 0 end local holdTime = initialOffset + fadeInDelay if adjustType ~= START_FADE_IMMEDIATELY then holdTime = holdTime + self:GetEntryHoldTime(alertEntry) end fadeTimeline:SetAnimationOffset(fadeTimeline:GetAnimation(2), holdTime) if(adjustType == START_FULLY_OPAQUE or adjustType == START_FADE_IMMEDIATELY) then -- If a playback offset of > 0 is specified we essentially want to start from the fully faded in state... -- That state is defined as however long the duration and delay of the initial animation fadeTimeline:PlayFromStart(initialOffset + fadeInDelay) else fadeTimeline:PlayFromStart() end end function ZO_FadingControlBuffer:UpdateFadeInDelay(alertEntry, fadeInDelayFactor) local fadeAnimation = alertEntry.m_fadeTimeline:GetFirstAnimation() local fadeStartDelay = self.translateDuration * fadeInDelayFactor alertEntry.m_fadeTimeline:SetAnimationOffset(fadeAnimation, fadeStartDelay) end local function ReleaseAllChildren(control) local childCount = control:GetNumChildren() for i = 1, childCount do local child = control:GetChild(i) if child.pool then -- Could evaluate to false if the child is not managed by this class. child.pool:ReleaseObject(child.key) end end end function ZO_FadingControlBuffer:ReleaseControl(alertControl) if alertControl.type == CONTROL_TYPE_ENTRY then self.currentNumDisplayedEntries = self.currentNumDisplayedEntries - 1 end ReleaseAllChildren(alertControl) alertControl.pool:ReleaseObject(alertControl.key) if alertControl.type == CONTROL_TYPE_ENTRY then local entryIndex = self:GetEntryIndex(alertControl) if entryIndex then table.remove(self.activeEntries, entryIndex) end end self:TryCondenseBuffer() self:DisplayNextQueuedEntry() if not self:HasQueuedEntry() and not self:HasEntries() then self:FireCallbacks("LastControlReleased") end end function ZO_FadingControlBuffer:CalcHeightOfEntryAfterPrepending(entryControl, newLines) local entry = entryControl.entry -- Figure out how many lines we are going to end up with. local totalLines = #entry.lines + #newLines if self.maxLinesPerEntry then totalLines = zo_min(totalLines, self.maxLinesPerEntry) end -- Calculate the height for the given number of lines. local height = self:CalcHeightOfEntry(entry._templateName, entry, totalLines) return height end function ZO_FadingControlBuffer:CalcHeightOfEntry(templateName, entry, maxLines) local entryControl = self:AcquireEntryObject(templateName) local templateData = self.templates[templateName] local offsetY = 0 local HEADER_ITEM = true if entry.header then offsetY = self:CalculateItemHeight(templateData.headerTemplateName, self.headerPools, entryControl, offsetY, HEADER_ITEM) end local lines = entry.lines if lines or maxLines then local linePools = self.linePools local numLines = maxLines or #lines for i = 1, numLines do offsetY = self:CalculateItemHeight(templateName, linePools, entryControl, offsetY) end end ReleaseAllChildren(entryControl) entryControl.pool:ReleaseObject(entryControl.key) return offsetY end function ZO_FadingControlBuffer:CalcHeightOfActiveEntries() local height = 0 for i = 1, #self.activeEntries do height = height + self.activeEntries[i].height end return height end function ZO_FadingControlBuffer:CanDisplayEntry(templateName, entry, prependToEntryControl) local notEnoughFreeEntries = false if self.maxDisplayedEntries then notEnoughFreeEntries = self.currentNumDisplayedEntries >= self.maxDisplayedEntries end if not prependToEntryControl and notEnoughFreeEntries then -- If the FadingControlBuffer does NOT fade in immediately and there isn't space to add a new entry, the new entry will be queued if self.fadesInImmediately then -- If the FadingControlBuffer DOES fade in immediately but there isn't space to add a new entry, try to kick old entries off to make space if not self:TryRemoveLastEntry() then -- If space can not be made (all entries are too recent), queue the next entry return false end else return false end end local currentHeight = self:CalcHeightOfActiveEntries() local additionalHeight if prependToEntryControl then additionalHeight = self:CalcHeightOfEntryAfterPrepending(prependToEntryControl, entry.lines) -- Subtract the height of the control we're prepending to, since the latter will be contained in self.activeEntries. currentHeight = currentHeight - prependToEntryControl.height else additionalHeight = self:CalcHeightOfEntry(templateName, entry) end local totalHeight = currentHeight + additionalHeight local maxHeight = self.maxHeight if maxHeight then if (type(maxHeight) == "function") then maxHeight = maxHeight() end if totalHeight > maxHeight then return false end end if self.fadesInImmediately then return not self.holdDisplayingEntries end return self.currentlyMovingEntries == 0 and self.currentlyFadingEntries == 0 and not self.holdDisplayingEntries end function ZO_FadingControlBuffer:HasQueuedEntry() return #self.queue > 0 end function ZO_FadingControlBuffer:EnqueueEntry(templateName, entry) table.insert(self.queue, entry) end function ZO_FadingControlBuffer:DisplayNextQueuedEntry() while self:HasQueuedEntry() do local queuedEntry = self.queue[1] local templateName = queuedEntry._templateName local templateData = self.templates[templateName] local handled = self:TryHandlingExistingEntry(templateName, templateData, queuedEntry) if not handled then if self:CanDisplayEntry(templateName, queuedEntry) then self:DisplayEntry(templateName, queuedEntry) handled = true end end if handled then table.remove(self.queue, 1) end if not self.fadesInImmediately or not handled then break end end end local function CalculateControlHeight(control) local height if control.GetTextHeight then height = control:GetTextHeight() if (height == 0) and control.GetFontHeight then -- Could happen if label control has no text. height = control:GetFontHeight() end else height = control:GetHeight() end return height end do local function Reset(object) object.m_translateTimeline:Stop() object.m_fadeTimeline:Stop() object:SetAlpha(0) object:SetHidden(true) object.key = nil if object.activeLines then ZO_ClearNumericallyIndexedTable(object.activeLines) end -- This isn't strictly necessary, but it keeps things clean and avoids potentially nasty bugs. local entry = object.entry if entry then entry._control = nil if entry.header then entry.header._control = nil end local lines = object.entry.lines if lines then for i = 1, #lines do lines[i]._control = nil end end object.entry = nil end end local function OnFadeStop(timeline, completedPlayback) if completedPlayback then local fadingControlBuffer = timeline.m_control.fadingControlBuffer fadingControlBuffer:ReleaseControl(timeline.m_control) end end local function OnTranslateStop(timeline) local fadingControlBuffer = timeline.m_control.fadingControlBuffer fadingControlBuffer.currentlyMovingEntries = fadingControlBuffer.currentlyMovingEntries - 1 end local function OnFadeOutAnimationStart(animation, control) local fadingControlBuffer = control.fadingControlBuffer fadingControlBuffer.currentlyFadingEntries = fadingControlBuffer.currentlyFadingEntries + 1 end local function OnFadeOutAnimationStop(animation, control) local fadingControlBuffer = control.fadingControlBuffer fadingControlBuffer.currentlyFadingEntries = fadingControlBuffer.currentlyFadingEntries - 1 end local function OnFadeInAnimationStop(animation, control) if control.fadeInHold then control.m_fadeTimeline:Stop() control.fadeInHold = false end end local function SetupControl(control, fadeAnimationName, translateAnimationName) local fadeTimeline = ANIMATION_MANAGER:CreateTimelineFromVirtual(fadeAnimationName, control) local translateTimeline = ANIMATION_MANAGER:CreateTimelineFromVirtual(translateAnimationName, control) fadeTimeline.m_control = control fadeTimeline:SetHandler("OnStop", OnFadeStop) fadeTimeline:GetAnimation(1):SetHandler("OnStop", OnFadeInAnimationStop) fadeTimeline:GetAnimation(2):SetHandler("OnPlay", OnFadeOutAnimationStart) fadeTimeline:GetAnimation(2):SetHandler("OnStop", OnFadeOutAnimationStop) translateTimeline.m_control = control translateTimeline:SetHandler("OnStop", OnTranslateStop) control.m_fadeTimeline = fadeTimeline control.m_translateTimeline = translateTimeline return control end function ZO_FadingControlBuffer:AcquireEntryObject(templateName) local pool = self.entryPools[templateName] if not pool then local function Factory(pool) local parent = self.control local name = parent:GetName() .. templateName .. "Entry" .. pool:GetNextControlId() local control = CreateControl(name, parent, CT_CONTROL) control:SetResizeToFitDescendents(true) control.pool = pool control.activeLines = {} control.type = CONTROL_TYPE_ENTRY return SetupControl(control, self.fadeAnimationName, self.translateAnimationName) end pool = ZO_ObjectPool:New(Factory, Reset) self.entryPools[templateName] = pool end local control, key = pool:AcquireObject() control:SetHidden(false) control.key = key control.fadingControlBuffer = self ZO_ClearNumericallyIndexedTable(control.activeLines) return control end local function AdjustAnchors(control, newRelativeTo, newOffsetY) for i = 0, MAX_ANCHORS - 1 do local isValid, point, _, relPoint, offsetX = control:GetAnchor(i) if isValid then control:SetAnchor(point, newRelativeTo, relPoint, offsetX, control.anchorOffsetY[i] + newOffsetY) end end end function ZO_FadingControlBuffer:AcquireItemObject(name, templateName, pools, parent, offsetY) local pool = pools[templateName] if not pool then local function Factory(pool) local name = templateName .. name local control = ZO_ObjectPool_CreateNamedControl(name, templateName, pool, pool.parent) control.pool = pool control.type = CONTROL_TYPE_ITEM -- Save the anchor Y offsets so we can preserve them when adjusting anchors later. control.anchorOffsetY = {nil, nil} -- Pre-allocate space for two anchors. for i = 0, MAX_ANCHORS - 1 do local isValid, _, _, _, _, offsetY = control:GetAnchor(i) if isValid then control.anchorOffsetY[i] = offsetY end end return SetupControl(control, self.fadeAnimationName, self.translateAnimationName) end pool = ZO_ObjectPool:New(Factory, Reset) pools[templateName] = pool end -- We set the parent before acquiring the object so we can reuse the same factory function across multiple parents. -- To keep the item control name simple, it must always be created as a child of an entry control. pool.parent = parent local control, key = pool:AcquireObject() control:SetParent(parent) control:SetHidden(false) control.key = key control.fadingControlBuffer = self return control end function ZO_FadingControlBuffer:CalculateItemHeight(templateName, pools, parent, offsetY, isHeader) local control = self:AcquireItemObject(isHeader and "Header" or "Line", templateName, pools, parent, offsetY) local height = CalculateControlHeight(control) return (offsetY + height) end local HEADER_INDEX = 1 local PRESERVE_FADE = true function ZO_FadingControlBuffer:SetupItem(hasHeader, item, templateName, setupFn, pools, parent, offsetY, isHeader, shouldAppend) if item then local control = self:AcquireItemObject(isHeader and "Header" or "Line", templateName, pools, parent, offsetY) setupFn(control, item) local height = CalculateControlHeight(control) offsetY = offsetY + height item._control = control control.height = height control.targetBottomY = height control.fadeInHold = true control:SetAlpha(0) local insertionIndex if shouldAppend then -- append after existing lines table.insert(parent.activeLines, control) elseif isHeader or not hasHeader then -- prepend to header location table.insert(parent.activeLines, HEADER_INDEX, control) else -- insert after header, before existing lines table.insert(parent.activeLines, HEADER_INDEX + 1, control) end AdjustAnchors(control, parent, 0) self:MoveEntriesOrLines(parent.activeLines, PRESERVE_FADE) local fadeInDelayFactor = .25 self:UpdateFadeInDelay(control, fadeInDelayFactor) self:UpdateFadeOutDelayAndPlayFromOffset(control, START_AT_BEGINNING) end return offsetY end end function ZO_FadingControlBuffer:AddLinesToExistingEntry(entryControl, newLines, shouldAppend) local entry = entryControl.entry local currentLines = entry.lines local offsetY = 0 -- Release lines until we are within our line limit. if self.maxLinesPerEntry then local numCurrentLines = #currentLines local totalLines = numCurrentLines + #newLines if totalLines > self.maxLinesPerEntry then local numLinesToRemove = zo_min(totalLines - self.maxLinesPerEntry, numCurrentLines) -- if appending, remove from the front. If prepending, remove from the back local startIndex = shouldAppend and numLinesToRemove or numCurrentLines for i = startIndex, startIndex - numLinesToRemove + 1, -1 do local line = table.remove(currentLines, i) local lineControl = line._control offsetY = offsetY - CalculateControlHeight(lineControl) local lineIndex = self:GetLineIndex(entryControl, lineControl) table.remove(entryControl.activeLines, lineIndex) lineControl.fadeInHold = false self:UpdateFadeOutDelayAndPlayFromOffset(lineControl, START_FADE_IMMEDIATELY) end end end local templateName = entry._templateName local templateData = self.templates[templateName] local NOT_HEADER = false local hasHeader = (entry.header ~= nil) local linePools = self.linePools if shouldAppend then for _, line in ZO_NumericallyIndexedTableIterator(newLines) do table.insert(currentLines, line) offsetY = self:SetupItem(hasHeader, line, templateName, templateData.setup, linePools, entryControl, offsetY, NOT_HEADER, shouldAppend) end else for _, line in ZO_NumericallyIndexedTableReverseIterator(newLines) do table.insert(currentLines, 1, line) offsetY = self:SetupItem(hasHeader, line, templateName, templateData.setup, linePools, entryControl, offsetY, NOT_HEADER, shouldAppend) end end entryControl.height = entryControl.height + offsetY entryControl.targetBottomY = entryControl.targetBottomY + offsetY entryControl.setupTime = GetFrameTimeMilliseconds() self.lastHandledEntry = entry self:FireCallbacks("OnEntryUpdated") self:MoveEntriesOrLines(self.activeEntries) end do local MIN_DISPLAY_TIME_MS = 500 -- Push the oldest entry off the screen to make room for the new one, even if the old one hasn't timed out function ZO_FadingControlBuffer:TryRemoveLastEntry() local entries = self.activeEntries local currentTime = GetFrameTimeMilliseconds() local lastEntryControl = entries[#entries] local timeDisplayed = currentTime - lastEntryControl.setupTime if timeDisplayed < MIN_DISPLAY_TIME_MS then return false end table.remove(entries, #entries) lastEntryControl.fadeInHold = false self:UpdateFadeOutDelayAndPlayFromOffset(lastEntryControl, START_FADE_IMMEDIATELY) return true end end function ZO_FadingControlBuffer:DisplayEntry(templateName, entry) local entryControl = self:AcquireEntryObject(templateName) local templateData = self.templates[templateName] self.lastHandledEntry = entry self:FireCallbacks("OnEntryUpdated") -- Call the setup function for the header and each of the lines. local offsetY = 0 local HEADER_ITEM = true offsetY = self:SetupItem(HEADER_ITEM, entry.header, templateData.headerTemplateName, templateData.headerSetup, self.headerPools, entryControl, offsetY, HEADER_ITEM) local lines = entry.lines if lines then local NOT_HEADER = false local shouldAppend = templateData.displayOlderLinesFirst local hasHeader = (entry.header ~= nil) local linePools = self.linePools local iterator = shouldAppend and ZO_NumericallyIndexedTableIterator or ZO_NumericallyIndexedTableReverseIterator for _, line in iterator(lines) do offsetY = self:SetupItem(hasHeader, line, templateName, templateData.setup, linePools, entryControl, offsetY, NOT_HEADER, shouldAppend) end end entry._control = entryControl entryControl.entry = entry entryControl.height = offsetY + self.additionalVerticalSpacing entryControl.targetBottomY = entryControl.height entryControl.setupTime = GetFrameTimeMilliseconds() self.anchor:Set(entryControl) entryControl:SetAlpha(0) local needsMove = self.activeEntries[1] ~= nil if self.displayOlderEntriesFirst then -- append new entries to end of list table.insert(self.activeEntries, entryControl) else -- prepend new entries to beginning of list, and move rest of list over table.insert(self.activeEntries, 1, entryControl) end self.currentNumDisplayedEntries = self.currentNumDisplayedEntries + 1 if needsMove then self:MoveEntriesOrLines(self.activeEntries) end local fadeInDelayFactor = 0 if self.fadesInImmediately then fadeInDelayFactor = .25 elseif needsMove then fadeInDelayFactor = .9 end self:UpdateFadeInDelay(entryControl, fadeInDelayFactor) self:UpdateFadeOutDelayAndPlayFromOffset(entryControl, START_AT_BEGINNING) end function ZO_FadingControlBuffer:MoveEntriesOrLines(entriesOrLines, preserveFade) local targetBottomY = 0 for i = 1, #entriesOrLines do local control = entriesOrLines[i] local topY = control.targetBottomY - control.height if targetBottomY > topY then targetBottomY = self:MoveEntriesOrLinesCalculations(control, targetBottomY, topY, preserveFade) else targetBottomY = zo_max(targetBottomY, control.targetBottomY) end end end local PRESERVE_FADE = true function ZO_FadingControlBuffer:TryCondenseBuffer() local entriesOrLines = self.activeEntries local targetBottomY = 0 for _, control in ipairs(self.activeEntries) do local topY = control.targetBottomY - control.height if targetBottomY < topY + self.additionalVerticalSpacing then targetBottomY = self:MoveEntriesOrLinesCalculations(control, targetBottomY, topY, PRESERVE_FADE) else targetBottomY = zo_max(targetBottomY, control.targetBottomY) end end end function ZO_FadingControlBuffer:MoveEntriesOrLinesCalculations(control, targetBottomY, topY, preserveFade) local neededY = targetBottomY - topY local heightAdjustment = neededY targetBottomY = control.targetBottomY + neededY control.targetBottomY = targetBottomY local translateAnimation = control.m_translateTimeline:GetFirstAnimation() if translateAnimation:IsPlaying() then local _, existingHeightDelta = translateAnimation:GetTranslateDeltas() local easingFunction = translateAnimation:GetEasingFunction() local progress = easingFunction and easingFunction(control.m_translateTimeline:GetProgress()) or control.m_translateTimeline:GetProgress() heightAdjustment = heightAdjustment + existingHeightDelta * (1.0 - progress) * self.pushDirection control.m_translateTimeline:Stop() end translateAnimation:SetTranslateDeltas(0, heightAdjustment * self.pushDirection) translateAnimation:SetDuration(self.translateDuration) control.m_translateTimeline:PlayFromStart() self.currentlyMovingEntries = self.currentlyMovingEntries + 1 if not self.fadesInImmediately and not preserveFade then control.m_fadeTimeline:Stop() control:SetAlpha(1) self:UpdateFadeOutDelayAndPlayFromOffset(control, START_FULLY_OPAQUE) end return targetBottomY end --Get the narration text for the given entry data function ZO_FadingControlBuffer:GetEntryNarrationText(entryData) local narrations = {} local templateData = self.templates[entryData._templateName] if templateData then --Generate the narration for the header if there is one if entryData.header then if templateData.headerNarrationText then ZO_AppendNarration(narrations, templateData.headerNarrationText(entryData)) else ZO_AppendNarration(narrations, SCREEN_NARRATION_MANAGER:CreateNarratableObject(entryData.header.text)) end end --Iterate through each line of the entry and get the narration local lines = entryData.lines if lines then local narrationFunction = templateData.narrationText for _, line in ipairs(lines) do if narrationFunction then ZO_AppendNarration(narrations, narrationFunction(line)) else ZO_AppendNarration(narrations, SCREEN_NARRATION_MANAGER:CreateNarratableObject(line.text)) end end end end return narrations end --Gets the narration text for the fading control buffer function ZO_FadingControlBuffer:GetNarrationText(entryData) local narrations = {} --If we want to narrate all entries, loop through each active entry and add it to the narration if self.narrateAllEntries then for _, entryControl in ipairs(self.activeEntries) do local entryData = entryControl.entry if entryData then ZO_AppendNarration(narrations, self:GetEntryNarrationText(entryData)) end end else --If we are not narrating all entries at once, just grab the narration for the entry that was most recently updated local entryData = self.lastHandledEntry if entryData then ZO_AppendNarration(narrations, self:GetEntryNarrationText(entryData)) end end return narrations end