view ObjectiveTracker/Frame.lua @ 37:e84d645c8ab8

- revised the tracker update function to build its complete data list up front and use the values as points of comparison for determining possible out of place blocks, which will be iterated over afterward to remove what wasn't re-used - also entailed revising the exact role of global event handlers and function hooks, limiting their directions of communication so one doesn't end up calling the other multiple or inifinity times - schema handling polish
author Nenue
date Mon, 18 Apr 2016 07:56:23 -0400
parents a487841050be
children 1f8f9cc3d956
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 T = B:RegisterModule("ObjectiveTracker", _G.VeneerObjectiveWrapper, 'BuffFrame')
local _G, ipairs, max, min, unpack, floor, pairs, tostring, type, band = _G, ipairs, max, min, unpack, floor, pairs, tostring, type, bit.band
local IsResting, UnitXP, UnitXPMax, GetXPExhaustion, tinsert, tremove = IsResting, UnitXP, UnitXPMax, GetXPExhaustion, table.insert, table.remove
local UnitLevel, IsQuestWatched, UIParent = UnitLevel, IsQuestWatched, UIParent
local GetAutoQuestPopUp, GetQuestLogCompletionText = GetAutoQuestPopUp, GetQuestLogCompletionText
local PERCENTAGE_STRING, GetQuestProgressBarPercent = PERCENTAGE_STRING, GetQuestProgressBarPercent
local Default, AutoQuest, Quest, Bonus, Cheevs = T.DefaultHandler, T.AutoQuest, T.Quest, T.Bonus, T.Cheevs
local InCombatLockdown, format, lshift, CreateFrame = InCombatLockdown, format, bit.lshift, CreateFrame
local IsModifiedClick, ChatEdit_GetActiveWindow = IsModifiedClick, ChatEdit_GetActiveWindow
local print = B.print('Layout')
local oprint = B.print('Objectives')
local bprint = B.print('Block')
local tprint = B.print('Tracker')
local lprint = B.print('Layout')
local unitLevel = 1
local OBJECTIVE_TRACKER_UPDATE_REASON = OBJECTIVE_TRACKER_UPDATE_REASON
local debug = false

--- FRAMES
local Wrapper = _G.VeneerObjectiveWrapper
local Scroller = Wrapper.scrollArea
local Scroll = _G.VeneerObjectiveScroll
local orderedHandlers = T.orderedHandlers
local orderedNames = T.orderedNames

--- FRAME TEMP VARIABLES
local wrapperWidth, wrapperHeight = 0, 0
local scrollWidth, scrollHeight

--- SCHEMA VARIABLES
local schemaName, lastSchema = {
  tracker = '',
  block = '',
  line = ''
}, {}
local trackerSchema, blockSchema, lineSchema

local itemButtonSize, itemButtonSpacing =  36, 1
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 wrapperPosition = {'RIGHT', UIParent, 'RIGHT', -84, 0 }
local rewardSize = 24

local headerHeight, headerColor, headerSpacing = 16, {1,.75,0,1}, 2
local headerbg = {'VERTICAL', 1, 1, 0.5, 0.5, 1, 1, 0.5, 0}
local headerFont, headerSize, headerOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 14, 'OUTLINE'

local titlebg = {'HORIZONTAL', 1, 0, .7, 0, 1, 0, .7, .2}
local titleFont, titleSize, titleOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Bold.ttf]], 16, 'OUTLINE'
local titleColor = {0,.7,1,1}

local textbg =  {'HORIZONTAL', 0, 0, 0, 0.4, 0, 0, 0, 0 }
local textFont, textSize, textOutline = [[Interface\Addons\SharedMedia_MyMedia\font\ArchivoNarrow-Regular.ttf]], 16, 'OUTLINE'
local textColor = {1,1,1,1}

local selectionbg = {'HORIZONTAL', 1, 1, 1, 0, 1, 1, 1, 0.225}
local titleSpacing, textSpacing, blockSpacing = 3, 3, 1
local titleIndent, textIndent,selectionIndent = 2, 5, 50
--- END SCHEMA
local blockPosition

--- schema swapper
T.UpdateSchema = function(layer, newSchema)
  if not (T.Conf.Schema[layer] and T.Conf.Schema[layer][newSchema]) then
    return
  elseif schemaName[layer] == newSchema then
    return
  end
  lastSchema[layer] = schemaName[layer]
  schemaName[layer] = newSchema
  local c = T.Conf.Schema[layer][newSchema]

  if layer == 'tracker' then
    headerHeight, headerSpacing = c.headerHeight, c.headerSpacing
    headerColor = c.headerColor
    headerbg = c.headerbg
    headerFont, headerSize, headerOutline = unpack(c.headerFont)
    trackerSchema = newSchema
  elseif layer == 'block' then
    titlebg = c.titlebg
    titleFont, titleSize, titleOutline = unpack(c.titleFont)
    selectionbg = c.selectionbg
    titleSpacing, textSpacing, blockSpacing = c.titleSpacing, c.textSpacing, c.blockSpacing
    titleIndent, textIndent,selectionIndent = c.titleIndex, c.textIndex, c.selectionIndent
    titleColor = c.titleColor
    print(unpack(c.titleColor))
    rewardSize = 24
    textFont, textSize, textOutline = unpack(c.textFont)
    textbg =  c.textbg
    textIndent = c.textIndent
    rewardSize = c.rewardSize
    blockSchema = newSchema
  elseif layer == 'line' then
    textColor = c.textColor
    lineSchema = newSchema
  end
  tprint('|cFFFF0088UpdateSchema:|r', layer, lastSchema[layer], '->', newSchema)
end
-- todo: figure out why objectives go invisible
local anchorPoint, anchorFrame
local abs, GetTime = math.abs, GetTime
Default.AddTracker = function(handler, frame, index)
  if index == 1 then
    print('|cFF00FF00### beginning wrapper layout -----------------')
    anchorPoint, anchorFrame = 'TOP', Scroll
    wrapperHeight = 0
  end

  frame.destinationOffset = -wrapperHeight
  print(frame.destinationOffset, frame.previousOffset)
  if  handler.initialized and (abs(frame.previousOffset - frame.destinationOffset) > 0.9) and frame:IsVisible() then
    if frame.wasEmpty then
      frame.previousOffset = -Wrapper:GetHeight()
    end

    local postFrame, postPoint = anchorFrame, anchorPoint
    local delta = frame.destinationOffset - frame.previousOffset
    local _, _, _, _, offset = frame:GetPoint(1)
    print('  |cFF00FFBBpushing', frame:GetName(), delta, 'pixels, from', frame.previousOffset, '(', offset, ')')
    frame.SlideIn.translation:SetTarget(frame)
    frame.SlideIn.translation:SetOffset(0, delta)
    frame.SlideIn:Play()
    --for i, b in ipairs(handler.usedBlocks) do
      --b.SlideIn.translation:SetOffset(0, delta)
     -- b.SlideIn:Play()
    --end
    local start = GetTime()
    frame.SlideIn:SetScript('OnFinished', function()
      print('  |cFF00BBFF'..frame:GetName(), 'moved', delta, 'over duration of ', GetTime()-start)
      frame:SetParent(Scroll)
      frame:SetPoint('TOP', Scroll, 'TOP', 0, frame.destinationOffset)
      frame.previousOffset = frame.destinationOffset
      frame.SlideIn:SetScript('OnFinished', nil)
      if Wrapper.destinationHeight then
        Wrapper:SetHeight(Wrapper.destinationHeight)
        Scroller:SetHeight(Wrapper.destinationHeight)
        Scroll:SetHeight(Wrapper.destinationHeight)
        Wrapper.previousHeight = Wrapper.destinationHeight
        Wrapper.destinationHeight = nil
      end

    end)
  else
    print('  |cFF00BBFFpinning '..handler.name..' to', anchorFrame:GetName(), anchorPoint, '|rcurrent frame height:', frame.height)
    print('  |cFFFF0088total height:', wrapperHeight)
    frame:ClearAllPoints()
    frame:SetParent(Scroll)
    frame:SetPoint('TOP', Scroll, 'TOP', 0, frame.destinationOffset)
    frame.previousOffset = frame.destinationOffset
    handler.initialized = true
  end

  frame.title:SetFont(headerFont, headerSize, headerOutline)
  frame.titlebg:SetHeight(headerHeight)
  frame.title:SetTextColor(unpack(headerColor))

  if frame.height ~= frame.previousHeight then
    frame:SetHeight(frame.height)
  end

  frame:Show()
  if frame.wasEmpty then
    frame.headerFade:Play()
    frame.wasEmpty = nil
  end

  wrapperHeight = wrapperHeight + frame.height
  anchorFrame = handler.frame
  anchorPoint = 'BOTTOM'

end

Default.AddBlock = function(self, block, blockIndex)
  local blockIndex = blockIndex or (self.currentBlock + 1)
  local print = bprint
  local tracker = self.frame
  local info = block.info

  block.index = blockIndex

  print('blockschema', blockSchema, block.schema)
  if blockSchema ~= block.schema then
    T.UpdateSchema('block', block.schema)
    print('new schema detected, applicating...')
  end

  block:SetWidth(T.Conf.Wrapper.Width)
  block.title:SetSpacing(titleSpacing)
  block.title:SetPoint('TOP', block, 'TOP', 0, -titleSpacing)
  block.title:SetPoint('LEFT', block, 'LEFT', titleIndent, 0)
  block.title:SetTextColor(unpack(titleColor))
  block.titlebg:SetTexture(1,1,1,1)
  block.titlebg:SetGradientAlpha(unpack(titlebg))
  block.titlebg:SetPoint('TOP', block, 'TOP', 0, 0)
  block.titlebg:SetPoint('BOTTOM', block.title, 'BOTTOM', 0, -titleSpacing)
  block.status:SetSpacing(textSpacing)
  block.status:SetPoint('TOP', block.titlebg, 'BOTTOM', 0, -textSpacing)
  block.status:SetPoint('LEFT', block.titlebg, 'LEFT', textIndent, 0)
  block.statusbg:SetPoint('TOP', block.titlebg, 'BOTTOM', 0, 0)
  block.statusbg:SetPoint('BOTTOM', block, 'BOTTOM', 0, 0)
  block.statusbg:SetTexture(1,1,1,1)
  block.statusbg:SetGradientAlpha(unpack(textbg))
  block.SelectionOverlay:SetGradientAlpha(unpack(selectionbg))
  block.SelectionOverlay:SetPoint('TOPLEFT', selectionIndent, 0)
  block.SelectionOverlay:SetPoint('BOTTOMRIGHT')

  local anchor, target, point, x, y = 'TOPRIGHT', block, 'TOPRIGHT', -2, -2
  for i, tile in ipairs(block.rewardTile)  do
    --print(rewardSize)
    tile:SetSize(rewardSize, rewardSize)
    tile:ClearAllPoints()
    tile:SetPoint(anchor, target, point, x, y)
    block.rewardLabel[i]:SetPoint('TOP', tile, 'TOP', 0, 0)
    anchor, target, point, x, y = 'TOPRIGHT', tile, 'TOPLEFT', -2, 0
  end


  local titleHeight = floor(block.title:GetHeight()+.5)
  local titlebgHeight = titleHeight + titleSpacing*2
  block.titlebg:SetHeight(titlebgHeight)

  local statusHeight = floor(block.status:GetHeight()+.5)
  local statusbgHeight = statusHeight + textSpacing*2
  local attachmentHeight =floor(block.attachmentHeight + .5)

  print('    |cFF0088FFanchor to', self.currentAnchor:GetName())
  print('    |cFF00FF00attachment:|r', attachmentHeight, '|cFF00FF00title:|r', titlebgHeight, '('.. titleHeight..')')
  if attachmentHeight > 0 then
    attachmentHeight = attachmentHeight + textSpacing
  end

  block.height = titlebgHeight + attachmentHeight
  block:SetHeight(block.height)

  if block.debug then
    local func = (B.Conf.GuidesMode == true) and 'Show' or 'Hide'
    for _, region in ipairs(block.debug) do
      region[func]()
    end
  end

  --- Handler vars
  if blockIndex == 1 then
    tracker.previousHeight = tracker.height
    tracker.height = headerHeight
    blockPosition = -headerHeight
    tprint('    |cFF88FF00AddBlock:|r new layout: headerHeight =', headerHeight, 'previousHeight =', tracker.previousHeight)
  else
    blockPosition = blockPosition
    tprint('    |cFF8888FFAddBlock:|r advancing: height =', tracker.height)
  end
  self.currentBlock = blockIndex
  self.currentAnchor = block

  block:SetPoint('TOPLEFT', self.frame, 'TOPLEFT', 0, blockPosition)
  block:SetPoint('RIGHT', tracker,'RIGHT', 0, 0)
  self.numBlocks = self.numBlocks + 1
  print('    |cFFFFFF00'..tracker.height..'|r', '|cFF00FF00'..block:GetName()..'|r', block.height, tracker.height)
  tracker.height = tracker.height + block.height
  blockPosition = blockPosition - block.height
end

--- Used as an iterator of sorts for cascaded tag icon placements (the daily/faction/account icons)
Default.AddTag = function (handler, block, tagInfo, tagPoint, tagAnchor, tagRelative)
  local print = bprint

  for order, tagName in ipairs(block.info.tagInfo) do
    local tag = block[tagName]
    if block.tagCoords[tagName] and tag then
      tag:SetTexCoord(unpack(block.tagCoords[tagName]))
      tag:Show()
      tag:SetPoint(tagPoint, tagAnchor, tagRelative, 0, 0)
      tagPoint, tagAnchor, tagRelative = 'TOPRIGHT', tag, 'TOPLEFT'
    else
      block[tagName]:Hide()
    end
  end

  return tagPoint, tagAnchor, tagRelative
end

--- Adds the given line to the current content and advances the anchor pointer to that new line for the following call.
Default.AddLine = function(handler, block, text, attachment, template)
  local print = lprint
  local lineIndex = block.currentLine + 1
  local line = handler:GetLine(block, lineIndex)

  line.index = lineIndex
  if template then
    if line.schema ~= template then
      print('      |cFF00FF00change schema', template)
      T.UpdateSchema('line', template)
      line.status:SetSpacing(textSpacing)
      line.status:SetPoint('LEFT', line, 'LEFT', textIndent, 0)
      line.status:SetPoint('RIGHT', line, 'RIGHT',0, 0)
      line.status:SetTextColor(unpack(textColor))
      line.schema = template
    else
      print('      |cFFFFFF00keep schema', line.schema)
    end
  end
  line:SetPoint('TOP', block.endPoint, 'BOTTOM', 0, -textSpacing)
  line.status:SetPoint('LEFT', line, 'LEFT', textIndent, 0)
  line:SetPoint('LEFT', block, 'LEFT')
  line:SetPoint('RIGHT', block, 'RIGHT')
  line:Show()


  tprint('      |cFF0088FFAddLine|r (|cFF00FFFF'..tostring(line.schema)..'|r):', line:GetName())


  -- fill in the text, then derive pixel-rounded height
  line.status:SetText(text)
  line.height = floor(line.status:GetStringHeight()+.5)

  -- For progressbar and timer lines, status text may be used as the title heading
  if attachment then
    print('      |cFFFF0088doing things with a widget', attachment:GetSize())
    line.height = attachment:GetHeight()
    if text then
      line.height = max(line.height, line.status:GetStringHeight())
    end
    if attachment.status:GetText() then
      line.height = max(line.height, attachment.status:GetStringHeight())
    end
    attachment:Show()
  end

  line:SetHeight(line.height)
  block.attachmentHeight = block.attachmentHeight + line.height + textSpacing

  local debug_points = ''
  for i = 1, line:GetNumPoints() do
    local point, parent, anchor = line:GetPoint(i)
    debug_points = debug_points .. tostring(parent:GetName()) .. ', ' .. anchor ..  ' '
  end

  print('      |cFF0088FFsetting line #'..lineIndex..' for|r', block.info.title, "\n     |cFF0088FFsize:|r", line.height,
    "|cFF0088FFpoint:|r", debug_points, "|cFF0088FFwidget:|r", (line.widget and 'Y' or 'N'))
  block.currentLine = lineIndex
  block.endPoint = line -- edge used for the next block

  return lineIndex
end

--- Creates or retrieves a complete line data object
Default.GetLine = function(handler, block, lineIndex)
  local print = lprint
  local blockIndex = block.index
  local lines = block.lines
  if not lineIndex then
    lineIndex = block.currentLine + 1
    print('        |cFFFFFF00generating a frame')
  end

  block.numLines = max(block.numLines, lineIndex)

  if not lines[lineIndex] then
    print('     |cFF00FF88created line #'..lineIndex..' from for '..handler.name..' block #'..blockIndex)
    lines[lineIndex] = CreateFrame('Frame', 'Vn'..handler.name .. blockIndex..'ObjectiveLine'..lineIndex, block, 'VeneerTrackerObjective')
    local line = lines[lineIndex]
    line.index = lineIndex
    line.height = 0
    line.schema = ''
    B.SetConfigLayers(line)

    if debug then
      for _, region in ipairs(lines[lineIndex].debug) do
        region:Show()
      end
    end

  end
  return lines[lineIndex]
end



--- Creates or retrieves a complete block frame object
--- todo: make it use data index to avoid re-coloring every block
Default.GetBlock = function(handler, index)
  local print = bprint
  print('|cFF0088FFgetting a block for index', index ..',', #handler.usedBlocks,'used', #handler.freeBlocks, 'free')
  local block = handler.InfoBlock[index]
  local used = handler.usedBlocks

  if not block then
    if #handler.freeBlocks >= 1 then
      block = tremove(handler.freeBlocks)
      tinsert(handler.usedBlocks, block)
      block.posIndex = #handler.usedBlocks
      print(' |cFF00FF00 assigning from free heap', block:GetName())
    else

      local blockIndex = (#handler.usedBlocks + #handler.freeBlocks) + 1
      block = CreateFrame('Frame', 'Veneer'..tostring(handler)..'Block'..blockIndex, handler.frame, 'VeneerTrackerBlock')
      --block:SetParent()
      block.schema = ''
      block.lines = {}
      block.numLines = 0
      block.currentLine = 0
      block.attachmentHeight = 0
      block.offset = 0
      B.SetConfigLayers(block)
      --- methods for event handlers

      block.Select = handler.Select
      block.Open = handler.Open
      block.Remove = handler.Remove
      block.Link = handler.Link
      block.clickZone:SetScript('OnMouseUp', function(self, ...) handler.OnMouseUp(block, ...) end)
      block.clickZone:SetScript('OnMouseDown', function(self, ...) handler.OnMouseDown(block, ...) end)
      block:ClearAllPoints()
      block.index = blockIndex
      print('  |cFF00FFBBcreating new|r', block:GetName())
    end
    handler.InfoBlock[index] = block
    tinsert(handler.usedBlocks, block)
    block.posIndex = #handler.usedBlocks
  else
    print('  |cFFFFFF00use existing block|r', block:GetName())
    local found = false
    for i, entry in ipairs(used) do
      if entry == block then
        found = true
        break
      end
    end
    if not found then
      tinsert(used, block)
      block.posIndex = #used
    end
  end
  return block
end




----------
--- Top level methods


T.UpdateBlockAction = function (block, itemButton)
  local print = bprint
  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 T.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

T.UpdateItemButtonCooldown = function(button)

end

function T:FinishWrapper ()
  if wrapperHeight > Wrapper.previousHeight then
    Wrapper:SetHeight(wrapperHeight)
    Scroller:SetHeight(wrapperHeight*3)
    Scroll:SetHeight(wrapperHeight)
    Wrapper.previousHeight = wrapperHeight
    Wrapper.destinationHeight = wrapperHeight
  end
  Scroller:SetVerticalScroll(B.Conf.ObjectiveScroll or 0)
  print('|cFF00FF00### end of wrapper layout', Wrapper:GetSize())
  print('  |cFF00FF00Scroller:', Scroller:GetSize())
  print('  |cFF00FF00Scroll:', Scroll:GetSize())
  for i = 1, Wrapper:GetNumPoints() do
    print('|cFF00FF00 ', Wrapper:GetPoint(i))
  end
  for i = 1, Scroller:GetNumPoints() do
    print('|cFF00FF00 ', Scroller:GetPoint(i))
  end
  for i = 1, Scroll:GetNumPoints() do
    print('|cFF00FF00 ', Scroll:GetPoint(i))
  end

  Wrapper:Show()
  Scroller:Show()
  Scroll:Show()
end