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