annotate Bar.lua @ 82:5b7fd67a28ea

Fixed incorrect state propagator
author Flick <flickerstreak@gmail.com>
date Wed, 25 Jun 2008 23:03:06 +0000
parents 57f8151ea0f0
children f32e2375e39b
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@25 164 function Bar:SetButtonGrid(r,c,s)
flickerstreak@25 165 if r > 0 and c > 0 and s > 0 then
flickerstreak@25 166 local cfg = self.config
flickerstreak@25 167 cfg.btnRows = r
flickerstreak@25 168 cfg.btnColumns = c
flickerstreak@25 169 cfg.spacing = s
flickerstreak@25 170 end
flickerstreak@75 171 ReAction:RefreshBar(self)
flickerstreak@25 172 end
flickerstreak@25 173
flickerstreak@25 174 function Bar:GetName()
flickerstreak@25 175 return self.name
flickerstreak@25 176 end
flickerstreak@25 177
flickerstreak@77 178 function Bar:GetFrame()
flickerstreak@77 179 -- this method is included for documentation purposes. It is overridden
flickerstreak@77 180 -- in the New method for each object.
flickerstreak@77 181 error("Invalid Bar object: used without initialization")
flickerstreak@77 182 end
flickerstreak@77 183
flickerstreak@77 184 function Bar:GetButtonFrame()
flickerstreak@77 185 -- this method is included for documentation purposes. It is overridden
flickerstreak@77 186 -- in the New method for each object.
flickerstreak@77 187 error("Invalid Bar object: used without initialization")
flickerstreak@77 188 end
flickerstreak@77 189
flickerstreak@75 190 -- only ReAction:RenameBar() should call this function
flickerstreak@33 191 function Bar:SetName(name)
flickerstreak@33 192 self.name = name
flickerstreak@75 193 -- controlLabelString is defined in Overlay.lua
flickerstreak@33 194 if self.controlLabelString then
flickerstreak@33 195 self.controlLabelString:SetText(self.name)
flickerstreak@33 196 end
flickerstreak@33 197 end
flickerstreak@33 198
flickerstreak@75 199 function Bar:AddButton(idx, button)
flickerstreak@77 200 -- store in a reverse-index array
flickerstreak@75 201 self.buttons[button] = idx
flickerstreak@77 202 self:GetButtonFrame():SetAttribute("addchild",button:GetFrame())
flickerstreak@75 203 SecureStateHeader_Refresh(self:GetFrame())
flickerstreak@75 204 end
flickerstreak@75 205
flickerstreak@75 206 function Bar:RemoveButton(button)
flickerstreak@75 207 self.buttons[button] = nil
flickerstreak@75 208 end
flickerstreak@75 209
flickerstreak@75 210 function Bar:IterateButtons() -- iterator returns button, idx
flickerstreak@75 211 return pairs(self.buttons)
flickerstreak@75 212 end
flickerstreak@75 213
flickerstreak@75 214 function Bar:PlaceButton(button, baseW, baseH)
flickerstreak@75 215 local idx = self.buttons[button]
flickerstreak@75 216 if not idx then return end
flickerstreak@25 217 local r, c, s = self:GetButtonGrid()
flickerstreak@25 218 local bh, bw = self:GetButtonSize()
flickerstreak@75 219 local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based
flickerstreak@25 220 local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s
flickerstreak@25 221 local scale = bw/baseW
flickerstreak@75 222 local f = button:GetFrame()
flickerstreak@25 223
flickerstreak@25 224 f:ClearAllPoints()
flickerstreak@25 225 f:SetPoint("TOPLEFT",x/scale,-y/scale)
flickerstreak@25 226 f:SetScale(scale)
flickerstreak@25 227 end
flickerstreak@25 228
flickerstreak@75 229 -- Creates (or updates) a named binding which binds a key press to a call to SetAttribute()
flickerstreak@75 230 -- pass a nil key to unbind
flickerstreak@75 231 function Bar:SetAttributeBinding( name, key, attribute, value )
flickerstreak@75 232 if not name then
flickerstreak@75 233 error("usage - Bar:SetAttributeBinding(name [, key, attribute, value]")
flickerstreak@75 234 end
flickerstreak@75 235 local f = self:GetFrame()
flickerstreak@71 236
flickerstreak@75 237 -- clear the old binding, if any
flickerstreak@75 238 if self.keybinds[name] then
flickerstreak@75 239 SetOverrideBinding(f, false, self.keybinds[name], nil)
flickerstreak@75 240 end
flickerstreak@75 241 if key then
flickerstreak@75 242 f:SetAttribute(format("attribute-name-%s",name), attribute)
flickerstreak@75 243 f:SetAttribute(format("attribute-value-%s",name), value)
flickerstreak@75 244 SetOverrideBindingClick(f, false, key, f:GetName(), name) -- binding name is the virtual mouse button
flickerstreak@75 245 end
flickerstreak@75 246 self.keybinds[name] = key
flickerstreak@68 247 end
flickerstreak@28 248
flickerstreak@75 249 -- Sets up a state driver 'name' for the bar, using the provided 'rule'
flickerstreak@75 250 -- Also sets attributes 'statemap-<name>-<key>'=<value> for each entry in the passed map
flickerstreak@75 251 -- if 'rule' is nil or an empty string, the driver is unregistered.
flickerstreak@75 252 function Bar:SetStateDriver( name, rule, map )
flickerstreak@75 253 local f = self:GetFrame()
flickerstreak@75 254 if rule and #rule > 0 then
flickerstreak@75 255 if map then
flickerstreak@75 256 for key, value in pairs(map) do
flickerstreak@75 257 f:SetAttribute( format("statemap-%s-%s",name,key), value )
flickerstreak@72 258 end
flickerstreak@72 259 end
flickerstreak@75 260 RegisterStateDriver(f, name, rule)
flickerstreak@75 261 self.statedrivers[name] = true
flickerstreak@75 262 elseif self.statedrivers[name] then
flickerstreak@75 263 UnregisterStateDriver(f, name)
flickerstreak@75 264 self.statedrivers[name] = nil
flickerstreak@72 265 end
flickerstreak@72 266 end
flickerstreak@72 267
flickerstreak@81 268 -- Set an attribute on the frame (or each button if 'doButtons' = true)
flickerstreak@75 269 -- Either or both 'map' and 'default' can be passed:
flickerstreak@75 270 -- - If 'map' is omitted, then 'default' is set to the attribute.
flickerstreak@75 271 -- - If 'map' is provided, then it is interpreted as an unordered
flickerstreak@75 272 -- table of the form { ["statename"] = ["value"] }, and will be
flickerstreak@75 273 -- converted into a SecureStateHeaderTemplate style state-parsed
flickerstreak@75 274 -- string, e.g. "<state1>:<value1>;<state2>:<value2>". If 'default'
flickerstreak@75 275 -- is also provided, then its value will be converted to a string
flickerstreak@75 276 -- and appended.
flickerstreak@81 277 function Bar:SetStateAttribute( attribute, map, default, doButtons )
flickerstreak@75 278 local value = default
flickerstreak@75 279 if map then
flickerstreak@75 280 local tmp = { }
flickerstreak@75 281 for state, value in pairs(map) do
flickerstreak@75 282 table.insert(tmp, format("%s:%s",tostring(state),tostring(value)))
flickerstreak@68 283 end
flickerstreak@75 284 if default then
flickerstreak@75 285 table.insert(tmp, tostring(default))
flickerstreak@75 286 end
flickerstreak@75 287 value = table.concat(tmp,";")
flickerstreak@68 288 end
flickerstreak@81 289 if doButtons then
flickerstreak@81 290 for b in self:IterateButtons() do
flickerstreak@81 291 b:GetFrame():SetAttribute(attribute,value)
flickerstreak@81 292 end
flickerstreak@81 293 else
flickerstreak@81 294 self:GetFrame():SetAttribute(attribute, value)
flickerstreak@81 295 end
flickerstreak@81 296 SecureStateHeader_Refresh(self:GetFrame())
flickerstreak@71 297 end