annotate Veneer.lua @ 87:27db212af783

- polished position management code; goes something like: - core:Reanchor() to soft fix all anchors - module:Reanchor() or core:InternalReanchor(module) to position a specific set of neighboring frames
author Nenue
date Wed, 19 Oct 2016 16:51:17 -0400
parents 16b300d96724
children b107b4df7eb6
rev   line source
Nenue@54 1 -- Veneer
Nenue@71 2 -- Base framework for making things draggable.
Nenue@0 3
Nenue@84 4
Nenue@84 5
Nenue@84 6
Nenue@84 7 SLASH_VENEER1 = "/veneer"
Nenue@84 8 SLASH_VENEER2 = "/vn"
Nenue@84 9
Nenue@84 10 SlashCmdList.VENEER = function(cmd)
Nenue@84 11 end
Nenue@84 12 VeneerCore = {
Nenue@84 13 Frames = {},
Nenue@84 14 ConfigLayers = {},
Nenue@84 15 FrameClusters = {},
Nenue@84 16 parserDepth = 0,
Nenue@84 17 pendingCalls = {},
Nenue@84 18 }
Nenue@84 19 VeneerHandlerMixin = {
Nenue@87 20 Reanchor = nop,
Nenue@87 21 anchorPoint = 'CENTER', -- indicates the initial cluster group point
Nenue@87 22 --anchorPath = 'BOTTOM', -- indicates the point from which the frame is anchored in a cluster arrangement
Nenue@84 23 }
Nenue@84 24 local print = DEVIAN_WORKSPACE and function(...) print('Veneer', ...) end or nop
Nenue@80 25 local wipe = table.wipe
Nenue@0 26
Nenue@59 27 local defaults = {
Nenue@59 28 enableAll = true,
Nenue@59 29 enableModule = {
Nenue@59 30 BuffFrame = true,
Nenue@59 31 },
Nenue@59 32 BuffFrame = {
Nenue@59 33 width = 48,
Nenue@59 34 height = 48,
Nenue@59 35 }
Nenue@59 36 }
Nenue@84 37
Nenue@71 38 local configMode
Nenue@79 39 local anonID = 0
Nenue@79 40 local tostring = tostring
Nenue@79 41 local IsFrameHandle = IsFrameHandle
Nenue@79 42 local GetAnonymousName = function(key)
Nenue@79 43 if not key then
Nenue@71 44 anonID = anonID + 1
Nenue@79 45 key = anonID
Nenue@71 46 end
Nenue@79 47 return 'VN' .. key
Nenue@71 48 end
Nenue@79 49 local GetTableName = function(table)
Nenue@79 50 return (IsFrameHandle(table) and table:GetName()) or tostring(table)
Nenue@79 51 end
Nenue@79 52
Nenue@87 53 local OFFSET_PARALLELS = {
Nenue@87 54 TOP = {'LEFT', 'RIGHT', 'SetHeight'},
Nenue@87 55 BOTTOM = {'LEFT', 'RIGHT', 'SetHeight'},
Nenue@87 56 LEFT = {'TOP', 'BOTTOM', 'SetWidth'},
Nenue@87 57 RIGHT = {'TOP', 'BOTTOM', 'SetWidth'},
Nenue@87 58 }
Nenue@87 59 local ANCHOR_OFFSET_POINT = {
Nenue@87 60 TOP = 'BOTTOM',
Nenue@87 61 TOPLEFT = 'BOTTOMRIGHT',
Nenue@87 62 TOPRIGHT = 'BOTTOMLEFT',
Nenue@87 63 LEFT = 'RIGHT',
Nenue@87 64 RIGHT = 'LEFT',
Nenue@87 65 CENTER = 'CENTER',
Nenue@87 66 BOTTOM = 'TOP',
Nenue@87 67 BOTTOMRIGHT = 'TOPLEFT',
Nenue@87 68 BOTTOMLEFT = 'TOPRIGHT',
Nenue@87 69 }
Nenue@87 70 local ANCHOR_INSET_DELTA = {
Nenue@87 71 TOP = {0, -1},
Nenue@87 72 TOPLEFT = {1, -1},
Nenue@87 73 TOPRIGHT = {-1,-1},
Nenue@87 74 LEFT = {1, 0},
Nenue@87 75 BOTTOMLEFT = {1, 1},
Nenue@87 76 BOTTOM = {0, 1},
Nenue@87 77 BOTTOMRIGHT = {-1, 1},
Nenue@87 78 RIGHT = {-1, 0},
Nenue@87 79 CENTER = {0, 0},
Nenue@72 80 }
Nenue@72 81
Nenue@84 82 function VeneerCore:print(...)
Nenue@84 83 local txt = '|cFFFFFF00Veneer|r:'
Nenue@84 84 for i = 1, select('#', ...) do
Nenue@84 85 txt = txt .. ' '.. tostring(select(i, ...))
Nenue@84 86 end
Nenue@84 87
Nenue@84 88 DEFAULT_CHAT_FRAME:AddMessage(txt)
Nenue@84 89 end
Nenue@84 90
Nenue@84 91 function VeneerCore:OnLoad()
Nenue@84 92 print('|cFFFFFF00Veneer!|r')
Nenue@84 93 self:RegisterEvent('ADDON_LOADED')
Nenue@84 94 self:RegisterEvent('PLAYER_LOGIN')
Nenue@84 95
Nenue@84 96 self.DEVIAN_PNAME = 'Veneer'
Nenue@84 97 self:RegisterForDrag('LeftButton')
Nenue@84 98 end
Nenue@84 99
Nenue@84 100 function VeneerCore:OnEvent(event, ...)
Nenue@84 101 if event == 'ADDON_LOADED' or event == 'PLAYER_LOGIN' then
Nenue@84 102 if IsLoggedIn() and not self.intialized then
Nenue@84 103 self:Setup()
Nenue@87 104 self:UnregisterEvent('ADDON_LOADED')
Nenue@87 105 self:UnregisterEvent('PLAYER_LOGIN')
Nenue@87 106 self:Reanchor()
Nenue@87 107 self:Update()
Nenue@84 108 end
Nenue@84 109 end
Nenue@84 110 end
Nenue@84 111
Nenue@84 112 function VeneerCore:OnDragStart()
Nenue@84 113 self:StartMoving()
Nenue@84 114 end
Nenue@84 115
Nenue@84 116
Nenue@84 117 function VeneerCore:OnDragStop()
Nenue@84 118 self:StopMovingOrSizing()
Nenue@84 119 end
Nenue@84 120
Nenue@84 121 function VeneerCore:Setup ()
Nenue@87 122 self.initialized = true
Nenue@84 123 if (not VeneerData) or (not VeneerData.version) then
Nenue@84 124 VeneerData = defaults
Nenue@84 125 end
Nenue@84 126 self.data = VeneerData
Nenue@87 127 self:ExecuteOnClusters(nil, 'Setup')
Nenue@87 128 end
Nenue@84 129
Nenue@84 130
Nenue@87 131 function VeneerCore:GetClusterFromArgs (...)
Nenue@87 132 local primaryAnchor
Nenue@87 133 local insertPosition
Nenue@87 134 local clusterTable = self.FrameClusters
Nenue@87 135 for i = 1, select('#', ...) do
Nenue@87 136 local arg = select(i, ...)
Nenue@87 137 local argType = type(arg)
Nenue@87 138 if argType == 'string' then
Nenue@87 139 if not primaryAnchor then
Nenue@87 140 primaryAnchor = arg
Nenue@87 141 end
Nenue@87 142 clusterTable[arg] = clusterTable[arg] or {}
Nenue@87 143 clusterTable = clusterTable[arg]
Nenue@87 144 print(string.rep(' ', i)..'anchor cluster', i, arg)
Nenue@87 145 elseif argType == 'boolean' then
Nenue@87 146 insertPosition = 1
Nenue@87 147 end
Nenue@87 148 end
Nenue@87 149 if not primaryAnchor then
Nenue@87 150 primaryAnchor = 'TOPLEFT'
Nenue@87 151 end
Nenue@87 152 if not insertPosition then
Nenue@87 153 insertPosition = #clusterTable + 1
Nenue@87 154 end
Nenue@87 155
Nenue@87 156
Nenue@87 157 return primaryAnchor, clusterTable, insertPosition
Nenue@84 158 end
Nenue@84 159
Nenue@84 160 function VeneerCore:AddHandler(handler, ...)
Nenue@84 161 print('*** Adding handler:', handler.moduleName or handler:GetName())
Nenue@87 162
Nenue@87 163 local anchorGroup, clusterTable, clusterIndex = self:GetClusterFromArgs(...)
Nenue@87 164 if clusterIndex == 1 then
Nenue@87 165 for i, frame in ipairs(clusterTable) do
Nenue@87 166 frame.clusterIndex = i + 1
Nenue@87 167 end
Nenue@84 168 end
Nenue@87 169 tinsert(clusterTable, clusterIndex, handler)
Nenue@87 170 print('cluster', anchorGroup, 'table', clusterTable, 'position', clusterIndex)
Nenue@87 171
Nenue@87 172
Nenue@87 173 handler.anchorCluster = clusterTable
Nenue@87 174 handler.anchorIndex = clusterIndex
Nenue@84 175 for k,v in pairs(VeneerHandlerMixin) do
Nenue@84 176 if not handler[k] then
Nenue@87 177 print(' * from mixin:', k)
Nenue@84 178 handler[k] = v
Nenue@84 179 end
Nenue@84 180 end
Nenue@87 181 if self.initialized then
Nenue@87 182 print(' -- doing initialization')
Nenue@87 183 if handler.Setup and not handler.initialized then
Nenue@87 184 handler:Setup()
Nenue@87 185 handler.initialized = true
Nenue@87 186 end
Nenue@87 187 if handler.Update then
Nenue@87 188 handler:Update()
Nenue@87 189 end
Nenue@87 190 self:InternalReanchor(handler)
Nenue@87 191
Nenue@87 192 end
Nenue@87 193 end
Nenue@87 194
Nenue@87 195 function VeneerCore:Reanchor()
Nenue@87 196 self:ExecuteOnClusters(nil, 'Reanchor')
Nenue@87 197 end
Nenue@87 198
Nenue@87 199 function VeneerCore:Update()
Nenue@87 200 self:ExecuteOnClusters(nil, 'Update')
Nenue@87 201 end
Nenue@87 202
Nenue@87 203 -- updates anchor relations to and from the target handler
Nenue@87 204 function VeneerCore:GetAnchor(...)
Nenue@87 205
Nenue@87 206 end
Nenue@87 207
Nenue@87 208 function VeneerCore:InternalReanchor(handler, printFunc)
Nenue@87 209 print('|cFF00FFFFVeneer:InternalReanchor('..handler:GetName()..')')
Nenue@87 210 local anchorPoint = handler.anchorPath or handler.anchorPoint
Nenue@87 211 local anchorParent, anchorTo = UIParent, anchorPoint
Nenue@87 212 for i, frame in ipairs(handler.anchorCluster) do
Nenue@87 213 if frame ~= handler then
Nenue@87 214 anchorParent = frame
Nenue@87 215 anchorTo = ANCHOR_OFFSET_POINT[anchorPoint]
Nenue@87 216
Nenue@87 217 else
Nenue@87 218 local nextFrame = handler.anchorCluster[i+1]
Nenue@87 219 if nextFrame then
Nenue@87 220
Nenue@87 221 local subPoint = nextFrame.anchorPath or nextFrame.anchorPoint
Nenue@87 222 local subTo = ANCHOR_OFFSET_POINT[subPoint]
Nenue@87 223 nextFrame:ClearAllPoints()
Nenue@87 224 nextFrame:SetPoint(subPoint, handler, subTo, 0, 0)
Nenue@87 225 print(' -- pushing '..nextFrame:GetName()..' down the anchor chain', subPoint, subTo)
Nenue@87 226 end
Nenue@87 227 break
Nenue@87 228 end
Nenue@87 229 end
Nenue@87 230
Nenue@87 231 handler:ClearAllPoints()
Nenue@87 232 handler:SetPoint(anchorPoint, anchorParent, anchorTo, 0, 0)
Nenue@87 233
Nenue@87 234 print(handler.anchorPoint, anchorParent, anchorTo)
Nenue@87 235 if printFunc then
Nenue@87 236 printFunc('|cFF88FF00'..handler:GetName()..':SetPoint(', handler.anchorPoint, anchorParent, anchorTo)
Nenue@87 237 end
Nenue@87 238
Nenue@84 239 end
Nenue@84 240
Nenue@84 241 function VeneerCore:ExecuteOnClusters(layer, method)
Nenue@84 242 self.parserDepth = self.parserDepth + 1
Nenue@84 243 if not layer then
Nenue@87 244 if self.parserDepth > 1 then
Nenue@84 245 tinsert(self.pendingCalls, method)
Nenue@84 246 print('delaying walk for', method)
Nenue@84 247 return
Nenue@84 248 end
Nenue@87 249 print('|cFF00FF00Veneer:ExecuteOnClusters|r('..tostring(layer)..', '..method..')')
Nenue@84 250 else
Nenue@87 251 print(' Level '..self.parserDepth)
Nenue@84 252 end
Nenue@87 253
Nenue@87 254 layer = layer or self.FrameClusters
Nenue@84 255 for anchor, cluster in pairs(layer) do
Nenue@84 256 for index, frame in ipairs(cluster) do
Nenue@87 257 print(' '..anchor..'.'..index..' = '..frame:GetName())
Nenue@84 258 if frame[method] then
Nenue@87 259 print(' |cFF00FF00'..frame:GetName())
Nenue@87 260 frame[method](frame, true)
Nenue@84 261 end
Nenue@84 262 end
Nenue@84 263 if cluster.FrameClusters then
Nenue@84 264 self:ExecuteOnClusters(cluster.FrameClusters, method)
Nenue@84 265 end
Nenue@84 266 end
Nenue@84 267 self.parserDepth = self.parserDepth - 1
Nenue@84 268
Nenue@84 269 if (self.parserDepth == 0) and (#self.pendingCalls >= 1) then
Nenue@84 270 local delayedMethod = tremove(self.pendingCalls, 1)
Nenue@84 271 print('starting delayed walk for', delayedMethod)
Nenue@84 272 self:ExecuteOnClusters(nil, delayedMethod)
Nenue@84 273 end
Nenue@84 274 end
Nenue@84 275
Nenue@72 276 local VeneerButton_OnDragStart = function(self)
Nenue@72 277 self.startingLeft = self:GetLeft()
Nenue@72 278 self.startingBottom = self:GetBottom()
Nenue@72 279 self.anchors = self.anchors or {}
Nenue@72 280 table.wipe(self.anchors)
Nenue@72 281
Nenue@72 282 local frame = self:GetParent()
Nenue@72 283 local n = frame:GetNumPoints()
Nenue@72 284 for i = 1, n do
Nenue@72 285 local anchor, parent, relative, x, y = frame:GetPoint(i)
Nenue@72 286 self.anchors[i] = {
Nenue@72 287 anchor = anchor,
Nenue@72 288 parent = parent,
Nenue@72 289 relative = relative,
Nenue@72 290 x = x,
Nenue@72 291 y = y
Nenue@72 292 }
Nenue@72 293 end
Nenue@72 294
Nenue@72 295 print(self:GetName(), 'start moving', self.startingLeft, self.startingBottom)
Nenue@72 296 self:StartMoving()
Nenue@72 297 end
Nenue@72 298
Nenue@72 299 local VeneerButton_OnDragStop = function(self)
Nenue@72 300 self:StopMovingOrSizing()
Nenue@72 301 if self.OnDragStop then
Nenue@72 302 self.OnDragStop(self)
Nenue@72 303 else
Nenue@72 304 local frame = self:GetParent()
Nenue@72 305 local dx = self:GetLeft() - self.startingLeft
Nenue@72 306 local dy = self:GetBottom() - self.startingBottom
Nenue@72 307
Nenue@72 308 frame:ClearAllPoints()
Nenue@72 309 for i, point in ipairs(self.anchors) do
Nenue@72 310 frame:SetPoint(point.anchor, point.parent, point.relative, point.x + dx, point.y + dy)
Nenue@72 311 print('adjusting anchor', point.anchor, point.parent, point.relative, point.x + dx, point.y + dy)
Nenue@72 312 end
Nenue@72 313 end
Nenue@72 314 end
Nenue@72 315
Nenue@72 316 local Veneer_FixMovers = function()
Nenue@72 317 for frame, veneer in pairs(veneers) do
Nenue@72 318 if veneer:IsMoving() then
Nenue@72 319 VeneerButton_OnDragStop(veneer)
Nenue@72 320 end
Nenue@72 321 end
Nenue@72 322 end
Nenue@71 323
Nenue@71 324 local VeneerButton_Update = function(self)
Nenue@71 325 if configMode then
Nenue@72 326 self:SetScript('OnDragStart', VeneerButton_OnDragStart)
Nenue@72 327 self:SetScript('OnDragStop', VeneerButton_OnDragStop)
Nenue@72 328 self:SetMovable(true)
Nenue@72 329 self:EnableMouse(true)
Nenue@71 330 self:RegisterForDrag('LeftButton')
Nenue@71 331
Nenue@71 332 self.bg:SetColorTexture(0,1,0,0.5)
Nenue@72 333 for i, region in ipairs(self.configLayers) do
Nenue@72 334 region:Show()
Nenue@72 335 end
Nenue@72 336 self:Show()
Nenue@71 337 else
Nenue@71 338
Nenue@71 339 self:SetScript('OnDragStart', self.StartMoving)
Nenue@71 340 self:SetScript('OnDragStop', self.StopMovingOrSizing)
Nenue@71 341 self:SetMovable(false)
Nenue@71 342 self:EnableMouse(false)
Nenue@71 343
Nenue@71 344 self.bg:SetColorTexture(0,1,0,0)
Nenue@72 345 for i, region in ipairs(self.configLayers) do
Nenue@72 346 region:Hide()
Nenue@72 347 end
Nenue@72 348 if self.isHidden then
Nenue@72 349 self:Hide()
Nenue@72 350 end
Nenue@72 351
Nenue@71 352 end
Nenue@71 353 end
Nenue@71 354
Nenue@71 355 local ToggleVeneerConfig = function()
Nenue@71 356 if configMode then
Nenue@71 357 configMode = false
Nenue@84 358 Veneer:print('Config mode off.')
Nenue@71 359 else
Nenue@71 360 configMode = true
Nenue@84 361 Veneer:print('Config mode on.')
Nenue@71 362 end
Nenue@71 363
Nenue@71 364 for frame, veneer in pairs(veneers) do
Nenue@71 365 VeneerButton_Update(veneer)
Nenue@71 366 end
Nenue@71 367 end
Nenue@71 368
Nenue@71 369 local VeneerButton_OnShow = function(self)
Nenue@71 370 VeneerButton_Update(self)
Nenue@71 371 end
Nenue@71 372
Nenue@84 373 function VeneerCore:Acquire (frame, template)
Nenue@71 374 if not frame then
Nenue@71 375 print('|cFFFF4400Unable to acquire frame...|r')
Nenue@71 376 return
Nenue@71 377 end
Nenue@84 378 local veneer = self.Frames[frame]
Nenue@84 379 if not veneer then
Nenue@84 380 local name = type(frame) == 'table' and GetTableName(frame) or GetAnonymousName()
Nenue@84 381 veneer = CreateFrame('Frame', name, frame, template or 'VeneerTemplate')
Nenue@84 382 print('+veneer', name)
Nenue@71 383
Nenue@84 384 veneer:SetAllPoints(frame)
Nenue@84 385 veneer:SetParent(frame)
Nenue@84 386 veneer.label:SetText(name)
Nenue@84 387 veneer.bg:SetColorTexture(0,0,0,0)
Nenue@84 388 veneer:Hide()
Nenue@84 389 veneer:EnableMouse(false)
Nenue@84 390
Nenue@84 391 veneer:SetScript('OnShow', VeneerButton_OnShow)
Nenue@84 392
Nenue@84 393 -- find current X/Y
Nenue@84 394 veneer.currentLeft = frame:GetLeft()
Nenue@84 395 veneer.currentTop = frame:GetTop()
Nenue@84 396 self.Frames[frame] = veneer
Nenue@71 397 end
Nenue@71 398
Nenue@84 399 return veneer
Nenue@71 400 end
Nenue@0 401
Nenue@80 402 local mixin_probe = {
Nenue@80 403 'ArtifactFrame',
Nenue@80 404 'ArtifactFrameUnderlay',
Nenue@80 405 }
Nenue@80 406
Nenue@80 407