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