view ObjectiveTracker/Frame.lua @ 36:a487841050be

- attempts to solve the mystery of disappearing objectives
author Nenue
date Sun, 17 Apr 2016 13:00:31 -0400
parents 69d03f8e293e
children e84d645c8ab8
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
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 = c.headerFont, c.headerSize, c.headerOutline
    trackerSchema = newSchema
  elseif layer == 'block' then
    titlebg = c.titlebg
    titleFont, titleSize, titleOutline = c.titleFont, c.titleSize, c.titleOutline
    selectionbg = c.selectionbg
    titleSpacing, textSpacing, blockSpacing = c.titleSpacing, c.textSpacing, c.blockSpacing
    titleIndent, textIndent,selectionIndent = 2, 5, 50
    titleColor = c.titleColor
    rewardSize = 24
    blockSchema = newSchema
    textFont, textSize, textOutline = c.textFont, c.textSize, c.textOutline
    textbg =  c.textbg
    textIndent = c.textIndent
    rewardSize = c.rewardSize
  elseif layer == 'line' then
    textColor = c.textColor
    lineSchema = newSchema
  end
  print('|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 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

  if blockSchema ~= block.schema then
    print('new schema detected, applicating...')
    block.schema = blockSchema
    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.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
  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: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 = 'default'
    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, logIndex)
  local print = bprint
  print('|cFF0088FFgetting a block for logID', logIndex ..',', #handler.usedBlocks,'used', #handler.freeBlocks, 'free')
  local block = handler.LogBlock[logIndex]
  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.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.LogBlock[logIndex] = 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