comparison Bar.lua @ 75:06cd74bdc7da

- Cleaned up Bar interface - Move all attribute setting from Bar into State - Separated Moonkin and Tree of Life - Removed PossessBar module - Added some infrastructure for paged/mind control support to Action
author Flick <flickerstreak@gmail.com>
date Mon, 16 Jun 2008 18:46:08 +0000
parents dd01feae0d89
children da8ba8783924
comparison
equal deleted inserted replaced
74:00e28094e1a3 75:06cd74bdc7da
1 local ReAction = ReAction 1 local ReAction = ReAction
2 local L = ReAction.L 2 local L = ReAction.L
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
7 local format = string.format
6 local SecureStateHeader_Refresh = SecureStateHeader_Refresh 8 local SecureStateHeader_Refresh = SecureStateHeader_Refresh
7
8 9
9 10
10 -- update ReAction revision if this file is newer 11 -- update ReAction revision if this file is newer
11 local revision = tonumber(("$Revision$"):match("%d+")) 12 local revision = tonumber(("$Revision$"):match("%d+"))
12 if revision > ReAction.revision then 13 if revision > ReAction.revision then
13 ReAction.revision = revision 14 ReAction.revision = revision
14 end 15 end
15 16
17
16 ------ BAR CLASS ------ 18 ------ BAR CLASS ------
17 local Bar = { _classID = {} } 19 local Bar = { _classID = {} }
18 20
19 local function Constructor( self, name, config ) 21 local function Constructor( self, name, config )
22 if type(config) ~= "table" then
23 error("ReAction.Bar: config table required")
24 end
25 config.width = config.width or 480
26 config.height = config.height or 40
27
20 self.name, self.config = name, config 28 self.name, self.config = name, config
21 self.buttons = setmetatable({},{__mode="k"}) 29 self.buttons = setmetatable({},{__mode="k"})
22 30 self.statedrivers = { }
23 if type(config) ~= "table" then 31 self.keybinds = { }
24 error("ReAction.Bar: config table required")
25 end
26 32
27 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent 33 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
28 local f = CreateFrame("Frame",nil,parent,"SecureStateHeaderTemplate") 34 local f = CreateFrame("Button",name and format("ReAction-%s",name),parent,"SecureStateHeaderTemplate, SecureActionButtonTemplate")
35
36 -- The frame itself is read-only
37 function self:GetFrame()
38 return f
39 end
40
41 -- The bar itself is also a Button derived from SecureActionButtonTemplate, so it has an OnClick handler
42 -- which we can use as a virtual button for keybinds, which will send attribute-value changes to itself.
43 -- However, we don't ever want the user to be able to click it directly.
44 f:EnableMouse(false)
45 f:SetAttribute("type","attribute")
29 f:SetFrameStrata("MEDIUM") 46 f:SetFrameStrata("MEDIUM")
30 config.width = config.width or 480
31 config.height = config.height or 40
32 f:SetWidth(config.width) 47 f:SetWidth(config.width)
33 f:SetWidth(config.height) 48 f:SetWidth(config.height)
34 49 f:Show()
50
51 self:ApplyAnchor()
35 ReAction.RegisterCallback(self, "OnConfigModeChanged") 52 ReAction.RegisterCallback(self, "OnConfigModeChanged")
36
37 self.frame = f
38 self:ApplyAnchor()
39 f:Show()
40 self:RefreshLayout()
41 end 53 end
42 54
43 function Bar:Destroy() 55 function Bar:Destroy()
44 local f = self.frame 56 local f = self:GetFrame()
45 f:UnregisterAllEvents() 57 f:UnregisterAllEvents()
46 f:Hide() 58 f:Hide()
47 f:SetParent(UIParent) 59 f:SetParent(UIParent)
48 f:ClearAllPoints() 60 f:ClearAllPoints()
49 ReAction.UnregisterAllCallbacks(self) 61 ReAction.UnregisterAllCallbacks(self)
50 if self.statedriver then 62 for driver in pairs(self.statedrivers) do
51 UnregisterStateDriver(f, "reaction") 63 UnregisterStateDriver(f, driver)
52 end 64 end
53 self.labelString = nil 65 self.labelString = nil
54 self.controlFrame = nil 66 self.controlFrame = nil
55 self.frame = nil
56 self.config = nil 67 self.config = nil
57 end 68 end
58 69
59 function Bar:OnConfigModeChanged(event, mode) 70 function Bar:OnConfigModeChanged(event, mode)
60 self:ShowControls(mode) -- ShowControls() defined in Overlay.lua 71 self:ShowControls(mode) -- Bar:ShowControls() defined in Overlay.lua
61 end
62
63 function Bar:RefreshLayout()
64 ReAction:RefreshBar(self)
65 end 72 end
66 73
67 function Bar:ApplyAnchor() 74 function Bar:ApplyAnchor()
68 local f, config = self.frame, self.config 75 local f, config = self:GetFrame(), self.config
69 f:SetWidth(config.width) 76 f:SetWidth(config.width)
70 f:SetHeight(config.height) 77 f:SetHeight(config.height)
71 local anchor = config.anchor 78 local point = config.point
72 f:ClearAllPoints() 79 f:ClearAllPoints()
73 if anchor then 80 if point then
74 local anchorTo = f:GetParent() 81 local anchor = f:GetParent()
75 if config.anchorTo then 82 if config.anchor then
76 local bar = ReAction:GetBar(config.anchorTo) 83 local bar = ReAction:GetBar(config.anchor)
77 if bar then 84 if bar then
78 anchorTo = bar:GetFrame() 85 anchor = bar:GetFrame()
79 else 86 else
80 anchorTo = _G[config.anchorTo] 87 anchor = _G[config.anchor]
81 end 88 end
82 end 89 end
83 f:SetPoint(anchor, anchorTo or f:GetParent(), config.relativePoint, config.x or 0, config.y or 0) 90 f:SetPoint(point, anchor or f:GetParent(), config.relpoint, config.x or 0, config.y or 0)
84 else 91 else
85 f:SetPoint("CENTER") 92 f:SetPoint("CENTER")
86 end 93 end
87 end 94 end
88 95
89 function Bar:SetAnchor(point, frame, relativePoint, x, y) 96 function Bar:SetAnchor(point, frame, relativePoint, x, y)
90 local c = self.config 97 local c = self.config
91 c.anchor = point or c.anchor 98 c.point = point or c.point
92 c.anchorTo = frame and frame:GetName() or c.anchorTo 99 c.anchor = frame and frame:GetName() or c.anchor
93 c.relativePoint = relativePoint or c.relativePoint 100 c.relpoint = relativePoint or c.relpoint
94 c.x = x or c.x 101 c.x = x or c.x
95 c.y = y or c.y 102 c.y = y or c.y
96 self:ApplyAnchor() 103 self:ApplyAnchor()
97 end 104 end
98 105
99 function Bar:GetAnchor() 106 function Bar:GetAnchor()
100 local c = self.config 107 local c = self.config
101 return (c.anchor or "CENTER"), (c.anchorTo or self.frame:GetParent():GetName()), (c.relativePoint or c.anchor or "CENTER"), (c.x or 0), (c.y or 0) 108 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)
102 end
103
104 function Bar:GetFrame()
105 return self.frame
106 end 109 end
107 110
108 function Bar:GetSize() 111 function Bar:GetSize()
109 return self.frame:GetWidth() or 200, self.frame:GetHeight() or 200 112 local f = self:GetFrame()
113 return f:GetWidth(), f:GetHeight()
110 end 114 end
111 115
112 function Bar:SetSize(w,h) 116 function Bar:SetSize(w,h)
113 self.config.width = w 117 self.config.width = w
114 self.config.height = h 118 self.config.height = h
119 local f = self:GetFrame()
120 f:SetWidth(w)
121 f:SetHeight(h)
115 end 122 end
116 123
117 function Bar:GetButtonSize() 124 function Bar:GetButtonSize()
118 local w = self.config.btnWidth or 32 125 local w = self.config.btnWidth or 32
119 local h = self.config.btnHeight or 32 126 local h = self.config.btnHeight or 32
124 function Bar:SetButtonSize(w,h) 131 function Bar:SetButtonSize(w,h)
125 if w > 0 and h > 0 then 132 if w > 0 and h > 0 then
126 self.config.btnWidth = w 133 self.config.btnWidth = w
127 self.config.btnHeight = h 134 self.config.btnHeight = h
128 end 135 end
136 ReAction:RefreshBar(self)
129 end 137 end
130 138
131 function Bar:GetButtonGrid() 139 function Bar:GetButtonGrid()
132 local cfg = self.config 140 local cfg = self.config
133 local r = cfg.btnRows or 1 141 local r = cfg.btnRows or 1
141 local cfg = self.config 149 local cfg = self.config
142 cfg.btnRows = r 150 cfg.btnRows = r
143 cfg.btnColumns = c 151 cfg.btnColumns = c
144 cfg.spacing = s 152 cfg.spacing = s
145 end 153 end
154 ReAction:RefreshBar(self)
146 end 155 end
147 156
148 function Bar:GetName() 157 function Bar:GetName()
149 return self.name 158 return self.name
150 end 159 end
151 160
161 -- only ReAction:RenameBar() should call this function
152 function Bar:SetName(name) 162 function Bar:SetName(name)
153 self.name = name 163 self.name = name
164 -- controlLabelString is defined in Overlay.lua
154 if self.controlLabelString then 165 if self.controlLabelString then
155 self.controlLabelString:SetText(self.name) 166 self.controlLabelString:SetText(self.name)
156 end 167 end
157 end 168 end
158 169
159 function Bar:PlaceButton(f, idx, baseW, baseH) 170 function Bar:AddButton(idx, button)
171 self.buttons[button] = idx
172 SecureStateHeader_Refresh(self:GetFrame())
173 end
174
175 function Bar:RemoveButton(button)
176 self.buttons[button] = nil
177 end
178
179 function Bar:IterateButtons() -- iterator returns button, idx
180 return pairs(self.buttons)
181 end
182
183 function Bar:PlaceButton(button, baseW, baseH)
184 local idx = self.buttons[button]
185 if not idx then return end
160 local r, c, s = self:GetButtonGrid() 186 local r, c, s = self:GetButtonGrid()
161 local bh, bw = self:GetButtonSize() 187 local bh, bw = self:GetButtonSize()
162 local row, col = floor((idx-1)/c), mod((idx-1),c) -- zero-based 188 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
163 local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s 189 local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s
164 local scale = bw/baseW 190 local scale = bw/baseW
191 local f = button:GetFrame()
165 192
166 f:ClearAllPoints() 193 f:ClearAllPoints()
167 f:SetPoint("TOPLEFT",x/scale,-y/scale) 194 f:SetPoint("TOPLEFT",x/scale,-y/scale)
168 f:SetScale(scale) 195 f:SetScale(scale)
169 self.buttons[f] = true 196 end
170 end 197
171 198 -- Creates (or updates) a named binding which binds a key press to a call to SetAttribute()
172 199 -- pass a nil key to unbind
173 -- multi-state functions -- 200 function Bar:SetAttributeBinding( name, key, attribute, value )
174 function Bar:GetNumPages() 201 if not name then
175 return self.config.nPages or 1 202 error("usage - Bar:SetAttributeBinding(name [, key, attribute, value]")
176 end 203 end
177 204 local f = self:GetFrame()
178 -- 205
179 -- 'rule' is a rule-string to pass to RegisterStateDriver 206 -- clear the old binding, if any
180 -- 'states' is a { ["statename"] = <don't care> } table of all state names 207 if self.keybinds[name] then
181 -- 'keybinds' is a { ["statename"] = keybind } table of all keybound states 208 SetOverrideBinding(f, false, self.keybinds[name], nil)
182 -- 209 end
183 function Bar:SetStateDriver( rule, states, keybinds ) 210 if key then
184 local f = self.frame 211 f:SetAttribute(format("attribute-name-%s",name), attribute)
185 local kbprefix = "" 212 f:SetAttribute(format("attribute-value-%s",name), value)
186 do 213 SetOverrideBindingClick(f, false, key, f:GetName(), name) -- binding name is the virtual mouse button
214 end
215 self.keybinds[name] = key
216 end
217
218 -- Sets up a state driver 'name' for the bar, using the provided 'rule'
219 -- Also sets attributes 'statemap-<name>-<key>'=<value> for each entry in the passed map
220 -- if 'rule' is nil or an empty string, the driver is unregistered.
221 function Bar:SetStateDriver( name, rule, map )
222 local f = self:GetFrame()
223 if rule and #rule > 0 then
224 if map then
225 for key, value in pairs(map) do
226 f:SetAttribute( format("statemap-%s-%s",name,key), value )
227 end
228 end
229 RegisterStateDriver(f, name, rule)
230 self.statedrivers[name] = true
231 elseif self.statedrivers[name] then
232 UnregisterStateDriver(f, name)
233 self.statedrivers[name] = nil
234 end
235 end
236
237 -- Set an attribute on the frame (or its buttons if 'doButtons' = true)
238 -- Either or both 'map' and 'default' can be passed:
239 -- - If 'map' is omitted, then 'default' is set to the attribute.
240 -- - If 'map' is provided, then it is interpreted as an unordered
241 -- table of the form { ["statename"] = ["value"] }, and will be
242 -- converted into a SecureStateHeaderTemplate style state-parsed
243 -- string, e.g. "<state1>:<value1>;<state2>:<value2>". If 'default'
244 -- is also provided, then its value will be converted to a string
245 -- and appended.
246 function Bar:SetStateAttribute( attribute, map, default, doButtons )
247 local value = default
248 if map then
187 local tmp = { } 249 local tmp = { }
188 for s, k in pairs(keybinds) do 250 for state, value in pairs(map) do
189 if k and #k > 0 then -- filter out false table entries 251 table.insert(tmp, format("%s:%s",tostring(state),tostring(value)))
190 -- if in a keybound state, set the stack to the new state but stay in the keybound state. 252 end
191 -- use $s as a placeholder for the current state, it will be gsub()'d in later 253 if default then
192 table.insert(tmp,("%s:$s set() %s"):format(s,s)) 254 table.insert(tmp, tostring(default))
255 end
256 value = table.concat(tmp,";")
257 end
258 if doButtons then
259 for b in pairs(self.buttons) do
260 local f = b.GetFrame and b:GetFrame()
261 if f then
262 f:SetAttribute(attribute, value)
193 end 263 end
194 end 264 end
195 table.insert(tmp,kbprefix) -- to get a trailing ';' if the table is not empty 265 else
196 kbprefix = table.concat(tmp,";") 266 self:GetFrame():SetAttribute(attribute, value)
197 end 267 end
198 for state in pairs(states) do 268 SecureStateHeader_Refresh(self:GetFrame())
199 -- For all states: if in a keybound state, stay there (with stack manipulation, see above). 269 end
200 -- Otherwise, go to the state
201 f:SetAttribute(("statemap-reaction-%s"):format(state),("%s%s"):format(kbprefix:gsub("%$s",state),state))
202
203 local binding = keybinds[state]
204 self:SetStateKeybind(binding, state) -- set the binding even if nil, to clear it unconditionally
205 if binding then
206 -- for key bindings, use the state-stack to toggle between the last state and the keybound state
207 -- use a different 'virtual state' passed to attribute 'reaction-state' for key bindings, "<state>_binding"
208 f:SetAttribute(("statemap-reaction-%s_binding"):format(state), ("%s:pop();*:set(%s)"):format(state,state))
209 end
210 end
211
212 if rule and #rule > 0 then
213 self.stateDriver = true
214 RegisterStateDriver(f, "reaction", rule)
215 elseif self.statedriver then
216 self.statedriver = false
217 UnregisterStateDriver(f, "reaction")
218 end
219 end
220
221 function Bar:SetHideStates(s)
222 for f in pairs(self.buttons) do
223 if f:GetParent() == self.frame then
224 f:SetAttribute("hidestates",s)
225 end
226 end
227 SecureStateHeader_Refresh(self.frame)
228 end
229
230 function Bar:SetStateKeybind(key, state, defaultstate)
231 -- Lazily create a tiny offscreen button which sends "<state>_binding" values to the
232 -- bar frame's state-reaction attribute, by using an override binding to generate a
233 -- click on the button with a virtual mouse button "state".
234 -- This gets around making the bar itself a clickable button, which is not desirable
235 local f = self.statebuttonframe
236 if key then
237 if not f then
238 f = CreateFrame("Button",self:GetName().."_statebutton",self.frame,"SecureActionButtonTemplate")
239 f:SetPoint("BOTTOMRIGHT",UIParent,"TOPLEFT")
240 f:SetWidth(1)
241 f:SetHeight(1)
242 f:SetAttribute("type*","attribute")
243 f:SetAttribute("attribute-name*","state-reaction")
244 f:SetAttribute("attribute-frame*",self.frame)
245 f:Show()
246 f.bindings = { }
247 self.statebuttonframe = f
248 end
249 f:SetAttribute(("attribute-value-%s"):format(state),("%s_binding"):format(state))
250 -- clear the old binding, if any, for this state
251 if f.bindings[state] then
252 SetOverrideBinding(self.frame, false, f.bindings[state], nil)
253 end
254 SetOverrideBindingClick(self.frame, false, key, f:GetName(), state) -- the state name is used as the virtual button
255 f.bindings[state] = key
256 elseif f then
257 key = f.bindings[state]
258 if key then
259 SetOverrideBinding(self.frame, false, key, nil)
260 f.bindings[state] = nil
261 end
262 end
263 end
264
265 function Bar:SetStatePageMap(state, map) -- map is a { ["statename"] = pagenumber } table
266 local f = self.frame
267 local tmp = { }
268 for s, p in pairs(map) do
269 table.insert(tmp, ("%s:page%d"):format(s,p))
270 end
271 local spec = table.concat(tmp,";")
272 local current = f:GetAttribute("statebutton")
273 if spec ~= f:GetAttribute("statebutton") then
274 f:SetAttribute("statebutton", spec)
275 end
276 SecureStateHeader_Refresh(f)
277 end
278
279 function Bar:SetStateKeybindOverrideMap(states) -- 'states' is an array of state-names that should have keybind overrides enabled
280 local f = self.frame
281 for i = 1, #states do
282 local s = states[i]
283 states[i] = ("%s:%s"):format(s,s)
284 end
285 table.insert(states,"_defaultbindings")
286 f:SetAttribute("statebindings",table.concat(states,";"))
287 SecureStateHeader_Refresh(f)
288 for b in pairs(self.buttons) do
289 -- TODO: signal child frames that they should maintain multiple bindings
290 end
291 end
292
293 local _ofskeys = { "point", "relpoint", "x", "y" }
294 function Bar:SetStateAnchorMap( map ) -- 'map' is a { ["statename"] = { point=point, relpoint=relpoint, x=x, y=y } } table
295 local f = self.frame
296 local c = self.config
297 local default = { point = c.anchor, relpoint = c.relativePoint, x = c.x, y = c.y }
298 for _, key in pairs(_ofskeys) do
299 local t = { }
300 for state, info in pairs(map) do
301 if info[key] then
302 table.insert(t, ("%s:%s"):format(state, info[key]))
303 end
304 end
305 if #t > 0 and default[key] then table.insert(t, tostring(default[key])) end
306 f:SetAttribute(("headofs%s"):format(key), table.concat(t,";") or "")
307 end
308 SecureStateHeader_Refresh(f)
309 end
310
311 function Bar:SetStateScaleMap( map ) -- 'map' is a { ["statename"] = scalevalue } table
312 local f = self.frame
313 local t = { }
314 for state, scale in pairs(map) do
315 table.insert( t, ("%s:%s"):format(state,scale) )
316 end
317 if #t > 0 then table.insert(t, "1.0") end
318 f:SetAttribute("headscale",table.concat(t,";") or "")
319 SecureStateHeader_Refresh(f)
320 end
321
322 270
323 271
324 ------ Export as a class-factory ------ 272 ------ Export as a class-factory ------
325 ReAction.Bar = { 273 ReAction.Bar = {
326 prototype = Bar, 274 prototype = Bar,
327 new = function(self, ...) 275 New = function(self, ...)
328 local x = { } 276 local x = { }
329 for k,v in pairs(Bar) do 277 for k,v in pairs(Bar) do
330 x[k] = v 278 x[k] = v
331 end 279 end
332 Constructor(x, ...) 280 Constructor(x, ...)