comparison classes/ReAction.lua @ 7:f920db5fc6b1

version 0.3
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:25:29 +0000
parents dfd829db3ad0
children c05fd3e18b4f
comparison
equal deleted inserted replaced
6:2da5089ab7ff 7:f920db5fc6b1
1 --
2 -- ReAction is a base class for action button management. It provides a
3 -- framework for button setup, placement, recycling, keybinding, etc.
4 -- It does not define any layout or actual action functionality,
5 -- which is deferred to derived classes.
6 --
7 -- ReAction implements the ReBar.IButton interface. It is designed to be used with ReBar
8 -- for grouping and laying out buttons.
9 --
10 -- ReAction supports the ReBound keybinding interface (only).
11 --
12 -- Each instance of a ReAction-derived object is associated with a single action
13 -- button frame, which may or may not have a one-to-one mapping with actionID.
14 --
15 -- ReAction makes use of a configuration structure, which can (and should) be
16 -- extended by implementations. A single config structure is shared amongst all
17 -- ReAction class instances within a single ReBar group, so the structure should
18 -- contain sub-tables for any property that needs to be defined on a per-button
19 -- basis. Each button is passed a 'barIdx' parameter to be used as an index into
20 -- such tables.
21 --
22 -- The base config structure is as follows:
23 --
24 -- config = {
25 -- type = "ReAction", -- static string (used by ReBar)
26 -- subtype = "string", -- ReAction implementation identifier (index into ReAction.buttonTypes)
27 -- ids = { {paged list}, {paged list}, ... } -- indexed by self.barIdx
28 -- }
29 --
30
31
32 local AceOO = AceLibrary("AceOO-2.0")
33 local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc
34
35
1 -- private constants 36 -- private constants
2 local namePrefix = "ReActionButton"
3 local _G = getfenv(0)
4 local ACTION_FREE = { }
5 local MAX_ACTIONS = 120
6
7 local hotKeyDefaultColor = { r=1.0, g=1.0, b=1.0, a=1.0 }
8 local hotKeyDisabledColor = { r=0.6, g=0.6, b=0.6, a=1.0 }
9 local hotKeyOutOfRangeColor = { r=1.0, g=0.2, b=0.2, a=1.0 }
10
11 local hotKeyModifierColors = {
12 S = { r=0.6, g=0.6, b=1.0, a=1.0 }, -- shift
13 C = { r=1.0, g=0.82, b=0, a=1.0 }, -- ctrl
14 A = { r=0.1, g=1.0, b=0.1, a=1.0 }, -- alt
15 }
16
17 -- TODO: localize these key names with GetBindingText(KEY_) 37 -- TODO: localize these key names with GetBindingText(KEY_)
18 local keybindAbbreviations = { 38 local keybindAbbreviations = {
19 ["Mouse Button "] = "M-", 39 ["Mouse Button "] = "M-",
20 ["Spacebar"] = "Sp", 40 ["Spacebar"] = "Sp",
21 ["Num Pad "] = "Num-", 41 ["Num Pad "] = "Num-",
22 ["Page Up"] = "PgUp", 42 ["Page Up"] = "PgUp",
23 ["Page Down"] = "PgDn", 43 ["Page Down"] = "PgDn",
24 [" Arrow"] = "", 44 [" Arrow"] = "",
25 } 45 }
26 46
27 local equippedActionBorderColor = { r=0, g=1.0, b=0, a=0.35 } 47
28 48 ------------------------
29 local actionUsableColor = { r=1.0, g=1.0, b=1.0, a=1.0 } 49 -- Interface Declarations
30 local actionNotUsableColor = { r=0.4, g=0.4, b=0.4, a=1.0 } 50 ------------------------
31 local actionNotEnoughManaColor = { r=0.2, g=0.2, b=0.7, a=1.0 } 51
32 local actionOutOfRangeColor = { r=1.0, g=0.2, b=0.2, a=1.0 } 52 -- The ActionType interface defines what the button does when it is clicked. At a
33 53 -- minimum it must do the equivalent of SetAttribute("type", ...) and handle events that
34 -- private variables 54 -- cause the action to change.
35 local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc 55 -- ReAction implementations must provide this interface (implicitly or explicitly).
36 local actionButtonTbl = { } 56 local IActionType = AceOO.Interface {
37 57 SetID = "function", -- SetID(id, [page]) optional argument indicates page #: omitting indicates default. self.config.idx[barIdx] must be updated.
38 58 GetID = "function", -- id = GetID([page]) optional argument indicates page #: omitting indicates current page
39 59 IsActionEmpty = "function", -- bool = IsActionEmpty()
40 -- ReActionButton is a class prototype object. 60 SetupAction = "function", -- one-time setup
41 ReActionButton = AceLibrary("AceOO-2.0").Class("AceEvent-2.0") 61 UpdateAction = "function", -- general action properties should be refreshed
42 62 PickupAction = "function", -- pick up the action on the button and put on the cursor
43 ------------------- 63 PlaceAction = "function", -- place the action on the cursor
44 -- Class methods 64 UpdateTooltip = "function", -- update the tooltip with the action's info
45 ------------------- 65 }
46 66
47 -- In addition to supporting the 'new' creation method (in which case an action ID must be specified directly), 67 -- The Display interface defines the "look and feel" of the action button. It should define the
48 -- ReActionButton supports the 'acquire'/'release' creation method, which recycles objects and manages actionID assignment. 68 -- actual widgets and widget layout and provide methods to update the display.
49 69 -- ReAction implementations must provide this interface (implicitly or explicitly).
50 function ReActionButton:acquire(parent, config, barIdx) 70 -- Note that ReAction implementations may also require additional display interfaces to be supported.
71 --
72 -- Also note: the class 'new' method must take *only* the primary button ID as an argument.
73 local IDisplay = AceOO.Interface {
74 SetupDisplay = "function", -- SetupDisplay(buttonName), one-time setup
75 UpdateDisplay = "function", -- UpdateDisplay(), general display state should be refreshed
76 TempShow = "function", -- TempShow(visible), calls to this can be nested so keep track.
77 GetActionFrame = "function", -- f = GetActionFrame(), return a frame derived from SecureActionButtonTemplate (note: this is inherited unimplemented from ReBar.IButton)
78 GetBaseButtonSize = "function", -- sz = GetBaseButtonSize(), return size in pixels of the nominal button (square)
79 DisplayID = "function", -- DisplayID(id), show the action ID (or equivalent). Pass nil to hide.
80 DisplayHotkey = "function", -- DisplayHotkey(keyText), set the hotkey display text
81 }
82
83
84 ----------------------------
85 -- ReAction class definition
86 ----------------------------
87
88 ReAction = AceOO.Class("AceEvent-2.0", ReBar.IButton, IActionType, IDisplay)
89 ReAction.virtual = true
90 ReAction.IActionType = IActionType
91 ReAction.IDisplay = IDisplay
92
93
94 -----------------------
95 -- Static class members
96 -----------------------
97
98 ReAction.recycler = CreateFrame("Frame",nil,UIParent)
99 ReAction.recycler:SetAllPoints(UIParent)
100 ReAction.recycler:Hide()
101
102 ReAction.buttonTypes = { }
103
104
105
106 -----------------------
107 -- Static class methods
108 -----------------------
109
110 function ReAction:AddButtonType( name, class, maxIDs )
111 self.buttonTypes[name] = { subtype = class, maxIDs = maxIDs }
112 end
113
114 function ReAction:GetButtonType( name )
115 if name then
116 local t = self.buttonTypes[name]
117 if t then
118 return t.subtype, t.maxIDs
119 end
120 end
121 end
122
123 function ReAction:GetButtonTypeList()
124 local list = { }
125 for name, _ in pairs(self.buttonTypes) do
126 table.insert(list,name)
127 end
128 return list
129 end
130
131 function ReAction:GetAvailableID( subtype, hint )
132 local class, maxIDs = self:GetButtonType(subtype)
133
134 -- store the list of action buttons in use in the button type class factory
135 if class._idTbl == nil then class._idTbl = { } end
136 local t = class._idTbl
51 local id = nil 137 local id = nil
52 for i = 1, MAX_ACTIONS do 138
53 if actionButtonTbl[i] == nil or actionButtonTbl[i].inUse == false then 139 -- find lowest ID not in use
140 for i = 1, maxIDs do
141 if t[i] == nil or t[i].inUse == false then
54 id = i 142 id = i
55 break 143 break
56 end 144 end
57 end 145 end
58 146
59 if id == nil then return nil end -- all buttons and action ids are in use 147 if id == nil then return nil end -- all action ids are in use
60 148
61 local hint = config.actionIDs[barIdx] 149 -- if a hint is given, see if that one is free instead
62 if hint and (actionButtonTbl[hint] == nil or actionButtonTbl[hint].inUse == false) then 150 if hint and (t[hint] == nil or t[hint].inUse == false) then
63 id = hint 151 id = hint
64 end 152 end
65 153
66 if actionButtonTbl[id] == nil then 154 if t[id] == nil then
67 actionButtonTbl[id] = { } 155 t[id] = { }
68 end 156 end
69 local t = actionButtonTbl[id] 157 t[id].inUse = true
70 158
71 t.inUse = true 159 return id, t[id]
72 if t.button then 160 end
73 t.button:Configure(parent,config,barIdx) 161
74 else 162 function ReAction:Acquire(config, barIdx, pages, buttonsPerPage)
75 t.button = self:new(parent,config,barIdx,id) 163 local btnType = self:GetButtonType(config.subtype)
76 end 164 if not btnType then
77 165 error("ReAction: Unknown button type specified.")
78 -- fix screwy config with overlapping IDs 166 end
79 config.actionIDs[barIdx] = id 167 pages = pages or 1
80 return t.button 168
81 end 169 local ids = { }
82 170 local primary = nil
83 function ReActionButton:release( b ) 171
172 for i = 1, pages do
173 local hint = config.ids[barIdx] and config.ids[barIdx][i]
174 if hint == nil and i > 1 and ids[i-1] then
175 hint = ids[i-1] + (buttonsPerPage or 0)
176 end
177 local id, p = self:GetAvailableID(config.subtype, hint)
178 if id == nil then
179 break
180 end
181 primary = primary or p
182 if id then
183 ids[i] = id
184 end
185 end
186
187 if primary then
188 if not primary.button then
189 primary.button = btnType:new(ids[1])
190 end
191 if primary.button then
192 config.ids[barIdx] = ids
193 primary.button:Configure(config,barIdx)
194 end
195 end
196
197 return primary and primary.button
198 end
199
200 function ReAction:Release( b )
84 if b then 201 if b then
85 actionButtonTbl[b:GetActionID()].inUse = false 202 for i = 1, #b.config.ids[b.barIdx] do
203 local id = b:GetID(i)
204 if id then
205 b.class._idTbl[id].inUse = false
206 end
207 end
208 b.config = nil
86 b:Recycle() 209 b:Recycle()
87 end 210 end
88 end 211 end
89 212
90 213 function ReAction:ShowAllIds()
91 214 for _, t in pairs(self.buttonTypes) do
92 function ReActionButton:ShowAllActionIDs() 215 if t.subtype._idTbl then
93 for _, b in ipairs(actionButtonTbl) do 216 for _, tbl in pairs(t.subtype._idTbl) do
94 b:ShowActionID() 217 if tbl.button then tbl.button:DisplayID(tbl.button:GetID()) end
95 end 218 end
96 end 219 end
97 220 end
98 function ReActionButton:HideAllActionIDs() 221 self.showIDs_ = true
99 for _, b in ipairs(actionButtonTbl) do 222 end
100 b:HideActionID() 223
101 end 224 function ReAction:HideAllIds()
225 for _, t in pairs(self.buttonTypes) do
226 if t.subtype._idTbl then
227 for _, tbl in pairs(t.subtype._idTbl) do
228 if tbl.button then tbl.button:DisplayID(nil) end
229 end
230 end
231 end
232 self.showIDs_ = false
102 end 233 end
103 234
104 235
105 ---------------------- 236 ----------------------
106 -- Instance methods 237 -- Instance methods
107 ---------------------- 238 ----------------------
108 function ReActionButton.prototype:init( parentFrame, config, barIdx, id ) 239
109 ReActionButton.super.prototype.init(self) 240 -- constructor
110 241
111 -- create the button widget 242 function ReAction.prototype:init()
112 self.name = namePrefix..id 243 ReAction.super.prototype.init(self)
113 self.button = CreateFrame("CheckButton", self.name, parentFrame, "ReActionButtonTemplate") 244 end
245
246
247 -- ReBar.IButton interface
248
249 function ReAction.prototype:BarUnlocked()
250 self:TempShow(true)
251 end
252
253 function ReAction.prototype:BarLocked()
254 self:TempShow(false)
255 end
256
257 function ReAction.prototype:PlaceButton(parent, point, x, y, sz)
258 local b = self:GetActionFrame()
259 local baseSize = self:GetBaseButtonSize()
260 local scale = baseSize and baseSize ~= 0 and ((sz or 1) / baseSize) or 1
261 if scale == 0 then scale = 1 end
262 b:ClearAllPoints()
263 b:SetParent(parent)
264 b:SetScale(scale)
265 b:SetPoint(point,x/scale,y/scale)
266 end
267
268 function ReAction.prototype:SetPages( n )
269 n = tonumber(n)
270 local ids = self.config.ids[self.barIdx]
271 if n and n >= 1 then
272 -- note that as long as n >= 1 then id[1] will never be modified, which is what we want
273 -- because then the button frame ID would be out of sync with the static button name
274 while #ids < n do
275 local id = ReAction:GetAvailableID(self.config.subtype, ids[#ids] + #self.config.ids)
276 if id == nil then
277 break
278 end
279 self:SetID( id, #ids + 1 )
280 table.insert(ids, id)
281 end
282 while #ids > n do
283 local id = table.remove(ids)
284 self:SetID( nil, #ids + 1 )
285 self.class._idTbl[id].inUse = false
286 end
287 end
288 end
289
290 -- Event handlers
291
292 function ReAction.prototype:UPDATE_BINDINGS()
293 self:DisplayHotkey(self:GetKeyBindingText(nil, true))
294 end
295
296
297 -- Internal functions
298
299 function ReAction.prototype:Recycle()
300 --self:SetKeyBinding(nil) -- TODO: only if we're using override bindings
301 self:UnregisterAllEvents()
114 302
115 -- store references to the various sub-frames so we don't have to look it up all the time 303 -- tuck the frame away
116 self.frames = { 304 local b = self:GetActionFrame()
117 hotkey = _G[self.name.."HotKey"], 305 b:SetParent(ReAction.recycler)
118 count = _G[self.name.."Count"],
119 cooldown = _G[self.name.."Cooldown"],
120 -- nCooldown = _G[self.name.."CooldownNumeric"],
121 macro = _G[self.name.."Name"],
122 icon = _G[self.name.."Icon"],
123 border = _G[self.name.."Border"],
124 normalTexture = _G[self.name.."NormalTexture"],
125 flash = _G[self.name.."Flash"],
126 actionID = _G[self.name.."ActionID"],
127 }
128
129 -- provide a reference back to this object for the frame to use in event handlers
130 self.button.rxnBtn = self
131
132 -- set the action ID
133 self:SetActionID(id)
134
135 -- register button with ReBinder for keybinding
136 ReBinder:AddKeybindTarget(self.button)
137
138 -- initialize
139 self:Configure(parentFrame, config, barIdx)
140 end
141
142 local function tcopy(t)
143 local r = { }
144 for k, v in pairs(t) do
145 r[k] = (type(v) == "table" and tcopy(v) or v)
146 end
147 return r
148 end
149
150 function ReActionButton.prototype:Recycle()
151 local b = self.button
152
153 self:SetKeyBinding(nil)
154 self:UpdateDisplay()
155 b:UnregisterAllEvents()
156 b:SetParent(ReActionButtonRecycleFrame)
157 b:ClearAllPoints() 306 b:ClearAllPoints()
158 b:SetPoint("TOPLEFT",0,0) 307 b:SetPoint("TOPLEFT",0,0)
159 b:Hide() 308 b:Hide()
160 self.config = tcopy(self.config) -- ew, but necessary 309 end
161 end 310
162 311 function ReAction.prototype:Configure( config, barIdx )
163 function ReActionButton.prototype:BarUnlocked()
164 self:ShowGridTmp()
165 end
166
167 function ReActionButton.prototype:BarLocked()
168 self:HideGridTmp()
169 end
170
171
172 -- set the button location
173 function ReActionButton.prototype:PlaceButton(point, x, y, sz)
174 local b = self.button
175 local scale = sz / 36
176 b:ClearAllPoints()
177 b:SetScale( scale )
178 b:SetPoint(point,x/scale,y/scale)
179 end
180
181
182 function ReActionButton.prototype:ShowActionID()
183 self.frames.actionID:Show()
184 end
185
186 function ReActionButton.prototype:HideActionID()
187 self.frames.actionID:Hide()
188 end
189
190
191
192 -- configuration and setup
193 function ReActionButton.prototype:Configure( parentFrame, config, barIdx )
194 self.config = config 312 self.config = config
195 self.barIdx = barIdx 313 self.barIdx = barIdx
196 self.showGridTmp_ = 0 314
197 315 local ids = config.ids[barIdx]
198 self.button:ClearAllPoints() 316 self:SetID(ids[1]) -- default id
199 self.button:SetParent(parentFrame) 317 for i = 1, #ids do
200 318 self:SetID(ids[i], i) -- paged ids
201 self:SetupAttributes() 319 end
202 self:RegisterStaticEvents() 320 self:UpdateAction()
203
204 self:ApplyLayout()
205 self:ApplyStyle()
206
207 self:UpdateDisplay() 321 self:UpdateDisplay()
208 end 322 self:DisplayHotkey(self:GetKeyBindingText(nil, true))
209 323
210 function ReActionButton.prototype:SetupAttributes()
211 local b = self.button
212 b:SetAttribute("type", "action")
213 b:SetAttribute("shift-type*", ATTRIBUTE_NOOP)
214 b:SetAttribute("checkselfcast", true)
215 b:SetAttribute("useparent-unit", true)
216 end
217
218 function ReActionButton.prototype:RegisterStaticEvents()
219 self:RegisterEvent("PLAYER_ENTERING_WORLD")
220 self:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
221 self:RegisterEvent("UPDATE_BINDINGS") 324 self:RegisterEvent("UPDATE_BINDINGS")
222 self:RegisterEvent("ACTIONBAR_SHOWGRID") 325 end
223 self:RegisterEvent("ACTIONBAR_HIDEGRID") 326
224 end 327 function ReAction.prototype:SetKeyBinding( k, mouseBtn )
225
226 function ReActionButton.prototype:RegisterActionEvents()
227 self:RegisterEvent("ACTIONBAR_UPDATE_STATE")
228 self:RegisterEvent("ACTIONBAR_UPDATE_USABLE")
229 self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN", "ACTIONBAR_UPDATE_USABLE")
230 self:RegisterEvent("UPDATE_INVENTORY_ALERTS", "ACTIONBAR_UPDATE_USABLE")
231 self:RegisterEvent("PLAYER_AURAS_CHANGED", "ACTIONBAR_UPDATE_USABLE")
232 self:RegisterEvent("PLAYER_TARGET_CHANGED", "ACTIONBAR_UPDATE_USABLE")
233 self:RegisterEvent("UNIT_INVENTORY_CHANGED")
234 self:RegisterEvent("CRAFT_SHOW")
235 self:RegisterEvent("CRAFT_CLOSE", "CRAFT_SHOW")
236 self:RegisterEvent("TRADE_SKILL_SHOW", "CRAFT_SHOW")
237 self:RegisterEvent("TRADE_SKILL_CLOSE", "CRAFT_SHOW")
238 self:RegisterEvent("PLAYER_ENTER_COMBAT", "CRAFT_SHOW")
239 self:RegisterEvent("PLAYER_LEAVE_COMBAT")
240 self:RegisterEvent("START_AUTOREPEAT_SPELL")
241 self:RegisterEvent("STOP_AUTOREPEAT_SPELL")
242
243 self.button:SetScript("OnUpdate", function() self:OnUpdate(arg1) end)
244 self.actionEventsRegistered = true
245 end
246
247 function ReActionButton.prototype:UnregisterActionEvents()
248 self:UnregisterEvent("ACTIONBAR_UPDATE_STATE")
249 self:UnregisterEvent("ACTIONBAR_UPDATE_USABLE")
250 self:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
251 self:UnregisterEvent("UPDATE_INVENTORY_ALERTS")
252 self:UnregisterEvent("PLAYER_AURAS_CHANGED")
253 self:UnregisterEvent("PLAYER_TARGET_CHANGED")
254 self:UnregisterEvent("UNIT_INVENTORY_CHANGED")
255 self:UnregisterEvent("CRAFT_SHOW")
256 self:UnregisterEvent("CRAFT_CLOSE")
257 self:UnregisterEvent("TRADE_SKILL_SHOW")
258 self:UnregisterEvent("TRADE_SKILL_CLOSE")
259 self:UnregisterEvent("PLAYER_ENTER_COMBAT")
260 self:UnregisterEvent("PLAYER_LEAVE_COMBAT")
261 self:UnregisterEvent("START_AUTOREPEAT_SPELL")
262 self:UnregisterEvent("STOP_AUTOREPEAT_SPELL")
263
264 self.button:SetScript("OnUpdate", nil)
265 self.actionEventsRegistered = false
266 end
267
268
269 -- event handlers
270 function ReActionButton.prototype:ACTIONBAR_SLOT_CHANGED()
271 if arg1 == 0 or arg1 == self:GetActionID() then
272 self:UpdateDisplay()
273 end
274 end
275
276 function ReActionButton.prototype:PLAYER_ENTERING_WORLD()
277 self:UpdateDisplay()
278 end
279
280 function ReActionButton.prototype:UPDATE_BINDINGS()
281 self:UpdateDisplay()
282 end
283
284 function ReActionButton.prototype:ACTIONBAR_SHOWGRID()
285 self:ShowGridTmp()
286 end
287
288 function ReActionButton.prototype:ACTIONBAR_HIDEGRID()
289 self:HideGridTmp()
290 end
291
292 function ReActionButton.prototype:ACTIONBAR_UPDATE_STATE()
293 self:UpdateCheckedState()
294 end
295
296 function ReActionButton.prototype:ACTIONBAR_UPDATE_USABLE()
297 self:UpdateUsable()
298 self:UpdateCooldown()
299 self:ColorHotKey()
300 end
301
302 function ReActionButton.prototype:UNIT_INVENTORY_CHANGED()
303 if arg1 == "player" then
304 self:UpdateDisplay()
305 end
306 end
307
308 function ReActionButton.prototype:CRAFT_SHOW()
309 self:UpdateCheckedState()
310 end
311
312 function ReActionButton.prototype:PLAYER_ENTER_COMBAT()
313 if IsAttackAction(self:GetActionID()) then
314 self:StartFlash()
315 end
316 end
317
318 function ReActionButton.prototype:PLAYER_LEAVE_COMBAT()
319 if IsAttackAction(self:GetActionID()) then
320 self:StopFlash()
321 end
322 end
323
324 function ReActionButton.prototype:START_AUTOREPEAT_SPELL()
325 if IsAutoRepeatAction(self:GetActionID()) then
326 self:StartFlash()
327 end
328 end
329
330 function ReActionButton.prototype:STOP_AUTOREPEAT_SPELL()
331 if self:IsFlashing() and not IsAttackAction(self:GetActionID()) then
332 self:StopFlash()
333 end
334 end
335
336
337 -- OnUpdate handler
338 function ReActionButton.prototype:OnUpdate(elapsed)
339 local action = self:GetActionID()
340 local f = self.frames
341
342 -- handle flashing
343 if self:IsFlashing() then
344 self.flashtime = self.flashtime - elapsed
345 if self.flashtime <= 0 then
346 local overtime = -self.flashtime
347 if overtime >= ATTACK_BUTTON_FLASH_TIME then
348 overtime = 0
349 end
350 self.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
351
352 if f.flash:IsVisible() then
353 f.flash:Hide()
354 else
355 f.flash:Show()
356 end
357 end
358 end
359
360 -- Handle range indicator
361 if self.rangeTimer then
362 self.rangeTimer = self.rangeTimer - elapsed
363 if self.rangeTimer <= 0 then
364 self:ColorHotKey()
365 self:UpdateUsable()
366 self.rangeTimer = TOOLTIP_UPDATE_TIME
367 end
368 end
369
370 -- handle toltip update
371 if self.tooltipTime then
372 self.tooltipTime = self.tooltipTime - elapsed
373 if self.tooltipTime <= 0 then
374 if GameTooltip:IsOwned(self.button) then
375 self:UpdateTooltip()
376 else
377 self.tooltipTime = nil
378 end
379 end
380 end
381 end
382
383
384
385
386 -- keybinding functions
387 function ReActionButton.prototype:SetKeyBinding( k )
388 if k == nil or kbValidate(k) then 328 if k == nil or kbValidate(k) then
389 local current = self:GetKeyBinding() 329 local current = self:GetKeyBinding(mouseBtn)
390 ClearOverrideBindings(self.button) 330 -- !!!TODO: do we need this?
331 -- ClearOverrideBindings(self:GetActionFrame())
391 if current then 332 if current then
392 SetBinding(current,nil) 333 SetBinding(current,nil)
393 end 334 end
394 if k then 335 if k then
395 SetBindingClick(k, self.name, "LeftButton") 336 -- TODO: use OverrideBinding and store the keybinding in the profile.
396 end 337 SetBindingClick(k, self:GetActionFrame():GetName(), mouseBtn or "LeftButton")
397 end 338 end
398 end 339 end
399 340 end
400 function ReActionButton.prototype:GetKeyBinding() 341
401 return GetBindingKey("CLICK "..self.name..":LeftButton") 342 function ReAction.prototype:GetKeyBinding( mouseBtn )
402 end 343 return GetBindingKey("CLICK "..self:GetActionFrame():GetName()..":"..(mouseBtn or "LeftButton"))
403 344 end
404 -- action ID functions 345
405 function ReActionButton.prototype:SetActionID( id ) 346 function ReAction.prototype:GetKeyBindingText( mouseBtn, abbrev )
406 self.actionID = tonumber(id) -- force data integrity 347 local key = self:GetKeyBinding(mouseBtn)
407 self:ApplyActionID() 348 local txt = key and GetBindingText(key, "KEY_", abbrev and 1) or ""
408 end 349
409 350 if txt and abbrev then
410 function ReActionButton.prototype:GetActionID() 351 -- further abbreviate some key names
411 return self.actionID 352 for pat, rep in pairs(keybindAbbreviations) do
412 end 353 txt = string.gsub(txt,pat,rep)
413 354 end
414 function ReActionButton.prototype:ApplyActionID() 355 end
415 local action = tonumber(self:GetActionID()) 356 return txt
416 self.button:SetAttribute("action",action) 357 end
417 self.frames.actionID:SetText(action or "") 358
418 end 359 function ReAction.prototype:SetTooltip()
419 360 GameTooltip_SetDefaultAnchor(GameTooltip, self:GetActionFrame())
420 function ReActionButton.prototype:ShowActionID()
421 self.frames.actionID:Show()
422 end
423
424 function ReActionButton.prototype:HideActionID()
425 self.frames.actionID:Hide()
426 end
427
428 function ReActionButton:ShowAllActionIDs() -- class-wide function
429 for _, tbl in pairs(actionButtonTbl) do
430 if tbl.button then tbl.button:ShowActionID() end
431 end
432 end
433
434 function ReActionButton:HideAllActionIDs() -- class-wide function
435 for _, tbl in pairs(actionButtonTbl) do
436 if tbl.button then tbl.button:HideActionID() end
437 end
438 end
439
440
441 -- action transfer functions
442 function ReActionButton.prototype:ShouldPickupAction(mouseButton)
443 return IsShiftKeyDown() and not SecureButton_GetModifiedAttribute(self.button, "type", mouseButton)
444 end
445
446 function ReActionButton.prototype:ShowGridTmp()
447 self.showGridTmp_ = self.showGridTmp_ + 1
448 self:UpdateVisibility()
449 end
450
451 function ReActionButton.prototype:HideGridTmp()
452 self.showGridTmp_ = self.showGridTmp_ - 1
453 self:UpdateVisibility()
454 end
455
456 function ReActionButton.prototype:ShowGrid()
457 self.config.showGrid = true
458 self:UpdateVisibility()
459 end
460
461 function ReActionButton.prototype:HideGrid()
462 self.config.showGrid = false
463 self:UpdateVisibility()
464 end
465
466
467
468 -- layout & style functions
469 function ReActionButton.prototype:ApplyLayout()
470 local f = self.frames
471
472 if self.config.keyBindLoc then
473 local h = f.hotkey
474 local loc = self.config.keyBindLoc
475 local top = string.match(loc,"TOP")
476 local bottom = string.match(loc, "BOTTOM")
477 h:ClearAllPoints()
478 h:SetWidth(40)
479 h:SetPoint(top or bottom,0,top and 2 or -2)
480 local j
481 if string.match(loc,"LEFT") then
482 j = "LEFT"
483 elseif string.match(loc,"RIGHT") then
484 j = "RIGHT"
485 else
486 j = "CENTER"
487 end
488 h:SetJustifyH(j)
489 end
490
491 if self.config.stackCountLoc then
492 local c = f.count
493 local loc = self.config.stackCountLoc
494 local top = string.match(loc,"TOP")
495 local bottom = string.match(loc, "BOTTOM")
496 c:ClearAllPoints()
497 c:SetWidth(40)
498 c:SetPoint(top or bottom,0,top and 2 or -2)
499 local j
500 if string.match(loc,"LEFT") then
501 j = "LEFT"
502 elseif string.match(loc,"RIGHT") then
503 j = "RIGHT"
504 else
505 j = "CENTER"
506 end
507 c:SetJustifyH(j)
508 end
509
510 if self.config.showKeyBind then
511 f.hotkey:Show()
512 else
513 f.hotkey:Hide()
514 end
515
516 if self.config.showStackCount then
517 f.count:Show()
518 else
519 f.count:Hide()
520 end
521
522 --[[
523 if self.config.showNumericCooldown then
524 f.nCooldown:Show()
525 else
526 f.nCooldown:Hide()
527 end
528 ]]
529
530 if self.config.showMacroName then
531 f.macro:Show()
532 else
533 f.macro:Hide()
534 end
535 end
536
537 function ReActionButton.prototype:ApplyStyle()
538 local f = self.frames
539 -- for now, just a static style
540 f.hotkey:SetFontObject(NumberFontNormal)
541 f.count:SetFontObject(NumberFontNormalYellow)
542 end
543
544
545
546 -- start/stop flashing
547 function ReActionButton.prototype:StartFlash()
548 self.flashing = true
549 self.flashtime = 0
550 self:UpdateCheckedState()
551 end
552
553 function ReActionButton.prototype:StopFlash()
554 self.flashing = false
555 self.frames.flash:Hide()
556 self:UpdateCheckedState()
557 end
558
559 function ReActionButton.prototype:IsFlashing()
560 return self.flashing
561 end
562
563
564
565
566
567 -- set the tooltip
568 function ReActionButton.prototype:SetTooltip()
569 GameTooltip_SetDefaultAnchor(GameTooltip, self.button)
570 self:UpdateTooltip() 361 self:UpdateTooltip()
571 end 362 end
572 363
573 function ReActionButton.prototype:ClearTooltip() 364 function ReAction.prototype:ClearTooltip()
574 tooltipTime = nil 365 tooltipTime = nil
575 GameTooltip:Hide() 366 GameTooltip:Hide()
576 end 367 end
577
578
579
580 -- colorize the hotkey
581 function ReActionButton.prototype:ColorHotKey()
582 local action = self:GetActionID()
583 local c = hotKeyDefaultColor
584
585 if action and HasAction(action) then
586 if IsActionInRange(action) == 0 then
587 c = hotKeyOutOfRangeColor
588 elseif self.config.keyBindColorCode then
589 local modKey = string.match( self.frames.hotkey:GetText() or "", "([ACS])%-")
590 c = modKey and hotKeyModifierColors[modKey] or c
591 end
592 else
593 c = hotKeyDisabledColor
594 end
595
596 self.frames.hotkey:SetTextColor(c.r, c.g, c.b)
597 end
598
599
600
601
602 -- display update functions
603 function ReActionButton.prototype:UpdateDisplay()
604 self:UpdateIcon()
605 self:UpdateHotkey()
606 self:UpdateCount()
607 self:UpdateMacroText()
608 self:UpdateUsable()
609 self:UpdateCooldown()
610 self:UpdateFlash()
611 self:UpdateEvents()
612 self:UpdateVisibility()
613 self:UpdateTooltip()
614 end
615
616 function ReActionButton.prototype:UpdateIcon()
617 local f = self.frames
618 local b = self.button
619
620 local action = self:GetActionID()
621 local texture = action and GetActionTexture(action)
622
623 if action and texture then
624 f.icon:SetTexture(texture)
625 f.icon:Show()
626 self.rangeTimer = -1
627 b:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
628 else
629 f.icon:Hide()
630 f.cooldown:Hide()
631 self.rangeTimer = nil
632 b:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
633 end
634
635 self:UpdateCheckedState()
636
637 -- Add a green border if action is an equipped item
638 if action and IsEquippedAction(action) then
639 local c = equippedActionBorderColor
640 f.border:SetVertexColor(c.r, c.g, c.b, c.a or 1)
641 f.border:Show()
642 else
643 f.border:Hide()
644 end
645 end
646
647 function ReActionButton.prototype:UpdateCheckedState()
648 local action = self:GetActionID()
649 if action and (IsCurrentAction(action) or IsAutoRepeatAction(action)) then
650 self.button:SetChecked(1)
651 else
652 self.button:SetChecked(0)
653 end
654 end
655
656
657 function ReActionButton.prototype:UpdateHotkey()
658 local action = self:GetActionID()
659 local b = self.button
660 local f = self.frames
661 local key = self:GetKeyBinding()
662 local txt = GetBindingText(key, "KEY_",1)
663
664 -- abbreviate long key names
665 for pat, rep in pairs(keybindAbbreviations) do
666 txt = string.gsub(txt,pat,rep)
667 end
668
669 if txt then
670 f.hotkey:SetText(string.upper(txt))
671 self:ColorHotKey()
672 else
673 f.hotkey:SetText("")
674 end
675 end
676
677 function ReActionButton.prototype:UpdateCount()
678 local action = self:GetActionID()
679 if action and (IsConsumableAction(action) or IsStackableAction(action)) then
680 self.frames.count:SetText(GetActionCount(action))
681 else
682 self.frames.count:SetText("")
683 end
684 end
685
686 function ReActionButton.prototype:UpdateMacroText()
687 local action = self:GetActionID()
688 self.frames.macro:SetText(action and GetActionText(action) or "")
689 end
690
691 function ReActionButton.prototype:UpdateUsable()
692 local f = self.frames
693 local action = self:GetActionID()
694 local isUsable, notEnoughMana
695 if action then
696 isUsable, notEnoughMana = IsUsableAction(action)
697 end
698 if isUsable then
699 local c = actionUsableColor
700 if IsActionInRange(action) == 0 then
701 c = actionOutOfRangeColor
702 else
703 f.normalTexture:SetVertexColor(c.r, c.g, c.b, c.a)
704 end
705 f.icon:SetVertexColor(c.r, c.g, c.b, c.a)
706 elseif notEnoughMana then
707 local c = actionNotEnoughManaColor
708 f.icon:SetVertexColor(c.r, c.g, c.b, c.a)
709 f.normalTexture:SetVertexColor(c.r, c.g, c.b, c.a)
710 else
711 local c = actionNotUsableColor
712 f.icon:SetVertexColor(c.r, c.g, c.b, c.a)
713 f.normalTexture:SetVertexColor(1.0, 1.0, 1.0)
714 end
715 end
716
717 function ReActionButton.prototype:UpdateCooldown()
718 local action = self:GetActionID()
719 if action then
720 local start, duration, enable = GetActionCooldown(self:GetActionID())
721 CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enable)
722 -- do numeric cooldown stuff here
723 end
724 end
725
726 function ReActionButton.prototype:UpdateFlash()
727 local b = self.button
728 local action = self:GetActionID()
729 if action and ((IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action)) then
730 self:StartFlash()
731 else
732 self:StopFlash()
733 end
734 end
735
736 function ReActionButton.prototype:UpdateVisibility()
737 local action = self:GetActionID()
738 local b = self.button
739
740 if b:GetAttribute("statehidden") then
741 b:Hide()
742 elseif action and HasAction(action) then
743 b:GetNormalTexture():SetAlpha(1.0)
744 b:Show()
745 elseif self.showGridTmp_ > 0 or self.config.showGrid then
746 b:GetNormalTexture():SetAlpha(0.5)
747 self.frames.cooldown:Hide()
748 b:Show()
749 else
750 b:Hide()
751 end
752 end
753
754 function ReActionButton.prototype:UpdateEvents()
755 local action = self:GetActionID()
756 if action and HasAction(action) then
757 self:RegisterActionEvents()
758 elseif self.actionEventsRegistered then
759 self:UnregisterActionEvents()
760 end
761 end
762
763 function ReActionButton.prototype:UpdateTooltip()
764 local action = self:GetActionID()
765 if GameTooltip:IsOwned(self.button) and action and GameTooltip:SetAction(action) then
766 self.tooltipTime = TOOLTIP_UPDATE_TIME
767 else
768 self.tooltipTime = nil
769 end
770 end
771
772
773