annotate classes/Bar.lua @ 237:704f4a05a1d7

Demodulificate all bar options (except state options)
author Flick
date Thu, 24 Mar 2011 13:11:30 -0700
parents 0e20f65375d5
children 09c8e9baa35a
rev   line source
flickerstreak@175 1 local addonName, addonTable = ...
flickerstreak@175 2 local ReAction = addonTable.ReAction
flickerstreak@122 3 local L = ReAction.L
flickerstreak@183 4 local LKB = ReAction.LKB
flickerstreak@122 5 local _G = _G
flickerstreak@122 6 local CreateFrame = CreateFrame
flickerstreak@122 7 local floor = math.floor
flickerstreak@122 8 local fmod = math.fmod
flickerstreak@122 9 local format = string.format
flickerstreak@122 10
flickerstreak@193 11 local LSG = LibStub("ReAction-LibShowActionGrid-1.0")
flickerstreak@147 12
flickerstreak@155 13 ---- Secure snippets ----
flickerstreak@155 14 local _reaction_init =
flickerstreak@155 15 [[
flickerstreak@155 16 anchorKeys = newtable("point","relPoint","x","y")
flickerstreak@155 17
flickerstreak@155 18 state = nil
flickerstreak@155 19 set_state = nil
flickerstreak@155 20 state_override = nil
flickerstreak@156 21 unit_exists = nil
flickerstreak@155 22
flickerstreak@155 23 showAll = false
flickerstreak@155 24 hidden = false
flickerstreak@155 25
flickerstreak@155 26 defaultAlpha = 1.0
flickerstreak@155 27 defaultScale = 1.0
flickerstreak@155 28 defaultAnchor = newtable()
flickerstreak@155 29
flickerstreak@155 30 activeStates = newtable()
flickerstreak@155 31 settings = newtable()
flickerstreak@155 32 extensions = newtable()
flickerstreak@155 33 ]]
flickerstreak@155 34
flickerstreak@155 35 local _reaction_refresh =
flickerstreak@155 36 [[
flickerstreak@155 37 local oldState = state
flickerstreak@155 38 state = state_override or set_state or state
flickerstreak@155 39
flickerstreak@156 40 local hide = nil
flickerstreak@155 41 if state then
flickerstreak@155 42 local settings = settings[state]
flickerstreak@155 43 if settings then
flickerstreak@155 44 -- show/hide
flickerstreak@156 45 hide = settings.hide
flickerstreak@155 46 -- re-anchor
flickerstreak@155 47 local old_anchor = activeStates.anchor
flickerstreak@155 48 activeStates.anchor = settings.anchorEnable and state
flickerstreak@155 49 if old_anchor ~= activeStates.anchor or not set_state then
flickerstreak@155 50 if activeStates.anchor then
flickerstreak@155 51 if settings.anchorPoint then
flickerstreak@155 52 self:ClearAllPoints()
flickerstreak@155 53 local f = self:GetAttribute("frameref-anchor-"..state)
flickerstreak@155 54 if f then
flickerstreak@155 55 self:SetPoint(settings.anchorPoint, f, settings.anchorRelPoint, settings.anchorX, settings.anchorY)
flickerstreak@155 56 end
flickerstreak@155 57 end
flickerstreak@155 58 elseif defaultAnchor.point then
flickerstreak@155 59 self:ClearAllPoints()
flickerstreak@155 60 self:SetPoint(defaultAnchor.point, defaultAnchor.frame,
flickerstreak@155 61 defaultAnchor.relPoint, defaultAnchor.x, defaultAnchor.y)
flickerstreak@155 62 end
flickerstreak@155 63 end
flickerstreak@155 64 -- re-scale
flickerstreak@155 65 local old_scale = activeStates.scale
flickerstreak@155 66 activeStates.scale = settings.enableScale and state
flickerstreak@155 67 if old_scale ~= activeStates.scale or not set_state then
flickerstreak@155 68 self:SetScale(activeStates.scale and settings.scale or defaultScale)
flickerstreak@155 69 end
flickerstreak@155 70 -- alpha
flickerstreak@155 71 local old_alpha = activeStates.alpha
flickerstreak@155 72 activeStates.alpha = settings.enableAlpha and state
flickerstreak@155 73 if old_alpha ~= activeStates.alpha or not set_state then
flickerstreak@155 74 self:SetAlpha(activeStates.alpha and settings.alpha or defaultAlpha)
flickerstreak@155 75 end
flickerstreak@155 76 end
flickerstreak@155 77 end
flickerstreak@155 78
flickerstreak@156 79 -- hide if state or unit_exists says to
flickerstreak@156 80 hide = not showAll and (hide or unithide)
flickerstreak@156 81 if hide ~= hidden then
flickerstreak@156 82 hidden = hide
flickerstreak@156 83 if hide then
flickerstreak@156 84 self:Hide()
flickerstreak@156 85 else
flickerstreak@156 86 self:Show()
flickerstreak@156 87 end
flickerstreak@156 88 end
flickerstreak@156 89
flickerstreak@155 90 for _, attr in pairs(extensions) do
flickerstreak@155 91 control:RunAttribute(attr)
flickerstreak@155 92 end
flickerstreak@155 93
flickerstreak@155 94 control:ChildUpdate()
flickerstreak@155 95
flickerstreak@155 96 if showAll then
flickerstreak@155 97 control:CallMethod("UpdateHiddenLabel", state and settings[state] and settings[state].hide)
flickerstreak@155 98 end
flickerstreak@155 99
flickerstreak@155 100 if oldState ~= state then
flickerstreak@155 101 control:CallMethod("StateRefresh", state)
flickerstreak@155 102 end
flickerstreak@155 103 ]]
flickerstreak@155 104
flickerstreak@155 105 local _onstate_reaction = -- function( self, stateid, newstate )
flickerstreak@155 106 [[
flickerstreak@155 107 set_state = newstate
flickerstreak@155 108 ]] .. _reaction_refresh
flickerstreak@155 109
flickerstreak@155 110 local _onstate_showgrid = -- function( self, stateid, newstate )
flickerstreak@155 111 [[
flickerstreak@155 112 control:ChildUpdate(stateid,newstate)
flickerstreak@155 113 control:CallMethod("UpdateShowGrid")
flickerstreak@155 114 ]]
flickerstreak@155 115
flickerstreak@155 116 local _onstate_unitexists = -- function( self, stateid, newstate )
flickerstreak@155 117 [[
flickerstreak@159 118 unithide = not newstate or newstate == "hide"
flickerstreak@155 119 ]] .. _reaction_refresh
flickerstreak@155 120
flickerstreak@155 121 local _onclick = -- function( self, button, down )
flickerstreak@155 122 [[
flickerstreak@155 123 if state_override == button then
flickerstreak@155 124 state_override = nil -- toggle
flickerstreak@155 125 else
flickerstreak@155 126 state_override = button
flickerstreak@155 127 end
flickerstreak@155 128 ]] .. _reaction_refresh
flickerstreak@155 129
flickerstreak@157 130 -- For reference
flickerstreak@157 131 -- the option field names must match the field names of the options table, below
flickerstreak@157 132 local stateProperties = {
flickerstreak@157 133 hide = true,
flickerstreak@157 134 --keybindState = true, TODO: broken
flickerstreak@157 135 anchorEnable = true,
flickerstreak@157 136 anchorFrame = true,
flickerstreak@157 137 anchorPoint = true,
flickerstreak@157 138 anchorRelPoint = true,
flickerstreak@157 139 anchorX = true,
flickerstreak@157 140 anchorY = true,
flickerstreak@157 141 enableScale = true,
flickerstreak@157 142 scale = true,
flickerstreak@157 143 enableAlpha = true,
flickerstreak@157 144 alpha = true,
flickerstreak@157 145 }
flickerstreak@157 146
flickerstreak@157 147
flickerstreak@157 148 ---- Utility functions ----
flickerstreak@157 149
flickerstreak@157 150 -- traverse a table tree by key list and fetch the result or first nil
flickerstreak@157 151 local function tfetch(t, ...)
flickerstreak@157 152 for i = 1, select('#', ...) do
flickerstreak@157 153 t = t and t[select(i, ...)]
flickerstreak@157 154 end
flickerstreak@157 155 return t
flickerstreak@157 156 end
flickerstreak@157 157
flickerstreak@157 158 -- traverse a table tree by key list and build tree as necessary
flickerstreak@157 159 local function tbuild(t, ...)
flickerstreak@157 160 for i = 1, select('#', ...) do
flickerstreak@157 161 local key = select(i, ...)
flickerstreak@157 162 if not t[key] then t[key] = { } end
flickerstreak@157 163 t = t[key]
flickerstreak@157 164 end
flickerstreak@157 165 return t
flickerstreak@157 166 end
flickerstreak@157 167
flickerstreak@157 168 -- return a new array of keys of table 't', sorted by comparing
flickerstreak@157 169 -- sub-fields (obtained via tfetch) of the table values
flickerstreak@157 170 local function fieldsort( t, ... )
flickerstreak@157 171 local r = { }
flickerstreak@157 172 for k in pairs(t) do
flickerstreak@157 173 table.insert(r,k)
flickerstreak@157 174 end
flickerstreak@157 175 local path = { ... }
flickerstreak@157 176 table.sort(r, function(lhs, rhs)
flickerstreak@157 177 local olhs = tfetch(t[lhs], unpack(path)) or 0
flickerstreak@157 178 local orhs = tfetch(t[rhs], unpack(path)) or 0
flickerstreak@157 179 return olhs < orhs
flickerstreak@157 180 end)
flickerstreak@157 181 return r
flickerstreak@157 182 end
flickerstreak@157 183
flickerstreak@155 184
flickerstreak@147 185 ---- Bar class ----
flickerstreak@122 186 local Bar = { }
flickerstreak@147 187 local frameList = { }
flickerstreak@122 188
flickerstreak@122 189 ReAction.Bar = Bar -- export to ReAction
flickerstreak@122 190
Flick@234 191 function Bar:New( name, config, buttonClass )
flickerstreak@122 192 if type(config) ~= "table" then
flickerstreak@122 193 error("ReAction.Bar: config table required")
flickerstreak@122 194 end
flickerstreak@122 195
flickerstreak@122 196 -- create new self
flickerstreak@122 197 self = setmetatable(
flickerstreak@122 198 {
Flick@234 199 config = config,
Flick@234 200 name = name,
Flick@234 201 buttons = { },
Flick@234 202 buttonClass = buttonClass,
Flick@234 203 width = config.width or 480,
Flick@234 204 height = config.height or 40,
flickerstreak@122 205 },
flickerstreak@147 206 {__index = self} )
flickerstreak@122 207
flickerstreak@122 208 -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is
flickerstreak@122 209 -- not mouse-clickable by the user.
flickerstreak@122 210 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
flickerstreak@147 211 name = name and "ReAction-"..name
flickerstreak@147 212 local f = name and frameList[name]
flickerstreak@147 213 if not f then
flickerstreak@147 214 f = CreateFrame("Button", name, parent, "SecureHandlerStateTemplate, SecureHandlerClickTemplate")
flickerstreak@147 215 if name then
flickerstreak@147 216 frameList[name] = f
flickerstreak@147 217 end
flickerstreak@147 218 end
flickerstreak@122 219 f:SetFrameStrata("MEDIUM")
flickerstreak@122 220 f:SetWidth(self.width)
flickerstreak@146 221 f:SetHeight(self.height)
flickerstreak@122 222 f:SetAlpha(config.alpha or 1.0)
flickerstreak@122 223 f:Show()
flickerstreak@122 224 f:EnableMouse(false)
flickerstreak@122 225 f:SetClampedToScreen(true)
flickerstreak@171 226 LSG:AddFrame(f)
flickerstreak@122 227
flickerstreak@155 228 -- secure handlers
flickerstreak@155 229 f:Execute(_reaction_init)
flickerstreak@155 230 f:SetAttribute("_onstate-reaction", _onstate_reaction)
flickerstreak@155 231 f:SetAttribute("_onstate-showgrid", _onstate_showgrid)
flickerstreak@155 232 f:SetAttribute("_onstate-unitexists", _onstate_unitexists)
flickerstreak@155 233 f:SetAttribute("_onclick", _onclick)
flickerstreak@155 234
flickerstreak@155 235 -- secure handler CallMethod()s
flickerstreak@155 236 f.UpdateShowGrid = function() self:UpdateShowGrid() end
flickerstreak@155 237 f.StateRefresh = function() self:RefreshControls() end
flickerstreak@155 238 f.UpdateHiddenLabel = function(f,hidden) self:SetLabelSubtext(hidden and L["Hidden"]) end
flickerstreak@122 239
flickerstreak@122 240 -- Override the default frame accessor to provide strict read-only access
flickerstreak@122 241 function self:GetFrame()
flickerstreak@122 242 return f
flickerstreak@122 243 end
flickerstreak@122 244
flickerstreak@122 245 self:ApplyAnchor()
flickerstreak@147 246 self:SetConfigMode(ReAction:GetConfigMode())
flickerstreak@147 247 self:SetKeybindMode(ReAction:GetKeybindMode())
flickerstreak@147 248
flickerstreak@213 249 if ReAction.LBF then
flickerstreak@213 250 local g = ReAction.LBF:Group(L["ReAction"], self.name)
flickerstreak@213 251 self.config.ButtonFacade = self.config.ButtonFacade or {
flickerstreak@213 252 skinID = "Blizzard",
flickerstreak@213 253 backdrop = true,
flickerstreak@213 254 gloss = 0,
flickerstreak@213 255 colors = {},
flickerstreak@213 256 }
flickerstreak@213 257 local c = self.config.ButtonFacade
flickerstreak@213 258 g:Skin(c.skinID, c.gloss, c.backdrop, c.colors)
flickerstreak@213 259 self.LBFGroup = g
flickerstreak@213 260 end
flickerstreak@213 261
flickerstreak@122 262 ReAction.RegisterCallback(self, "OnConfigModeChanged")
flickerstreak@122 263
Flick@234 264 buttonClass:SetupBar(self)
Flick@234 265
flickerstreak@122 266 return self
flickerstreak@122 267 end
flickerstreak@122 268
flickerstreak@122 269 function Bar:Destroy()
flickerstreak@122 270 local f = self:GetFrame()
Flick@234 271 for idx, b in self:IterateButtons() do
Flick@234 272 b:Destroy()
Flick@234 273 end
flickerstreak@122 274 f:UnregisterAllEvents()
flickerstreak@125 275 self:ShowControls(false)
flickerstreak@122 276 ReAction.UnregisterAllCallbacks(self)
flickerstreak@171 277 LKB.UnregisterAllCallbacks(self)
flickerstreak@213 278 if self.LBFGroup then
flickerstreak@213 279 self.LBFGroup:Delete(true)
flickerstreak@213 280 end
flickerstreak@171 281 LSG:RemoveFrame(f)
flickerstreak@122 282 f:SetParent(UIParent)
flickerstreak@122 283 f:ClearAllPoints()
flickerstreak@147 284 f:Hide()
flickerstreak@122 285 end
flickerstreak@122 286
flickerstreak@147 287 --
flickerstreak@147 288 -- Events
flickerstreak@147 289 --
flickerstreak@147 290
flickerstreak@147 291 function Bar:OnConfigModeChanged(event, mode)
flickerstreak@147 292 self:SetConfigMode(mode)
flickerstreak@123 293 end
flickerstreak@123 294
flickerstreak@153 295 --
flickerstreak@153 296 -- Accessors
flickerstreak@153 297 --
flickerstreak@153 298
flickerstreak@153 299 function Bar:GetName()
flickerstreak@153 300 return self.name
flickerstreak@153 301 end
flickerstreak@153 302
flickerstreak@153 303 -- only ReAction:RenameBar() should call this function. Calling from any other
flickerstreak@153 304 -- context will desync the bar list in the ReAction class.
flickerstreak@153 305 function Bar:SetName(name)
flickerstreak@213 306 if self.LBFGroup then
flickerstreak@213 307 -- LBF doesn't offer a method of renaming a group, so delete and remake the group.
flickerstreak@213 308 local c = self.config.ButtonFacade
flickerstreak@213 309 local g = ReAction.LBF:Group(L["ReAction"], name)
Flick@234 310 for idx, b in self:IterateButtons() do
flickerstreak@213 311 self.LBFGroup:RemoveButton(b:GetFrame(), true)
flickerstreak@213 312 g:AddButton(b:GetFrame())
flickerstreak@213 313 end
flickerstreak@213 314 self.LBFGroup:Delete(true)
flickerstreak@213 315 self.LBFGroup = g
flickerstreak@213 316 self.LBFGroup:Skin(c.skinID, c.gloss, c.backdrop, c.colors)
flickerstreak@213 317 end
flickerstreak@153 318 self.name = name
flickerstreak@154 319 if self.overlay then
flickerstreak@154 320 self.overlay:SetLabel(self.name)
flickerstreak@154 321 end
flickerstreak@153 322 end
flickerstreak@153 323
flickerstreak@153 324 function Bar:GetFrame()
flickerstreak@153 325 -- this method is included for documentation purposes. It is overridden
flickerstreak@153 326 -- for each object in the :New() method.
flickerstreak@153 327 error("Invalid Bar object: used without initialization")
flickerstreak@153 328 end
flickerstreak@153 329
Flick@234 330 function Bar:GetButton(idx)
Flick@234 331 return self.buttons[idx]
Flick@234 332 end
Flick@234 333
Flick@237 334 function Bar:GetButtonClass()
Flick@237 335 return self.buttonClass
Flick@237 336 end
Flick@237 337
flickerstreak@153 338 function Bar:GetConfig()
flickerstreak@153 339 return self.config
flickerstreak@153 340 end
flickerstreak@153 341
flickerstreak@153 342 function Bar:GetAnchor()
flickerstreak@122 343 local c = self.config
flickerstreak@153 344 return (c.point or "CENTER"),
flickerstreak@153 345 (c.anchor or self:GetFrame():GetParent():GetName()),
flickerstreak@153 346 (c.relpoint or c.point or "CENTER"),
flickerstreak@153 347 (c.x or 0),
flickerstreak@153 348 (c.y or 0)
flickerstreak@122 349 end
flickerstreak@122 350
flickerstreak@122 351 function Bar:SetAnchor(point, frame, relativePoint, x, y)
flickerstreak@122 352 local c = self.config
flickerstreak@122 353 c.point = point or c.point
flickerstreak@122 354 c.anchor = frame or c.anchor
flickerstreak@122 355 c.relpoint = relativePoint or c.relpoint
flickerstreak@122 356 c.x = x or c.x
flickerstreak@122 357 c.y = y or c.y
flickerstreak@122 358 self:ApplyAnchor()
flickerstreak@122 359 ReAction:RefreshBar(self)
flickerstreak@122 360 end
flickerstreak@122 361
flickerstreak@122 362 function Bar:GetSize()
flickerstreak@122 363 local f = self:GetFrame()
flickerstreak@122 364 return f:GetWidth(), f:GetHeight()
flickerstreak@122 365 end
flickerstreak@122 366
flickerstreak@122 367 function Bar:SetSize(w,h)
flickerstreak@122 368 local f = self:GetFrame()
flickerstreak@122 369 self.config.width = w
flickerstreak@122 370 self.config.height = h
flickerstreak@122 371 f:SetWidth(w)
flickerstreak@122 372 f:SetHeight(h)
flickerstreak@122 373 end
flickerstreak@122 374
flickerstreak@122 375 function Bar:GetButtonSize()
flickerstreak@122 376 local w = self.config.btnWidth or 32
flickerstreak@122 377 local h = self.config.btnHeight or 32
flickerstreak@122 378 -- TODO: get from modules?
flickerstreak@122 379 return w,h
flickerstreak@122 380 end
flickerstreak@122 381
flickerstreak@122 382 function Bar:SetButtonSize(w,h)
flickerstreak@122 383 if w > 0 and h > 0 then
flickerstreak@122 384 self.config.btnWidth = w
flickerstreak@122 385 self.config.btnHeight = h
flickerstreak@122 386 end
flickerstreak@122 387 ReAction:RefreshBar(self)
flickerstreak@122 388 end
flickerstreak@122 389
flickerstreak@153 390 function Bar:GetNumButtons()
flickerstreak@153 391 local r,c = self:GetButtonGrid()
flickerstreak@153 392 return r*c
flickerstreak@153 393 end
flickerstreak@153 394
flickerstreak@122 395 function Bar:GetButtonGrid()
flickerstreak@122 396 local cfg = self.config
flickerstreak@122 397 local r = cfg.btnRows or 1
flickerstreak@122 398 local c = cfg.btnColumns or 1
flickerstreak@122 399 local s = cfg.spacing or 4
flickerstreak@122 400 return r,c,s
flickerstreak@122 401 end
flickerstreak@122 402
flickerstreak@122 403 function Bar:SetButtonGrid(r,c,s)
flickerstreak@122 404 if r > 0 and c > 0 and s > 0 then
flickerstreak@122 405 local cfg = self.config
flickerstreak@122 406 cfg.btnRows = r
flickerstreak@122 407 cfg.btnColumns = c
flickerstreak@122 408 cfg.spacing = s
flickerstreak@122 409 end
Flick@234 410 self.buttonClass:SetupBar(self)
flickerstreak@122 411 ReAction:RefreshBar(self)
flickerstreak@122 412 end
flickerstreak@122 413
flickerstreak@122 414 function Bar:GetAlpha()
flickerstreak@122 415 return self.config.alpha or 1.0
flickerstreak@122 416 end
flickerstreak@122 417
flickerstreak@122 418 function Bar:SetAlpha(value)
flickerstreak@122 419 self.config.alpha = value
flickerstreak@122 420 self:GetFrame():SetAlpha(value or 1.0)
flickerstreak@155 421 self:UpdateDefaultStateAlpha()
flickerstreak@122 422 ReAction:RefreshBar(self)
flickerstreak@122 423 end
flickerstreak@122 424
flickerstreak@122 425 function Bar:IterateButtons()
Flick@234 426 -- iterator returns idx, button, but does NOT iterate in index order
flickerstreak@122 427 return pairs(self.buttons)
flickerstreak@122 428 end
flickerstreak@122 429
flickerstreak@153 430 --
flickerstreak@153 431 -- Methods
flickerstreak@153 432 --
flickerstreak@153 433
flickerstreak@147 434 function Bar:SetConfigMode(mode)
flickerstreak@155 435 self:SetSecureData("showAll",mode)
flickerstreak@147 436 self:ShowControls(mode)
Flick@234 437 for idx, b in self:IterateButtons() do
flickerstreak@147 438 b:ShowGridTemp(mode)
flickerstreak@147 439 b:UpdateActionIDLabel(mode)
flickerstreak@147 440 end
flickerstreak@147 441 end
flickerstreak@147 442
flickerstreak@147 443 function Bar:SetKeybindMode(mode)
flickerstreak@155 444 self:SetSecureData("showAll",mode)
Flick@234 445 for idx, b in self:IterateButtons() do
flickerstreak@147 446 b:SetKeybindMode(mode)
flickerstreak@147 447 end
flickerstreak@147 448 end
flickerstreak@147 449
flickerstreak@153 450 function Bar:ApplyAnchor()
flickerstreak@153 451 local f = self:GetFrame()
flickerstreak@153 452 local c = self.config
flickerstreak@153 453 local p = c.point
flickerstreak@153 454
flickerstreak@153 455 f:SetWidth(c.width)
flickerstreak@153 456 f:SetHeight(c.height)
flickerstreak@153 457 f:ClearAllPoints()
flickerstreak@153 458
flickerstreak@153 459 if p then
flickerstreak@153 460 local a = f:GetParent()
flickerstreak@153 461 if c.anchor then
flickerstreak@153 462 local bar = ReAction:GetBar(c.anchor)
flickerstreak@153 463 if bar then
flickerstreak@153 464 a = bar:GetFrame()
flickerstreak@153 465 else
flickerstreak@153 466 a = _G[c.anchor]
flickerstreak@153 467 end
flickerstreak@153 468 end
flickerstreak@153 469 local fr = a or f:GetParent()
flickerstreak@153 470 f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0)
flickerstreak@153 471 else
flickerstreak@153 472 f:SetPoint("CENTER")
flickerstreak@153 473 end
flickerstreak@155 474
flickerstreak@155 475 self:UpdateDefaultStateAnchor()
flickerstreak@153 476 end
flickerstreak@153 477
flickerstreak@153 478 function Bar:ClipNButtons( n )
flickerstreak@153 479 local cfg = self.config
flickerstreak@153 480 local r = cfg.btnRows or 1
flickerstreak@153 481 local c = cfg.btnColumns or 1
flickerstreak@153 482
flickerstreak@153 483 cfg.btnRows = ceil(n/c)
flickerstreak@153 484 cfg.btnColumns = min(n,c)
flickerstreak@153 485 end
flickerstreak@153 486
flickerstreak@153 487 function Bar:AddButton(idx, button)
flickerstreak@153 488 local f = self:GetFrame()
flickerstreak@153 489
Flick@234 490 self.buttons[idx] = button
flickerstreak@153 491
flickerstreak@153 492 -- Store a properly wrapped reference to the child frame as an attribute
flickerstreak@153 493 -- (accessible via "frameref-btn#")
flickerstreak@153 494 f:SetFrameRef(format("btn%d",idx), button:GetFrame())
flickerstreak@213 495
flickerstreak@213 496 -- button constructors are responsible for calling SkinButton
flickerstreak@153 497 end
flickerstreak@153 498
flickerstreak@153 499 function Bar:RemoveButton(button)
Flick@234 500 local idx = button:GetIndex()
flickerstreak@153 501 if idx then
flickerstreak@153 502 self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil)
Flick@234 503 self.buttons[idx] = nil
flickerstreak@153 504 end
flickerstreak@213 505 if self.LBFGroup then
flickerstreak@213 506 self.LBFGroup:RemoveButton(button:GetFrame(),true)
flickerstreak@213 507 end
flickerstreak@153 508 end
flickerstreak@153 509
flickerstreak@122 510 function Bar:PlaceButton(button, baseW, baseH)
Flick@234 511 local idx = button:GetIndex()
flickerstreak@122 512 if idx then
flickerstreak@122 513 local r, c, s = self:GetButtonGrid()
flickerstreak@122 514 local bh, bw = self:GetButtonSize()
flickerstreak@122 515 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
flickerstreak@122 516 local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s)
flickerstreak@122 517 local scale = bw/baseW
flickerstreak@122 518 local b = button:GetFrame()
flickerstreak@122 519
flickerstreak@122 520 b:ClearAllPoints()
flickerstreak@122 521 b:SetPoint("TOPLEFT",x/scale,y/scale)
flickerstreak@122 522 b:SetScale(scale)
flickerstreak@122 523 end
flickerstreak@122 524 end
flickerstreak@122 525
flickerstreak@213 526 function Bar:SkinButton( button, data )
flickerstreak@213 527 if self.LBFGroup then
flickerstreak@213 528 self.LBFGroup:AddButton(button:GetFrame(), data)
flickerstreak@213 529 end
flickerstreak@147 530 end
flickerstreak@148 531
flickerstreak@155 532 function Bar:UpdateShowGrid()
Flick@234 533 for idx, button in self:IterateButtons() do
flickerstreak@155 534 button:UpdateShowGrid()
flickerstreak@155 535 end
flickerstreak@155 536 end
flickerstreak@155 537
flickerstreak@154 538 function Bar:ShowControls(show)
flickerstreak@154 539 if show then
flickerstreak@155 540 if not self.overlay then
flickerstreak@155 541 self.overlay = Bar.Overlay:New(self) -- see Overlay.lua
flickerstreak@154 542 end
flickerstreak@155 543 self.overlay:Show()
flickerstreak@155 544 self:RefreshSecureState()
flickerstreak@155 545 elseif self.overlay then
flickerstreak@155 546 self.overlay:Hide()
flickerstreak@154 547 end
flickerstreak@154 548 end
flickerstreak@154 549
flickerstreak@154 550 function Bar:RefreshControls()
flickerstreak@154 551 if self.overlay and self.overlay:IsShown() then
flickerstreak@154 552 self.overlay:RefreshControls()
flickerstreak@154 553 end
flickerstreak@154 554 end
flickerstreak@154 555
flickerstreak@154 556 function Bar:SetLabelSubtext(text)
flickerstreak@154 557 if self.overlay then
flickerstreak@154 558 self.overlay:SetLabelSubtext(text)
flickerstreak@154 559 end
flickerstreak@154 560 end
flickerstreak@154 561
flickerstreak@154 562 --
flickerstreak@154 563 -- Secure state functions
flickerstreak@154 564 --
flickerstreak@154 565
flickerstreak@155 566 function Bar:GetSecureState()
flickerstreak@155 567 local env = GetManagedEnvironment(self:GetFrame())
flickerstreak@155 568 return env and env.state
flickerstreak@155 569 end
flickerstreak@155 570
flickerstreak@155 571 function Bar:GetStateProperty(state, propname)
flickerstreak@155 572 -- override in modules/State.lua for now
flickerstreak@155 573 end
flickerstreak@155 574
flickerstreak@155 575 function Bar:SetStateProperty(state, propname, value)
flickerstreak@155 576 -- override in modules/State.lua for now
flickerstreak@155 577 end
flickerstreak@155 578
flickerstreak@155 579 function Bar:RefreshSecureState()
flickerstreak@155 580 self:GetFrame():Execute(_reaction_refresh)
flickerstreak@155 581 end
flickerstreak@155 582
flickerstreak@155 583 -- usage: SetSecureData(globalname, [tblkey1, tblkey2, ...], value)
flickerstreak@155 584 function Bar:SetSecureData( ... )
flickerstreak@155 585 local n = select('#',...)
flickerstreak@155 586 if n < 2 then
flickerstreak@155 587 error("ReAction.Bar:SetSecureData() requires at least 2 arguments")
flickerstreak@155 588 end
flickerstreak@155 589 local f = self:GetFrame()
flickerstreak@155 590 f:SetAttribute("data-depth",n-1)
flickerstreak@155 591 f:SetAttribute("data-value",select(n,...))
flickerstreak@155 592 for i = 1, n-1 do
flickerstreak@155 593 local key = select(i,...)
flickerstreak@155 594 if key == nil then
flickerstreak@155 595 error("ReAction.Bar:SetSecureData() - nil table key in argument list (#"..i..")")
flickerstreak@155 596 end
flickerstreak@155 597 f:SetAttribute("data-key-"..i, key)
flickerstreak@155 598 end
flickerstreak@155 599 f:Execute(
flickerstreak@155 600 [[
flickerstreak@155 601 local n = self:GetAttribute("data-depth")
flickerstreak@155 602 if n > 0 then
flickerstreak@155 603 local value = self:GetAttribute("data-value")
flickerstreak@155 604 local t = _G
flickerstreak@155 605 for i = 1, n do
flickerstreak@155 606 local key = self:GetAttribute("data-key-"..i)
flickerstreak@155 607 if not key then return end
flickerstreak@155 608 if not t[key] then
flickerstreak@155 609 t[key] = newtable()
flickerstreak@155 610 end
flickerstreak@155 611 if i == n then
flickerstreak@155 612 t[key] = value
flickerstreak@155 613 else
flickerstreak@155 614 t = t[key]
flickerstreak@155 615 end
flickerstreak@155 616 end
flickerstreak@155 617 end
flickerstreak@155 618 ]])
flickerstreak@155 619 self:RefreshSecureState()
flickerstreak@155 620 end
flickerstreak@155 621
flickerstreak@155 622 function Bar:SetSecureStateData( state, key, value )
flickerstreak@155 623 self:SetSecureData("settings",state,key,value)
flickerstreak@155 624 end
flickerstreak@155 625
flickerstreak@155 626 -- sets a snippet to be run as an extension to _onstate-reaction
flickerstreak@155 627 function Bar:SetSecureStateExtension( id, snippet )
flickerstreak@155 628 if id == nil then
flickerstreak@155 629 error("ReAction.Bar:SetSecureStateExtension() requires an id")
flickerstreak@155 630 end
flickerstreak@155 631 local f = self:GetFrame()
flickerstreak@155 632 f:SetAttribute("input-secure-ext-id",id)
flickerstreak@155 633 f:SetAttribute("secure-ext-"..id,snippet)
flickerstreak@155 634 f:Execute(
flickerstreak@155 635 [[
flickerstreak@155 636 local id = self:GetAttribute("input-secure-ext-id")
flickerstreak@155 637 if id then
flickerstreak@155 638 extensions[id] = self:GetAttribute("secure-ext-"..id) or nil
flickerstreak@155 639 end
flickerstreak@155 640 ]])
flickerstreak@155 641 self:RefreshSecureState()
flickerstreak@155 642 end
flickerstreak@155 643
flickerstreak@155 644 function Bar:SetFrameRef( name, refFrame )
flickerstreak@155 645 if refFrame then
flickerstreak@155 646 local _, explicit = refFrame:IsProtected()
flickerstreak@155 647 if not explicit then
flickerstreak@155 648 refFrame = nil
flickerstreak@155 649 end
flickerstreak@155 650 end
flickerstreak@155 651 if refFrame then
flickerstreak@155 652 self:GetFrame():SetFrameRef(name,refFrame)
flickerstreak@155 653 else
flickerstreak@155 654 self:GetFrame():SetAttribute("frameref-"..name,nil)
flickerstreak@155 655 end
flickerstreak@155 656 end
flickerstreak@155 657
flickerstreak@157 658 function Bar:SetStateDriver( states )
flickerstreak@157 659 if states then
flickerstreak@157 660 for state, props in pairs(states) do
flickerstreak@157 661 self:SetSecureStateData(state, "active_", true) -- make sure there's a 'settings' field for this state
flickerstreak@157 662 for propname, value in pairs(props) do
flickerstreak@157 663 if propname == "anchorFrame" then
flickerstreak@157 664 self:SetFrameRef("anchor-"..state, _G[value])
flickerstreak@157 665 elseif propname == "rule" then
flickerstreak@157 666 -- do nothing
flickerstreak@157 667 else
flickerstreak@157 668 self:SetSecureStateData(state, propname, value)
flickerstreak@157 669 end
flickerstreak@157 670 end
flickerstreak@157 671 end
flickerstreak@157 672 end
flickerstreak@157 673 local rule = states and self:BuildStateRule(states)
flickerstreak@155 674 if rule then
flickerstreak@155 675 RegisterStateDriver(self:GetFrame(),"reaction",rule)
flickerstreak@155 676 elseif self.statedriver then
flickerstreak@155 677 UnregisterStateDriver(self:GetFrame(),"reaction")
flickerstreak@155 678 end
flickerstreak@155 679 self.statedriver = rule
flickerstreak@157 680 self:BuildStateKeybinds(states)
flickerstreak@155 681 self:RefreshSecureState()
flickerstreak@155 682 end
flickerstreak@155 683
flickerstreak@148 684 -- pass unit=nil to set up the unit elsewhere, if you want something more complex
flickerstreak@148 685 function Bar:RegisterUnitWatch( unit, enable )
flickerstreak@148 686 local f = self:GetFrame()
flickerstreak@148 687 if unit then
flickerstreak@148 688 f:SetAttribute("unit",unit)
flickerstreak@148 689 end
flickerstreak@156 690 if enable then
Flick@234 691 if not self.unitwatch then
Flick@234 692 RegisterUnitWatch(self:GetFrame(),true)
Flick@234 693 end
flickerstreak@156 694 elseif self.unitwatch then
flickerstreak@156 695 UnregisterUnitWatch(self:GetFrame())
flickerstreak@156 696 end
flickerstreak@148 697 self.unitwatch = enable
flickerstreak@156 698 self:RefreshSecureState()
flickerstreak@148 699 end
flickerstreak@148 700
flickerstreak@155 701 function Bar:SetStateKeybind( key, state )
flickerstreak@155 702 local f = self:GetFrame()
flickerstreak@155 703 local binds = self.statebinds
flickerstreak@155 704 if not binds then
flickerstreak@155 705 binds = { }
flickerstreak@155 706 self.statebinds = binds
flickerstreak@155 707 end
flickerstreak@155 708
flickerstreak@155 709 -- clear the old binding, if any
flickerstreak@155 710 if binds[state] then
flickerstreak@155 711 SetOverrideBinding(f, false, binds[state], nil)
flickerstreak@155 712 end
flickerstreak@155 713
flickerstreak@155 714 if key then
flickerstreak@157 715 SetOverrideBindingClick(f, false, key, f:GetName(), state) -- state name is virtual mouse button
flickerstreak@155 716 end
flickerstreak@155 717 binds[state] = key
flickerstreak@155 718 end
flickerstreak@155 719
flickerstreak@155 720 function Bar:GetStateKeybind( state )
flickerstreak@155 721 if self.statebinds and state then
flickerstreak@155 722 return self.statebinds[state]
flickerstreak@155 723 end
flickerstreak@155 724 end
flickerstreak@155 725
flickerstreak@155 726 function Bar:UpdateDefaultStateAnchor()
flickerstreak@155 727 local point, frame, relPoint, x, y = self:GetAnchor()
flickerstreak@155 728 local f = self:GetFrame()
flickerstreak@155 729 f:SetAttribute("defaultAnchor-point",point)
flickerstreak@155 730 f:SetAttribute("defaultAnchor-relPoint",relPoint)
flickerstreak@155 731 f:SetAttribute("defaultAnchor-x",x)
flickerstreak@155 732 f:SetAttribute("defaultAnchor-y",y)
flickerstreak@155 733 self:SetFrameRef("defaultAnchor",_G[frame or "UIParent"])
flickerstreak@155 734 f:Execute([[
flickerstreak@155 735 for _, k in pairs(anchorKeys) do
flickerstreak@155 736 defaultAnchor[k] = self:GetAttribute("defaultAnchor-"..k)
flickerstreak@155 737 end
flickerstreak@155 738 defaultAnchor.frame = self:GetAttribute("frameref-defaultAnchor")
flickerstreak@155 739 ]])
flickerstreak@155 740 end
flickerstreak@155 741
flickerstreak@155 742 function Bar:UpdateDefaultStateAlpha()
flickerstreak@155 743 local f = self:GetFrame()
flickerstreak@155 744 f:SetAttribute("defaultAlpha",self:GetAlpha())
flickerstreak@155 745 f:Execute([[
flickerstreak@155 746 defaultAlpha = self:GetAttribute("defaultAlpha")
flickerstreak@155 747 ]])
flickerstreak@155 748 end
flickerstreak@157 749
flickerstreak@157 750 ---- secure state driver rules ----
flickerstreak@157 751
flickerstreak@157 752 local playerClass = select(2, UnitClass("player"))
flickerstreak@157 753 local function ClassFilter(...)
flickerstreak@157 754 for i = 1, select('#',...) do
flickerstreak@157 755 if playerClass == select(i,...) then
flickerstreak@157 756 return false
flickerstreak@157 757 end
flickerstreak@157 758 end
flickerstreak@157 759 return true
flickerstreak@157 760 end
flickerstreak@157 761
flickerstreak@157 762 local ruleformats = {
flickerstreak@157 763 stealth = { format = "stealth", filter = ClassFilter("ROGUE","DRUID") },
flickerstreak@157 764 nostealth = { format = "nostealth", filter = ClassFilter("ROGUE","DRUID") },
flickerstreak@157 765 shadowdance = { format = "bonusbar:2", filter = ClassFilter("ROGUE") },
flickerstreak@157 766 shadowform = { format = "form:1", filter = ClassFilter("PRIEST") },
flickerstreak@157 767 noshadowform = { format = "noform", filter = ClassFilter("PRIEST") },
flickerstreak@157 768 battle = { format = "stance:1", filter = ClassFilter("WARRIOR") },
flickerstreak@157 769 defensive = { format = "stance:2", filter = ClassFilter("WARRIOR") },
flickerstreak@157 770 berserker = { format = "stance:3", filter = ClassFilter("WARRIOR") },
flickerstreak@157 771 caster = { format = "form:0/2/4/5/6", filter = ClassFilter("DRUID") },
flickerstreak@157 772 bear = { format = "form:1", filter = ClassFilter("DRUID") },
flickerstreak@157 773 cat = { format = "form:3", filter = ClassFilter("DRUID") },
flickerstreak@157 774 tree = { format = "form:5", filter = ClassFilter("DRUID") },
flickerstreak@157 775 moonkin = { format = "form:5", filter = ClassFilter("DRUID") },
flickerstreak@178 776 demon = { format = "form:2", filter = ClassFilter("WARLOCK") },
flickerstreak@178 777 nodemon = { format = "noform", filter = ClassFilter("WARLOCK") },
flickerstreak@157 778 pet = { format = "pet" },
flickerstreak@157 779 nopet = { format = "nopet" },
flickerstreak@176 780 harm = { format = "@target,harm" },
flickerstreak@176 781 help = { format = "@target,help" },
flickerstreak@176 782 notarget = { format = "@target,noexists" },
flickerstreak@176 783 focusharm = { format = "@focus,harm" },
flickerstreak@176 784 focushelp = { format = "@focus,help" },
flickerstreak@176 785 nofocus = { format = "@focus,noexists" },
flickerstreak@157 786 raid = { format = "group:raid" },
flickerstreak@157 787 party = { format = "group:party" },
flickerstreak@157 788 solo = { format = "nogroup" },
flickerstreak@157 789 combat = { format = "combat" },
flickerstreak@157 790 nocombat = { format = "nocombat" },
flickerstreak@176 791 possess = { format = "@vehicle,noexists,bonusbar:5" },
flickerstreak@176 792 vehicle = { format = "@vehicle,exists,bonusbar:5" },
flickerstreak@157 793 }
flickerstreak@157 794
flickerstreak@157 795 function Bar.InitRuleFormats()
flickerstreak@157 796 local forms = { }
flickerstreak@157 797 for i = 1, GetNumShapeshiftForms() do
flickerstreak@157 798 local _, name = GetShapeshiftFormInfo(i)
flickerstreak@157 799 forms[name] = i;
flickerstreak@157 800 end
flickerstreak@157 801 -- use 9 if not found since 9 is never a valid stance/form
flickerstreak@157 802 local defensive = forms[GetSpellInfo(71)] or 9
flickerstreak@157 803 local berserker = forms[GetSpellInfo(2458)] or 9
flickerstreak@176 804 local bear = forms[GetSpellInfo(5487)] or 9
flickerstreak@157 805 local aquatic = forms[GetSpellInfo(1066)] or 9
flickerstreak@157 806 local cat = forms[GetSpellInfo(768)] or 9
flickerstreak@157 807 local travel = forms[GetSpellInfo(783)] or 9
flickerstreak@157 808 local tree = forms[GetSpellInfo(33891)] or 9
flickerstreak@157 809 local moonkin = forms[GetSpellInfo(24858)] or 9
flickerstreak@157 810 local flight = forms[GetSpellInfo(40120)] or forms[GetSpellInfo(33943)] or 9
flickerstreak@157 811
flickerstreak@157 812 ruleformats.defensive.format = "stance:"..defensive
flickerstreak@157 813 ruleformats.berserker.format = "stance:"..berserker
flickerstreak@157 814 ruleformats.caster.format = format("form:0/%d/%d/%d", aquatic, travel, flight)
flickerstreak@157 815 ruleformats.bear.format = "form:"..bear
flickerstreak@157 816 ruleformats.cat.format = "form:"..cat
flickerstreak@157 817 ruleformats.tree.format = "form:"..tree
flickerstreak@157 818 ruleformats.moonkin.format = "form:"..moonkin
flickerstreak@157 819 end
flickerstreak@157 820
flickerstreak@157 821 function Bar:BuildStateRule(states)
flickerstreak@157 822 -- states is a table :
flickerstreak@157 823 -- states[statename].rule = {
flickerstreak@157 824 -- order = #,
flickerstreak@157 825 -- type = "default"/"custom"/"any"/"all",
flickerstreak@157 826 -- values = { ... }, -- keys of ruleformats[]
flickerstreak@157 827 -- custom = "...",
flickerstreak@157 828 -- }
flickerstreak@157 829 local rules = { }
flickerstreak@157 830 local default
flickerstreak@157 831
flickerstreak@157 832 for idx, state in ipairs(fieldsort(states, "rule", "order")) do
flickerstreak@157 833 local c = states[state].rule
flickerstreak@157 834 local type = c.type
flickerstreak@157 835 if type == "default" then
flickerstreak@157 836 default = default or state
flickerstreak@157 837 elseif type == "custom" then
flickerstreak@157 838 if c.custom then
flickerstreak@157 839 -- strip out all spaces from the custom rule
flickerstreak@157 840 table.insert(rules, format("%s %s", c.custom:gsub("%s",""), state))
flickerstreak@157 841 end
flickerstreak@157 842 elseif type == "any" or type == "all" then
flickerstreak@157 843 if c.values then
flickerstreak@157 844 local clauses = { }
flickerstreak@157 845 for key, value in pairs(c.values) do
flickerstreak@157 846 if ruleformats[key] and not ruleformats[key].filter then
flickerstreak@157 847 table.insert(clauses, ruleformats[key].format)
flickerstreak@157 848 end
flickerstreak@157 849 end
flickerstreak@157 850 if #clauses > 0 then
flickerstreak@157 851 local sep = (type == "any") and "][" or ","
flickerstreak@157 852 table.insert(rules, format("[%s] %s", table.concat(clauses,sep), state))
flickerstreak@157 853 end
flickerstreak@157 854 end
flickerstreak@157 855 end
flickerstreak@157 856 end
flickerstreak@157 857 -- make sure that the default, if any, is last
flickerstreak@157 858 if default then
flickerstreak@157 859 table.insert(rules, default)
flickerstreak@157 860 end
flickerstreak@157 861 return table.concat(rules,";")
flickerstreak@157 862 end
flickerstreak@157 863
flickerstreak@157 864 function Bar:BuildStateKeybinds( states )
flickerstreak@157 865 if states then
flickerstreak@157 866 for name, state in pairs(states) do
flickerstreak@157 867 local rule = tfetch(state, "rule")
flickerstreak@157 868 if rule and rule.type == "keybind" then
flickerstreak@157 869 self:SetStateKeybind(rule.keybind, name)
flickerstreak@157 870 else
flickerstreak@157 871 self:SetStateKeybind(nil, name) -- this clears an existing keybind
flickerstreak@157 872 end
flickerstreak@157 873 end
flickerstreak@157 874 end
flickerstreak@157 875 end
flickerstreak@157 876