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