Mercurial > wow > reaction
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 |