Mercurial > wow > buffalo2
view ObjectiveFrame.lua @ 13:9455693fc290
Init
- recall XML display state on reload
ObjectiveFrame
- quest coloring by relative level
- quest coloring by daily/weekly/complete status
- remember starting scroll value between reload
- limit anchor points to edges for regions affected by style attributes
ObjectiveInfo
- AutoQuest outline definitions
- Pull Quest title and tag data in addition to WatchInfo
ObjectiveStyle
- ensure consistent style table
- hardcode certain attributes for sanity
XML
- ensure consistent naming conventions for heading and content elements
- ensure hardcore anchors are based on edges
- expansion of file structure to deal with complexities of dynamic widgets and style caching
ObjectiveUI
- determine primary style by block handler when restoring original style
- moved framescript to 'ObjectiveWidgets' lua
author | Nenue |
---|---|
date | Sat, 02 Apr 2016 17:46:52 -0400 |
parents | 8238cddaddb1 |
children | ed642234f017 |
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 = ipairs, max, min, unpack, floor, pairs, tostring, type local IsResting, UnitXP, UnitXPMax, GetXPExhaustion = IsResting, UnitXP, UnitXPMax, GetXPExhaustion local UnitLevel, IsQuestWatched, UIParent = UnitLevel, IsQuestWatched, UIParent local CreateFrame = CreateFrame local print = B.print('Objectives') local unitLevel = 1 -------------------------------------------------------------------- --- 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} --- These are mostly aesthetic choices so it lives here local Scroller_OnShow = function() Wrapper.watchMoneyReasons = 0; mod.UpdateWrapper() mod.SetEvents() for i, region in ipairs(Wrapper.headerComplex) do region:Show() end end local Scroller_OnHide = function() local self = Wrapper Wrapper:UnregisterAllEvents() Wrapper:SetScript('OnEvent', nil) for i, region in ipairs(Wrapper.headerComplex) do region:Hide() end end local Scroller_OnMouseWheel = function(self, delta) local r = Scroll:GetHeight() - Scroller:GetHeight() local s = B.Conf.ObjectiveScroll - delta * floor(r/5+.5) local from = self:GetVerticalScroll() if s >= r then s = r elseif s < 1 then s = 0 end self:SetVerticalScroll(s) B.Conf.ObjectiveScroll = s print('|cFF00FF00OnMouseWheel', 'from = ', from, 'scroll =', s, ' range =', r, 'current =', self:GetVerticalScroll()) mod.UpdateActionButtons('SCROLLING') end local WrapperCloseButton_OnClick = function(self) Wrapper:Minimize() if B.Conf.FrameState[Wrapper:GetName()] == 1 then self:GetNormalTexture():SetTexture([[Interface\PaperDollInfoFrame\UI-Character-SkillsPageDown-Up]]) self:GetPushedTexture():SetTexture([[Interface\PaperDollInfoFrame\UI-Character-SkillsPageDown-Down]]) else self:GetNormalTexture():SetTexture([[Interface\PaperDollInfoFrame\UI-Character-SkillsPageUp-Up]]) self:GetPushedTexture():SetTexture([[Interface\PaperDollInfoFrame\UI-Character-SkillsPageUp-Down]]) end end local WrapperCloseButton_OnMouseWheel = function(self, delta) end mod.InitializeTrackers = function() for i, name in ipairs(orderedNames) do if not mod.orderedHandlers[i] then if mod.Tracker(name, i) then local handler = mod[name] local tracker = CreateFrame('Frame', 'Veneer'..name..'Tracker', Scroll, 'VeneerTrackerTemplate') tracker.title:SetText(name) mod.SetBlockStyle(tracker, 'Tracker', 'Normal') handler.Tracker = tracker mod.orderedTrackers[i] = tracker mod.namedTrackers[name] = tracker mod.indexedTrackers[handler] = tracker print('created new tracker frames for new handler') end end end Scroller:SetScrollChild(Scroll) Scroller:SetScript('OnMouseWheel', Scroller_OnMouseWheel) Scroller:SetScript('OnShow', Scroller_OnShow) Scroller:SetScript('OnHide', Scroller_OnHide) Wrapper.close:SetScript('OnClick', WrapperCloseButton_OnClick) Wrapper.close:SetScript('OnMouseWheel', WrapperCloseButton_OnMouseWheel) Wrapper:SetPoint(unpack(wrapperPosition)) if B.Conf.ObjectiveTrackerMinimized then Scroller_OnShow(Scroller) end mod.UpdateWrapperStyle() end mod.InitializeXPTracker = function() local XPBar = Wrapper.XPBar if UnitLevel('player') == 100 then XPBar:Hide() return end --- xp bar XPBar:SetWidth(wrapperWidth - Wrapper.close:GetWidth()) XPBar.statusbg:SetAllPoints(XPBar) XPBar:RegisterEvent('DISABLE_XP_GAIN') XPBar:RegisterEvent('ENABLE_XP_GAIN') XPBar:SetScript('OnEvent', mod.UpdateXP) if not IsXPUserDisabled() then mod.EnableXP(XPBar) else mod.DisableXP(XPBar) end mod.UpdateXP(XPBar) end mod.EnableXP = function(self) self:RegisterEvent('PLAYER_XP_UPDATE') self:RegisterEvent('PLAYER_LEVEL_UP') self:RegisterEvent('PLAYER_UPDATE_RESTING') self.statusbg:SetTexture(0,0,0,.25) self:Show() end mod.DisableXP = function(self) self:UnregisterEvent('PLAYER_XP_UPDATE') self:UnregisterEvent('PLAYER_LEVEL_UP') self:UnregisterEvent('PLAYER_UPDATE_RESTING') self.statusbg:SetTexture(0.5,0.5,0.5,0.5) self:Hide() end mod.UpdateXP = function(self, event) if event == 'DISABLE_XP_GAIN' then mod.DisableXP(self) elseif event == 'ENABLE_XP_GAIN' then mod.EnableXP(self) end if not IsXPUserDisabled() then local xp = UnitXP('player') local xpmax = UnitXPMax('player') local rest = GetXPExhaustion() self.foreground:SetWidth((xp/xpmax) * self:GetWidth()) if rest then self.rested:ClearAllPoints() if xp == 0 then self.rested:SetPoint('TOPLEFT', self, 'TOPLEFT', 0, 0) else self.rested:SetPoint('TOPLEFT', self.fg, 'TOPRIGHT', 0, 0) end if (xp + rest) > xpmax then self.rested:SetPoint('BOTTOMRIGHT', self, 'BOTTOMRIGHT', 0, 0) else self.rested:SetWidth((rest/xpmax) * self:GetWidth()) end self.rested:SetPoint('BOTTOM', self, 'BOTTOM') self.rested:Show() else self.rested:Hide() end if IsResting() then self.statusbg:SetTexture(.2,.8,.2,.5) else self.statusbg:SetTexture(0,0,0,.25) end self.xpText:SetText(xp .. '/'.. xpmax .. (rest and (' ('..tostring(rest)..')') or '')) end end local segments = {'Left', 'Right', 'Tile'} mod.UpdateWrapperStyle = function() --[[for _, segment in ipairs(segments) do local texture, a1, a2, a3, a4, width = unpack(mod.defaults.Style.Wrapper.BackgroundComplex[segment]) Wrapper['Background'..segment]:SetAtlas(texture) Wrapper['Background'..segment]:SetTexCoord(a1, a2, a3, a4) if width then Wrapper['Background'..segment]:SetWidth(width) end end]] 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 mod.UpdateTrackerBlock = function (handler, blockIndex, info) local print = B.print('ObjectiveBlockParse') print(' |cFF00FFFFUpdateTrackerBlock('..blockIndex..'|r') if not blockIndex or not info then return end local mainStyle = 'Normal' local subStyle local tracker = handler.Tracker local t = handler:GetBlock(blockIndex) if previousBlock then t:SetPoint('TOPLEFT', previousBlock, 'BOTTOMLEFT', 0, 0) t:SetPoint('RIGHT', tracker,'RIGHT', 0, 0) end --print(t:GetName(), t:GetSize()) --print(t:GetPoint(1)) t.info = info if info.questID then handler.QuestBlock[info.questID] = t end if info.questLogIndex then handler.LogBlock[info.questLogIndex] = t end if info.watchIndex then handler.WatchBlock[info.watchIndex] = t end info.blockIndex = blockIndex handler.BlockInfo[blockIndex] = info t.Select = handler.Select t.Open = handler.Open t.Remove = handler.Remove t.Link = handler.Link t:SetScript('OnMouseUp', handler.OnMouseUp) t:SetScript('OnMouseDown', handler.OnMouseDown) t.title:SetText(info.title) t.attachmentHeight = 0 if info.isComplete then t.status:Show() t.status:SetText(info.completionText) elseif info.numObjectives >= 1 then t.attachmentHeight = textSpacing t.status:Show() print(' - objective lines:', info.numObjectives, 'can wrap:', t.status:CanWordWrap()) local text = '' --- todo: implement objective displays -- in an accumulator loop, call upon handler for the appropriate display frame, each defining: -- * 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 text = mod.UpdateObjectives(t, info, text) t.status:SetText(text) t.status:SetWordWrap(true) elseif info.description then t.status:SetText(info.description) t.status:SetWordWrap(true) else t.status:SetText(nil) end if info.isComplete then mainStyle = 'Complete' end if info.superTracked then subStyle = 'Super' elseif info.isDaily then subStyle = 'Daily' end 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 info.level then local levelDiff = unitLevel - info.level if levelDiff > 9 then t.title:SetTextColor(0.7, 0.7, 0.7, 1) elseif levelDiff > 1 then t.title:SetTextColor(0.5, 1, 0.5, 1) elseif levelDiff < -1 then t.title:SetTextColor(1, 0.4, 0.25, 1) elseif levelDiff < -4 then t.title:SetTextColor(1, 0, 0, 1) else t.title:SetTextColor(1,1,1,1) end end if Devian and Devian.InWorkspace() then t.debugText:Show() t.debugText:SetText(tostring(blockIndex) .. '\n' .. tostring(info.itemButton and info.itemButton:GetName()) .. "\n" .. (info.level and info.level or '-')) end --- metrics are calculated in SetStyle t:SetStyle('TrackerBlock', handler.name, mainStyle, subStyle) t:Show() print(' |cFF00FFFF)|r -> ', t, t:GetHeight()) return t end mod.UpdateObjectives = function(block, info, text) local print = B.print('ObjectiveBlockParse') local attachmentHeight = block.attachmentHeight if info.description then print(' -- has description text:', select('#', info.description), info.description) text = info.description end local completionScore, completionMax = 0, 0 for o, obj in ipairs(info.objectives) do --- achievement criteria if obj.flags then if bit.band(obj.flags, 0x00000001) > 0 then obj.type = 'ProgressBar' elseif bit.band(obj.flags, 0x00000002) then obj.type = 'Hidden' obj.widget = nil else obj.type = 'Text' obj.widget = nil text = text .. ((text == '') and "" or "\n") .. obj.text end print('obj.type =', obj.type) print(' ** qtyStr:', obj.quantityString, 'qty:', obj.quantity, 'assetID:', obj.assetID) obj.widget = mod.SetWidget(obj, info) if obj.finished then obj.progress = 2 elseif obj.quantity > 0 then obj.progress = 1 else obj.progress = 0 end --- none of the above (most quests) else local line = obj.text local color = '00FFFF' if obj.finished then obj.progress = 2 color = 'FFFFFF' elseif obj.type == 'monster' then color = 'FFFF00' elseif obj.type == 'item' then color = '44DDFF' elseif obj.type == 'object' then color = 'FF44DD' end text = text .. ((text == '') and "" or "\n") .. '|cFF'..color.. line .. '|r' end if obj.widget then obj.widget:Show() obj.widget:SetPoint('TOPLEFT', block.objectives, 'BOTTOMLEFT', 0, -attachmentHeight) print('have a widget, height is', obj.widget.height) attachmentHeight = attachmentHeight + obj.widget.height completionScore = completionScore + obj.progress completionMax = completionMax + 2 end end block.completionScore = completionScore / completionMax block.attachmentHeight = attachmentHeight return text end mod.UpdateTracker = function(handler) print('|cFF00FF88UpdateTracker(|r|cFFFF4400' .. type(handler) .. '|r :: |cFF88FFFF' .. tostring(handler) .. '|r') local tracker = handler.Tracker local blockIndex = 0 local trackerHeight = headerHeight local w = 300 previousBlock = tracker.title local numWatched = handler.GetNumWatched() local numBlocks = handler.numBlocks local actualBlocks = 0 for watchIndex = 1, 25 do blockIndex = blockIndex + 1 if watchIndex <= numWatched then local info = handler:GetInfo(watchIndex) if info then local currentBlock = mod.UpdateTrackerBlock(handler, blockIndex, info) previousBlock = currentBlock print('|cFF00FF00'..currentBlock:GetName()..'|r', currentBlock.height) 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 = handler.usedBlocks local free = handler.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('Stopping scan at', blockIndex) break -- done with quest stuff end end handler.numWatched = numWatched handler.numBlocks = numBlocks handler.actualBlocks = actualBlocks handler:Report() previousBlock = nil if numBlocks > 0 then tracker.height = trackerHeight else tracker.height = 0 end print('|cFF00FF88)|r ->', numBlocks, 'blocks; height', tracker.height, 'last block: ') end mod.Quest.numButtons = 0 local usedButtons = mod.Quest.itemButtons local freeButtons = mod.Quest.freeButtons mod.UpdateWrapper = function() unitLevel = UnitLevel('player') wrapperWidth = wrapperMaxWidth scrollWidth = wrapperWidth local wrapperBlocks = 0 -- Update scroll child vertical size scrollHeight = 0 for i, handler in ipairs(orderedHandlers) do mod.UpdateTracker(handler) if handler.actualBlocks >= 1 then local tracker = handler.Tracker print('setting', handler.Tracker, 'to anchor to offset', -scrollHeight) tracker:SetParent(Scroll) tracker:SetPoint('TOPLEFT', Scroll, 'TOPLEFT', 0, - scrollHeight) tracker:SetSize(wrapperWidth, tracker.height) print('adding ', tracker.height) scrollHeight = scrollHeight + tracker.height 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, 'x', scrollHeight) print('|cFFFFFF00params:|r scroll:', scrollWidth, 'x', scrollHeight) print('|cFFFFFF00params:|r wrapper:', wrapperWidth, 'x', wrapperHeight) print('|cFFFFFF00params:|r header:', headerHeight) mod.SetBlockStyle(Scroller, 'Scroller', 'Normal') mod.SetBlockStyle(Scroller, 'Scroll', 'Normal') mod.SetBlockStyle(Wrapper, 'Wrapper', 'Normal') 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('FULL_UPDATE') 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