annotate classes/ReAction.lua @ 8:c05fd3e18b4f

Version 0.31
author Flick <flickerstreak@gmail.com>
date Tue, 20 Mar 2007 21:33:59 +0000
parents f920db5fc6b1
children f3a7bfebc283
rev   line source
flickerstreak@7 1 --
flickerstreak@7 2 -- ReAction is a base class for action button management. It provides a
flickerstreak@7 3 -- framework for button setup, placement, recycling, keybinding, etc.
flickerstreak@7 4 -- It does not define any layout or actual action functionality,
flickerstreak@7 5 -- which is deferred to derived classes.
flickerstreak@7 6 --
flickerstreak@7 7 -- ReAction implements the ReBar.IButton interface. It is designed to be used with ReBar
flickerstreak@7 8 -- for grouping and laying out buttons.
flickerstreak@7 9 --
flickerstreak@7 10 -- ReAction supports the ReBound keybinding interface (only).
flickerstreak@7 11 --
flickerstreak@7 12 -- Each instance of a ReAction-derived object is associated with a single action
flickerstreak@7 13 -- button frame, which may or may not have a one-to-one mapping with actionID.
flickerstreak@7 14 --
flickerstreak@7 15 -- ReAction makes use of a configuration structure, which can (and should) be
flickerstreak@7 16 -- extended by implementations. A single config structure is shared amongst all
flickerstreak@7 17 -- ReAction class instances within a single ReBar group, so the structure should
flickerstreak@7 18 -- contain sub-tables for any property that needs to be defined on a per-button
flickerstreak@7 19 -- basis. Each button is passed a 'barIdx' parameter to be used as an index into
flickerstreak@7 20 -- such tables.
flickerstreak@7 21 --
flickerstreak@7 22 -- The base config structure is as follows:
flickerstreak@7 23 --
flickerstreak@7 24 -- config = {
flickerstreak@7 25 -- type = "ReAction", -- static string (used by ReBar)
flickerstreak@7 26 -- subtype = "string", -- ReAction implementation identifier (index into ReAction.buttonTypes)
flickerstreak@7 27 -- ids = { {paged list}, {paged list}, ... } -- indexed by self.barIdx
flickerstreak@7 28 -- }
flickerstreak@7 29 --
flickerstreak@7 30
flickerstreak@7 31
flickerstreak@7 32 local AceOO = AceLibrary("AceOO-2.0")
flickerstreak@7 33 local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc
flickerstreak@7 34
flickerstreak@7 35
flickerstreak@1 36 -- private constants
flickerstreak@2 37 -- TODO: localize these key names with GetBindingText(KEY_)
flickerstreak@2 38 local keybindAbbreviations = {
flickerstreak@2 39 ["Mouse Button "] = "M-",
flickerstreak@2 40 ["Spacebar"] = "Sp",
flickerstreak@2 41 ["Num Pad "] = "Num-",
flickerstreak@2 42 ["Page Up"] = "PgUp",
flickerstreak@2 43 ["Page Down"] = "PgDn",
flickerstreak@2 44 [" Arrow"] = "",
flickerstreak@2 45 }
flickerstreak@2 46
flickerstreak@1 47
flickerstreak@7 48 ------------------------
flickerstreak@7 49 -- Interface Declarations
flickerstreak@7 50 ------------------------
flickerstreak@1 51
flickerstreak@7 52 -- The ActionType interface defines what the button does when it is clicked. At a
flickerstreak@7 53 -- minimum it must do the equivalent of SetAttribute("type", ...) and handle events that
flickerstreak@7 54 -- cause the action to change.
flickerstreak@7 55 -- ReAction implementations must provide this interface (implicitly or explicitly).
flickerstreak@7 56 local IActionType = AceOO.Interface {
flickerstreak@7 57 SetID = "function", -- SetID(id, [page]) optional argument indicates page #: omitting indicates default. self.config.idx[barIdx] must be updated.
flickerstreak@7 58 GetID = "function", -- id = GetID([page]) optional argument indicates page #: omitting indicates current page
flickerstreak@7 59 IsActionEmpty = "function", -- bool = IsActionEmpty()
flickerstreak@7 60 SetupAction = "function", -- one-time setup
flickerstreak@7 61 UpdateAction = "function", -- general action properties should be refreshed
flickerstreak@7 62 PickupAction = "function", -- pick up the action on the button and put on the cursor
flickerstreak@7 63 PlaceAction = "function", -- place the action on the cursor
flickerstreak@7 64 UpdateTooltip = "function", -- update the tooltip with the action's info
flickerstreak@7 65 }
flickerstreak@1 66
flickerstreak@7 67 -- The Display interface defines the "look and feel" of the action button. It should define the
flickerstreak@7 68 -- actual widgets and widget layout and provide methods to update the display.
flickerstreak@7 69 -- ReAction implementations must provide this interface (implicitly or explicitly).
flickerstreak@7 70 -- Note that ReAction implementations may also require additional display interfaces to be supported.
flickerstreak@7 71 --
flickerstreak@7 72 -- Also note: the class 'new' method must take *only* the primary button ID as an argument.
flickerstreak@7 73 local IDisplay = AceOO.Interface {
flickerstreak@7 74 SetupDisplay = "function", -- SetupDisplay(buttonName), one-time setup
flickerstreak@7 75 UpdateDisplay = "function", -- UpdateDisplay(), general display state should be refreshed
flickerstreak@7 76 TempShow = "function", -- TempShow(visible), calls to this can be nested so keep track.
flickerstreak@7 77 GetActionFrame = "function", -- f = GetActionFrame(), return a frame derived from SecureActionButtonTemplate (note: this is inherited unimplemented from ReBar.IButton)
flickerstreak@7 78 GetBaseButtonSize = "function", -- sz = GetBaseButtonSize(), return size in pixels of the nominal button (square)
flickerstreak@8 79 DisplayID = "function", -- DisplayID(show), true/false to show/hide the action ID (or equivalent)
flickerstreak@7 80 DisplayHotkey = "function", -- DisplayHotkey(keyText), set the hotkey display text
flickerstreak@7 81 }
flickerstreak@1 82
flickerstreak@1 83
flickerstreak@7 84 ----------------------------
flickerstreak@7 85 -- ReAction class definition
flickerstreak@7 86 ----------------------------
flickerstreak@1 87
flickerstreak@7 88 ReAction = AceOO.Class("AceEvent-2.0", ReBar.IButton, IActionType, IDisplay)
flickerstreak@7 89 ReAction.virtual = true
flickerstreak@7 90 ReAction.IActionType = IActionType
flickerstreak@7 91 ReAction.IDisplay = IDisplay
flickerstreak@1 92
flickerstreak@1 93
flickerstreak@7 94 -----------------------
flickerstreak@7 95 -- Static class members
flickerstreak@7 96 -----------------------
flickerstreak@7 97
flickerstreak@7 98 ReAction.recycler = CreateFrame("Frame",nil,UIParent)
flickerstreak@7 99 ReAction.recycler:SetAllPoints(UIParent)
flickerstreak@7 100 ReAction.recycler:Hide()
flickerstreak@7 101
flickerstreak@7 102 ReAction.buttonTypes = { }
flickerstreak@7 103
flickerstreak@7 104
flickerstreak@7 105
flickerstreak@7 106 -----------------------
flickerstreak@7 107 -- Static class methods
flickerstreak@7 108 -----------------------
flickerstreak@7 109
flickerstreak@7 110 function ReAction:AddButtonType( name, class, maxIDs )
flickerstreak@7 111 self.buttonTypes[name] = { subtype = class, maxIDs = maxIDs }
flickerstreak@7 112 end
flickerstreak@7 113
flickerstreak@7 114 function ReAction:GetButtonType( name )
flickerstreak@7 115 if name then
flickerstreak@7 116 local t = self.buttonTypes[name]
flickerstreak@7 117 if t then
flickerstreak@7 118 return t.subtype, t.maxIDs
flickerstreak@7 119 end
flickerstreak@7 120 end
flickerstreak@7 121 end
flickerstreak@7 122
flickerstreak@7 123 function ReAction:GetButtonTypeList()
flickerstreak@7 124 local list = { }
flickerstreak@7 125 for name, _ in pairs(self.buttonTypes) do
flickerstreak@7 126 table.insert(list,name)
flickerstreak@7 127 end
flickerstreak@7 128 return list
flickerstreak@7 129 end
flickerstreak@7 130
flickerstreak@7 131 function ReAction:GetAvailableID( subtype, hint )
flickerstreak@7 132 local class, maxIDs = self:GetButtonType(subtype)
flickerstreak@7 133
flickerstreak@7 134 -- store the list of action buttons in use in the button type class factory
flickerstreak@7 135 if class._idTbl == nil then class._idTbl = { } end
flickerstreak@7 136 local t = class._idTbl
flickerstreak@1 137 local id = nil
flickerstreak@7 138
flickerstreak@7 139 -- find lowest ID not in use
flickerstreak@7 140 for i = 1, maxIDs do
flickerstreak@7 141 if t[i] == nil or t[i].inUse == false then
flickerstreak@1 142 id = i
flickerstreak@1 143 break
flickerstreak@1 144 end
flickerstreak@1 145 end
flickerstreak@1 146
flickerstreak@7 147 if id == nil then return nil end -- all action ids are in use
flickerstreak@1 148
flickerstreak@8 149 -- if a hint is given, see if that one is free instead, as long as it's < maxIDs
flickerstreak@8 150 if hint and hint > 0 and hint <= maxIDs and (t[hint] == nil or t[hint].inUse == false) then
flickerstreak@1 151 id = hint
flickerstreak@1 152 end
flickerstreak@1 153
flickerstreak@7 154 if t[id] == nil then
flickerstreak@7 155 t[id] = { }
flickerstreak@1 156 end
flickerstreak@7 157 t[id].inUse = true
flickerstreak@1 158
flickerstreak@7 159 return id, t[id]
flickerstreak@7 160 end
flickerstreak@7 161
flickerstreak@7 162 function ReAction:Acquire(config, barIdx, pages, buttonsPerPage)
flickerstreak@7 163 local btnType = self:GetButtonType(config.subtype)
flickerstreak@7 164 if not btnType then
flickerstreak@7 165 error("ReAction: Unknown button type specified.")
flickerstreak@7 166 end
flickerstreak@7 167 pages = pages or 1
flickerstreak@7 168
flickerstreak@7 169 local ids = { }
flickerstreak@7 170 local primary = nil
flickerstreak@7 171
flickerstreak@7 172 for i = 1, pages do
flickerstreak@7 173 local hint = config.ids[barIdx] and config.ids[barIdx][i]
flickerstreak@7 174 if hint == nil and i > 1 and ids[i-1] then
flickerstreak@7 175 hint = ids[i-1] + (buttonsPerPage or 0)
flickerstreak@7 176 end
flickerstreak@7 177 local id, p = self:GetAvailableID(config.subtype, hint)
flickerstreak@7 178 if id == nil then
flickerstreak@7 179 break
flickerstreak@7 180 end
flickerstreak@7 181 primary = primary or p
flickerstreak@7 182 if id then
flickerstreak@7 183 ids[i] = id
flickerstreak@7 184 end
flickerstreak@1 185 end
flickerstreak@1 186
flickerstreak@7 187 if primary then
flickerstreak@7 188 if not primary.button then
flickerstreak@7 189 primary.button = btnType:new(ids[1])
flickerstreak@7 190 end
flickerstreak@7 191 if primary.button then
flickerstreak@7 192 config.ids[barIdx] = ids
flickerstreak@7 193 primary.button:Configure(config,barIdx)
flickerstreak@7 194 end
flickerstreak@7 195 end
flickerstreak@7 196
flickerstreak@7 197 return primary and primary.button
flickerstreak@1 198 end
flickerstreak@1 199
flickerstreak@7 200 function ReAction:Release( b )
flickerstreak@1 201 if b then
flickerstreak@7 202 for i = 1, #b.config.ids[b.barIdx] do
flickerstreak@7 203 local id = b:GetID(i)
flickerstreak@7 204 if id then
flickerstreak@7 205 b.class._idTbl[id].inUse = false
flickerstreak@7 206 end
flickerstreak@7 207 end
flickerstreak@7 208 b.config = nil
flickerstreak@1 209 b:Recycle()
flickerstreak@1 210 end
flickerstreak@1 211 end
flickerstreak@1 212
flickerstreak@7 213 function ReAction:ShowAllIds()
flickerstreak@7 214 for _, t in pairs(self.buttonTypes) do
flickerstreak@7 215 if t.subtype._idTbl then
flickerstreak@7 216 for _, tbl in pairs(t.subtype._idTbl) do
flickerstreak@8 217 if tbl.button then tbl.button:DisplayID(true) end
flickerstreak@7 218 end
flickerstreak@7 219 end
flickerstreak@1 220 end
flickerstreak@7 221 self.showIDs_ = true
flickerstreak@1 222 end
flickerstreak@1 223
flickerstreak@7 224 function ReAction:HideAllIds()
flickerstreak@7 225 for _, t in pairs(self.buttonTypes) do
flickerstreak@7 226 if t.subtype._idTbl then
flickerstreak@7 227 for _, tbl in pairs(t.subtype._idTbl) do
flickerstreak@8 228 if tbl.button then tbl.button:DisplayID(false) end
flickerstreak@7 229 end
flickerstreak@7 230 end
flickerstreak@1 231 end
flickerstreak@7 232 self.showIDs_ = false
flickerstreak@1 233 end
flickerstreak@1 234
flickerstreak@1 235
flickerstreak@1 236 ----------------------
flickerstreak@1 237 -- Instance methods
flickerstreak@1 238 ----------------------
flickerstreak@1 239
flickerstreak@7 240 -- constructor
flickerstreak@1 241
flickerstreak@7 242 function ReAction.prototype:init()
flickerstreak@7 243 ReAction.super.prototype.init(self)
flickerstreak@2 244 end
flickerstreak@2 245
flickerstreak@7 246
flickerstreak@7 247 -- ReBar.IButton interface
flickerstreak@7 248
flickerstreak@7 249 function ReAction.prototype:BarUnlocked()
flickerstreak@7 250 self:TempShow(true)
flickerstreak@8 251 self:DisplayID(true)
flickerstreak@1 252 end
flickerstreak@1 253
flickerstreak@7 254 function ReAction.prototype:BarLocked()
flickerstreak@7 255 self:TempShow(false)
flickerstreak@8 256 self:DisplayID(false)
flickerstreak@1 257 end
flickerstreak@1 258
flickerstreak@7 259 function ReAction.prototype:PlaceButton(parent, point, x, y, sz)
flickerstreak@7 260 local b = self:GetActionFrame()
flickerstreak@7 261 local baseSize = self:GetBaseButtonSize()
flickerstreak@7 262 local scale = baseSize and baseSize ~= 0 and ((sz or 1) / baseSize) or 1
flickerstreak@7 263 if scale == 0 then scale = 1 end
flickerstreak@1 264 b:ClearAllPoints()
flickerstreak@7 265 b:SetParent(parent)
flickerstreak@7 266 b:SetScale(scale)
flickerstreak@1 267 b:SetPoint(point,x/scale,y/scale)
flickerstreak@1 268 end
flickerstreak@1 269
flickerstreak@7 270 function ReAction.prototype:SetPages( n )
flickerstreak@7 271 n = tonumber(n)
flickerstreak@7 272 local ids = self.config.ids[self.barIdx]
flickerstreak@7 273 if n and n >= 1 then
flickerstreak@7 274 -- note that as long as n >= 1 then id[1] will never be modified, which is what we want
flickerstreak@7 275 -- because then the button frame ID would be out of sync with the static button name
flickerstreak@7 276 while #ids < n do
flickerstreak@7 277 local id = ReAction:GetAvailableID(self.config.subtype, ids[#ids] + #self.config.ids)
flickerstreak@7 278 if id == nil then
flickerstreak@7 279 break
flickerstreak@1 280 end
flickerstreak@7 281 self:SetID( id, #ids + 1 )
flickerstreak@7 282 table.insert(ids, id)
flickerstreak@7 283 end
flickerstreak@7 284 while #ids > n do
flickerstreak@7 285 local id = table.remove(ids)
flickerstreak@7 286 self:SetID( nil, #ids + 1 )
flickerstreak@7 287 self.class._idTbl[id].inUse = false
flickerstreak@1 288 end
flickerstreak@1 289 end
flickerstreak@1 290 end
flickerstreak@1 291
flickerstreak@7 292 -- Event handlers
flickerstreak@1 293
flickerstreak@7 294 function ReAction.prototype:UPDATE_BINDINGS()
flickerstreak@7 295 self:DisplayHotkey(self:GetKeyBindingText(nil, true))
flickerstreak@7 296 end
flickerstreak@1 297
flickerstreak@1 298
flickerstreak@7 299 -- Internal functions
flickerstreak@7 300
flickerstreak@7 301 function ReAction.prototype:Recycle()
flickerstreak@7 302 --self:SetKeyBinding(nil) -- TODO: only if we're using override bindings
flickerstreak@7 303 self:UnregisterAllEvents()
flickerstreak@7 304
flickerstreak@7 305 -- tuck the frame away
flickerstreak@7 306 local b = self:GetActionFrame()
flickerstreak@7 307 b:SetParent(ReAction.recycler)
flickerstreak@7 308 b:ClearAllPoints()
flickerstreak@7 309 b:SetPoint("TOPLEFT",0,0)
flickerstreak@7 310 b:Hide()
flickerstreak@7 311 end
flickerstreak@7 312
flickerstreak@7 313 function ReAction.prototype:Configure( config, barIdx )
flickerstreak@7 314 self.config = config
flickerstreak@7 315 self.barIdx = barIdx
flickerstreak@7 316
flickerstreak@7 317 local ids = config.ids[barIdx]
flickerstreak@7 318 self:SetID(ids[1]) -- default id
flickerstreak@7 319 for i = 1, #ids do
flickerstreak@7 320 self:SetID(ids[i], i) -- paged ids
flickerstreak@7 321 end
flickerstreak@7 322 self:UpdateAction()
flickerstreak@7 323 self:UpdateDisplay()
flickerstreak@7 324 self:DisplayHotkey(self:GetKeyBindingText(nil, true))
flickerstreak@7 325
flickerstreak@7 326 self:RegisterEvent("UPDATE_BINDINGS")
flickerstreak@7 327 end
flickerstreak@7 328
flickerstreak@7 329 function ReAction.prototype:SetKeyBinding( k, mouseBtn )
flickerstreak@1 330 if k == nil or kbValidate(k) then
flickerstreak@7 331 local current = self:GetKeyBinding(mouseBtn)
flickerstreak@7 332 -- !!!TODO: do we need this?
flickerstreak@7 333 -- ClearOverrideBindings(self:GetActionFrame())
flickerstreak@1 334 if current then
flickerstreak@1 335 SetBinding(current,nil)
flickerstreak@1 336 end
flickerstreak@1 337 if k then
flickerstreak@7 338 -- TODO: use OverrideBinding and store the keybinding in the profile.
flickerstreak@7 339 SetBindingClick(k, self:GetActionFrame():GetName(), mouseBtn or "LeftButton")
flickerstreak@1 340 end
flickerstreak@1 341 end
flickerstreak@1 342 end
flickerstreak@1 343
flickerstreak@7 344 function ReAction.prototype:GetKeyBinding( mouseBtn )
flickerstreak@7 345 return GetBindingKey("CLICK "..self:GetActionFrame():GetName()..":"..(mouseBtn or "LeftButton"))
flickerstreak@1 346 end
flickerstreak@1 347
flickerstreak@7 348 function ReAction.prototype:GetKeyBindingText( mouseBtn, abbrev )
flickerstreak@7 349 local key = self:GetKeyBinding(mouseBtn)
flickerstreak@7 350 local txt = key and GetBindingText(key, "KEY_", abbrev and 1) or ""
flickerstreak@7 351
flickerstreak@7 352 if txt and abbrev then
flickerstreak@7 353 -- further abbreviate some key names
flickerstreak@7 354 for pat, rep in pairs(keybindAbbreviations) do
flickerstreak@7 355 txt = string.gsub(txt,pat,rep)
flickerstreak@7 356 end
flickerstreak@7 357 end
flickerstreak@7 358 return txt
flickerstreak@1 359 end
flickerstreak@1 360
flickerstreak@7 361 function ReAction.prototype:SetTooltip()
flickerstreak@7 362 GameTooltip_SetDefaultAnchor(GameTooltip, self:GetActionFrame())
flickerstreak@1 363 self:UpdateTooltip()
flickerstreak@1 364 end
flickerstreak@1 365
flickerstreak@7 366 function ReAction.prototype:ClearTooltip()
flickerstreak@1 367 tooltipTime = nil
flickerstreak@1 368 GameTooltip:Hide()
flickerstreak@1 369 end