annotate Bar.lua @ 86:f32e2375e39b

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