Mercurial > wow > reaction
comparison Bar.lua @ 90:7cabc8ac6c16
Updates for wow 3.0
- TOC update
- updated changed APIs/frame names
- rewrote state code per new SecureHandlers API
- cleaned up Bar, ActionButton code
- removed AceLibrary/Dewdrop, menu from bar right-click
- fixed various small bugs
Updated WowAce external locations
Updated README.html
| author | Flick <flickerstreak@gmail.com> |
|---|---|
| date | Wed, 15 Oct 2008 16:29:41 +0000 |
| parents | f32e2375e39b |
| children | 39265b16d208 |
comparison
equal
deleted
inserted
replaced
| 89:491a6ffe7260 | 90:7cabc8ac6c16 |
|---|---|
| 3 local _G = _G | 3 local _G = _G |
| 4 local CreateFrame = CreateFrame | 4 local CreateFrame = CreateFrame |
| 5 local floor = math.floor | 5 local floor = math.floor |
| 6 local fmod = math.fmod | 6 local fmod = math.fmod |
| 7 local format = string.format | 7 local format = string.format |
| 8 local SecureStateHeader_Refresh = SecureStateHeader_Refresh | |
| 9 | 8 |
| 10 ReAction:UpdateRevision("$Revision$") | 9 ReAction:UpdateRevision("$Revision$") |
| 11 | 10 |
| 12 | 11 local Bar = { } |
| 13 ------ BAR CLASS ------ | 12 local proto = { __index = Bar } |
| 14 local Bar = { _classID = {} } | 13 local weak = { __mode = "k" } |
| 14 | |
| 15 ReAction.Bar = Bar -- export to ReAction | 15 ReAction.Bar = Bar -- export to ReAction |
| 16 | 16 |
| 17 function Bar:New( name, config ) | 17 function Bar:New( name, config ) |
| 18 -- create new self | |
| 19 self = setmetatable( { }, {__index = Bar} ) | |
| 20 if type(config) ~= "table" then | 18 if type(config) ~= "table" then |
| 21 error("ReAction.Bar: config table required") | 19 error("ReAction.Bar: config table required") |
| 22 end | 20 end |
| 23 config.width = config.width or 480 | 21 |
| 24 config.height = config.height or 40 | 22 -- create new self |
| 25 | 23 self = setmetatable( |
| 26 self.name, self.config = name, config | 24 { |
| 27 self.buttons = setmetatable({},{__mode="k"}) | 25 config = config, |
| 28 self.statedrivers = { } | 26 name = name, |
| 29 self.keybinds = { } | 27 buttons = setmetatable( { }, weak ), |
| 30 | 28 width = config.width or 480, |
| 29 height = config.height or 40 | |
| 30 }, | |
| 31 proto ) | |
| 32 | |
| 33 -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is | |
| 34 -- not mouse-clickable by the user. | |
| 31 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent | 35 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent |
| 32 local f = CreateFrame("Button",name and format("ReAction-%s",name),parent,"SecureStateHeaderTemplate, SecureActionButtonTemplate") | 36 local f = CreateFrame("Button", name and format("ReAction-%s",name), parent, |
| 37 "SecureHandlerStateTemplate, SecureHandlerClickTemplate") | |
| 33 f:SetFrameStrata("MEDIUM") | 38 f:SetFrameStrata("MEDIUM") |
| 34 f:SetWidth(config.width) | 39 f:SetWidth(self.width) |
| 35 f:SetWidth(config.height) | 40 f:SetWidth(self.height) |
| 36 f:Show() | 41 f:Show() |
| 37 | |
| 38 -- The bar itself is also a Button derived from SecureActionButtonTemplate, so it has an OnClick handler | |
| 39 -- which we can use as a virtual button for keybinds, which will send attribute-value changes to itself. | |
| 40 -- However, we don't ever want the user to be able to click it directly. | |
| 41 f:EnableMouse(false) | 42 f:EnableMouse(false) |
| 42 f:SetAttribute("type","attribute") | 43 f:SetClampedToScreen(true) |
| 43 | 44 |
| 44 -- Buttons are contained in an anonymous intermediate sub-frame. This arrangement is to specifically | 45 -- Override the default frame accessor to provide strict read-only access |
| 45 -- address the issue of the interaction with hidestates and auto-hiding empty action buttons (the two | |
| 46 -- don't play nicely together). It also has the fringe benefit of making show/hide faster because a | |
| 47 -- single frame is shown/hidden instead of potentially dozens. Unfortunately it does add an extra layer | |
| 48 -- of indirection to all state changes, as a secondary (trivial) statemap must be invoked. This | |
| 49 -- complicates frame setup slightly. | |
| 50 local bf = CreateFrame("Frame", nil, f, "SecureStateHeaderTemplate") | |
| 51 bf:SetAllPoints() | |
| 52 bf:Show() | |
| 53 bf:SetAttribute("useparent*",true) -- this facilitates SecureButton_GetModifiedAttribute() | |
| 54 bf:SetAttribute("statemap-parent","$input") -- However, we also need SetAttribute(state-parent) propagation too | |
| 55 f:SetAttribute("addchild",bf) | |
| 56 | |
| 57 -- Both frames are read-only. Override the default accessors for this object. | |
| 58 function self:GetFrame() | 46 function self:GetFrame() |
| 59 return f | 47 return f |
| 60 end | 48 end |
| 61 | 49 |
| 62 function self:GetButtonFrame() | |
| 63 return bf | |
| 64 end | |
| 65 | |
| 66 self:ApplyAnchor() | 50 self:ApplyAnchor() |
| 67 ReAction.RegisterCallback(self, "OnConfigModeChanged") | 51 ReAction.RegisterCallback(self, "OnConfigModeChanged") |
| 68 | 52 |
| 69 return self | 53 return self |
| 70 end | 54 end |
| 71 | 55 |
| 72 function Bar:Destroy() | 56 function Bar:Destroy() |
| 73 local f = self:GetFrame() | 57 local f = self:GetFrame() |
| 74 f:UnregisterAllEvents() | 58 f:UnregisterAllEvents() |
| 59 ReAction.UnregisterAllCallbacks(self) | |
| 75 f:Hide() | 60 f:Hide() |
| 76 f:SetParent(UIParent) | 61 f:SetParent(UIParent) |
| 77 f:ClearAllPoints() | 62 f:ClearAllPoints() |
| 78 ReAction.UnregisterAllCallbacks(self) | |
| 79 for driver in pairs(self.statedrivers) do | |
| 80 UnregisterStateDriver(f, driver) | |
| 81 end | |
| 82 self.labelString = nil | |
| 83 self.controlFrame = nil | |
| 84 self.config = nil | |
| 85 end | 63 end |
| 86 | 64 |
| 87 function Bar:OnConfigModeChanged(event, mode) | 65 function Bar:OnConfigModeChanged(event, mode) |
| 88 self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua | 66 self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua |
| 89 end | 67 end |
| 90 | 68 |
| 91 function Bar:ApplyAnchor() | 69 function Bar:ApplyAnchor() |
| 92 local f, config = self:GetFrame(), self.config | 70 local f = self:GetFrame() |
| 93 f:SetWidth(config.width) | 71 local c = self.config |
| 94 f:SetHeight(config.height) | 72 local p = c.point |
| 95 local point = config.point | 73 |
| 74 f:SetWidth(c.width) | |
| 75 f:SetHeight(c.height) | |
| 96 f:ClearAllPoints() | 76 f:ClearAllPoints() |
| 97 if point then | 77 |
| 98 local anchor = f:GetParent() | 78 if p then |
| 99 if config.anchor then | 79 local a = f:GetParent() |
| 100 local bar = ReAction:GetBar(config.anchor) | 80 if c.anchor then |
| 81 local bar = ReAction:GetBar(c.anchor) | |
| 101 if bar then | 82 if bar then |
| 102 anchor = bar:GetFrame() | 83 a = bar:GetFrame() |
| 103 else | 84 else |
| 104 anchor = _G[config.anchor] | 85 a = _G[c.anchor] |
| 105 end | 86 end |
| 106 end | 87 end |
| 107 f:SetPoint(point, anchor or f:GetParent(), config.relpoint, config.x or 0, config.y or 0) | 88 local fr = a or f:GetParent() |
| 89 f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) | |
| 108 else | 90 else |
| 109 f:SetPoint("CENTER") | 91 f:SetPoint("CENTER") |
| 110 end | 92 end |
| 111 end | 93 end |
| 112 | 94 |
| 120 self:ApplyAnchor() | 102 self:ApplyAnchor() |
| 121 end | 103 end |
| 122 | 104 |
| 123 function Bar:GetAnchor() | 105 function Bar:GetAnchor() |
| 124 local c = self.config | 106 local c = self.config |
| 125 return (c.point or "CENTER"), (c.anchor or self:GetFrame():GetParent():GetName()), (c.relpoint or c.point or "CENTER"), (c.x or 0), (c.y or 0) | 107 return (c.point or "CENTER"), |
| 108 (c.anchor or self:GetFrame():GetParent():GetName()), | |
| 109 (c.relpoint or c.point or "CENTER"), | |
| 110 (c.x or 0), | |
| 111 (c.y or 0) | |
| 126 end | 112 end |
| 127 | 113 |
| 128 function Bar:GetSize() | 114 function Bar:GetSize() |
| 129 local f = self:GetFrame() | 115 local f = self:GetFrame() |
| 130 return f:GetWidth(), f:GetHeight() | 116 return f:GetWidth(), f:GetHeight() |
| 131 end | 117 end |
| 132 | 118 |
| 133 function Bar:SetSize(w,h) | 119 function Bar:SetSize(w,h) |
| 120 local f = self:GetFrame() | |
| 134 self.config.width = w | 121 self.config.width = w |
| 135 self.config.height = h | 122 self.config.height = h |
| 136 local f = self:GetFrame() | |
| 137 f:SetWidth(w) | 123 f:SetWidth(w) |
| 138 f:SetHeight(h) | 124 f:SetHeight(h) |
| 139 end | 125 end |
| 140 | 126 |
| 141 function Bar:GetButtonSize() | 127 function Bar:GetButtonSize() |
| 180 return self.name | 166 return self.name |
| 181 end | 167 end |
| 182 | 168 |
| 183 function Bar:GetFrame() | 169 function Bar:GetFrame() |
| 184 -- this method is included for documentation purposes. It is overridden | 170 -- this method is included for documentation purposes. It is overridden |
| 185 -- in the New method for each object. | 171 -- for each object in the :New() method. |
| 186 error("Invalid Bar object: used without initialization") | 172 error("Invalid Bar object: used without initialization") |
| 187 end | 173 end |
| 188 | 174 |
| 189 function Bar:GetButtonFrame() | 175 -- only ReAction:RenameBar() should call this function. Calling from any other |
| 190 -- this method is included for documentation purposes. It is overridden | 176 -- context will desync the bar list in the ReAction class. |
| 191 -- in the New method for each object. | |
| 192 error("Invalid Bar object: used without initialization") | |
| 193 end | |
| 194 | |
| 195 -- only ReAction:RenameBar() should call this function | |
| 196 function Bar:SetName(name) | 177 function Bar:SetName(name) |
| 197 self.name = name | 178 self.name = name |
| 198 -- controlLabelString is defined in Overlay.lua | 179 self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua |
| 199 if self.controlLabelString then | |
| 200 self.controlLabelString:SetText(self.name) | |
| 201 end | |
| 202 end | 180 end |
| 203 | 181 |
| 204 function Bar:AddButton(idx, button) | 182 function Bar:AddButton(idx, button) |
| 205 -- store in a reverse-index array | 183 local f = self:GetFrame() |
| 184 | |
| 185 -- store in a weak reverse-index array | |
| 206 self.buttons[button] = idx | 186 self.buttons[button] = idx |
| 207 self:GetButtonFrame():SetAttribute("addchild",button:GetFrame()) | 187 |
| 208 SecureStateHeader_Refresh(self:GetFrame()) | 188 -- Store a properly wrapped reference to the child frame as an attribute |
| 189 -- (accessible via "frameref-btn#") | |
| 190 f:SetFrameRef(format("btn%d",idx), button:GetFrame()) | |
| 209 end | 191 end |
| 210 | 192 |
| 211 function Bar:RemoveButton(button) | 193 function Bar:RemoveButton(button) |
| 212 self.buttons[button] = nil | 194 local idx = self.buttons[button] |
| 213 end | 195 if idx then |
| 214 | 196 self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) |
| 215 function Bar:IterateButtons() -- iterator returns button, idx | 197 self.buttons[button] = nil |
| 198 end | |
| 199 end | |
| 200 | |
| 201 -- iterator returns button, idx and does NOT iterate in index order | |
| 202 function Bar:IterateButtons() | |
| 216 return pairs(self.buttons) | 203 return pairs(self.buttons) |
| 217 end | 204 end |
| 218 | 205 |
| 219 function Bar:PlaceButton(button, baseW, baseH) | 206 function Bar:PlaceButton(button, baseW, baseH) |
| 220 local idx = self.buttons[button] | 207 local idx = self.buttons[button] |
| 221 if not idx then return end | 208 if idx then |
| 222 local r, c, s = self:GetButtonGrid() | 209 local r, c, s = self:GetButtonGrid() |
| 223 local bh, bw = self:GetButtonSize() | 210 local bh, bw = self:GetButtonSize() |
| 224 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based | 211 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based |
| 225 local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s | 212 local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) |
| 226 local scale = bw/baseW | 213 local scale = bw/baseW |
| 227 local f = button:GetFrame() | 214 local b = button:GetFrame() |
| 228 | 215 |
| 229 f:ClearAllPoints() | 216 b:ClearAllPoints() |
| 230 f:SetPoint("TOPLEFT",x/scale,-y/scale) | 217 b:SetPoint("TOPLEFT",x/scale,y/scale) |
| 231 f:SetScale(scale) | 218 b:SetScale(scale) |
| 232 end | 219 end |
| 233 | 220 end |
| 234 -- Creates (or updates) a named binding which binds a key press to a call to SetAttribute() | 221 |
| 235 -- pass a nil key to unbind | |
| 236 function Bar:SetAttributeBinding( name, key, attribute, value ) | |
| 237 if not name then | |
| 238 error("usage - Bar:SetAttributeBinding(name [, key, attribute, value]") | |
| 239 end | |
| 240 local f = self:GetFrame() | |
| 241 | |
| 242 -- clear the old binding, if any | |
| 243 if self.keybinds[name] then | |
| 244 SetOverrideBinding(f, false, self.keybinds[name], nil) | |
| 245 end | |
| 246 if key then | |
| 247 f:SetAttribute(format("attribute-name-%s",name), attribute) | |
| 248 f:SetAttribute(format("attribute-value-%s",name), value) | |
| 249 SetOverrideBindingClick(f, false, key, f:GetName(), name) -- binding name is the virtual mouse button | |
| 250 end | |
| 251 self.keybinds[name] = key | |
| 252 end | |
| 253 | |
| 254 -- Sets up a state driver 'name' for the bar, using the provided 'rule' | |
| 255 -- Also sets attributes 'statemap-<name>-<key>'=<value> for each entry in the passed map | |
| 256 -- if 'rule' is nil or an empty string, the driver is unregistered. | |
| 257 function Bar:SetStateDriver( name, rule, map ) | |
| 258 local f = self:GetFrame() | |
| 259 if rule and #rule > 0 then | |
| 260 if map then | |
| 261 for key, value in pairs(map) do | |
| 262 f:SetAttribute( format("statemap-%s-%s",name,key), value ) | |
| 263 end | |
| 264 end | |
| 265 RegisterStateDriver(f, name, rule) | |
| 266 self.statedrivers[name] = true | |
| 267 elseif self.statedrivers[name] then | |
| 268 UnregisterStateDriver(f, name) | |
| 269 self.statedrivers[name] = nil | |
| 270 end | |
| 271 end | |
| 272 | |
| 273 -- Set an attribute on the frame (or each button if 'doButtons' = true) | |
| 274 -- Either or both 'map' and 'default' can be passed: | |
| 275 -- - If 'map' is omitted, then 'default' is set to the attribute. | |
| 276 -- - If 'map' is provided, then it is interpreted as an unordered | |
| 277 -- table of the form { ["statename"] = ["value"] }, and will be | |
| 278 -- converted into a SecureStateHeaderTemplate style state-parsed | |
| 279 -- string, e.g. "<state1>:<value1>;<state2>:<value2>". If 'default' | |
| 280 -- is also provided, then its value will be converted to a string | |
| 281 -- and appended. | |
| 282 function Bar:SetStateAttribute( attribute, map, default, doButtons ) | |
| 283 local value = default | |
| 284 if map then | |
| 285 local tmp = { } | |
| 286 for state, value in pairs(map) do | |
| 287 table.insert(tmp, format("%s:%s",tostring(state),tostring(value))) | |
| 288 end | |
| 289 if default then | |
| 290 table.insert(tmp, tostring(default)) | |
| 291 end | |
| 292 value = table.concat(tmp,";") | |
| 293 end | |
| 294 if doButtons then | |
| 295 for b in self:IterateButtons() do | |
| 296 b:GetFrame():SetAttribute(attribute,value) | |
| 297 end | |
| 298 else | |
| 299 self:GetFrame():SetAttribute(attribute, value) | |
| 300 end | |
| 301 SecureStateHeader_Refresh(self:GetFrame()) | |
| 302 end |
