Mercurial > wow > reaction
view classes/ReAction.lua @ 10:f3a7bfebc283
Version 0.33
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Tue, 20 Mar 2007 21:37:38 +0000 |
parents | c05fd3e18b4f |
children | 90bf38d48efd |
line wrap: on
line source
-- -- ReAction is a base class for action button management. It provides a -- framework for button setup, placement, recycling, keybinding, etc. -- It does not define any layout or actual action functionality, -- which is deferred to derived classes. -- -- ReAction implements the ReBar.IButton interface. It is designed to be used with ReBar -- for grouping and laying out buttons. -- -- Each instance of a ReAction-derived object is associated with a single action -- button frame, which may or may not have a one-to-one mapping with actionID. -- -- ReAction makes use of a configuration structure, which can (and should) be -- extended by implementations. A single config structure is shared amongst all -- ReAction class instances within a single ReBar group, so the structure should -- contain sub-tables for any property that needs to be defined on a per-button -- basis. Each button is passed a 'barIdx' parameter to be used as an index into -- such tables. -- -- The base config structure is as follows: -- -- config = { -- type = "ReAction", -- static string (used by ReBar) -- subtype = "string", -- ReAction implementation identifier (index into ReAction.buttonTypes) -- ids = { {paged list}, {paged list}, ... } -- indexed by self.barIdx -- } -- local AceOO = AceLibrary("AceOO-2.0") local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc -- private constants -- TODO: localize these key names with GetBindingText(KEY_) local keybindAbbreviations = { ["Mouse Button "] = "M-", ["Spacebar"] = "Sp", ["Num Pad "] = "Num-", ["Page Up"] = "PgUp", ["Page Down"] = "PgDn", [" Arrow"] = "", } ------------------------ -- Interface Declarations ------------------------ -- The ActionType interface defines what the button does when it is clicked. At a -- minimum it must do the equivalent of SetAttribute("type", ...) and handle events that -- cause the action to change. -- ReAction implementations must provide this interface (implicitly or explicitly). local IActionType = AceOO.Interface { SetID = "function", -- SetID(id, [page]) optional argument indicates page #: omitting indicates default. self.config.idx[barIdx] must be updated. GetID = "function", -- id = GetID([page]) optional argument indicates page #: omitting indicates current page IsActionEmpty = "function", -- bool = IsActionEmpty() SetupAction = "function", -- one-time setup UpdateAction = "function", -- general action properties should be refreshed PickupAction = "function", -- pick up the action on the button and put on the cursor PlaceAction = "function", -- place the action on the cursor UpdateTooltip = "function", -- update the tooltip with the action's info } -- The Display interface defines the "look and feel" of the action button. It should define the -- actual widgets and widget layout and provide methods to update the display. -- ReAction implementations must provide this interface (implicitly or explicitly). -- Note that ReAction implementations may also require additional display interfaces to be supported. -- -- Also note: the class 'new' method must take *only* the primary button ID as an argument. local IDisplay = AceOO.Interface { SetupDisplay = "function", -- SetupDisplay(buttonName), one-time setup UpdateDisplay = "function", -- UpdateDisplay(), general display state should be refreshed TempShow = "function", -- TempShow(visible), calls to this can be nested so keep track. GetActionFrame = "function", -- f = GetActionFrame(), return a frame derived from SecureActionButtonTemplate (note: this is inherited unimplemented from ReBar.IButton) GetBaseButtonSize = "function", -- sz = GetBaseButtonSize(), return size in pixels of the nominal button (square) DisplayID = "function", -- DisplayID(show), true/false to show/hide the action ID (or equivalent) DisplayHotkey = "function", -- DisplayHotkey(keyText, button), set the hotkey display text. 2nd argument specifies which button the hotkey is for, default is "LeftButton". } ---------------------------- -- ReAction class definition ---------------------------- ReAction = AceOO.Class("AceEvent-2.0", ReBar.IButton, IActionType, IDisplay) ReAction.virtual = true ReAction.IActionType = IActionType ReAction.IDisplay = IDisplay ----------------------- -- Static class members ----------------------- ReAction.recycler = CreateFrame("Frame",nil,UIParent) ReAction.recycler:SetAllPoints(UIParent) ReAction.recycler:Hide() ReAction.buttonTypes = { } ----------------------- -- Static class methods ----------------------- function ReAction:AddButtonType( name, class, maxIDs ) self.buttonTypes[name] = { subtype = class, maxIDs = maxIDs } end function ReAction:GetButtonType( name ) if name then local t = self.buttonTypes[name] if t then return t.subtype, t.maxIDs end end end function ReAction:GetButtonTypeList() local list = { } for name, _ in pairs(self.buttonTypes) do table.insert(list,name) end return list end function ReAction:GetAvailableID( subtype, hint ) local class, maxIDs = self:GetButtonType(subtype) -- store the list of action buttons in use in the button type class factory if class._idTbl == nil then class._idTbl = { } end local t = class._idTbl local id = nil -- find lowest ID not in use for i = 1, maxIDs do if t[i] == nil or t[i].inUse == false then id = i break end end if id == nil then return nil end -- all action ids are in use -- if a hint is given, see if that one is free instead, as long as it's < maxIDs if hint and hint > 0 and hint <= maxIDs and (t[hint] == nil or t[hint].inUse == false) then id = hint end if t[id] == nil then t[id] = { } end t[id].inUse = true return id, t[id] end function ReAction:Acquire(config, barIdx, pages, buttonsPerPage) local btnType = self:GetButtonType(config.subtype) if not btnType then error("ReAction: Unknown button type specified.") end pages = pages or 1 local ids = { } local primary = nil for i = 1, pages do local hint = config.ids[barIdx] and config.ids[barIdx][i] if hint == nil and i > 1 and ids[i-1] then hint = ids[i-1] + (buttonsPerPage or 0) end local id, p = self:GetAvailableID(config.subtype, hint) if id == nil then break end primary = primary or p if id then ids[i] = id end end if primary then if not primary.button then primary.button = btnType:new(ids[1]) end if primary.button then config.ids[barIdx] = ids primary.button:Configure(config,barIdx) end end return primary and primary.button end function ReAction:Release( b ) if b then for i = 1, #b.config.ids[b.barIdx] do local id = b:GetID(i) if id then b.class._idTbl[id].inUse = false end end b.config = nil b:Recycle() end end function ReAction:ShowAllIds() for _, t in pairs(self.buttonTypes) do if t.subtype._idTbl then for _, tbl in pairs(t.subtype._idTbl) do if tbl.button then tbl.button:DisplayID(true) end end end end self.showIDs_ = true end function ReAction:HideAllIds() for _, t in pairs(self.buttonTypes) do if t.subtype._idTbl then for _, tbl in pairs(t.subtype._idTbl) do if tbl.button then tbl.button:DisplayID(false) end end end end self.showIDs_ = false end ---------------------- -- Instance methods ---------------------- -- constructor function ReAction.prototype:init() ReAction.super.prototype.init(self) end -- ReBar.IButton interface function ReAction.prototype:BarUnlocked() self:TempShow(true) self:DisplayID(true) end function ReAction.prototype:BarLocked() self:TempShow(false) self:DisplayID(false) end function ReAction.prototype:PlaceButton(parent, point, x, y, sz) local b = self:GetActionFrame() local baseSize = self:GetBaseButtonSize() local scale = baseSize and baseSize ~= 0 and ((sz or 1) / baseSize) or 1 if scale == 0 then scale = 1 end b:ClearAllPoints() b:SetParent(parent) b:SetScale(scale) b:SetPoint(point,x/scale,y/scale) end function ReAction.prototype:SetPages( n ) n = tonumber(n) local ids = self.config.ids[self.barIdx] if n and n >= 1 then -- note that as long as n >= 1 then id[1] will never be modified, which is what we want -- because then the button frame ID would be out of sync with the static button name while #ids < n do local id = ReAction:GetAvailableID(self.config.subtype, ids[#ids] + #self.config.ids) if id == nil then break end self:SetID( id, #ids + 1 ) table.insert(ids, id) end while #ids > n do local id = table.remove(ids) self:SetID( nil, #ids + 1 ) self.class._idTbl[id].inUse = false end end end -- Event handlers function ReAction.prototype:UPDATE_BINDINGS() self:DisplayHotkey(self:GetKeyBindingText("LeftButton", true),"LeftButton") self:DisplayHotkey(self:GetKeyBindingText("RightButton",true),"RightButton") end -- Internal functions function ReAction.prototype:Recycle() self:UnregisterAllEvents() -- tuck the frame away local b = self:GetActionFrame() b:SetParent(ReAction.recycler) b:ClearAllPoints() b:SetPoint("TOPLEFT",0,0) b:Hide() end function ReAction.prototype:Configure( config, barIdx ) self.config = config self.barIdx = barIdx local ids = config.ids[barIdx] self:SetID(ids[1]) -- default id for i = 1, #ids do self:SetID(ids[i], i) -- paged ids end self:UpdateAction() self:UpdateDisplay() self:DisplayHotkey(self:GetKeyBindingText(nil, true)) self:RegisterEvent("UPDATE_BINDINGS") end function ReAction.prototype:SetKeyBinding( k, mouseBtn ) if k == nil or kbValidate(k) then local current = self:GetKeyBinding(mouseBtn) if current then SetBinding(current,nil) end if k then SetBindingClick(k, self:GetActionFrame():GetName(), mouseBtn or "LeftButton") end end end function ReAction.prototype:GetKeyBinding( mouseBtn ) return GetBindingKey("CLICK "..self:GetActionFrame():GetName()..":"..(mouseBtn or "LeftButton")) end function ReAction.prototype:GetKeyBindingText( mouseBtn, abbrev ) local key = self:GetKeyBinding(mouseBtn) local txt = key and GetBindingText(key, "KEY_", abbrev and 1) or "" if txt and abbrev then -- further abbreviate some key names for pat, rep in pairs(keybindAbbreviations) do txt = string.gsub(txt,pat,rep) end end return txt end function ReAction.prototype:SetTooltip() GameTooltip_SetDefaultAnchor(GameTooltip, self:GetActionFrame()) self:UpdateTooltip() end function ReAction.prototype:ClearTooltip() tooltipTime = nil GameTooltip:Hide() end