Mercurial > wow > reaction
view 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 |
line wrap: on
line source
local ReAction = ReAction local L = ReAction.L local _G = _G local CreateFrame = CreateFrame local floor = math.floor local SecureStateHeader_Refresh = SecureStateHeader_Refresh -- update ReAction revision if this file is newer local revision = tonumber(("$Revision$"):match("%d+")) if revision > ReAction.revision then ReAction.revision = revision end ------ BAR CLASS ------ local Bar = { _classID = {} } local function Constructor( self, name, config ) self.name, self.config = name, config self.buttons = setmetatable({},{__mode="k"}) if type(config) ~= "table" then error("ReAction.Bar: config table required") end local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent local f = CreateFrame("Frame",nil,parent,"SecureStateHeaderTemplate") f:SetFrameStrata("MEDIUM") config.width = config.width or 480 config.height = config.height or 40 f:SetWidth(config.width) f:SetWidth(config.height) ReAction.RegisterCallback(self, "OnConfigModeChanged") self.frame = f self:ApplyAnchor() f:Show() self:RefreshLayout() end function Bar:Destroy() local f = self.frame f:UnregisterAllEvents() f:Hide() f:SetParent(UIParent) f:ClearAllPoints() ReAction.UnregisterAllCallbacks(self) if self.statedriver then UnregisterStateDriver(f, "reaction") end self.labelString = nil self.controlFrame = nil self.frame = nil self.config = nil end function Bar:OnConfigModeChanged(event, mode) self:ShowControls(mode) -- ShowControls() defined in Overlay.lua end function Bar:RefreshLayout() ReAction:RefreshBar(self) end function Bar:ApplyAnchor() local f, config = self.frame, self.config f:SetWidth(config.width) f:SetHeight(config.height) local anchor = config.anchor f:ClearAllPoints() if anchor then local anchorTo = f:GetParent() if config.anchorTo then local bar = ReAction:GetBar(config.anchorTo) if bar then anchorTo = bar:GetFrame() else anchorTo = _G[config.anchorTo] end end f:SetPoint(anchor, anchorTo or f:GetParent(), config.relativePoint, config.x or 0, config.y or 0) else f:SetPoint("CENTER") end end function Bar:SetAnchor(point, frame, relativePoint, x, y) local c = self.config c.anchor = point or c.anchor c.anchorTo = frame and frame:GetName() or c.anchorTo c.relativePoint = relativePoint or c.relativePoint c.x = x or c.x c.y = y or c.y self:ApplyAnchor() end function Bar:GetAnchor() local c = self.config 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) end function Bar:GetFrame() return self.frame end function Bar:GetSize() return self.frame:GetWidth() or 200, self.frame:GetHeight() or 200 end function Bar:SetSize(w,h) self.config.width = w self.config.height = h end function Bar:GetButtonSize() local w = self.config.btnWidth or 32 local h = self.config.btnHeight or 32 -- TODO: get from modules? return w,h end function Bar:SetButtonSize(w,h) if w > 0 and h > 0 then self.config.btnWidth = w self.config.btnHeight = h end end function Bar:GetButtonGrid() local cfg = self.config local r = cfg.btnRows or 1 local c = cfg.btnColumns or 1 local s = cfg.spacing or 4 return r,c,s end function Bar:SetButtonGrid(r,c,s) if r > 0 and c > 0 and s > 0 then local cfg = self.config cfg.btnRows = r cfg.btnColumns = c cfg.spacing = s end end function Bar:GetName() return self.name end function Bar:SetName(name) self.name = name if self.controlLabelString then self.controlLabelString:SetText(self.name) end end function Bar:PlaceButton(f, idx, baseW, baseH) local r, c, s = self:GetButtonGrid() local bh, bw = self:GetButtonSize() local row, col = floor((idx-1)/c), mod((idx-1),c) -- zero-based local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s local scale = bw/baseW f:ClearAllPoints() f:SetPoint("TOPLEFT",x/scale,-y/scale) f:SetScale(scale) self.buttons[f] = true end -- multi-state functions -- function Bar:GetNumPages() return self.config.nPages or 1 end -- -- 'rule' is a rule-string to pass to RegisterStateDriver -- 'states' is a { ["statename"] = <don't care> } table of all state names -- 'keybinds' is a { ["statename"] = keybind } table of all keybound states -- function Bar:SetStateDriver( rule, states, keybinds ) local f = self.frame local kbprefix = "" do local tmp = { } for s, k in pairs(keybinds) do if k and #k > 0 then -- filter out false table entries -- if in a keybound state, set the stack to the new state but stay in the keybound state. -- use $s as a placeholder for the current state, it will be gsub()'d in later table.insert(tmp,("%s:$s set() %s"):format(s,s)) end end table.insert(tmp,kbprefix) -- to get a trailing ';' if the table is not empty kbprefix = table.concat(tmp,";") end for state in pairs(states) do -- For all states: if in a keybound state, stay there (with stack manipulation, see above). -- Otherwise, go to the state f:SetAttribute(("statemap-reaction-%s"):format(state),("%s%s"):format(kbprefix:gsub("%$s",state),state)) local binding = keybinds[state] self:SetStateKeybind(binding, state) -- set the binding even if nil, to clear it unconditionally if binding then -- for key bindings, use the state-stack to toggle between the last state and the keybound state -- use a different 'virtual state' passed to attribute 'reaction-state' for key bindings, "<state>_binding" f:SetAttribute(("statemap-reaction-%s_binding"):format(state), ("%s:pop();*:set(%s)"):format(state,state)) end end if rule and #rule > 0 then self.stateDriver = true RegisterStateDriver(f, "reaction", rule) elseif self.statedriver then self.statedriver = false UnregisterStateDriver(f, "reaction") end end function Bar:SetHideStates(s) for f in pairs(self.buttons) do if f:GetParent() == self.frame then f:SetAttribute("hidestates",s) end end SecureStateHeader_Refresh(self.frame) end function Bar:SetStateKeybind(key, state, defaultstate) -- Lazily create a tiny offscreen button which sends "<state>_binding" values to the -- bar frame's state-reaction attribute, by using an override binding to generate a -- click on the button with a virtual mouse button "state". -- This gets around making the bar itself a clickable button, which is not desirable local f = self.statebuttonframe if key then if not f then f = CreateFrame("Button",self:GetName().."_statebutton",self.frame,"SecureActionButtonTemplate") f:SetPoint("BOTTOMRIGHT",UIParent,"TOPLEFT") f:SetWidth(1) f:SetHeight(1) f:SetAttribute("type*","attribute") f:SetAttribute("attribute-name*","state-reaction") f:SetAttribute("attribute-frame*",self.frame) f:Show() f.bindings = { } self.statebuttonframe = f end f:SetAttribute(("attribute-value-%s"):format(state),("%s_binding"):format(state)) -- clear the old binding, if any, for this state if f.bindings[state] then SetOverrideBinding(self.frame, false, f.bindings[state], nil) end SetOverrideBindingClick(self.frame, false, key, f:GetName(), state) -- the state name is used as the virtual button f.bindings[state] = key elseif f then key = f.bindings[state] if key then SetOverrideBinding(self.frame, false, key, nil) f.bindings[state] = nil end end end function Bar:SetStatePageMap(state, map) -- map is a { ["statename"] = pagenumber } table local f = self.frame local tmp = { } for s, p in pairs(map) do table.insert(tmp, ("%s:page%d"):format(s,p)) end local spec = table.concat(tmp,";") local current = f:GetAttribute("statebutton") if spec ~= f:GetAttribute("statebutton") then f:SetAttribute("statebutton", spec) end SecureStateHeader_Refresh(f) end function Bar:SetStateKeybindOverrideMap(states) -- 'states' is an array of state-names that should have keybind overrides enabled local f = self.frame for i = 1, #states do local s = states[i] states[i] = ("%s:%s"):format(s,s) end table.insert(states,"_defaultbindings") f:SetAttribute("statebindings",table.concat(states,";")) SecureStateHeader_Refresh(f) for b in pairs(self.buttons) do -- TODO: signal child frames that they should maintain multiple bindings end end local _ofskeys = { "point", "relpoint", "x", "y" } function Bar:SetStateAnchorMap( map ) -- 'map' is a { ["statename"] = { point=point, relpoint=relpoint, x=x, y=y } } table local f = self.frame local c = self.config local default = { point = c.anchor, relpoint = c.relativePoint, x = c.x, y = c.y } for _, key in pairs(_ofskeys) do local t = { } for state, info in pairs(map) do if info[key] then table.insert(t, ("%s:%s"):format(state, info[key])) end end if #t > 0 and default[key] then table.insert(t, tostring(default[key])) end f:SetAttribute(("headofs%s"):format(key), table.concat(t,";") or "") end SecureStateHeader_Refresh(f) end function Bar:SetStateScaleMap( map ) -- 'map' is a { ["statename"] = scalevalue } table local f = self.frame local t = { } for state, scale in pairs(map) do table.insert( t, ("%s:%s"):format(state,scale) ) end if #t > 0 then table.insert(t, "1.0") end f:SetAttribute("headscale",table.concat(t,";") or "") SecureStateHeader_Refresh(f) end ------ Export as a class-factory ------ ReAction.Bar = { prototype = Bar, new = function(self, ...) local x = { } for k,v in pairs(Bar) do x[k] = v end Constructor(x, ...) return x end }