annotate Bar.lua @ 73:dd01feae0d89

Split the bar overlay out into its own file
author Flick <flickerstreak@gmail.com>
date Thu, 05 Jun 2008 18:52:53 +0000
parents aa88aed52124
children 06cd74bdc7da
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@71 6 local SecureStateHeader_Refresh = SecureStateHeader_Refresh
flickerstreak@33 7
flickerstreak@33 8
flickerstreak@25 9
flickerstreak@25 10 -- update ReAction revision if this file is newer
flickerstreak@33 11 local revision = tonumber(("$Revision$"):match("%d+"))
flickerstreak@25 12 if revision > ReAction.revision then
flickerstreak@52 13 ReAction.revision = revision
flickerstreak@25 14 end
flickerstreak@25 15
flickerstreak@28 16 ------ BAR CLASS ------
flickerstreak@28 17 local Bar = { _classID = {} }
flickerstreak@25 18
flickerstreak@28 19 local function Constructor( self, name, config )
flickerstreak@25 20 self.name, self.config = name, config
flickerstreak@68 21 self.buttons = setmetatable({},{__mode="k"})
flickerstreak@25 22
flickerstreak@25 23 if type(config) ~= "table" then
flickerstreak@28 24 error("ReAction.Bar: config table required")
flickerstreak@25 25 end
flickerstreak@25 26
flickerstreak@54 27 local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent
flickerstreak@68 28 local f = CreateFrame("Frame",nil,parent,"SecureStateHeaderTemplate")
flickerstreak@25 29 f:SetFrameStrata("MEDIUM")
flickerstreak@30 30 config.width = config.width or 480
flickerstreak@30 31 config.height = config.height or 40
flickerstreak@25 32 f:SetWidth(config.width)
flickerstreak@25 33 f:SetWidth(config.height)
flickerstreak@25 34
flickerstreak@63 35 ReAction.RegisterCallback(self, "OnConfigModeChanged")
flickerstreak@63 36
flickerstreak@25 37 self.frame = f
flickerstreak@25 38 self:ApplyAnchor()
flickerstreak@25 39 f:Show()
flickerstreak@68 40 self:RefreshLayout()
flickerstreak@25 41 end
flickerstreak@25 42
flickerstreak@25 43 function Bar:Destroy()
flickerstreak@25 44 local f = self.frame
flickerstreak@25 45 f:UnregisterAllEvents()
flickerstreak@25 46 f:Hide()
flickerstreak@25 47 f:SetParent(UIParent)
flickerstreak@25 48 f:ClearAllPoints()
flickerstreak@63 49 ReAction.UnregisterAllCallbacks(self)
flickerstreak@72 50 if self.statedriver then
flickerstreak@72 51 UnregisterStateDriver(f, "reaction")
flickerstreak@72 52 end
flickerstreak@25 53 self.labelString = nil
flickerstreak@25 54 self.controlFrame = nil
flickerstreak@25 55 self.frame = nil
flickerstreak@25 56 self.config = nil
flickerstreak@25 57 end
flickerstreak@25 58
flickerstreak@63 59 function Bar:OnConfigModeChanged(event, mode)
flickerstreak@73 60 self:ShowControls(mode) -- ShowControls() defined in Overlay.lua
flickerstreak@63 61 end
flickerstreak@63 62
flickerstreak@25 63 function Bar:RefreshLayout()
flickerstreak@63 64 ReAction:RefreshBar(self)
flickerstreak@25 65 end
flickerstreak@25 66
flickerstreak@25 67 function Bar:ApplyAnchor()
flickerstreak@25 68 local f, config = self.frame, self.config
flickerstreak@25 69 f:SetWidth(config.width)
flickerstreak@25 70 f:SetHeight(config.height)
flickerstreak@25 71 local anchor = config.anchor
flickerstreak@51 72 f:ClearAllPoints()
flickerstreak@25 73 if anchor then
flickerstreak@52 74 local anchorTo = f:GetParent()
flickerstreak@25 75 if config.anchorTo then
flickerstreak@52 76 local bar = ReAction:GetBar(config.anchorTo)
flickerstreak@52 77 if bar then
flickerstreak@52 78 anchorTo = bar:GetFrame()
flickerstreak@52 79 else
flickerstreak@52 80 anchorTo = _G[config.anchorTo]
flickerstreak@52 81 end
flickerstreak@25 82 end
flickerstreak@52 83 f:SetPoint(anchor, anchorTo or f:GetParent(), config.relativePoint, config.x or 0, config.y or 0)
flickerstreak@25 84 else
flickerstreak@25 85 f:SetPoint("CENTER")
flickerstreak@25 86 end
flickerstreak@25 87 end
flickerstreak@25 88
flickerstreak@51 89 function Bar:SetAnchor(point, frame, relativePoint, x, y)
flickerstreak@51 90 local c = self.config
flickerstreak@51 91 c.anchor = point or c.anchor
flickerstreak@51 92 c.anchorTo = frame and frame:GetName() or c.anchorTo
flickerstreak@51 93 c.relativePoint = relativePoint or c.relativePoint
flickerstreak@51 94 c.x = x or c.x
flickerstreak@51 95 c.y = y or c.y
flickerstreak@51 96 self:ApplyAnchor()
flickerstreak@51 97 end
flickerstreak@51 98
flickerstreak@51 99 function Bar:GetAnchor()
flickerstreak@51 100 local c = self.config
flickerstreak@51 101 return (c.anchor or "CENTER"), (c.anchorTo or self.frame:GetParent():GetName()), (c.relativePoint or c.anchor or "CENTER"), (c.x or 0), (c.y or 0)
flickerstreak@51 102 end
flickerstreak@51 103
flickerstreak@25 104 function Bar:GetFrame()
flickerstreak@25 105 return self.frame
flickerstreak@25 106 end
flickerstreak@25 107
flickerstreak@25 108 function Bar:GetSize()
flickerstreak@25 109 return self.frame:GetWidth() or 200, self.frame:GetHeight() or 200
flickerstreak@25 110 end
flickerstreak@25 111
flickerstreak@25 112 function Bar:SetSize(w,h)
flickerstreak@25 113 self.config.width = w
flickerstreak@25 114 self.config.height = h
flickerstreak@25 115 end
flickerstreak@25 116
flickerstreak@25 117 function Bar:GetButtonSize()
flickerstreak@25 118 local w = self.config.btnWidth or 32
flickerstreak@25 119 local h = self.config.btnHeight or 32
flickerstreak@25 120 -- TODO: get from modules?
flickerstreak@25 121 return w,h
flickerstreak@25 122 end
flickerstreak@25 123
flickerstreak@25 124 function Bar:SetButtonSize(w,h)
flickerstreak@25 125 if w > 0 and h > 0 then
flickerstreak@25 126 self.config.btnWidth = w
flickerstreak@25 127 self.config.btnHeight = h
flickerstreak@25 128 end
flickerstreak@25 129 end
flickerstreak@25 130
flickerstreak@25 131 function Bar:GetButtonGrid()
flickerstreak@25 132 local cfg = self.config
flickerstreak@25 133 local r = cfg.btnRows or 1
flickerstreak@25 134 local c = cfg.btnColumns or 1
flickerstreak@25 135 local s = cfg.spacing or 4
flickerstreak@25 136 return r,c,s
flickerstreak@25 137 end
flickerstreak@25 138
flickerstreak@25 139 function Bar:SetButtonGrid(r,c,s)
flickerstreak@25 140 if r > 0 and c > 0 and s > 0 then
flickerstreak@25 141 local cfg = self.config
flickerstreak@25 142 cfg.btnRows = r
flickerstreak@25 143 cfg.btnColumns = c
flickerstreak@25 144 cfg.spacing = s
flickerstreak@25 145 end
flickerstreak@25 146 end
flickerstreak@25 147
flickerstreak@25 148 function Bar:GetName()
flickerstreak@25 149 return self.name
flickerstreak@25 150 end
flickerstreak@25 151
flickerstreak@33 152 function Bar:SetName(name)
flickerstreak@33 153 self.name = name
flickerstreak@33 154 if self.controlLabelString then
flickerstreak@33 155 self.controlLabelString:SetText(self.name)
flickerstreak@33 156 end
flickerstreak@33 157 end
flickerstreak@33 158
flickerstreak@25 159 function Bar:PlaceButton(f, idx, baseW, baseH)
flickerstreak@25 160 local r, c, s = self:GetButtonGrid()
flickerstreak@25 161 local bh, bw = self:GetButtonSize()
flickerstreak@25 162 local row, col = floor((idx-1)/c), mod((idx-1),c) -- zero-based
flickerstreak@25 163 local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s
flickerstreak@25 164 local scale = bw/baseW
flickerstreak@25 165
flickerstreak@25 166 f:ClearAllPoints()
flickerstreak@25 167 f:SetPoint("TOPLEFT",x/scale,-y/scale)
flickerstreak@25 168 f:SetScale(scale)
flickerstreak@68 169 self.buttons[f] = true
flickerstreak@25 170 end
flickerstreak@25 171
flickerstreak@71 172
flickerstreak@71 173 -- multi-state functions --
flickerstreak@68 174 function Bar:GetNumPages()
flickerstreak@68 175 return self.config.nPages or 1
flickerstreak@68 176 end
flickerstreak@28 177
flickerstreak@72 178 --
flickerstreak@72 179 -- 'rule' is a rule-string to pass to RegisterStateDriver
flickerstreak@72 180 -- 'states' is a { ["statename"] = <don't care> } table of all state names
flickerstreak@72 181 -- 'keybinds' is a { ["statename"] = keybind } table of all keybound states
flickerstreak@72 182 --
flickerstreak@72 183 function Bar:SetStateDriver( rule, states, keybinds )
flickerstreak@72 184 local f = self.frame
flickerstreak@72 185 local kbprefix = ""
flickerstreak@72 186 do
flickerstreak@72 187 local tmp = { }
flickerstreak@72 188 for s, k in pairs(keybinds) do
flickerstreak@72 189 if k and #k > 0 then -- filter out false table entries
flickerstreak@72 190 -- if in a keybound state, set the stack to the new state but stay in the keybound state.
flickerstreak@72 191 -- use $s as a placeholder for the current state, it will be gsub()'d in later
flickerstreak@72 192 table.insert(tmp,("%s:$s set() %s"):format(s,s))
flickerstreak@72 193 end
flickerstreak@72 194 end
flickerstreak@72 195 table.insert(tmp,kbprefix) -- to get a trailing ';' if the table is not empty
flickerstreak@72 196 kbprefix = table.concat(tmp,";")
flickerstreak@72 197 end
flickerstreak@72 198 for state in pairs(states) do
flickerstreak@72 199 -- For all states: if in a keybound state, stay there (with stack manipulation, see above).
flickerstreak@72 200 -- Otherwise, go to the state
flickerstreak@72 201 f:SetAttribute(("statemap-reaction-%s"):format(state),("%s%s"):format(kbprefix:gsub("%$s",state),state))
flickerstreak@72 202
flickerstreak@72 203 local binding = keybinds[state]
flickerstreak@72 204 self:SetStateKeybind(binding, state) -- set the binding even if nil, to clear it unconditionally
flickerstreak@72 205 if binding then
flickerstreak@72 206 -- for key bindings, use the state-stack to toggle between the last state and the keybound state
flickerstreak@72 207 -- use a different 'virtual state' passed to attribute 'reaction-state' for key bindings, "<state>_binding"
flickerstreak@72 208 f:SetAttribute(("statemap-reaction-%s_binding"):format(state), ("%s:pop();*:set(%s)"):format(state,state))
flickerstreak@72 209 end
flickerstreak@72 210 end
flickerstreak@72 211
flickerstreak@72 212 if rule and #rule > 0 then
flickerstreak@72 213 self.stateDriver = true
flickerstreak@72 214 RegisterStateDriver(f, "reaction", rule)
flickerstreak@72 215 elseif self.statedriver then
flickerstreak@72 216 self.statedriver = false
flickerstreak@72 217 UnregisterStateDriver(f, "reaction")
flickerstreak@72 218 end
flickerstreak@72 219 end
flickerstreak@72 220
flickerstreak@68 221 function Bar:SetHideStates(s)
flickerstreak@68 222 for f in pairs(self.buttons) do
flickerstreak@68 223 if f:GetParent() == self.frame then
flickerstreak@68 224 f:SetAttribute("hidestates",s)
flickerstreak@68 225 end
flickerstreak@68 226 end
flickerstreak@68 227 SecureStateHeader_Refresh(self.frame)
flickerstreak@68 228 end
flickerstreak@28 229
flickerstreak@70 230 function Bar:SetStateKeybind(key, state, defaultstate)
flickerstreak@72 231 -- Lazily create a tiny offscreen button which sends "<state>_binding" values to the
flickerstreak@72 232 -- bar frame's state-reaction attribute, by using an override binding to generate a
flickerstreak@72 233 -- click on the button with a virtual mouse button "state".
flickerstreak@72 234 -- This gets around making the bar itself a clickable button, which is not desirable
flickerstreak@68 235 local f = self.statebuttonframe
flickerstreak@70 236 if key then
flickerstreak@68 237 if not f then
flickerstreak@72 238 f = CreateFrame("Button",self:GetName().."_statebutton",self.frame,"SecureActionButtonTemplate")
flickerstreak@68 239 f:SetPoint("BOTTOMRIGHT",UIParent,"TOPLEFT")
flickerstreak@68 240 f:SetWidth(1)
flickerstreak@68 241 f:SetHeight(1)
flickerstreak@72 242 f:SetAttribute("type*","attribute")
flickerstreak@72 243 f:SetAttribute("attribute-name*","state-reaction")
flickerstreak@72 244 f:SetAttribute("attribute-frame*",self.frame)
flickerstreak@68 245 f:Show()
flickerstreak@72 246 f.bindings = { }
flickerstreak@68 247 self.statebuttonframe = f
flickerstreak@68 248 end
flickerstreak@72 249 f:SetAttribute(("attribute-value-%s"):format(state),("%s_binding"):format(state))
flickerstreak@72 250 -- clear the old binding, if any, for this state
flickerstreak@72 251 if f.bindings[state] then
flickerstreak@72 252 SetOverrideBinding(self.frame, false, f.bindings[state], nil)
flickerstreak@72 253 end
flickerstreak@72 254 SetOverrideBindingClick(self.frame, false, key, f:GetName(), state) -- the state name is used as the virtual button
flickerstreak@72 255 f.bindings[state] = key
flickerstreak@68 256 elseif f then
flickerstreak@72 257 key = f.bindings[state]
flickerstreak@70 258 if key then
flickerstreak@72 259 SetOverrideBinding(self.frame, false, key, nil)
flickerstreak@72 260 f.bindings[state] = nil
flickerstreak@70 261 end
flickerstreak@68 262 end
flickerstreak@68 263 end
flickerstreak@33 264
flickerstreak@68 265 function Bar:SetStatePageMap(state, map) -- map is a { ["statename"] = pagenumber } table
flickerstreak@68 266 local f = self.frame
flickerstreak@68 267 local tmp = { }
flickerstreak@68 268 for s, p in pairs(map) do
flickerstreak@71 269 table.insert(tmp, ("%s:page%d"):format(s,p))
flickerstreak@68 270 end
flickerstreak@68 271 local spec = table.concat(tmp,";")
flickerstreak@72 272 local current = f:GetAttribute("statebutton")
flickerstreak@72 273 if spec ~= f:GetAttribute("statebutton") then
flickerstreak@72 274 f:SetAttribute("statebutton", spec)
flickerstreak@72 275 end
flickerstreak@71 276 SecureStateHeader_Refresh(f)
flickerstreak@68 277 end
flickerstreak@33 278
flickerstreak@68 279 function Bar:SetStateKeybindOverrideMap(states) -- 'states' is an array of state-names that should have keybind overrides enabled
flickerstreak@68 280 local f = self.frame
flickerstreak@68 281 for i = 1, #states do
flickerstreak@68 282 local s = states[i]
flickerstreak@68 283 states[i] = ("%s:%s"):format(s,s)
flickerstreak@68 284 end
flickerstreak@71 285 table.insert(states,"_defaultbindings")
flickerstreak@68 286 f:SetAttribute("statebindings",table.concat(states,";"))
flickerstreak@71 287 SecureStateHeader_Refresh(f)
flickerstreak@68 288 for b in pairs(self.buttons) do
flickerstreak@71 289 -- TODO: signal child frames that they should maintain multiple bindings
flickerstreak@68 290 end
flickerstreak@68 291 end
flickerstreak@33 292
flickerstreak@71 293 local _ofskeys = { "point", "relpoint", "x", "y" }
flickerstreak@71 294 function Bar:SetStateAnchorMap( map ) -- 'map' is a { ["statename"] = { point=point, relpoint=relpoint, x=x, y=y } } table
flickerstreak@71 295 local f = self.frame
flickerstreak@71 296 local c = self.config
flickerstreak@71 297 local default = { point = c.anchor, relpoint = c.relativePoint, x = c.x, y = c.y }
flickerstreak@71 298 for _, key in pairs(_ofskeys) do
flickerstreak@71 299 local t = { }
flickerstreak@71 300 for state, info in pairs(map) do
flickerstreak@71 301 if info[key] then
flickerstreak@71 302 table.insert(t, ("%s:%s"):format(state, info[key]))
flickerstreak@71 303 end
flickerstreak@71 304 end
flickerstreak@71 305 if #t > 0 and default[key] then table.insert(t, tostring(default[key])) end
flickerstreak@71 306 f:SetAttribute(("headofs%s"):format(key), table.concat(t,";") or "")
flickerstreak@71 307 end
flickerstreak@71 308 SecureStateHeader_Refresh(f)
flickerstreak@71 309 end
flickerstreak@71 310
flickerstreak@71 311 function Bar:SetStateScaleMap( map ) -- 'map' is a { ["statename"] = scalevalue } table
flickerstreak@71 312 local f = self.frame
flickerstreak@71 313 local t = { }
flickerstreak@71 314 for state, scale in pairs(map) do
flickerstreak@71 315 table.insert( t, ("%s:%s"):format(state,scale) )
flickerstreak@71 316 end
flickerstreak@71 317 if #t > 0 then table.insert(t, "1.0") end
flickerstreak@71 318 f:SetAttribute("headscale",table.concat(t,";") or "")
flickerstreak@71 319 SecureStateHeader_Refresh(f)
flickerstreak@71 320 end
flickerstreak@71 321
flickerstreak@71 322
flickerstreak@33 323
flickerstreak@28 324 ------ Export as a class-factory ------
flickerstreak@28 325 ReAction.Bar = {
flickerstreak@73 326 prototype = Bar,
flickerstreak@28 327 new = function(self, ...)
flickerstreak@28 328 local x = { }
flickerstreak@28 329 for k,v in pairs(Bar) do
flickerstreak@28 330 x[k] = v
flickerstreak@28 331 end
flickerstreak@28 332 Constructor(x, ...)
flickerstreak@28 333 return x
flickerstreak@28 334 end
flickerstreak@28 335 }