annotate Veneer.lua @ 90:6e2cb847c3c6

Implement a mixin template for the basic visual config widgets.
author Nenue
date Mon, 24 Oct 2016 18:28:40 -0400
parents 74e714637d6a
children caded2668701
rev   line source
Nenue@88 1 -- Veneer Custom Interface Framework
Nenue@88 2 -- 1. vn OnLoad
Nenue@88 3 -- 2. OnEvent where IsLoggedIn() == true
Nenue@88 4 -- 3. Setup() where (not self.initialized)
Nenue@88 5 -- 4. Update()
Nenue@88 6 -- 5. Reanchor()
Nenue@84 7
Nenue@84 8 SLASH_VENEER1 = "/veneer"
Nenue@84 9 SLASH_VENEER2 = "/vn"
Nenue@90 10 local VENEER_VERSION = 703
Nenue@84 11
Nenue@84 12 SlashCmdList.VENEER = function(cmd)
Nenue@90 13
Nenue@90 14 if Veneer.ConfigMode then
Nenue@90 15 Veneer.ConfigMode = false
Nenue@90 16 else
Nenue@90 17 Veneer.ConfigMode = true
Nenue@90 18 end
Nenue@90 19 Veneer:UpdateConfigLayers()
Nenue@84 20 end
Nenue@88 21
Nenue@84 22 VeneerCore = {
Nenue@84 23 Frames = {},
Nenue@84 24 ConfigLayers = {},
Nenue@84 25 FrameClusters = {},
Nenue@84 26 parserDepth = 0,
Nenue@84 27 pendingCalls = {},
Nenue@90 28 AddOnCheck = {}
Nenue@84 29 }
Nenue@84 30 local print = DEVIAN_WORKSPACE and function(...) print('Veneer', ...) end or nop
Nenue@80 31 local wipe = table.wipe
Nenue@0 32
Nenue@59 33 local defaults = {
Nenue@59 34 enableAll = true,
Nenue@59 35 enableModule = {
Nenue@59 36 BuffFrame = true,
Nenue@59 37 },
Nenue@59 38 BuffFrame = {
Nenue@59 39 width = 48,
Nenue@59 40 height = 48,
Nenue@90 41 },
Nenue@90 42 ConfigMode = true
Nenue@59 43 }
Nenue@84 44
Nenue@71 45 local configMode
Nenue@79 46 local anonID = 0
Nenue@79 47 local tostring = tostring
Nenue@79 48 local IsFrameHandle = IsFrameHandle
Nenue@79 49 local GetAnonymousName = function(key)
Nenue@79 50 if not key then
Nenue@71 51 anonID = anonID + 1
Nenue@79 52 key = anonID
Nenue@71 53 end
Nenue@79 54 return 'VN' .. key
Nenue@71 55 end
Nenue@79 56 local GetTableName = function(table)
Nenue@79 57 return (IsFrameHandle(table) and table:GetName()) or tostring(table)
Nenue@79 58 end
Nenue@79 59
Nenue@87 60 local OFFSET_PARALLELS = {
Nenue@87 61 TOP = {'LEFT', 'RIGHT', 'SetHeight'},
Nenue@87 62 BOTTOM = {'LEFT', 'RIGHT', 'SetHeight'},
Nenue@87 63 LEFT = {'TOP', 'BOTTOM', 'SetWidth'},
Nenue@87 64 RIGHT = {'TOP', 'BOTTOM', 'SetWidth'},
Nenue@87 65 }
Nenue@87 66 local ANCHOR_OFFSET_POINT = {
Nenue@87 67 TOP = 'BOTTOM',
Nenue@87 68 TOPLEFT = 'BOTTOMRIGHT',
Nenue@87 69 TOPRIGHT = 'BOTTOMLEFT',
Nenue@87 70 LEFT = 'RIGHT',
Nenue@87 71 RIGHT = 'LEFT',
Nenue@87 72 CENTER = 'CENTER',
Nenue@87 73 BOTTOM = 'TOP',
Nenue@87 74 BOTTOMRIGHT = 'TOPLEFT',
Nenue@87 75 BOTTOMLEFT = 'TOPRIGHT',
Nenue@87 76 }
Nenue@87 77 local ANCHOR_INSET_DELTA = {
Nenue@87 78 TOP = {0, -1},
Nenue@87 79 TOPLEFT = {1, -1},
Nenue@87 80 TOPRIGHT = {-1,-1},
Nenue@87 81 LEFT = {1, 0},
Nenue@87 82 BOTTOMLEFT = {1, 1},
Nenue@87 83 BOTTOM = {0, 1},
Nenue@87 84 BOTTOMRIGHT = {-1, 1},
Nenue@87 85 RIGHT = {-1, 0},
Nenue@87 86 CENTER = {0, 0},
Nenue@72 87 }
Nenue@72 88
Nenue@84 89 function VeneerCore:print(...)
Nenue@84 90 local txt = '|cFFFFFF00Veneer|r:'
Nenue@84 91 for i = 1, select('#', ...) do
Nenue@84 92 txt = txt .. ' '.. tostring(select(i, ...))
Nenue@84 93 end
Nenue@84 94
Nenue@84 95 DEFAULT_CHAT_FRAME:AddMessage(txt)
Nenue@84 96 end
Nenue@84 97
Nenue@84 98 function VeneerCore:OnLoad()
Nenue@84 99 print('|cFFFFFF00Veneer!|r')
Nenue@84 100 self:RegisterEvent('ADDON_LOADED')
Nenue@84 101 self:RegisterEvent('PLAYER_LOGIN')
Nenue@84 102
Nenue@84 103 self.DEVIAN_PNAME = 'Veneer'
Nenue@84 104 self:RegisterForDrag('LeftButton')
Nenue@88 105
Nenue@88 106
Nenue@84 107 end
Nenue@84 108
Nenue@90 109 local select, IsAddOnLoaded, IsLoggedIn = select, IsAddOnLoaded, IsLoggedIn
Nenue@90 110
Nenue@84 111 function VeneerCore:OnEvent(event, ...)
Nenue@90 112 print(event, ...)
Nenue@84 113 if event == 'ADDON_LOADED' or event == 'PLAYER_LOGIN' then
Nenue@90 114 print(IsLoggedIn(), self.initialized)
Nenue@84 115 if IsLoggedIn() and not self.intialized then
Nenue@84 116 self:Setup()
Nenue@90 117 self.intialized = true
Nenue@90 118 print('popping init sequence', self.intialized)
Nenue@90 119 end
Nenue@90 120
Nenue@90 121
Nenue@90 122 if self.intialized then
Nenue@90 123 local addon = ...
Nenue@90 124 if self.AddOnCheck[addon] then
Nenue@90 125 print(' - setting up '..addon..' dependent modules:')
Nenue@90 126 local keepChecking = false
Nenue@90 127 for index, handler in ipairs(self.AddOnCheck[addon]) do
Nenue@90 128 print(' -', handler:GetName(), (not handler.initialized) and (handler.addonFrame and not _G[handler.addonFrame]))
Nenue@90 129 if not handler.initialized then
Nenue@90 130 print(' '..handler:GetName()..':Setup()')
Nenue@90 131 handler:Setup()
Nenue@90 132 handler.initialized = true
Nenue@90 133 end
Nenue@90 134 end
Nenue@90 135 if not keepChecking then
Nenue@90 136 self.AddOnCheck[addon] = nil
Nenue@90 137 end
Nenue@90 138 end
Nenue@84 139 end
Nenue@84 140 end
Nenue@84 141 end
Nenue@84 142
Nenue@84 143 function VeneerCore:OnDragStart()
Nenue@84 144 self:StartMoving()
Nenue@84 145 end
Nenue@84 146
Nenue@84 147 function VeneerCore:OnDragStop()
Nenue@84 148 self:StopMovingOrSizing()
Nenue@84 149 end
Nenue@84 150
Nenue@84 151 function VeneerCore:Setup ()
Nenue@90 152 local resetConfig = (not VeneerData)
Nenue@90 153 if (not VeneerData) then
Nenue@84 154 VeneerData = defaults
Nenue@90 155 VeneerData.version = VENEER_VERSION
Nenue@90 156 self:print('Fresh install.')
Nenue@90 157 elseif (VeneerData.version and VeneerData.version < VENEER_VERSION) then
Nenue@90 158 local oldVars = VeneerData
Nenue@90 159 VeneerData = defaults
Nenue@90 160 VeneerData.version = VENEER_VERSION
Nenue@90 161 VeneerData.oldSettings = oldVars
Nenue@90 162 self:print('Resetting to temp defaults. Use /vn retro to use old settings.')
Nenue@84 163 end
Nenue@84 164 self.data = VeneerData
Nenue@90 165 self:ExecuteOnClusters(nil, function(frame)
Nenue@90 166 if (not frame.addonTrigger) or select(2,IsAddOnLoaded(frame.addonTrigger)) then
Nenue@90 167 if not frame.initialized then
Nenue@90 168 frame:Setup()
Nenue@90 169 frame.initialized = true
Nenue@90 170 end
Nenue@90 171 end
Nenue@90 172 end)
Nenue@90 173
Nenue@90 174 self.ConfigMode = VeneerData.ConfigMode
Nenue@90 175 self:UpdateConfigLayers()
Nenue@90 176 self:Reanchor()
Nenue@90 177 self:Update()
Nenue@87 178 end
Nenue@84 179
Nenue@90 180 function VeneerCore:UpdateConfigLayers()
Nenue@90 181 if VeneerData then
Nenue@90 182
Nenue@90 183 VeneerData.ConfigMode = self.ConfigMode
Nenue@90 184 end
Nenue@90 185
Nenue@90 186 self:print('Config mode '..(self.ConfigMode and '|cFF00FF00ON|r' or '|cFFFF0000OFF|r')..'.')
Nenue@90 187 self:ExecuteOnClusters(nil, function(frame)
Nenue@90 188 if frame.UpdateConfigLayers then
Nenue@90 189 frame:UpdateConfigLayers(self.ConfigMode)
Nenue@90 190 end
Nenue@90 191
Nenue@90 192
Nenue@90 193 if type(frame.ConfigLayer) == 'table' then
Nenue@90 194 for index, region in ipairs(frame.ConfigLayer) do
Nenue@90 195 print('setting', frame:GetName() .. '['.. index..']', 'to', self.ConfigMode)
Nenue@90 196
Nenue@90 197 region:SetShown(self.ConfigMode)
Nenue@90 198 end
Nenue@90 199 end
Nenue@90 200
Nenue@90 201 self.ConfigLayers[frame] = frame:IsShown()
Nenue@90 202 if self.ConfigMode then
Nenue@90 203 print(frame:GetName(), self.ConfigLayers[frame])
Nenue@90 204 frame:SetShown(self.ConfigMode)
Nenue@90 205 else
Nenue@90 206 frame:SetShown(self.ConfigLayers[frame])
Nenue@90 207 end
Nenue@90 208 end)
Nenue@90 209 end
Nenue@84 210
Nenue@87 211 function VeneerCore:GetClusterFromArgs (...)
Nenue@87 212 local primaryAnchor
Nenue@87 213 local insertPosition
Nenue@90 214
Nenue@90 215
Nenue@90 216
Nenue@87 217 local clusterTable = self.FrameClusters
Nenue@87 218 for i = 1, select('#', ...) do
Nenue@87 219 local arg = select(i, ...)
Nenue@87 220 local argType = type(arg)
Nenue@87 221 if argType == 'string' then
Nenue@87 222 if not primaryAnchor then
Nenue@87 223 primaryAnchor = arg
Nenue@87 224 end
Nenue@87 225 clusterTable[arg] = clusterTable[arg] or {}
Nenue@87 226 clusterTable = clusterTable[arg]
Nenue@87 227 print(string.rep(' ', i)..'anchor cluster', i, arg)
Nenue@87 228 elseif argType == 'boolean' then
Nenue@87 229 insertPosition = 1
Nenue@87 230 end
Nenue@87 231 end
Nenue@87 232 if not primaryAnchor then
Nenue@87 233 primaryAnchor = 'TOPLEFT'
Nenue@87 234 end
Nenue@87 235 if not insertPosition then
Nenue@87 236 insertPosition = #clusterTable + 1
Nenue@87 237 end
Nenue@87 238 return primaryAnchor, clusterTable, insertPosition
Nenue@84 239 end
Nenue@84 240
Nenue@84 241 function VeneerCore:AddHandler(handler, ...)
Nenue@84 242 print('*** Adding handler:', handler.moduleName or handler:GetName())
Nenue@87 243
Nenue@90 244
Nenue@90 245 local anchorGroup, clusterTable, clusterIndex = self:GetClusterFromArgs(...)
Nenue@90 246 if clusterIndex == 1 then
Nenue@90 247 for i, frame in ipairs(clusterTable) do
Nenue@90 248 frame.clusterIndex = i + 1
Nenue@90 249 end
Nenue@87 250 end
Nenue@90 251 tinsert(clusterTable, clusterIndex, handler)
Nenue@90 252
Nenue@87 253 print('cluster', anchorGroup, 'table', clusterTable, 'position', clusterIndex)
Nenue@87 254
Nenue@87 255 handler.anchorCluster = clusterTable
Nenue@87 256 handler.anchorIndex = clusterIndex
Nenue@84 257 for k,v in pairs(VeneerHandlerMixin) do
Nenue@84 258 if not handler[k] then
Nenue@87 259 print(' * from mixin:', k)
Nenue@84 260 handler[k] = v
Nenue@84 261 end
Nenue@84 262 end
Nenue@90 263
Nenue@90 264 if handler.addonTrigger and not IsAddOnLoaded(handler.addonTrigger) then
Nenue@90 265 print('|cFFFF4400 -- dependency:', handler.addonTrigger)
Nenue@90 266 self.AddOnCheck[handler.addonTrigger] = self.AddOnCheck[handler.addonTrigger] or {}
Nenue@90 267 tinsert(self.AddOnCheck[handler.addonTrigger], handler)
Nenue@90 268 end
Nenue@90 269
Nenue@87 270 if self.initialized then
Nenue@90 271 print(' -- initialization check')
Nenue@90 272 if handler.Setup then
Nenue@90 273 local doInit = (not handler.initialized)
Nenue@90 274 if handler.addonTrigger and not IsAddOnLoaded(handler.addonTrigger) then
Nenue@90 275 doInit = false
Nenue@90 276 end
Nenue@90 277 -- room to add other checks
Nenue@90 278
Nenue@90 279 if doInit then
Nenue@90 280 handler:Setup()
Nenue@90 281 handler.initialized = true
Nenue@90 282 self:InternalReanchor(handler)
Nenue@90 283 end
Nenue@87 284 end
Nenue@87 285 end
Nenue@87 286 end
Nenue@87 287
Nenue@87 288 function VeneerCore:Reanchor()
Nenue@87 289 self:ExecuteOnClusters(nil, 'Reanchor')
Nenue@88 290 self:DynamicReanchor(self)
Nenue@87 291 end
Nenue@87 292
Nenue@87 293 function VeneerCore:Update()
Nenue@90 294 self:ExecuteOnClusters(nil, function(frame)
Nenue@90 295 if frame.initialized and frame.Update then
Nenue@90 296 frame:Update()
Nenue@90 297 end
Nenue@90 298 end)
Nenue@88 299 self:Reanchor()
Nenue@87 300 end
Nenue@87 301
Nenue@87 302 -- updates anchor relations to and from the target handler
Nenue@87 303 function VeneerCore:GetAnchor(...)
Nenue@87 304
Nenue@87 305 end
Nenue@87 306
Nenue@88 307 -- Evaluates frames visibility and chains them accordingly
Nenue@88 308
Nenue@88 309 function VeneerCore:DynamicReanchor(parent)
Nenue@88 310 parent = parent or self
Nenue@88 311 print('|cFF88FF00DynamicReanchor()')
Nenue@88 312 for anchorPoint, cluster in pairs(parent.FrameClusters) do
Nenue@88 313 local lastFrame
Nenue@88 314 for index, frame in ipairs(cluster) do
Nenue@90 315 print(' |cFF00FF00'..index, frame:GetName(), frame:IsVisible(), (lastFrame and ('|cFFFFFF00'..lastFrame:GetName()..'|r') or '|cFF00FFFFUIParent'))
Nenue@88 316 if frame:IsVisible() then
Nenue@90 317
Nenue@90 318 if frame.anchorFrame then
Nenue@90 319 frame:SetPoint(frame.anchorPoint, frame.anchorFrame, frame.anchorFrom, frame.anchorX, frame.anchorY)
Nenue@90 320 print(frame:GetTop(), frame:GetRight())
Nenue@88 321 else
Nenue@90 322 anchorPoint = frame.anchorPoint
Nenue@90 323 frame:ClearAllPoints()
Nenue@90 324 if lastFrame then
Nenue@90 325 frame:SetPoint(anchorPoint, lastFrame, ANCHOR_OFFSET_POINT[anchorPoint], 0, 0)
Nenue@90 326 else
Nenue@90 327 frame:SetPoint(anchorPoint, UIParent, anchorPoint, frame.anchorX, frame.anchorY)
Nenue@90 328 end
Nenue@90 329 print(frame:GetTop(), frame:GetRight())
Nenue@90 330 lastFrame = frame
Nenue@88 331 end
Nenue@90 332
Nenue@88 333 end
Nenue@88 334
Nenue@88 335 end
Nenue@88 336 end
Nenue@88 337 end
Nenue@88 338
Nenue@88 339 -- Evaluates the current visibility state and re-anchors adjacent blocks accordingly
Nenue@87 340 function VeneerCore:InternalReanchor(handler, printFunc)
Nenue@87 341 print('|cFF00FFFFVeneer:InternalReanchor('..handler:GetName()..')')
Nenue@90 342 if handler.anchorFrame then
Nenue@90 343 handler:SetPoint(handler.anchorPoint, handler.anchorFrame, handler.anchorFrom, handler.anchorX, handler.anchorY)
Nenue@90 344 return
Nenue@90 345 end
Nenue@90 346
Nenue@90 347
Nenue@87 348 local anchorPoint = handler.anchorPath or handler.anchorPoint
Nenue@87 349 local anchorParent, anchorTo = UIParent, anchorPoint
Nenue@88 350 local subPoint, subTo
Nenue@88 351 local nextFrame
Nenue@88 352 for index, frame in ipairs(handler.anchorCluster) do
Nenue@88 353 print(' |cFF00FF00'..index, frame:GetName(), frame:IsVisible())
Nenue@88 354 if frame:IsVisible() then
Nenue@88 355 if frame ~= handler then
Nenue@88 356 anchorParent = frame
Nenue@88 357 anchorTo = ANCHOR_OFFSET_POINT[anchorPoint]
Nenue@87 358
Nenue@88 359 else
Nenue@88 360 nextFrame = handler.anchorCluster[index+1]
Nenue@88 361 if nextFrame then
Nenue@88 362
Nenue@88 363 subPoint = nextFrame.anchorPath or nextFrame.anchorPoint
Nenue@88 364 subTo = ANCHOR_OFFSET_POINT[subPoint]
Nenue@88 365 nextFrame:ClearAllPoints()
Nenue@88 366 nextFrame:SetPoint(subPoint, handler, subTo, 0, 0)
Nenue@88 367 print(' -- pushing '..nextFrame:GetName()..' down the anchor chain', subPoint, subTo)
Nenue@88 368 end
Nenue@88 369 break
Nenue@87 370 end
Nenue@87 371 end
Nenue@87 372 end
Nenue@87 373
Nenue@88 374 if handler:IsVisible() then
Nenue@88 375 handler:SetPoint(anchorPoint, anchorParent, anchorTo, 0, 0)
Nenue@88 376 else
Nenue@88 377 if anchorParent and nextFrame then
Nenue@88 378 nextFrame:SetPoint(subPoint, handler, subTo, 0, 0)
Nenue@88 379 end
Nenue@88 380 end
Nenue@88 381
Nenue@87 382
Nenue@87 383 print(handler.anchorPoint, anchorParent, anchorTo)
Nenue@87 384 if printFunc then
Nenue@87 385 printFunc('|cFF88FF00'..handler:GetName()..':SetPoint(', handler.anchorPoint, anchorParent, anchorTo)
Nenue@87 386 end
Nenue@88 387 end
Nenue@87 388
Nenue@88 389 function VeneerCore:SlideBlock(frame, ...)
Nenue@89 390 local aX, aY = frame:GetLeft(), frame:GetTop()
Nenue@88 391
Nenue@89 392 frame:SetPoint('TOPLEFT', frame, 'BOTTOMLEFT', aX, aY)
Nenue@89 393 frame.animation = frame.animation or {}
Nenue@89 394 frame.animation.startX = aX
Nenue@89 395 frame.animation.startY = aY
Nenue@88 396
Nenue@89 397 local targetPoint, targetParent, targetAnchor, offsetX, offsetY = ...
Nenue@89 398 frame.BlockSlide:SetScript('OnFinished', function()
Nenue@89 399 frame:SetPoint(targetPoint, targetParent, targetAnchor, offsetX, offsetY)
Nenue@89 400 VeneerAnimationMixin.OnFinished(frame)
Nenue@89 401 end)
Nenue@88 402
Nenue@84 403 end
Nenue@84 404
Nenue@88 405
Nenue@84 406 function VeneerCore:ExecuteOnClusters(layer, method)
Nenue@84 407 self.parserDepth = self.parserDepth + 1
Nenue@84 408 if not layer then
Nenue@87 409 if self.parserDepth > 1 then
Nenue@84 410 tinsert(self.pendingCalls, method)
Nenue@84 411 print('delaying walk for', method)
Nenue@84 412 return
Nenue@84 413 end
Nenue@90 414 print('|cFF00FF00Veneer:ExecuteOnClusters|r('..tostring(layer)..', '..tostring(method)..')')
Nenue@84 415 else
Nenue@87 416 print(' Level '..self.parserDepth)
Nenue@84 417 end
Nenue@87 418
Nenue@87 419 layer = layer or self.FrameClusters
Nenue@84 420 for anchor, cluster in pairs(layer) do
Nenue@84 421 for index, frame in ipairs(cluster) do
Nenue@87 422 print(' '..anchor..'.'..index..' = '..frame:GetName())
Nenue@90 423 if type(method) == 'function' then
Nenue@90 424 method(frame, true)
Nenue@90 425 elseif frame[method] then
Nenue@87 426 print(' |cFF00FF00'..frame:GetName())
Nenue@87 427 frame[method](frame, true)
Nenue@84 428 end
Nenue@84 429 end
Nenue@84 430 if cluster.FrameClusters then
Nenue@84 431 self:ExecuteOnClusters(cluster.FrameClusters, method)
Nenue@84 432 end
Nenue@84 433 end
Nenue@84 434 self.parserDepth = self.parserDepth - 1
Nenue@84 435
Nenue@84 436 if (self.parserDepth == 0) and (#self.pendingCalls >= 1) then
Nenue@84 437 local delayedMethod = tremove(self.pendingCalls, 1)
Nenue@84 438 print('starting delayed walk for', delayedMethod)
Nenue@84 439 self:ExecuteOnClusters(nil, delayedMethod)
Nenue@84 440 end
Nenue@84 441 end
Nenue@84 442
Nenue@72 443
Nenue@71 444
Nenue@88 445 -- Takes frame handle and assigns a block to it
Nenue@84 446 function VeneerCore:Acquire (frame, template)
Nenue@71 447 if not frame then
Nenue@71 448 print('|cFFFF4400Unable to acquire frame...|r')
Nenue@71 449 return
Nenue@71 450 end
Nenue@84 451 local veneer = self.Frames[frame]
Nenue@84 452 if not veneer then
Nenue@84 453 local name = type(frame) == 'table' and GetTableName(frame) or GetAnonymousName()
Nenue@90 454 veneer = CreateFrame('Frame', name, frame, template or 'VeneerStatusButtonTemplate')
Nenue@90 455 print(self:GetName()..':Acquire()', frame:GetName(), template)
Nenue@71 456
Nenue@84 457 veneer:SetAllPoints(frame)
Nenue@84 458 veneer:SetParent(frame)
Nenue@84 459 veneer.label:SetText(name)
Nenue@84 460 veneer.bg:SetColorTexture(0,0,0,0)
Nenue@84 461 veneer:Hide()
Nenue@84 462 veneer:EnableMouse(false)
Nenue@84 463 -- find current X/Y
Nenue@84 464 veneer.currentLeft = frame:GetLeft()
Nenue@84 465 veneer.currentTop = frame:GetTop()
Nenue@84 466 self.Frames[frame] = veneer
Nenue@71 467 end
Nenue@84 468 return veneer
Nenue@88 469 end