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 |