diff Turok/Layout/Layout.lua @ 6:a9b8b0866ece

clear out log jam
author Nenue
date Sun, 21 Feb 2016 08:32:53 -0500
parents
children 9400a0ff8540
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Turok/Layout/Layout.lua	Sun Feb 21 08:32:53 2016 -0500
@@ -0,0 +1,725 @@
+--- LibFog-1.0
+-- In-house solution to processing frame configuration data.
+--[[
+--
+ Config Table fields:
+
+ background .. _color, _texture, _blend       properties of the texture referenced by frame.background
+ foregound  .. " " "                          properties of the texture referenced by frame.foreground
+ backdrop                                     arguments for SetBackdrop
+ border_color                                 sets the border color on an existing frame backdrop
+ alpha, alpha_ooc                             uiobject alpha for in and out of combat
+ combat_fade                                  true to change alpha levels based on combat
+ fade_time, fade_in, fade_out                 fade_time = fade_in or fade_out
+
+ anchor, parent, anchorTo, x, y               args for UIObject:SetPoint()
+ height, width                                args for Region:SetSize()
+ strata, layer, level                         override the strata, draw layer, and draw level where they apply
+
+ padding                                      space between frame and background edge
+ foreground_inset                             space between background and foreground edge
+ spacing                                      space between segmented foreground elements (e.g. combo points)
+
+ font, size, outline                          args for FontString:SetFont() respectively
+ justifyV, justifyH                           LEFT/CENTER/RIGHT, TOP/MIDDLE/BOTTOM
+
+ SetFrameLayout():
+ Applies:
+  anchor, parent, anchorTo, x, y
+  width, height,
+  padding, spacing, foreground_inset
+  backdrop, border_color, background_color
+  alpha, alpha_ooc
+
+ SetStatusTextures():
+ Applies:
+  foreground_texture, foreground_color, foreground_blend
+  background_texture, background_color, background_blend
+
+ Defines:
+  fill_direction, fill_width, fill_height, fill_inset
+  :TranslateX(progress)
+  :TranslateY(progress)
+  :SetFillPoints()
+
+ SetFontLayout():
+ Applies:
+  anchor, parent, anchorTo, x, y
+  width, height
+  font, size, outline
+  justifyH, justifyV
+
+ -
+ ]]
+local MAJOR, MINOR = "LibFog-1.0", 1
+local F, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
+if not F then return end
+
+local _G, UIParent, type, unpack, pairs, ipairs, min, abs, tostring, print = _G, UIParent, type, unpack, pairs, ipairs, math.min, math.abs, tostring, print
+local InCombatLockdown, GetTime, PlaySoundKitID, CreateFrame = InCombatLockdown, GetTime, PlaySoundKitID, CreateFrame
+local tinsert, wipe, concat = table.insert, table.wipe, table.concat
+local FADE_OUT_TIME, FADE_IN_TIME = 1, 0.4
+
+--@debug@
+local cType, cText, cNum, cWord, cKey, cPink, cBool = cType, cText, cNum, cWord, cKey, cPink, cBool
+local ADDON, scriptargs = ...
+scriptargs.LibFog = F
+local print = function(...)
+  if _G.Devian and _G.DevianDB.workspace ~= 1 then
+    _G.print('Layout', ...)
+  end
+end
+--- debug highlighters
+local namef  = function(s) return '|cFFFFFF00' ..tostring(s).. '|r' end    -- uiobject targeted function
+local namet  = function(s) return '|cFFFF4400 '..tostring(s).. '|r' end    --   region targeted function
+local valuef = function(s) return '|cFF88FF88'..(s ~= nil and ('"'..tostring(s)..'"') or ('nil'))..'|r' end    -- uiobject name
+local valuet = function(s) return '|cFFAACCAA'..(s ~= nil and ('"'..tostring(s)..'"') or ('nil'))..'|r' end    --   string value
+local valuen = function(s) return '|cFFAADD77' ..(s ~= nil and ('"'..tostring(s)..'"') or ('nil')).. '|r' end    --   string enumeration
+local valued = function(s) return '|cFFFFFFFF' ..(s ~= nil and ('"'..tostring(s)..'"') or ('nil')).. '|r' end    --   number
+--@end-debug@
+local GetPrint = function(trace)
+  if trace then
+    return print
+  else
+    return function() end
+  end
+end
+
+--- Layout control metadata
+local lastdb      -- fallback if a table isn't found mid-operation
+local ui_meta = {
+  beepkit = 15263, -- something to play when debugging
+}
+local ui_embeds = {
+  ['Frame'] = 'SetFrameLayout',
+  ['Texture'] = 'SetTextureLayout',
+  ['Font'] = 'SetFontLayout',
+}
+
+--- Resolve a parent object from varying depths
+-- @param self target object
+-- @parent string/number name of global frame or number of GetParent() hops to make
+-- @childKey index key to retrieve from the resolved parent object
+local function ParentValue(self, parent, childKey)
+  local print = GetPrint(self.trace)
+
+  local relativeTo = parent
+  if type(parent) == 'number' then
+    print(cWord('* Parent Trace:'), cNum(parent), cKey(self:GetName()))
+    relativeTo = self:GetParent()
+    for i = 2, parent do
+      if relativeTo.GetParent and relativeTo:GetParent() ~= UIParent then
+        --@debug@
+        print('  ', 'parent =', relativeTo:GetName())
+        --@end-debug
+        local next = relativeTo:GetParent()
+        relativeTo = next
+      else
+        --@debug@
+        print('  ',cWord(self:GetName()), 'parent =', cWord(parent))
+        --@debug-end@
+      end
+    end
+  end
+
+  -- This allows us to call for an immediate sub-frame and still get a usable value if it's not there
+  if childKey and relativeTo[childKey] then
+    return relativeTo[childKey]
+  end
+  return relativeTo
+end
+
+--- Animations
+F.animate_regions = {}
+setmetatable(F.animate_regions, {
+  __newindex = function (t,k,v)
+    rawset(t,k,v)
+    v.animationID = k
+    F.SetAnimationGroup(v)
+  end
+})
+
+--- Constructs the generic status frame
+function F:SetFrameLayout(c)
+  local print = GetPrint(c.trace)
+  self.trace = c.trace or self.trace
+
+  self.db = c
+  self.labels = {}
+
+  self.name = self:GetName()
+  self.combat = InCombatLockdown()
+  self:SetPoint(c.anchor, c.parent, c.anchorTo, c.x, c.y)
+  self:SetSize(c.width, c.height)
+  self:SetFrameStrata(c.strata)
+  self.throttle_rate = 0.010
+  self.throttle_point = GetTime()
+
+  if c.backdrop then
+    c.backdrop.edgeSize = c.padding*4
+    self:SetBackdrop(c.backdrop)
+  end
+  if c.background_color and c.border_color then
+    self:SetBackdropColor(unpack(c.background_color))
+    self:SetBackdropBorderColor(unpack(c.border_color))
+  end
+
+  --@debug@
+  print(namef('SetFrameLayout'),'(', valuet(self:GetName()),')')
+  print(cText('  dimensions:'), cNum(c.width), 'by', cNum(c.height), '::', c.x, c.y,  c.anchor, c.anchorTo, c.parent)
+  --@end-debug@
+
+  self.width = c.width
+  self.height = c.height
+  self.foreground_inset = c.foreground_inset
+  self.padding = c.padding
+  self.spacing = c.spacing
+  self.alpha = c.alpha
+
+  self.combatFade = c.combatFade
+  self.alpha_fade_in = c.alpha_fade_in or c.alpha_fade
+  self.alpha_fade_out = c.alpha_fade_out or c.alpha_fade
+  print('  fade on combat =', self.combatFade)
+  for _, type in ipairs({'alpha', 'alpha_ooc'}) do
+    self[type] = c[type]
+    _G.print('DB', self:GetName(), type, self[type])
+    for _, subtype in ipairs({'_passive', '_active'}) do
+      self[type..subtype] = c[type..subtype] or self[type]
+      _G.print('DB', self:GetName(), type..subtype, self[type..subtype])
+      for _, subsubtype in ipairs({'_empty', '_half', '_full'}) do
+        self[type..subtype..subsubtype] = c[type..subtype..subsubtype] or self[type..subtype]
+        _G.print('DB', self:GetName(), type..subtype..subsubtype, self[type..subtype..subsubtype])
+        self[type..subsubtype] = c[type..subsubtype] or self[type..subtype]
+        _G.print('DB', self:GetName(), type..subsubtype, self[type..subsubtype])
+      end
+    end
+  end
+  self.UpdateAlpha = F.UpdateAlpha
+  --if c.combatFade then
+  self.faderID = #F.animate_regions+1
+  F.animate_regions[#F.animate_regions+1] = self
+  print(cText('  animation target ID #'), cNum(self.faderID))
+  --end
+
+  self:UpdateAlpha(InCombatLockdown())
+
+  if self.lefttext then
+    print('  auto-lefttext')
+    F.SetFontLayout(self.lefttext, c.lefttext or c)
+  end
+  if self.righttext then
+    print('  auto-righttext')
+    F.SetFontLayout(self.righttext, c.righttext or c)
+  end
+end
+
+--- Seeds basic animations, else a template can be specified by the frame data
+function F:SetAnimationGroup(group)
+  local print = GetPrint(self.trace)
+
+  group = group or self.animationClass or nil
+  --@debug@
+  if not group then
+    print('  |cFFFF44AAseeding debug animations', self:GetName())
+    self.__flash = self:CreateAnimationGroup(self:GetName()..'Flasher')
+    self.__flash:SetToFinalAlpha(true)
+    local fade1 = self.__flash:CreateAnimation('Alpha')
+    fade1:SetChange(-1)
+    fade1:SetDuration(.6)
+    fade1:SetOrder(1)
+    local fade2 = self.__flash:CreateAnimation('Alpha')
+    fade2:SetChange(1)
+    fade2:SetDuration(.6)
+    fade2:SetOrder(2)
+    fade2:SetEndDelay(.6)
+    --@debug@
+    self.__flash.fade1 = fade1
+    self.__flash.fade2 = fade2
+    self.__flash:SetLooping('NONE')
+    self.__flash:SetScript('OnFinished', function()
+      print(self:GetName(), '[>>>] Done animating flash.')
+      self.flashing = nil
+    end)--@end-debug@
+    self.Flash = F.Flash
+
+    self.__fade = self:CreateAnimationGroup(self:GetName()..'Fader')
+    self.__fade:SetToFinalAlpha(true)
+    self.__fade:SetScript('OnFinished', function()
+      print(self:GetName(), '[>>>] Done animating fade.')
+      self.fading = nil
+      local a = self.__flash.fade1:GetToAlpha()
+      --self:SetAlpha(a)
+      if a == 0 and not self.__fade.noHide then
+        self:Hide()
+      end
+      if self.__fade.queuedFade then
+        print('[>>>]  ', cWord(self:GetName())..'.'..cKey('__fade:'), 'starting queued fade')
+        local fadeTo, fadeDuration, noHide = unpack(self.__fade.queuedFade)
+        self.__fade.queuedFade = nil
+        self:Fade(fadeTo, fadeDuration, noHide)
+      end
+    end)
+    self.Fade = F.Fade
+  end
+  --@end-debug@
+  if not self.animationID then
+    tinsert(F.animate_regions, self) -- uses rawset, so won't death loop
+    self.animationID = #F.animate_regions
+  end
+end
+
+function F:Flash(duration, peak, valley, holdUp, holdDown)
+  local print = GetPrint(self.trace)
+
+  local fl = self.__flash
+  print('[>>>]', self:GetName(), duration, peak, valley, holdUp, holdDown)
+  fl.fade1:SetFromAlpha(valley)
+  fl.fade1:SetToAlpha(peak)
+  fl.fade1:SetDuration(duration)
+  fl.fade1:SetEndDelay(holdUp)
+  fl.fade2:SetFromAlpha(peak)
+  fl.fade2:SetToAlpha(valley)
+  fl.fade2:SetDuration(duration)
+  fl.fade2:SetEndDelay(2)
+  self.flashing = true
+  fl:Play()
+end
+
+--- Fade to a value; fading to 0 will hide the frame
+function F:Fade(duration, fadeTo, noHide)
+
+  print('|cFFFF6600[>>>]', cText('Fade('), self:GetName(), cText(')'))
+  local fader = self.__fade
+  local fade1 = self.__flash.fade1
+  if fader:IsPlaying() then
+    self.__fade.queuedFade = {duration, fadeTo, noHide }
+    print('    ', cText('playing:'), cNum(fader:GetDuration())..'s', cNum(fade1:GetToAlpha()))
+    print('    ', cPink('queued {'),  cNum(duration)..'s', cNum(self:GetAlpha()), 'to', cNum(fadeTo), '}')
+    return
+  else
+    print('    ', cNum('starting {'), cNum(duration), cNum(self:GetAlpha()), 'to', cNum(fadeTo), '}')
+  end
+  fade1:SetParent(fader)
+  fade1:SetFromAlpha(self:GetAlpha())
+  fade1:SetToAlpha(fadeTo)
+  fade1:SetDuration(duration)
+  fade1:SetOrder(1)
+  self.__fade.noHide = noHide
+  self.fading = true
+  fader:Play()
+end
+
+function F:Beep()
+  if self.quiet then
+    return
+  end
+  PlaySoundKitID(F.beepkit) -- random anub'rhekan sounds
+end
+
+
+--- Defines the corners used in filling operations
+local directionBase = {
+  ['LEFT'] = 'TOPRIGHT',
+  ['RIGHT'] = 'TOPLEFT',
+  ['UP'] = 'BOTTOMLEFT',
+  ['DOWN'] = 'TOPLEFT'
+}
+local directionPeak ={
+  ['LEFT'] = 'BOTTOMLEFT',
+  ['RIGHT'] = 'BOTTOMRIGHT',
+  ['UP'] = 'TOPRIGHT',
+  ['DOWN'] = 'BOTTOMRIGHT',
+}
+local directionPeakTo = {
+  ['LEFT'] = 'BOTTOMRIGHT',
+  ['RIGHT'] = 'BOTTOMLEFT',
+  ['UP'] = 'BOTTOMRIGHT',
+  ['DOWN'] = 'TOPRIGHT'
+}
+local anchorInverse = {['LEFT'] = 'RIGHT', ['RIGHT'] = 'LEFT', ['TOP'] = 'BOTTOM', ['BOTTOM'] = 'TOP' }
+--- Directional coefficients for corner positions
+-- [1] = end X, [2] = end Y, [3] = base X, [4] = base Y
+local directionCoord = {
+  ['LEFT']   = {-1,  0, -1, -1},
+  ['RIGHT']  = { 1,  0, 1, -1},
+  ['UP']     = { 0, 1, 1, 1},
+  ['DOWN']   = { 0, -1, 1, -1},
+  ['CENTER'] = {.5, .5, 1, 1}
+}
+local paddingScale = {
+  ['UP'] = 1,
+  ['DOWN'] = -1,
+  ['LEFT'] = -1,
+  ['RIGHT'] = 1,
+}
+local embedScale = {
+  ['LEFT'] = {1, false},
+  ['RIGHT'] = {-1, false},
+  ['TOP'] = {false, -1},
+  ['BOTTOM'] = {false, 1}
+}
+
+--- Determines the correct relative X offset for a given progress ratio
+--- x = 0 + padding + spacing + i, where i = {embedded | icon_size +spacing}
+-- Factors padding and spacing parameters, including icon embedding dimensions.
+-- @param self frame on which the foreground/background textures and config data exist
+-- @param ratio progess from 0 to 1; 0 returns the starting corner coordinate, 1 returns end corner
+-- @param scale overrides the pixel fill length, with starting corner used as scaling origin
+local TranslateX    = function(self, ratio, scale)
+  if not scale then
+    scale = self.fill_width
+  end
+  if self.fill_inverse then
+    ratio = 1 - ratio
+  end
+  _G.print('Update', 'dx', self.fill_x)
+  local x =
+      scale * ratio * directionCoord[self.fill_direction][1]
+  return min(x, scale) + self.fill_x
+end
+
+--- Determines the correct relative Y offset for a given progress ratio
+-- Factors padding and spacing parameters, including icon embedding dimensions.
+-- @param self frame on which the foreground/background textures and config data exist
+-- @param ratio progess from 0 to 1; 0 returns the starting corner coordinate, 1 returns end corner
+-- @param scale overrides the pixel fill length, with starting corner used as scaling origin
+local TranslateY    = function(self, ratio, scale)
+  if not scale then
+    scale = self.fill_height
+  end
+  if self.fill_inverse then
+    ratio = 1 - ratio
+  end
+
+  _G.print('Update', 'dy', self.fill_y)
+  -- (height of bar offset by the size of any overlapping element) * progress ratio, offset by the size of any overlapping embed + inset
+  local y =
+      scale * ratio * directionCoord[self.fill_direction][2] - self.foreground_inset
+  return min(y, scale) + self.fill_y
+end
+local SetProgress = function(self, progress, scaleX, scaleY)
+  _G.print('Update', cText(self:GetName()),
+    "\n     d:", progress, 'dX:', directionCoord[self.fill_direction][1], 'dY:', directionCoord[self.fill_direction][2], 'dI:', self.fill_inverse,
+    "\n  peak:", self.fill_anchor, self.fill_anchorTo, TranslateX(self, progress), TranslateY(self,progress))
+
+  self.foreground:SetPoint(self.fill_anchor, self.background, self.fill_anchorTo, TranslateX(self, progress, scaleX), TranslateY(self, progress, scaleY))
+end
+
+--- Assigns textures to the generic foreground background bar regions
+-- Called as F.SetStatusTextures(MyFrame, config pointer)
+-- foreground_inset - number of pixels by which the foreground edges occlude the background (basically always negative)
+-- padding - number of pixels between the background edge and frame edge (basically always positive)
+function F:SetStatusTextures(c)
+  local print = GetPrint(self.trace)
+
+  if c == nil then
+    c = self.db or lastdb
+    if not c then
+      error('No config table found')
+    end
+  end
+  local relativeTo = ParentValue(self, c.parent, c.parentKey)
+
+
+  self.fill_direction = c.fill_direction or 'RIGHT'
+  self.fill_inset = c.padding - c.foreground_inset  -- foreground edge / frame edge
+  self.fill_width = self.width - self.fill_inset*2       -- foreground left / right edges
+  self.fill_height = self.height - self.fill_inset*2     --           " top / bottom "
+  self.fill_inverse = c.fill_inverse
+  self.fill_x, self.fill_y = 0, 0
+  self.fill_insets = {
+    LEFT = self.fill_inset,
+    TOP = self.fill_inset,
+    BOTTOM = self.fill_inset,
+    RIGHT = self.fill_inset
+  }
+
+
+  -- create filling points
+  self.fill_base     = directionBase[self.fill_direction]
+  self.fill_anchor   =   directionPeak[self.fill_direction]
+  self.fill_anchorTo =   directionPeakTo[self.fill_direction]
+  print("   foreground:", self.fill_base, self.fill_base, TranslateX(self, 0), TranslateY(self, 0))
+  print("   foreground fill:", self.fill_anchor, self.fill_anchorTo)
+
+  -- calculate icon embed
+  if self.icon and self.icon.embedded then
+    print(cText('#### EMBED ####'))
+    -- change anchor point so the icon is inside
+    self.icon.anchor = self.icon.anchorTo
+    print('   * Icon anchor:' , cText(self.icon.anchor), 'to', cText(self.icon.anchorTo))
+    print('   * Fill start:', cText(self.fill_base))
+
+
+    local coordSet = {nil, nil}
+    local baseSet = {nil, nil }
+    local endSet = {nil, nil}
+    for dir, coords in pairs(embedScale) do
+      if self.icon.anchor:match(dir) then
+        print('   xtrans { matches', dir, 'include {', coords[1], coords[2], '}')
+        if not coordSet[1] then coordSet[1] = coords[1] end
+        if not coordSet[2] then coordSet[2] = coords[2] end
+
+        -- the embedding position can overlap either corner
+        if self.fill_base:match(dir) then
+          print('   base corner also matches')
+          if not baseSet[1] then baseSet[1] = coords[1] end
+          if not baseSet[2] then baseSet[2] = coords[2] end
+        else
+
+          if not endSet[1] then endSet[1] = coords[1] end
+          if not endSet[2] then endSet[2] = coords[2] end
+        end
+      end
+    end
+
+    -- make sure there are values if none of them matched at all for some reason
+    coordSet = {coordSet[1] or 0, coordSet[2] or 0}
+    baseSet = {baseSet[1] or 0, baseSet[2] or 0 }
+    -- needs to produce a negative number
+    endSet = {endSet[1] or 0, endSet[2] or 0 }
+
+
+    print('  == xtrans push =', unpack(coordSet))
+    print('  == fbase delta =', unpack(baseSet))
+    print('  == ftail delta =', unpack(endSet))
+
+    -- determine the foreground displacement
+
+    self.icon_dx = (min(self.fill_width, self.fill_height) + self.spacing)
+    self.icon_dy = (min(self.fill_width, self.fill_height) + self.spacing)
+    print('   * Foreground compression:', cNum(self.icon_dx))
+
+    self.icon_size = self.icon.size
+    self.icon_x = self.padding * baseSet[1] - (self.icon.size - self.icon_dx)
+    self.icon_y = self.padding * baseSet[2] - (self.icon.size - self.icon_dy)
+    print('   * Icon dims:' , cNum(self.icon_size), ' offset:', cNum(self.icon_dx))
+
+
+    local ofi = self.fill_insets[self.fill_direction]
+    print('   * Fill inset('..cWord(self.fill_direction)..') from', cNum(ofi), 'to', cNum(self.fill_insets[self.fill_direction]))
+
+    -- used to place the starting corner
+    self.fill_x = (self.icon_dx) * baseSet[1]
+    self.fill_y = (self.icon_dy) * baseSet[2]
+    print('   * fill offset dX:', self.fill_x)
+    print('   * fill offset dY:', self.fill_y)
+
+    -- amount taken off of fill scale, subtract spacing
+    self.icon_dx_cut = abs(self.icon_dx * -baseSet[1])
+    self.icon_dy_cut = abs(self.icon_dy * -baseSet[2])
+
+    local ofw, ofh = self.fill_width, self.fill_height
+    self.fill_width = self.fill_width - self.icon_dx_cut
+    self.fill_height = self.fill_height - self.icon_dy_cut
+    print('   * Scale dX:', self.icon_dx_cut, cNum(ofw), 'to', cNum(self.fill_width))
+    print('   * Scale dY:', self.icon_dy_cut, cNum(ofh), 'to', cNum(self.fill_height))
+
+    self.icon:ClearAllPoints()
+    self.icon:SetPoint(self.icon.anchor, self, self.icon.anchorTo, self.icon_x, self.padding * coordSet[2])
+    self.icon:SetSize(self.icon_size, self.icon_size)
+  end
+
+  --@debug@
+  print(namet('SetStatusTextures'),'(', valuef(self:GetName()), ')')
+  print('   form:', self.padding,'-', self.foreground_inset, '+', self.fill_width, self.padding,'-', self.foreground_inset, '+','x', self.padding,'-', self.foreground_inset, '+', self.fill_height, self.padding,'-', self.foreground_inset, '+')
+  print('  ', valuen(concat(c.background_color,', ')),valuet(c.background_texture), valuen(concat(c.foreground_color,', ')), valuet(c.foreground_texture))
+  print('   background:', self.padding, self.padding, 'BOTTOMLEFT', 'BOTTOMLEFT' , '::', -self.padding, -self.padding, 'TOPRIGHT', 'TOPRIGHT')
+  --@end-debug@
+
+  self.background:ClearAllPoints()
+  self.background:SetPoint('BOTTOMLEFT', self, 'BOTTOMLEFT', self.padding, self.padding)
+  self.background:SetPoint('TOPRIGHT', self, 'TOPRIGHT', -self.padding, -self.padding)
+  if c.background_texture ~= '' and c.background_texture ~= nil then
+    self.background:SetTexture(c.background_texture)
+    self.background:SetVertexColor(unpack(c.background_color))
+  else
+    self.background:SetTexture(unpack(c.background_color))
+  end
+  self.background:SetBlendMode(c.background_blend)
+
+  if c.foreground_texture ~= '' and c.foreground_texture ~= nil then
+    self.foreground:SetTexture(c.foreground_texture)
+    self.foreground:SetVertexColor(unpack(c.foreground_color))
+  else
+    self.foreground:SetTexture(unpack(c.foreground_color))
+  end
+  self.foreground:SetBlendMode(c.foreground_blend)
+
+  self.foreground:ClearAllPoints()
+  local dx = (directionCoord[self.fill_direction][3] * -self.foreground_inset) + self.fill_x
+  local dy = (directionCoord[self.fill_direction][4] * -self.foreground_inset) + self.fill_y
+  print('   foreground base:', cNum(dx), cNum(dy))
+  self.foreground:SetPoint(self.fill_base, self.background, self.fill_base, dx, dy)
+
+  SetProgress(self, 1)
+
+  self.TranslateX = TranslateX
+  self.TranslateY = TranslateY
+  self.SetFillPoints = SetFillPoints
+  self.SetProgress = SetProgress
+end
+--- Sets properties for a generated FontString
+-- Takes the FontString object as a self, and applies config values.
+-- In particalur, the anchor parent is derived from either a fixed number of GetParent() or global name
+-- @usage Region:SetFontLayout([table])
+function F:SetFontLayout (c)
+  local print = GetPrint(self.trace)
+
+  if c == nil then
+    c = lastdb
+    if not c then
+      error('No config table found')
+    end
+  end
+  local relativeTo = ParentValue(self, c.parent, c.parentKey)
+
+  --@debug@
+  print(namet('FontLayout'),'(', valuet(self:GetName()), ')', valuet(c.font), valuet(c.size), valuet(c.outline))
+  print('  ', valuen(c.anchor or 'CENTER'), relativeTo:GetName(), valuen(c.anchorTo or 'CENTER'), valued(c.x or 0),
+    valued(c.y or 0))
+  --@end-debug@
+  self:SetPoint(
+    c.anchor or 'CENTER',
+    relativeTo,
+    c.anchorTo or 'CENTER',
+    c.x or 0,
+    c.y or 0)
+  self:SetSize(
+    c.width or relativeTo:GetWidth(),
+    c.height or relativeTo:GetHeight())
+  self:SetFont(
+    c.font,
+    c.size,
+    c.outline)
+  self:SetJustifyH(c.justifyH or 'CENTER')
+  self:SetJustifyV(c.justifyV or 'MIDDLE')
+  self:SetTextColor(unpack(c.text_color or {1,1,1,1}))
+end
+
+--- For setting up widget pieces
+function F:SetTextureLayout(c)
+  local print = GetPrint(self.trace)
+
+  if c == nil then
+    c = lastdb
+    if not c then
+      error('No config table found')
+    end
+  end
+  local relativeTo = ParentValue(self, c.parent, c.parentKey)
+
+  self.size = c.size
+  self.width = c.width
+  self.height = c.height
+  self.x = c.x
+  self.y = c.y
+  self.embedded = c.embedded
+  self.anchor = c.anchor
+  self.anchorTo = c.anchorTo
+
+  self:SetPoint(c.anchor, relativeTo, c.anchorTo, c.x, c.y)
+  self:SetSize(c.size or c.width, c.size or c.height)
+  self:SetAlpha(c.alpha)
+  self:SetDesaturated(c.desaturated or false)
+  print('|cFF00FFFFSetTextureLayout(|r', self:GetName(), '|cFF00FFFF)|r')
+  print('  ', c.anchor, relativeTo, c.anchorT, c.x, c.v)
+  if c.combatFade and not (relativeTo.faderID and F.animate_regions[relativeTo.faderID]) then
+    tinsert(F.animate_regions, self)
+    self.faderID = #F.animate_regions
+    --@debug@
+    print('register fadeable texture #'..self.faderID)
+    --@end-debug@
+  end
+end
+
+local alphaSubType = {
+  [1] = '_passive',
+  [2] = '_active'
+}
+local alphaFillType = {
+  [1] = '_empty',
+  [2] = '_half',
+  [3] = '_full',
+}
+function F:UpdateAlpha(inCombat, displayState, fillState)
+  local print = function() end
+  print(cWord('UpdateAlpha(')..self:GetName()..cWord(')'))
+  local alphaType = inCombat and 'alpha' or 'alpha_ooc'
+  local alphaDuration = inCombat and 'alpha_fade_in' or 'alpha_fade_out'
+  local displayState = displayState or self.displayState or nil
+  local fillState = fillState or self.fillState or nil
+
+  local alphaSubType = ''
+  if displayState then
+    alphaSubType = (displayState == 1) and '_passive' or ((displayState == 2) and '_active' or '' )
+  end
+
+  local alphaFillType = ''
+  if fillState then
+    alphaFillType = (fillState == 1) and '_half' or ((fillState == 2) and '_full' or '_empty')
+  end
+
+  local fadeTo = self[alphaType..alphaSubType..alphaFillType]
+  local fadeDuration = self[alphaDuration] or 0
+  print(' alphaKey:', cWord(alphaType..alphaSubType..alphaFillType))
+  print(' alphaTo:', cNum(fadeTo), 'duration:', cNum(fadeDuration))
+  if self:IsVisible() and fadeDuration ~= 0 then
+    self:Fade(fadeDuration, fadeTo or (inCombat and 1 or 0.5), true)
+    print('   |cFFFFFF00  :Fade()|r', 'dur='..cNum(fadeDuration), cNum(fadeTo), cWord(self:GetName()))
+  else
+    self:SetAlpha(fadeTo)
+    print('   |cFF00FF00  :SetAlpha('..fadeTo..')|r', cText(self:GetName()))
+  end
+end
+
+--- Sets an OnUpdate within a time-throttled wrapper
+-- throttle rate should be slightly smaller than 1/average frame rate
+function F:SetFrameScript(updateFunc, showFunc, hideFunc, ...)
+  local print = GetPrint(self.trace)
+
+  if not self.__oldscripts then
+    self.__oldscripts = {}
+  end
+
+  self:SetScript('OnUpdate', nil) -- clear any scripts
+  self:SetSCript('OnUpdate', function(self)
+    if GetTime() < self.throttle_time then
+      return
+    end
+    self.throttle_time = self.throttle_time + self.throttle_rate
+    updateFunc(self)
+  end) -- put the new function in its place
+  local changeSet = {
+    { self.__oldscripts,onUpdate = self:GetScript('OnUpdate') },
+  }
+
+  if showFunc then
+    tinsert(changeSet,
+      {onShow = self:GetScript('OnShow')})
+    self:SetScript('OnShow', showFunc)
+  end
+
+  if hideFunc then
+    tinsert(changeSet,
+      {onHide = self:GetScript('OnShow')})
+    self:SetScript('OnHide', hideFunc)
+  end
+end
+
+--- Embed
+function F:Embed(object)
+  print('Doing embed')
+  for k, v in pairs(ui_embeds) do
+    print('embedding Set'..k..'Layout')
+    object['Set'..k..'Layout'] = self[v]
+  end
+  object.SetStatusTextures = self.SetStatusTextures
+
+  --- map a generic layout method if embedded into a frame object
+  if object.GetObjectType and ui_embeds[object.GetObjectType()] then
+    if ui_embeds[object.GetObjectType()] then
+      object.SetLayout = ui_embeds[object.GetObjectType()]
+    end
+  end
+end
\ No newline at end of file