Mercurial > wow > reaction
diff Bar.lua @ 90:7cabc8ac6c16
Updates for wow 3.0
- TOC update
- updated changed APIs/frame names
- rewrote state code per new SecureHandlers API
- cleaned up Bar, ActionButton code
- removed AceLibrary/Dewdrop, menu from bar right-click
- fixed various small bugs
Updated WowAce external locations
Updated README.html
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Wed, 15 Oct 2008 16:29:41 +0000 |
parents | f32e2375e39b |
children | 39265b16d208 |
line wrap: on
line diff
--- a/Bar.lua Mon Oct 13 23:32:33 2008 +0000 +++ b/Bar.lua Wed Oct 15 16:29:41 2008 +0000 @@ -5,64 +5,48 @@ local floor = math.floor local fmod = math.fmod local format = string.format -local SecureStateHeader_Refresh = SecureStateHeader_Refresh ReAction:UpdateRevision("$Revision$") +local Bar = { } +local proto = { __index = Bar } +local weak = { __mode = "k" } ------- BAR CLASS ------ -local Bar = { _classID = {} } ReAction.Bar = Bar -- export to ReAction function Bar:New( name, config ) - -- create new self - self = setmetatable( { }, {__index = Bar} ) if type(config) ~= "table" then error("ReAction.Bar: config table required") end - config.width = config.width or 480 - config.height = config.height or 40 - self.name, self.config = name, config - self.buttons = setmetatable({},{__mode="k"}) - self.statedrivers = { } - self.keybinds = { } + -- create new self + self = setmetatable( + { + config = config, + name = name, + buttons = setmetatable( { }, weak ), + width = config.width or 480, + height = config.height or 40 + }, + proto ) + + -- The frame type is 'Button' in order to have an OnClick handler. However, the frame itself is + -- not mouse-clickable by the user. + local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent + local f = CreateFrame("Button", name and format("ReAction-%s",name), parent, + "SecureHandlerStateTemplate, SecureHandlerClickTemplate") + f:SetFrameStrata("MEDIUM") + f:SetWidth(self.width) + f:SetWidth(self.height) + f:Show() + f:EnableMouse(false) + f:SetClampedToScreen(true) - local parent = config.parent and (ReAction:GetBar(config.parent) or _G[config.parent]) or UIParent - local f = CreateFrame("Button",name and format("ReAction-%s",name),parent,"SecureStateHeaderTemplate, SecureActionButtonTemplate") - f:SetFrameStrata("MEDIUM") - f:SetWidth(config.width) - f:SetWidth(config.height) - f:Show() - - -- The bar itself is also a Button derived from SecureActionButtonTemplate, so it has an OnClick handler - -- which we can use as a virtual button for keybinds, which will send attribute-value changes to itself. - -- However, we don't ever want the user to be able to click it directly. - f:EnableMouse(false) - f:SetAttribute("type","attribute") - - -- Buttons are contained in an anonymous intermediate sub-frame. This arrangement is to specifically - -- address the issue of the interaction with hidestates and auto-hiding empty action buttons (the two - -- don't play nicely together). It also has the fringe benefit of making show/hide faster because a - -- single frame is shown/hidden instead of potentially dozens. Unfortunately it does add an extra layer - -- of indirection to all state changes, as a secondary (trivial) statemap must be invoked. This - -- complicates frame setup slightly. - local bf = CreateFrame("Frame", nil, f, "SecureStateHeaderTemplate") - bf:SetAllPoints() - bf:Show() - bf:SetAttribute("useparent*",true) -- this facilitates SecureButton_GetModifiedAttribute() - bf:SetAttribute("statemap-parent","$input") -- However, we also need SetAttribute(state-parent) propagation too - f:SetAttribute("addchild",bf) - - -- Both frames are read-only. Override the default accessors for this object. + -- Override the default frame accessor to provide strict read-only access function self:GetFrame() return f end - function self:GetButtonFrame() - return bf - end - self:ApplyAnchor() ReAction.RegisterCallback(self, "OnConfigModeChanged") @@ -72,16 +56,10 @@ function Bar:Destroy() local f = self:GetFrame() f:UnregisterAllEvents() + ReAction.UnregisterAllCallbacks(self) f:Hide() f:SetParent(UIParent) f:ClearAllPoints() - ReAction.UnregisterAllCallbacks(self) - for driver in pairs(self.statedrivers) do - UnregisterStateDriver(f, driver) - end - self.labelString = nil - self.controlFrame = nil - self.config = nil end function Bar:OnConfigModeChanged(event, mode) @@ -89,22 +67,26 @@ end function Bar:ApplyAnchor() - local f, config = self:GetFrame(), self.config - f:SetWidth(config.width) - f:SetHeight(config.height) - local point = config.point + local f = self:GetFrame() + local c = self.config + local p = c.point + + f:SetWidth(c.width) + f:SetHeight(c.height) f:ClearAllPoints() - if point then - local anchor = f:GetParent() - if config.anchor then - local bar = ReAction:GetBar(config.anchor) + + if p then + local a = f:GetParent() + if c.anchor then + local bar = ReAction:GetBar(c.anchor) if bar then - anchor = bar:GetFrame() + a = bar:GetFrame() else - anchor = _G[config.anchor] + a = _G[c.anchor] end end - f:SetPoint(point, anchor or f:GetParent(), config.relpoint, config.x or 0, config.y or 0) + local fr = a or f:GetParent() + f:SetPoint(p, a or f:GetParent(), c.relpoint, c.x or 0, c.y or 0) else f:SetPoint("CENTER") end @@ -122,7 +104,11 @@ function Bar:GetAnchor() local c = self.config - 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) + 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) end function Bar:GetSize() @@ -131,9 +117,9 @@ end function Bar:SetSize(w,h) + local f = self:GetFrame() self.config.width = w self.config.height = h - local f = self:GetFrame() f:SetWidth(w) f:SetHeight(h) end @@ -182,121 +168,54 @@ function Bar:GetFrame() -- this method is included for documentation purposes. It is overridden - -- in the New method for each object. + -- for each object in the :New() method. error("Invalid Bar object: used without initialization") end -function Bar:GetButtonFrame() - -- this method is included for documentation purposes. It is overridden - -- in the New method for each object. - error("Invalid Bar object: used without initialization") +-- only ReAction:RenameBar() should call this function. Calling from any other +-- context will desync the bar list in the ReAction class. +function Bar:SetName(name) + self.name = name + self:SetLabel(self.name) -- Bar:SetLabel() defined in Overlay.lua end --- only ReAction:RenameBar() should call this function -function Bar:SetName(name) - self.name = name - -- controlLabelString is defined in Overlay.lua - if self.controlLabelString then - self.controlLabelString:SetText(self.name) +function Bar:AddButton(idx, button) + local f = self:GetFrame() + + -- store in a weak reverse-index array + self.buttons[button] = idx + + -- Store a properly wrapped reference to the child frame as an attribute + -- (accessible via "frameref-btn#") + f:SetFrameRef(format("btn%d",idx), button:GetFrame()) +end + +function Bar:RemoveButton(button) + local idx = self.buttons[button] + if idx then + self:GetFrame():SetAttribute(format("frameref-btn%d",idx),nil) + self.buttons[button] = nil end end -function Bar:AddButton(idx, button) - -- store in a reverse-index array - self.buttons[button] = idx - self:GetButtonFrame():SetAttribute("addchild",button:GetFrame()) - SecureStateHeader_Refresh(self:GetFrame()) -end - -function Bar:RemoveButton(button) - self.buttons[button] = nil -end - -function Bar:IterateButtons() -- iterator returns button, idx +-- iterator returns button, idx and does NOT iterate in index order +function Bar:IterateButtons() return pairs(self.buttons) end function Bar:PlaceButton(button, baseW, baseH) local idx = self.buttons[button] - if not idx then return end - local r, c, s = self:GetButtonGrid() - local bh, bw = self:GetButtonSize() - local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based - local x, y = col*bw + (col+0.5)*s, row*bh + (row+0.5)*s - local scale = bw/baseW - local f = button:GetFrame() + if idx then + local r, c, s = self:GetButtonGrid() + local bh, bw = self:GetButtonSize() + local row, col = floor((idx-1)/c), fmod((idx-1),c) -- zero-based + local x, y = col*bw + (col+0.5)*s, -(row*bh + (row+0.5)*s) + local scale = bw/baseW + local b = button:GetFrame() - f:ClearAllPoints() - f:SetPoint("TOPLEFT",x/scale,-y/scale) - f:SetScale(scale) -end - --- Creates (or updates) a named binding which binds a key press to a call to SetAttribute() --- pass a nil key to unbind -function Bar:SetAttributeBinding( name, key, attribute, value ) - if not name then - error("usage - Bar:SetAttributeBinding(name [, key, attribute, value]") - end - local f = self:GetFrame() - - -- clear the old binding, if any - if self.keybinds[name] then - SetOverrideBinding(f, false, self.keybinds[name], nil) - end - if key then - f:SetAttribute(format("attribute-name-%s",name), attribute) - f:SetAttribute(format("attribute-value-%s",name), value) - SetOverrideBindingClick(f, false, key, f:GetName(), name) -- binding name is the virtual mouse button - end - self.keybinds[name] = key -end - --- Sets up a state driver 'name' for the bar, using the provided 'rule' --- Also sets attributes 'statemap-<name>-<key>'=<value> for each entry in the passed map --- if 'rule' is nil or an empty string, the driver is unregistered. -function Bar:SetStateDriver( name, rule, map ) - local f = self:GetFrame() - if rule and #rule > 0 then - if map then - for key, value in pairs(map) do - f:SetAttribute( format("statemap-%s-%s",name,key), value ) - end - end - RegisterStateDriver(f, name, rule) - self.statedrivers[name] = true - elseif self.statedrivers[name] then - UnregisterStateDriver(f, name) - self.statedrivers[name] = nil + b:ClearAllPoints() + b:SetPoint("TOPLEFT",x/scale,y/scale) + b:SetScale(scale) end end --- Set an attribute on the frame (or each button if 'doButtons' = true) --- Either or both 'map' and 'default' can be passed: --- - If 'map' is omitted, then 'default' is set to the attribute. --- - If 'map' is provided, then it is interpreted as an unordered --- table of the form { ["statename"] = ["value"] }, and will be --- converted into a SecureStateHeaderTemplate style state-parsed --- string, e.g. "<state1>:<value1>;<state2>:<value2>". If 'default' --- is also provided, then its value will be converted to a string --- and appended. -function Bar:SetStateAttribute( attribute, map, default, doButtons ) - local value = default - if map then - local tmp = { } - for state, value in pairs(map) do - table.insert(tmp, format("%s:%s",tostring(state),tostring(value))) - end - if default then - table.insert(tmp, tostring(default)) - end - value = table.concat(tmp,";") - end - if doButtons then - for b in self:IterateButtons() do - b:GetFrame():SetAttribute(attribute,value) - end - else - self:GetFrame():SetAttribute(attribute, value) - end - SecureStateHeader_Refresh(self:GetFrame()) -end