Mercurial > wow > buffalo2
view ObjectiveFrame.lua @ 19:605e8f0e46db
ObjectiveCore / Style / Events / Frame
- polishing the execution path for better performance
- make use of the Blizzard_ObjectiveTracker bitfield values to ensure compatibility in possible secure hooks
- avoid full updates when possible (using said bitfield values to indicate targeted sections)
- extreme streamlining of event handling layout: specific reason updates are invoked from API hooks; broader updates are invoked by when the event listener catches something vague like 'QUEST_LOG_UPDATE'
author | Nenue |
---|---|
date | Wed, 06 Apr 2016 07:38:35 -0400 |
parents | d1812fb10ae6 |
children | d5ee940de273 |
line wrap: on
line source
--- ${PACKAGE_NAME} -- @file-author@ -- @project-revision@ @project-hash@ -- @file-revision@ @file-hash@ -- Created: 3/30/2016 12:49 AM local B = select(2,...).frame local mod = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame') local ipairs, max, min, unpack, floor, pairs, tostring, type, band = ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band local IsResting, UnitXP, UnitXPMax, GetXPExhaustion = IsResting, UnitXP, UnitXPMax, GetXPExhaustion local UnitLevel, IsQuestWatched, UIParent = UnitLevel, IsQuestWatched, UIParent local DefaultTracker, Quest, Bonus, Cheevs = mod.DefaultTracker, mod.Quest, mod.Bonus, mod.Cheevs local CreateFrame = CreateFrame local print = B.print('ObjWrapper') local unitLevel = 1 local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_REASON -------------------------------------------------------------------- --- Global frame layout -------------------------------------------------------------------- --- Upvalues local Wrapper = VeneerObjectiveWrapper local Scroller = Wrapper.scrollArea local Scroll = VeneerObjectiveScroll local orderedHandlers = mod.orderedHandlers local orderedNames = mod.orderedNames --- Temp values set during updates local wrapperWidth, wrapperHeight local scrollWidth, scrollHeight local previousBlock local currentBlock --- todo: source these from config local itemButtonSize, itemButtonSpacing = 36, 1 local titleFont, textFont = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Regular.ttf]] local titleSize, textSize = 15, 15 local titleOutline, textOutline = "OUTLINE", "OUTLINE" local titleSpacing, textSpacing = 4, 3 local textIndent = 5 local wrapperMaxWidth, wrapperMaxHeight = 270, 490 -- these are the hard bounds, actual *Height variables are changed local wrapperHeadFont, wrapperHeadSize, wrapperHeadOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 16, 'NONE' local headerFont, headerSize, headerHeight = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 18, 24 local headerOutline, headerColor, headerSpacing = 'OUTLINE', {1,1,1,1}, 2 local wrapperPosition = {'RIGHT', UIParent, 'RIGHT', -84, 0} local band = bit.band local currentPosition, anchorFrame, anchorPoint function mod:Update (reason, dataID) local updateWrapper = 0 local hasStuff local insertingStuff reason = reason or OBJECTIVE_TRACKER_UPDATE_REASON currentPosition = 0 anchorPoint = 'TOPLEFT' anchorFrame = Scroll local wrapperHeight = 0 for id, handler in pairs(mod.orderedHandlers) do local frame = handler.frame print(format('|cFF00FFFFbitcheck (%04X vs %04x+%04x):|r', reason, handler.updateReasonModule, handler.updateReasonEvents), band(reason, handler.updateReasonModule + handler.updateReasonEvents)) if band(reason, handler.updateReasonModule + handler.updateReasonEvents) > 0 then handler:Update(reason, dataID) print('|cFF00FF00'..id..'|r', handler.displayName, 'count:', handler.numWatched) insertingStuff = true else print('|cFFFF0088'..id..'|r', 'no reason') end if handler.numWatched >= 1 then hasStuff = true currentPosition = currentPosition + 1 frame:SetParent(Scroll) frame:SetPoint('TOPLEFT', anchorFrame, anchorPoint, 0, 0) print(' |cFF00BBFFpinning to', anchorFrame:GetName(), anchorPoint) anchorFrame = handler.frame anchorPoint = 'BOTTOMLEFT' print('current frame height:', frame.height) wrapperHeight = wrapperHeight + frame.height print('|cFFFF0088total height:', wrapperHeight) else handler.frame:Hide() end end if hasStuff or insertingStuff then print('updating height to', wrapperHeight) Wrapper:SetHeight(wrapperHeight) Scroller:SetHeight(wrapperHeight) Scroll:SetHeight(wrapperHeight) print('|cFFFF8800Wrapper:', Wrapper:GetSize()) for i = 1, Wrapper:GetNumPoints() do print(' ', Wrapper:GetPoint(i)) end print(' |cFF00FFFFScroller:', Scroller:GetSize()) for i = 1, Scroller:GetNumPoints() do print(' ', Scroller:GetPoint(i)) end print(' |cFF00FFFFScroll:', Scroll:GetSize()) for i = 1, Scroll:GetNumPoints() do print(' ', Scroll:GetPoint(i)) end Wrapper:Show() Scroller:Show() Scroll:Show() end end DefaultTracker.GetBlock = function(handler, blockIndex) local block = handler.usedBlocks[blockIndex] if not handler.usedBlocks[blockIndex] then if #handler.freeBlocks >= 1 then block = handler.freeBlocks[#handler.freeBlocks] handler.freeBlocks[#handler.freeBlocks] = nil else block = CreateFrame('Frame', 'Veneer'..tostring(handler)..'Block'..blockIndex, Scroll, 'VeneerTrackerBlock') block.SetStyle = mod.SetBlockStyle block.Select = handler.Select block.Open = handler.Open block.Remove = handler.Remove block.Link = handler.Link block:SetScript('OnMouseUp', handler.OnMouseUp) block:SetScript('OnMouseDown', handler.OnMouseDown) block.attachmentHeight = 0 block:ClearAllPoints() end handler.usedBlocks[blockIndex] = block end return handler.usedBlocks[blockIndex] end DefaultTracker.Update = function (self, reason, dataID) local tracker = self.frame print('|cFFFF4400'..tracker:GetName().. '|r:Update()') local blockIndex = 0 local trackerHeight = 0 self.currentAnchor = tracker.titlebg local numWatched = self:GetNumWatched() local numBlocks = self.numBlocks local actualBlocks = 0 for watchIndex = 1, 25 do blockIndex = blockIndex + 1 if watchIndex <= numWatched then local info = self:GetInfo(watchIndex) if info then local currentBlock = self:UpdateBlock(blockIndex, info) self.currentAnchor = currentBlock print(' |cFFFFFF00'..watchIndex..'|r', '|cFF00FF00'..currentBlock:GetName()..'|r', currentBlock.height) print(currentBlock:IsVisible()) print(currentBlock:GetPoint(1)) trackerHeight = trackerHeight + currentBlock.height numBlocks = max(numBlocks, watchIndex) actualBlocks = actualBlocks + 1 else print(' |cFFFF0000Failed to draw info for index #'..watchIndex) end elseif watchIndex <= numBlocks then local used = self.usedBlocks local free = self.freeBlocks print('clean up dead quest block') if used[blockIndex] then used[blockIndex]:Hide() used[blockIndex]:ClearAllPoints() free[#free+1]= used[blockIndex] used[blockIndex] = nil end else print(' |cFFFF9900END|r @', blockIndex) break -- done with quest stuff end end self.numWatched = numWatched self.numBlocks = numBlocks self.actualBlocks = actualBlocks self:Report() tracker.previousHeight = tracker.height if numBlocks >= 1 then previousBlock = nil tracker.height = trackerHeight + tracker.titlebg:GetHeight() tracker:SetHeight(tracker.height) tracker:Show() print(tracker.height) else tracker.height = 0 tracker:Hide() end return tracker.numWatched, tracker.numAll end --- Updates the selected block frame to display the given info batch -- If `previousBlock` is set, it will attempt to anchor to that -- @param blockNum the ordered block to be updated, not a watchIndex value -- @param info the reference returned by the GetXInfo functions -- REMEMBER: t.info and questData[questID] are the same table DefaultTracker.UpdateBlock = function (self, blockIndex, info) local print = B.print('BlockParse') print(' Read list item |cFF00FFFF'..blockIndex..'|r') if not blockIndex or not info then return end local frame = self.frame local t = self:GetBlock(blockIndex) t.handler = self t.info = info t.mainStyle = info.mainStyle or 'Normal' t.subStyle = info.subStyle info.blockIndex = blockIndex if info.questID then self.QuestBlock[info.questID] = t end if info.questLogIndex then self.LogBlock[info.questLogIndex] = t end if info.watchIndex then self.WatchBlock[info.watchIndex] = t end self.BlockInfo[blockIndex] = info t.attachmentHeight = 0 if info.isComplete then if mod.AutoQuest.Info[info.questID] then t.status:SetText('(Click to Complete)') t.status:Show() else t.status:SetText('Ready to turn in') t.status:Show() end elseif info.completed then t.status:SetText(nil) t.status:Hide() elseif info.numObjectives >= 1 then t.attachmentHeight = 0 t.status:Show() print(' lines to parse:', info.numObjectives) local text = '' mod.UpdateObjectives(t, info, text) print(' |cFF00FF00attachment', t.attachmentHeight) elseif info.description then t.status:SetText(info.description) t.status:Show() else t.status:SetText(nil) t.status:Show() end t.title:SetText(info.title) if info.specialItem and not info.itemButton then print(' - |cFF00FFFFgenerating item button for info set') info.itemButton = mod.SetItemButton(t, info) else --info.itemButton = nil end if self.currentAnchor then t:SetPoint('TOPLEFT', self.currentAnchor, 'BOTTOMLEFT', 0, 0) t:SetPoint('RIGHT', frame,'RIGHT', 0, 0) print(' anchor to|cFF0088FF', self.currentAnchor:GetName()) end --- metrics are calculated in SetStyle t:SetStyle('TrackerBlock', self.name, t.mainStyle, t.subStyle) print(' |cFFFFFF00height|r', t.height) t:Show() print(' |cFF00FFFF)|r -> ', t, t:GetHeight()) local tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', t, 'TOPRIGHT' if info.rewardInfo then print('has immediate reward') if info.rewardInfo[1].type == 'currency' or info.rewardInfo[1].type == 'item' then t.icon:Show() t.iconLabel:SetText(info.rewardInfo[1].count) t.icon:SetSize(t.height, t.height) t.icon:SetPoint(tagPoint, tagAnchor, tagRelative, 0, 0) tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', t.icon, 'TOPLEFT' t.icon:SetTexture(info.rewardInfo[1].texture) end else t.icon:Hide() end if info.selected then t.SelectionOverlay:Show() else t.SelectionOverlay:Hide() end if info.frequencyTag then t.FrequencyTag:SetTexCoord(unpack(info.frequencyTag)) t.FrequencyTag:Show() t.FrequencyTag:SetPoint(tagPoint, tagAnchor, tagRelative, 0, 0) tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', t.FrequencyTag, 'TOPLEFT' else t.FrequencyTag:Hide() end if info.typeTag then t.TypeTag:SetTexCoord(unpack(info.typeTag)) t.TypeTag:Show() t.TypeTag:SetPoint(tagPoint, tagAnchor, tagRelative, 0, 0) tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', t.TypeTag, 'TOPLEFT' else t.TypeTag:Hide() end if info.completionTag then t.CompletionTag:SetTexCoord(unpack(info.completionTag)) t.CompletionTag:Show() t.CompletionTag:SetPoint(tagPoint, tagAnchor, tagRelative, 0, 0) tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', t.CompletionTag, 'TOPLEFT' else t.CompletionTag:Hide() end --[[if Devian and Devian.InWorkspace() then t.debugText:Show() t.debugText:SetText(tostring(blockIndex) .. '\n' .. tostring(info.itemButton and info.itemButton:GetName()) .. "\n" .. (tostring(t.mainStyle) .. '/' .. tostring(t.subStyle))) end]] return t end mod.UpdateObjectives = function(block, info, text) local print = B.print('BlockLine') print(' |cFF00FF00objective updates for', block:GetName()) local attachmentHeight = block.attachmentHeight print(attachmentHeight) if info.description and not(info.earnedBy or info.isComplete) then print(' -- has description text:', select('#', info.description), info.description) text = info.description end local completionScore, completionMax = 0, 0 for i, line in ipairs(info.objectives) do print(attachmentHeight) print(' |cFF88FF00objective', i) block.handler.ParseObjective(line, info) if line.title then info.title = line.title line.title = nil end if line.widget then if attachmentHeight == 0 then attachmentHeight = (block.status.spacing or block.status:GetSpacing()) * 2 --print(attachmentHeight) end line.widget:Show() line.widget:SetParent(block) line.widget:SetPoint('TOPLEFT', block.status, 'BOTTOMLEFT', 0, -attachmentHeight ) print(' has a widget, height is', line.widget.height) attachmentHeight = attachmentHeight + line.widget.height completionScore = completionScore + line.progress completionMax = completionMax + 2 end if line.displayText then print(' has text') text = text .. ((text == '') and "" or "\n") .. '|cFF'..line.displayColor.. line.displayText .. '|r' end end block.completionScore = completionScore / completionMax block.attachmentHeight = attachmentHeight if #text >= 1 then block.status:SetText(text) block.status:SetWordWrap(true) else block.status:SetText(nil) block.status:Hide() end end --- Objective parsers -- defines the following variables -- * height - height of whatever display widget is involved in conveying the task -- * lines - number of non-wrapped text lines to account for line space; may be discarded depending on things -- * money - boolean that determines listening for money events or not -- * progress - number ranging 0 to 2 indicating none/partial/full completion respectively DefaultTracker.ParseObjective = function(line, info) if line.finished then line.progress = 2 elseif line.quantity > 0 then line.progress = 1 else line.progress = 0 end end Bonus.ParseObjective = function(line, info) local print = B.print('BonusLine') line.displayColor = 'FFFFFF' if line.text and not info.title then line.title = line.text else line.displayText = line.text end line.progress = 0 print(' ', line.index,'|cFFFF0088-|r', line.objectiveType, line.text) if line.objectiveType == 'progressbar' then line.widgetType = 'ProgressBar' print(' |cFFFF44DDpercent='..tostring(GetQuestProgressBarPercent(info.questID))) line.value = GetQuestProgressBarPercent(info.questID) or 0 line.maxValue = 100 if line.value >= line.maxValue then line.progress = 1 elseif line.value > 0 then line.progress = 2 end line.format = PERCENTAGE_STRING line.widget = mod.SetWidget(line, info, 'ProgressBar', info.questID..'-'..line.index) print(' ** text:', line.text, 'value:', line.value, 'max:', line.maxValue) else line.widget = nil end end Cheevs.ParseObjective = function(line, info) local print = B.print('CheevsLine') line.progress = 0 if line.flags then if band(line.flags, 0x00000001) > 0 then line.format = "%d/%d" line.widget = mod.SetWidget(line, info, 'ProgressBar', line.criteriaID) elseif band(line.flags, 0x00000002) then line.widget = nil else line.widget = nil line.displayColor = 'FFFFFF' line.displayText = line.text end end print('line.type =', line.type) print(' ** qtyStr:', line.quantityString, 'qty:', line.quantity, 'assetID:', line.assetID) end Quest.ParseObjective = function(line) local print = B.print('QuestLine') print(' |cFFFF0088', line.type) local color = '00FFFF' line.progress = 0 if line.finished then line.progress = 2 color = 'FFFFFF' elseif line.type == 'monster' then color = 'FFFF00' elseif line.type == 'item' then color = '44DDFF' elseif line.type == 'object' then color = 'FF44DD' end line.displayColor = color line.displayText = line.text end mod.Quest.numButtons = 0 local usedButtons = mod.Quest.itemButtons local freeButtons = mod.Quest.freeButtons --[=[ mod.UpdateWrapper = function(reason) print('|cFF00FFFFUpdateWrapper:|r', reason) unitLevel = UnitLevel('player') wrapperWidth = mod.Conf.Wrapper.WrapperWidth scrollWidth = mod.Conf.Wrapper.WrapperWidth local wrapperBlocks = 0 -- Update scroll child vertical size scrollHeight = 0 for i, handler in ipairs(orderedHandlers) do mod.UpdateTracker(handler) local frame = handler.frame if handler.actualBlocks >= 1 then frame:SetParent(Scroll) frame:SetPoint('TOPLEFT', Scroll, 'TOPLEFT', 0, - scrollHeight) frame:SetSize(wrapperWidth, frame.height) print('|cFF00FFFF'..frame:GetName()..'|r h:|cFF00FF00', frame.height, '|r y:|cFF00FF00', -scrollHeight) scrollHeight = scrollHeight + frame.height frame:Show() else frame:Hide() end wrapperBlocks = wrapperBlocks + handler.actualBlocks end print('final scrollHeight:', scrollHeight) -- Update frame dimensions if scrollHeight > wrapperMaxHeight then print(' is larger than', wrapperMaxHeight) wrapperHeight = wrapperMaxHeight else wrapperHeight = scrollHeight B.Conf.ObjectiveScroll = 0 end scrollWidth = floor(scrollWidth+.5) scrollHeight = floor(scrollHeight+.5) wrapperWidth = floor(wrapperWidth+.5) wrapperHeight = floor(wrapperHeight+.5) headerHeight = floor(headerHeight+.5) if wrapperBlocks >= 1 then for i, region in ipairs(Wrapper.headerComplex) do region:Show() end else for i, region in ipairs(Wrapper.headerComplex) do region:Hide() end return end --[[wrapperHeight = scrollHeight print('|cFFFFFF00params:|r scroller:', scrollWidth .. ',' .. scrollHeight, 'scroll:', scrollWidth .. ',' .. scrollHeight, 'wrapper:', wrapperWidth .. ',' .. wrapperHeight, 'header:', headerHeight)]] --Scroller:SetSize(wrapperWidth, wrapperHeight) Scroller:SetPoint('TOPLEFT', Wrapper, 'TOPLEFT', 0, 0) Scroller:SetPoint('BOTTOMRIGHT', Wrapper, 'BOTTOMRIGHT') Scroll:SetSize(scrollWidth, scrollHeight) Scroll:SetPoint('TOPLEFT', Scroller, 'TOPLEFT', 0, B.Conf.ObjectiveScroll or 0) Scroll:SetPoint('RIGHT', Scroller, 'RIGHT') --Scroller:UpdateScrollChildRect() Wrapper:SetSize(wrapperWidth, wrapperHeight) --[[ update action buttons print('|cFF00FF00'..Scroll:GetName()..'|r:', Scroll:GetWidth(), Scroll:GetHeight(), '|cFF00FF00'..Scroller:GetName()..'|r:', Scroller:GetWidth(), Scroller:GetHeight(), '|cFF00FF00'..Wrapper:GetName()..'|r:', Wrapper:GetWidth(), Wrapper:GetHeight(), '|cFF0088FFvScrollRange|r:', floor(Scroller:GetVerticalScrollRange()+.5) ) --]] mod.UpdateActionButtons() end --]=] --- Queue any active item buttons for update for that frame mod.UpdateActionButtons = function(updateReason) Scroller.snap_upper = 0 Scroller.snap_lower = 0 local print = B.print('ItemButton') if updateReason then print = B.print('IB_'..updateReason) end local previousItem for questID, itemButton in pairs(usedButtons) do local info= mod.Quest.Info[questID] print('|cFF00FFFF'.. questID .. '|r', itemButton:GetName()) local block = mod.Quest.QuestBlock[questID] if block then -- Dispatch the probe if IsQuestWatched(info.questLogIndex) then itemButton.previousItem = previousItem print(' |cFFFFFF00probing', block:GetName()) block:SetScript('OnUpdate', function() if block:GetBottom() and not InCombatLockdown() then print(' '..block:GetName()..' |cFF00FF00probe hit!') mod.UpdateBlockAction(block, itemButton, itemButton.previousItem) -- needs to be previousItem from this scope block:SetScript('OnUpdate', nil) end end) previousItem = itemButton else print('hidden block or unwatched quest') itemButton.previousItem = nil itemButton:Hide() end elseif itemButton:IsVisible() then print(' |cFFFF0088hiding unwatched quest button', itemButton:GetName()) itemButton.previousItem = nil itemButton:Hide() else print(' |cFFBBBBBBignoring hidden log quest button', itemButton:GetName()) end end end mod.UpdateBlockAction = function (block, itemButton) print('**|cFF0088FF'..itemButton:GetName(), '|r:Update()') if itemButton.questID ~= block.info.questID then print('** |cFFFF0088mismatched block assignment', itemButton.questID,'<~>', block.info.questID) -- something happened between this and last frame, go back and set new probes return mod.UpdateActionButtons() end local previousItem = itemButton.previousItem local upper_bound = Scroller:GetTop() + Scroller.snap_upper local lower_bound = Scroller:GetBottom() + Scroller.snap_lower + itemButtonSize local point, anchor, relative if block:GetBottom() < lower_bound then print('** ',block:GetName() ,'|cFFFFFF00bottom =', floor(block:GetBottom()+.5), 'threschold =', floor(lower_bound+.5)) if previousItem then print('adjusting', previousItem:GetName()) previousItem:ClearAllPoints() previousItem:SetPoint('BOTTOM', itemButton, 'TOP', 0, itemButtonSpacing) end itemButton:ClearAllPoints() itemButton.x = Wrapper:GetLeft() -4 itemButton.y = Wrapper:GetBottom() point, anchor, relative = 'BOTTOMRIGHT', UIParent, 'BOTTOMLEFT' Scroller.snap_lower = Scroller.snap_lower + itemButtonSize + itemButtonSpacing elseif block:GetTop() > upper_bound then print('** ',block:GetName() ,'|cFFFFFF00top =', floor(block:GetTop()+.5), 'threschold =', floor(upper_bound+.5)) itemButton:ClearAllPoints() if previousItem then print('latch onto another piece') point, anchor, relative ='TOP', previousItem, 'BOTTOM' itemButton.x = 0 itemButton.y = -itemButtonSpacing else print('latch at corner', Scroller:GetLeft() -itemButtonSpacing, Scroller:GetTop()) point, anchor, relative = 'TOPRIGHT', UIParent, 'BOTTOMLEFT' itemButton.x = Scroller:GetLeft() -4 itemButton.y = Scroller:GetTop() end itemButton:Show() Scroller.snap_upper = Scroller.snap_upper - (itemButtonSize + itemButtonSpacing) else print('** ',block:GetName() ,'|cFF00FF00span =', floor(block:GetBottom()+.5), floor(block:GetTop()+.5), 'threschold =', floor(lower_bound+.5)) itemButton:ClearAllPoints() itemButton.x = block:GetLeft() - itemButtonSpacing itemButton.y = block:GetTop() point, anchor, relative = 'TOPRIGHT', UIParent, 'BOTTOMLEFT' end itemButton:SetPoint(point, anchor, relative, itemButton.x, itemButton.y) itemButton:Show() end mod.UpdateItemButtonCooldown = function(button) end local unitLevel = UnitLevel('player')