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