flickerstreak@7: -- private constants flickerstreak@7: local anchoredLabelColor = { r = 1.0, g = 0.82, b = 0.0 } flickerstreak@7: local nonAnchoredLabelColor = { r = 1.0, g = 1.0, b = 1.0 } flickerstreak@1: flickerstreak@7: local DRAG_UPDATE_RATE = 0.125 -- cap at 8 Hz flickerstreak@1: flickerstreak@7: local nStancePages = { flickerstreak@7: WARRIOR = 3, flickerstreak@7: ROGUE = 0, flickerstreak@7: HUNTER = 0, flickerstreak@7: DRUID = 3, -- updated to 4 if talented flickerstreak@7: PALADIN = 0, flickerstreak@7: SHAMAN = 0, -- As far as I know, ghost wolf is not a proper shapeshift form, it's just a buff flickerstreak@7: MAGE = 0, flickerstreak@7: PRIEST = 0, -- updated to 1 if talented flickerstreak@7: WARLOCK = 0, flickerstreak@1: } flickerstreak@1: flickerstreak@7: local stanceMaps = { flickerstreak@7: WARRIOR = { flickerstreak@7: -- note: warriors are never in shapeshift form 0. flickerstreak@7: [1] = "*:1", -- battle stance. All states go to state (page) 1. flickerstreak@7: [2] = "*:2", -- defensive stance. All states go to state (page) 2. flickerstreak@7: [3] = "*:3", -- berserker stance. All states go to state (page) 3. flickerstreak@7: }, flickerstreak@7: ROGUE = {}, flickerstreak@7: HUNTER = {}, flickerstreak@7: DRUID = { flickerstreak@7: [0] = "*:1", -- humanoid form. All states go to state (page) 1. flickerstreak@7: [1] = "*:2", -- bear/dire bear form. All states go to state (page) 2. flickerstreak@7: [2] = "*:1", -- aquatic form. All states to go state (page) 1 (same as humanoid) flickerstreak@7: [3] = "*:3", -- cat form. All states go to state (page) 3 flickerstreak@7: [4] = "*:1", -- travel form. All states go to state (page) 1 (same as humanoid) flickerstreak@7: [5] = "*:4", -- oomkin/tree form [talent only]. All states go to state (page) 4 flickerstreak@7: [6] = "*:1", -- flight form. All states go to state (page) 1 (same as humanoid) (??? How does this work with oomkin/tree?) flickerstreak@7: }, flickerstreak@7: PALADIN = {}, flickerstreak@7: SHAMAN = {}, flickerstreak@7: MAGE = {}, flickerstreak@7: PRIEST = { flickerstreak@7: [0] = "*:1", -- normal form. All states go to page 1. flickerstreak@7: [1] = "*:2", -- shadowform (talent only). All states go to page 2. flickerstreak@7: }, flickerstreak@7: WARLOCK = {}, flickerstreak@1: } flickerstreak@1: flickerstreak@7: local stealthMaps = { flickerstreak@7: WARRIOR = "", flickerstreak@7: ROGUE = "1:2", -- go to page 2 for rogues flickerstreak@7: HUNTER = "", flickerstreak@7: DRUID = "3:5", -- we'll replace with 1:2 if stance mapping is not enabled, and page 5 with 6 if oomkin/tree flickerstreak@7: PALADIN = "", flickerstreak@7: SHAMAN = "", flickerstreak@7: MAGE = "", flickerstreak@7: PRIEST = "", flickerstreak@1: } flickerstreak@1: flickerstreak@7: local unstealthMaps = { flickerstreak@7: WARRIOR = "", flickerstreak@7: ROGUE = "2:1", -- go to page 1 for rogues flickerstreak@7: HUNTER = "", flickerstreak@7: DRUID = "5:3", -- we'll replace with 2:1 if stance mapping is not enabled, and page 5 with 6 if oomkin/tree flickerstreak@7: PALADIN = "", flickerstreak@7: SHAMAN = "", flickerstreak@7: MAGE = "", flickerstreak@7: PRIEST = "", flickerstreak@7: } flickerstreak@7: flickerstreak@7: -- Immediately fix if a druid with oomkin or tree (note: can't have both) flickerstreak@7: local _, playerClass = UnitClass("player") flickerstreak@7: if playerClass == "DRUID" and GetNumShapeshiftForms() > 4 then flickerstreak@7: nStancePages.DRUID = 4 flickerstreak@7: stanceMaps.DRUID[5] = "*:4" flickerstreak@7: stealthMaps.DRUID = "3:6" flickerstreak@7: unstealthMaps.DRUID ="6:3" flickerstreak@7: end flickerstreak@7: flickerstreak@7: -- Immediately fix if a priest with shadowform flickerstreak@7: if playerClass == "PRIEST" and GetNumShapeshiftForms() > 1 then flickerstreak@7: nStancePages.PRIEST = 2 flickerstreak@7: end flickerstreak@7: flickerstreak@7: local AceOO = AceLibrary("AceOO-2.0") flickerstreak@7: flickerstreak@1: -- ReBar is an Ace 2 class prototype object. flickerstreak@7: ReBar = AceOO.Class("AceEvent-2.0", ReAnchor, ReAnchor.IAnchorable) flickerstreak@1: flickerstreak@1: flickerstreak@7: ---------------------- flickerstreak@7: -- Static members flickerstreak@7: ---------------------- flickerstreak@7: -- IButtonClass is an interface required of any button class type (not the class objects themselves) to be used with the bar flickerstreak@7: ReBar.IButtonClass = AceOO.Interface { flickerstreak@7: Acquire = "function", -- btn = Acquire(config, barIdx, pages, buttonsPerPage) flickerstreak@7: -- analogous to new(), this should re-use recycled frames for memory efficiency flickerstreak@7: Release = "function", -- Release(btn) should hide and dispose of the button (and recycle it) flickerstreak@7: } flickerstreak@7: flickerstreak@7: -- IButton is an interface required of any button objects to be used with the bar flickerstreak@7: ReBar.IButton = AceOO.Interface { flickerstreak@7: GetActionFrame = "function", -- obtains the action frame, presumably inherited from SecureActionButtonTemplate or similar. flickerstreak@7: BarLocked = "function", -- BarLocked() called when the bar is locked. flickerstreak@7: BarUnlocked = "function", -- BarUnlocked() called when the bar is unlocked. flickerstreak@7: PlaceButton = "function", -- PlaceButton(parent, anchorPoint, offsetX, offsetY, squareSz), one-stop position and size setting flickerstreak@7: SetPages = "function", -- SetPages(n): sets number of pages for multi-paging. Can error if paging not supported. flickerstreak@7: } flickerstreak@7: flickerstreak@7: -- wrap UIParent and WorldFrame in objects which implement the ReAnchor.IAnchorable interface flickerstreak@7: local UIParentPlaceable = { flickerstreak@7: GetFrame = function() return UIParent end, flickerstreak@7: GetAnchorage = function() return ReAnchor.anchorInside end, flickerstreak@7: } flickerstreak@7: flickerstreak@7: local WorldFramePlaceable = { flickerstreak@7: GetFrame = function() return WorldFrame end, flickerstreak@7: GetAnchorage = function() return ReAnchor.anchorInside end, flickerstreak@7: } flickerstreak@7: flickerstreak@7: ReBar.anchorTargets = { flickerstreak@7: UIParentPlaceable, flickerstreak@7: WorldFramePlaceable flickerstreak@7: } flickerstreak@7: flickerstreak@7: ReBar.UIParentAnchorTarget = { flickerstreak@7: UIParentPlaceable flickerstreak@7: } flickerstreak@7: flickerstreak@7: flickerstreak@7: flickerstreak@7: flickerstreak@7: flickerstreak@7: flickerstreak@7: -------------------- flickerstreak@7: -- instance methods flickerstreak@7: -------------------- flickerstreak@7: -- construction and destruction flickerstreak@1: function ReBar.prototype:init( config, id ) flickerstreak@1: ReBar.super.prototype.init(self) flickerstreak@1: flickerstreak@1: self.config = config flickerstreak@1: self.barID = id flickerstreak@1: self.buttons = { } flickerstreak@7: self.locked = true flickerstreak@7: flickerstreak@7: self.buttonClass = config.btnConfig and config.btnConfig.type and getglobal(config.btnConfig.type) flickerstreak@7: if not AceOO.inherits(self.buttonClass, ReBar.IButton) then flickerstreak@7: error("ReBar: Supplied Button type does not meet required interface.") flickerstreak@7: end flickerstreak@7: flickerstreak@1: flickerstreak@1: -- create the bar and control widgets flickerstreak@8: local name = "ReBar"..id flickerstreak@8: self.barFrame = CreateFrame("Frame", name, config.parent and getglobal(config.parent) or UIParent, "ReBarTemplate") flickerstreak@2: self.controlFrame = getglobal(self.barFrame:GetName().."Controls") flickerstreak@1: self.controlFrame.reBar = self flickerstreak@2: self.barFrame:SetClampedToScreen(true) flickerstreak@1: flickerstreak@8: -- get references to sub-frames flickerstreak@8: self.labelString = getglobal(name.."ControlsLabelString") flickerstreak@8: self.upArrow = getglobal(name.."PageUp") flickerstreak@8: self.downArrow = getglobal(name.."PageDown") flickerstreak@8: self.pageNum = getglobal(name.."PageNumber") flickerstreak@8: flickerstreak@1: -- set the text label on the control widget flickerstreak@2: self.labelString:SetText(id) flickerstreak@1: flickerstreak@7: -- initial stateheader state flickerstreak@7: self.barFrame.StateChanged = function() self:StateChanged() end flickerstreak@7: self.barFrame:SetAttribute("state",config.pages and config.pages.currentPage or 1) -- initial state flickerstreak@7: flickerstreak@1: -- initialize the bar layout flickerstreak@1: self:ApplySize() flickerstreak@1: self:ApplyAnchor() flickerstreak@7: self:AcquireButtons() flickerstreak@1: self:LayoutButtons() flickerstreak@1: self:ApplyVisibility() flickerstreak@1: flickerstreak@7: if self.config.pages then flickerstreak@7: self:SetPages(self.config.pages.n) flickerstreak@7: self:ApplyAutoStanceSwitch() flickerstreak@7: self:ApplyAutoStealthSwitch() flickerstreak@7: self:RefreshPageControls() flickerstreak@7: end flickerstreak@7: flickerstreak@7: -- add bar to anchorTargets list flickerstreak@7: table.insert(ReBar.anchorTargets, self) flickerstreak@1: end flickerstreak@1: flickerstreak@7: function ReBar.prototype:Destroy() flickerstreak@7: local f = self.barFrame flickerstreak@1: flickerstreak@7: self:HideControls() flickerstreak@7: f:Hide() flickerstreak@7: f:ClearAllPoints() flickerstreak@7: f:SetParent(nil) flickerstreak@7: f:SetPoint("BOTTOMRIGHT", UIParent, "TOPLEFT", 0, 0) flickerstreak@7: flickerstreak@7: while #self.buttons > 0 do flickerstreak@7: self.buttonClass:Release(table.remove(self.buttons)) flickerstreak@1: end flickerstreak@1: flickerstreak@7: -- remove from anchorTargets table flickerstreak@7: for idx, b in ipairs(ReBar.anchorTargets) do flickerstreak@7: if b == self then flickerstreak@7: table.remove(ReBar.anchorTargets, idx) flickerstreak@7: break flickerstreak@7: end flickerstreak@7: end flickerstreak@1: flickerstreak@7: -- remove from globals flickerstreak@7: local n = f:GetName() flickerstreak@7: setglobal(n, nil) flickerstreak@7: setglobal(n.."Control", nil) flickerstreak@7: setglobal(n.."Controls", nil) flickerstreak@7: setglobal(n.."ControlsLabelString", nil) flickerstreak@7: setglobal(n.."PageUp", nil) flickerstreak@7: setglobal(n.."PageDown", nil) flickerstreak@7: setglobal(n.."Page", nil) flickerstreak@7: setglobal(n.."PageNumber", nil) flickerstreak@7: setglobal(n.."PageNumberLabel", nil) flickerstreak@7: setglobal(n.."PageNumberLabelText", nil) flickerstreak@7: end flickerstreak@1: flickerstreak@1: flickerstreak@7: flickerstreak@7: flickerstreak@7: -- ReAnchor.IAnchorable interface implementation flickerstreak@7: function ReBar.prototype:GetFrame() flickerstreak@7: return self.barFrame flickerstreak@1: end flickerstreak@1: flickerstreak@7: function ReBar.prototype:GetAnchorage() flickerstreak@7: return ReAnchor.anchorOutside flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@7: flickerstreak@1: flickerstreak@1: -- show/hide the control frame flickerstreak@1: function ReBar.prototype:ShowControls() flickerstreak@7: self.locked = false flickerstreak@1: self.controlFrame:Show() flickerstreak@2: for _, b in ipairs(self.buttons) do flickerstreak@2: b:BarUnlocked() flickerstreak@7: b:DisplayVisibility() flickerstreak@2: end flickerstreak@7: self:ApplyVisibility() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:HideControls() flickerstreak@7: self.locked = true flickerstreak@1: local b = self.barFrame flickerstreak@1: if b.isMoving or b.resizing then flickerstreak@1: b:StopMovingOrSizing() flickerstreak@1: b:SetScript("OnUpdate",nil) flickerstreak@1: end flickerstreak@2: for _, b in ipairs(self.buttons) do flickerstreak@2: b:BarLocked() flickerstreak@7: b:DisplayVisibility() flickerstreak@2: end flickerstreak@1: self.controlFrame:Hide() flickerstreak@7: self:ApplyVisibility() flickerstreak@1: end flickerstreak@1: flickerstreak@7: function ReBar.prototype:GetControlFrame() flickerstreak@7: return self.controlFrame flickerstreak@7: end flickerstreak@1: flickerstreak@1: flickerstreak@1: -- accessors flickerstreak@1: function ReBar.prototype:GetVisibility() flickerstreak@1: return self.config.visible flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:ToggleVisibility() flickerstreak@7: self:SetVisibility( not self:GetVisibility() ) flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetVisibility(v) flickerstreak@7: self.config.visible = v and true or false -- force data integrity flickerstreak@1: self:ApplyVisibility() flickerstreak@1: end flickerstreak@1: flickerstreak@7: function ReBar.prototype:GetButtonList() flickerstreak@7: return self.buttons flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetGrowLeft() flickerstreak@7: return self.config.growLeft flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetGrowLeft(g) flickerstreak@7: self.config.growLeft = g and true or false flickerstreak@7: self:LayoutButtons() flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetGrowUp() flickerstreak@7: return self.config.growUp flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetGrowUp(g) flickerstreak@7: self.config.growUp = g and true or false flickerstreak@7: self:LayoutButtons() flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetColumnMajor() flickerstreak@7: return self.config.columnMajor flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetColumnMajor(m) flickerstreak@7: self.config.columnMajor = m and true or false flickerstreak@7: self:LayoutButtons() flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@7: -- paging methods flickerstreak@7: function ReBar.prototype:IsPagingDisabled() flickerstreak@7: return not (self.config.pages and self.config.pages.n > 1) flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetPages() flickerstreak@7: return self.config.pages and self.config.pages.n or 1 flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetPages(n) flickerstreak@7: n = tonumber(n) flickerstreak@7: if n and n >= 1 then flickerstreak@7: self.config.pages = self.config.pages or { } flickerstreak@7: self.config.pages.n = n flickerstreak@7: for _, btn in pairs(self.buttons) do flickerstreak@7: btn:SetPages(n) flickerstreak@7: end flickerstreak@7: if n > 1 then flickerstreak@7: local statebutton = "0:page1;" flickerstreak@7: -- map states 1-n to 'page1'-'pagen' flickerstreak@7: -- page 0 is the same as page 1. flickerstreak@7: for i = 1, n do flickerstreak@7: statebutton = statebutton..i..":page"..i..";" flickerstreak@7: end flickerstreak@7: self.barFrame:SetAttribute("statebutton",statebutton) flickerstreak@7: else flickerstreak@7: self.barFrame:SetAttribute("statebutton",ATTRIBUTE_NOOP) flickerstreak@7: end flickerstreak@7: self.barFrame:SetAttribute("statemap-anchor-next","1-"..n) flickerstreak@7: self.barFrame:SetAttribute("statemap-anchor-prev",tostring(n).."-1") flickerstreak@7: self:RefreshPageControls() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetAutoStanceSwitch() flickerstreak@7: return self.config.pages and self.config.pages.autoStanceSwitch flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetAutoStanceSwitch( s ) flickerstreak@7: if not self.config.pages then flickerstreak@7: self.config.pages = { n = 1 } flickerstreak@7: end flickerstreak@7: self.config.pages.autoStanceSwitch = s and true or false flickerstreak@7: self:ApplyAutoStanceSwitch() flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:ToggleAutoStanceSwitch() flickerstreak@7: self:SetAutoStanceSwitch( not self:GetAutoStanceSwitch() ) flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:ApplyAutoStanceSwitch() flickerstreak@7: local switch = self:GetAutoStanceSwitch() flickerstreak@7: if switch then flickerstreak@7: -- check that the number of pages available is sufficient flickerstreak@8: local totalPages = nStancePages[playerClass] + (self:GetAutoStealthSwitch() and 1 or 0) flickerstreak@7: if self:GetPages() < totalPages then flickerstreak@7: self:SetPages(totalPages) flickerstreak@7: end flickerstreak@8: for form, spec in pairs(stanceMaps[playerClass]) do flickerstreak@7: self.barFrame:SetAttribute("statemap-stance-"..form,spec) flickerstreak@7: end flickerstreak@7: -- set initial value flickerstreak@7: self.barFrame:SetAttribute("state-stance",GetShapeshiftForm(true)) flickerstreak@7: else flickerstreak@8: for form, _ in pairs(stanceMaps[playerClass]) do flickerstreak@7: self.barFrame:SetAttribute("statemap-stance-"..form, ATTRIBUTE_NOOP) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetAutoStealthSwitch() flickerstreak@7: return self.config.pages and self.config.pages.autoStealthSwitch flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetAutoStealthSwitch( s ) flickerstreak@7: if not self.config.pages then flickerstreak@7: self.config.pages = { n = 1 } flickerstreak@7: end flickerstreak@7: self.config.pages.autoStealthSwitch = s and true or false flickerstreak@7: self:ApplyAutoStealthSwitch() flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:ToggleAutoStealthSwitch() flickerstreak@7: self:SetAutoStealthSwitch( not self:GetAutoStealthSwitch() ) flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:ApplyAutoStealthSwitch() flickerstreak@7: local switch = self:GetAutoStealthSwitch() flickerstreak@7: if switch then flickerstreak@7: -- check that the number of pages available is sufficient flickerstreak@8: local totalPages = (self:GetAutoStanceSwitch() and nStancePages[playerClass] > 0 and nStancePages[playerClass] or 1) + 1 flickerstreak@7: if self:GetPages() < totalPages then flickerstreak@7: self:SetPages(totalPages) flickerstreak@7: end flickerstreak@7: local s, s2 flickerstreak@8: if playerClass == "DRUID" and not self:GetAutoStanceSwitch() then flickerstreak@7: -- change mapping for cat->prowl and prowl->cat to 1:2 and 2:1 since no stance mapping flickerstreak@7: s = "1:2" flickerstreak@7: s2 = "2:1" flickerstreak@7: end flickerstreak@8: self.barFrame:SetAttribute("statemap-stealth-1",s or stealthMaps[playerClass]) flickerstreak@8: self.barFrame:SetAttribute("statemap-stealth-0",s2 or unstealthMaps[playerClass]) flickerstreak@7: -- set initial value flickerstreak@7: self.barFrame:SetAttribute("state-stealth",IsStealthed() or 0) flickerstreak@7: else flickerstreak@7: self.barFrame:SetAttribute("statemap-stealth-1",ATTRIBUTE_NOOP) flickerstreak@7: self.barFrame:SetAttribute("statemap-stealth-0",ATTRIBUTE_NOOP) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:ArePageControlsHidden() flickerstreak@7: return not ( self.config.pages and self.config.pages.showControls ) flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:TogglePageControlsHidden() flickerstreak@7: if self.config.pages then flickerstreak@7: self.config.pages.showControls = not self.config.pages.showControls flickerstreak@7: self:RefreshPageControls() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:GetPageControlsLoc() flickerstreak@7: return self.config.pages and self.config.pages.controlsLoc flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:SetPageControlsLoc(loc) flickerstreak@7: if self.config.pages then flickerstreak@7: self.config.pages.controlsLoc = loc flickerstreak@7: self:RefreshPageControls() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:RefreshPageControls() flickerstreak@7: local b = self.barFrame; flickerstreak@8: local upArrow = self.upArrow flickerstreak@8: local downArrow = self.downArrow flickerstreak@8: local pageNum = self.pageNum flickerstreak@7: flickerstreak@7: if self:GetPages() > 1 and self.config.pages.showControls then flickerstreak@7: local loc = self.config.pages.controlsLoc flickerstreak@7: flickerstreak@8: upArrow:SetAttribute("hidestates",nil) flickerstreak@8: downArrow:SetAttribute("hidestates",nil) flickerstreak@8: flickerstreak@7: pageNum:ClearAllPoints() flickerstreak@7: upArrow:ClearAllPoints() flickerstreak@7: downArrow:ClearAllPoints() flickerstreak@7: flickerstreak@7: local vertical = { 0,0,0,1,1,0,1,1 } flickerstreak@7: local horizontal = { 0,1,1,1,0,0,1,0 } flickerstreak@7: local textures = { flickerstreak@7: upArrow:GetNormalTexture(), flickerstreak@7: upArrow:GetPushedTexture(), flickerstreak@7: upArrow:GetDisabledTexture(), flickerstreak@7: upArrow:GetHighlightTexture(), flickerstreak@7: downArrow:GetNormalTexture(), flickerstreak@7: downArrow:GetPushedTexture(), flickerstreak@7: downArrow:GetDisabledTexture(), flickerstreak@7: downArrow:GetHighlightTexture(), flickerstreak@7: } flickerstreak@7: flickerstreak@7: local offset = 10 flickerstreak@7: local mult = (loc == "RIGHT" or loc == "TOP") and 1 or -1 flickerstreak@7: local pageNumOffset = mult * (self.config.spacing/2 - offset) flickerstreak@7: local arrowOffset = mult * offset flickerstreak@7: flickerstreak@7: if loc == "Blizzard" or loc == nil then flickerstreak@7: pageNum:SetPoint("LEFT", b, "RIGHT", 28, 0) flickerstreak@7: upArrow:SetPoint("LEFT", b, "RIGHT", -2, 9) flickerstreak@7: downArrow:SetPoint("LEFT", b, "RIGHT", -2, -10) flickerstreak@7: for _, tex in ipairs(textures) do flickerstreak@7: tex:SetTexCoord(unpack(vertical)) flickerstreak@7: end flickerstreak@7: elseif loc == "RIGHT" or loc == "LEFT" then flickerstreak@7: local relPoint = loc == "RIGHT" and "LEFT" or "RIGHT" flickerstreak@7: pageNum:SetPoint(relPoint, b, loc, pageNumOffset, 0) flickerstreak@7: upArrow:SetPoint("BOTTOM",pageNum,"TOP",arrowOffset, -12) flickerstreak@7: downArrow:SetPoint("TOP",pageNum,"BOTTOM",arrowOffset, 12) flickerstreak@7: for _, tex in ipairs(textures) do flickerstreak@7: tex:SetTexCoord(unpack(vertical)) flickerstreak@7: end flickerstreak@7: else flickerstreak@7: pageNum:SetPoint(loc == "BOTTOM" and "TOP" or "BOTTOM", b, loc, 0, pageNumOffset) flickerstreak@7: upArrow:SetPoint("LEFT",pageNum,"RIGHT",-12,arrowOffset) flickerstreak@7: downArrow:SetPoint("RIGHT",pageNum,"LEFT",12,arrowOffset) flickerstreak@7: for _, tex in ipairs(textures) do flickerstreak@7: tex:SetTexCoord(unpack(horizontal)) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: self:StateChanged() flickerstreak@7: upArrow:Show() flickerstreak@7: downArrow:Show() flickerstreak@7: pageNum:Show() flickerstreak@7: else flickerstreak@8: upArrow:SetAttribute("hidestates","1-"..self:GetPages()) flickerstreak@8: downArrow:SetAttribute("hidestates","1-"..self:GetPages()) flickerstreak@8: flickerstreak@7: upArrow:Hide() flickerstreak@7: downArrow:Hide() flickerstreak@7: pageNum:Hide() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:StateChanged() flickerstreak@7: local page = self.barFrame:GetAttribute("state") or 1 flickerstreak@7: getglobal(self.barFrame:GetName().."PageNumberLabelText"):SetText(page) flickerstreak@7: if self.config.pages then self.config.pages.currentPage = page end flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@1: flickerstreak@1: -- layout methods flickerstreak@1: function ReBar.prototype:ApplySize() flickerstreak@1: local buttonSz = self.config.size or 36 flickerstreak@1: local spacing = self.config.spacing or 4 flickerstreak@1: local rows = self.config.rows or 1 flickerstreak@1: local columns = self.config.columns or 12 flickerstreak@7: local w = buttonSz * columns + spacing * columns flickerstreak@7: local h = buttonSz * rows + spacing * rows flickerstreak@1: local f = self.barFrame flickerstreak@1: flickerstreak@7: -- + 0.1: avoid issues with UI scaling flickerstreak@7: f:SetMinResize(buttonSz + spacing + 0.1, buttonSz + spacing + 0.1) flickerstreak@7: f:SetWidth(w + 0.1) flickerstreak@7: f:SetHeight(h + 0.1) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:ApplyAnchor() flickerstreak@1: local a = self.config.anchor flickerstreak@1: local f = self.barFrame flickerstreak@2: if a then flickerstreak@2: f:ClearAllPoints() flickerstreak@7: f:SetPoint(a.point,getglobal(a.frame),a.relPoint,a.x,a.y) flickerstreak@2: local color = anchoredLabelColor flickerstreak@7: if a.frame == "UIParent" or a.frame == "WorldFrame" then flickerstreak@2: color = nonAnchoredLabelColor flickerstreak@2: end flickerstreak@2: self.labelString:SetTextColor(color.r, color.g, color.b) flickerstreak@2: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:ApplyVisibility() flickerstreak@7: local v = self.config.visible or not self.locked flickerstreak@1: flickerstreak@1: if v then flickerstreak@1: self.barFrame:Show() flickerstreak@1: else flickerstreak@1: self.barFrame:Hide() flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:LayoutButtons() flickerstreak@1: local r = self.config.rows flickerstreak@1: local c = self.config.columns flickerstreak@1: local sp = self.config.spacing flickerstreak@1: local sz = self.config.size flickerstreak@1: local gSize = sp + sz flickerstreak@7: local point = (self.config.growUp and "BOTTOM" or "TOP")..(self.config.growLeft and "RIGHT" or "LEFT") flickerstreak@7: local major = self.config.columnMajor and r or c flickerstreak@7: flickerstreak@7: for i, b in ipairs(self.buttons) do flickerstreak@7: local x = sp/2 + gSize * math.fmod(i-1,major) flickerstreak@7: local y = sp/2 + gSize * math.floor((i-1)/major) flickerstreak@7: if self.config.columnMajor then x,y = y,x end flickerstreak@7: if self.config.growLeft then x = -x end flickerstreak@7: if not self.config.growUp then y = -y end flickerstreak@7: b:PlaceButton(self.barFrame, point, x, y, sz) flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:FlipRowsColumns() flickerstreak@7: self.config.rows, self.config.columns = self.config.columns, self.config.rows flickerstreak@7: self.config.columnMajor = not self.config.columnMajor flickerstreak@7: self:ApplySize() flickerstreak@7: self:LayoutButtons() flickerstreak@7: end flickerstreak@7: flickerstreak@7: function ReBar.prototype:AcquireButtons() flickerstreak@7: local n = self.config.rows * self.config.columns flickerstreak@1: flickerstreak@1: for i = 1, n do flickerstreak@1: if self.buttons[i] == nil then flickerstreak@7: local b = self.buttonClass:Acquire(self.config.btnConfig, i, self.config.pages and self.config.pages.n, n) flickerstreak@7: if b then flickerstreak@7: if not AceOO.inherits(b, ReBar.IButton) then flickerstreak@7: error("ReBar: specified Button class object did not meet required interface") flickerstreak@7: end flickerstreak@7: if self.locked == false then flickerstreak@7: b:BarUnlocked() -- buttons assume they are created in a barlocked state flickerstreak@7: end flickerstreak@7: self.buttons[i] = b flickerstreak@7: self.barFrame:SetAttribute("addchild",b:GetActionFrame()) flickerstreak@7: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@7: local maxn = table.maxn(self.buttons) flickerstreak@7: for i = n+1, table.maxn(self.buttons) do flickerstreak@7: local b = self.buttons[i] flickerstreak@7: self.buttons[i] = nil flickerstreak@7: if b then flickerstreak@7: self.buttonClass:Release(b) flickerstreak@7: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: function ReBar.prototype:StoreAnchor(f, p, rp, x, y) flickerstreak@1: local name = f:GetName() flickerstreak@1: -- no point if we can't store the name or the offsets are incomplete flickerstreak@1: if name and x and y then flickerstreak@1: self.config.anchor = { flickerstreak@7: frame = name, flickerstreak@1: point = p, flickerstreak@1: relPoint = rp or p, flickerstreak@1: x = x, flickerstreak@1: y = y flickerstreak@1: } flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@7: function ReBar.prototype:StickyIndicatorUpdate() flickerstreak@7: if IsShiftKeyDown() then flickerstreak@7: local snapRange = self.config.size + self.config.spacing flickerstreak@7: self:DisplaySnapIndicator(ReBar.anchorTargets, snapRange, 0, 0) flickerstreak@7: else flickerstreak@7: self:HideSnapIndicator() flickerstreak@7: end flickerstreak@7: end flickerstreak@7: flickerstreak@7: flickerstreak@1: -- mouse event handlers (clicking/dragging/resizing the bar) flickerstreak@1: function ReBar.prototype:BeginDrag() flickerstreak@1: local f = self.barFrame flickerstreak@1: f:StartMoving() flickerstreak@1: f.isMoving = true flickerstreak@7: self.updateTime = DRAG_UPDATE_RATE flickerstreak@7: f:SetScript("OnUpdate", function(frame, elapsed) self:StickyIndicatorUpdate(elapsed) end) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:FinishDrag() flickerstreak@7: local o, p, rp, x, y flickerstreak@1: local bf = self.barFrame flickerstreak@7: local snapRange = self.config.size + self.config.spacing flickerstreak@1: flickerstreak@1: bf:StopMovingOrSizing() flickerstreak@1: bf.isMoving = false flickerstreak@1: flickerstreak@1: bf:SetScript("OnUpdate",nil) flickerstreak@1: if IsShiftKeyDown() then flickerstreak@7: o, p, rp, x, y = self:GetClosestPointSnapped(ReBar.anchorTargets, snapRange, 0, 0) flickerstreak@1: end flickerstreak@1: flickerstreak@7: if o == nil then flickerstreak@7: o, p, rp, x, y = self:GetClosestVisiblePoint(ReBar.UIParentAnchorTarget, snapRange, 0, 0) flickerstreak@1: end flickerstreak@7: flickerstreak@7: self:HideSnapIndicator() flickerstreak@7: self:StoreAnchor(o:GetFrame(), p, rp, x, y) flickerstreak@7: self:ApplyAnchor() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:BeginBarResize( sizingPoint ) flickerstreak@1: local f = self.barFrame flickerstreak@1: f:StartSizing(sizingPoint) flickerstreak@1: f.resizing = true flickerstreak@7: self.updateTime = DRAG_UPDATE_RATE flickerstreak@7: f:SetScript("OnUpdate",function(frame, elapsed) self:ReflowButtons(elapsed) end) flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:BeginButtonResize( sizingPoint, mouseBtn ) flickerstreak@1: local f = self.barFrame flickerstreak@1: f:StartSizing(sizingPoint) flickerstreak@1: f.resizing = true flickerstreak@1: local r = self.config.rows flickerstreak@1: local c = self.config.columns flickerstreak@1: local s = self.config.spacing flickerstreak@1: local sz = self.config.size flickerstreak@7: self.updateTime = DRAG_UPDATE_RATE flickerstreak@1: if mouseBtn == "LeftButton" then flickerstreak@7: f:SetMinResize(c*(12 + s) +0.1, r*(12 + s) +0.1) flickerstreak@7: f:SetScript("OnUpdate",function(frame, elapsed) self:DragSizeButtons(elapsed) end) flickerstreak@1: elseif mouseBtn == "RightButton" then flickerstreak@7: f:SetMinResize(c*sz+0.1, r*sz+0.1) flickerstreak@7: f:SetScript("OnUpdate",function(frame, elapsed) self:DragSizeSpacing(elapsed) end) flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:FinishResize() flickerstreak@1: local f = self.barFrame flickerstreak@1: f:StopMovingOrSizing() flickerstreak@1: f.resizing = false flickerstreak@1: f:SetScript("OnUpdate",nil) flickerstreak@1: self:ApplySize() flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: flickerstreak@1: -- utility function to get the height, width, and button size attributes flickerstreak@1: function ReBar.prototype:GetLayout() flickerstreak@1: local c = self.config flickerstreak@1: local f = self.barFrame flickerstreak@1: return f:GetWidth(), f:GetHeight(), c.size, c.rows, c.columns, c.spacing flickerstreak@1: end flickerstreak@1: flickerstreak@7: flickerstreak@1: -- add and remove buttons dynamically as the bar is resized flickerstreak@7: function ReBar.prototype:ReflowButtons( elapsed ) flickerstreak@7: self.updateTime = self.updateTime - elapsed flickerstreak@7: if self.updateTime <= 0 then flickerstreak@7: self.updateTime = DRAG_UPDATE_RATE flickerstreak@7: flickerstreak@7: local w, h, sz, r, c, sp = self:GetLayout() flickerstreak@1: flickerstreak@7: self.config.rows = math.floor( (h+1) / (sz + sp) ) flickerstreak@7: self.config.columns = math.floor( (w+1) / (sz + sp) ) flickerstreak@1: flickerstreak@7: if self.config.rows ~= r or self.config.columns ~= c then flickerstreak@7: self:AcquireButtons() flickerstreak@7: self:LayoutButtons() flickerstreak@7: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: -- change the size of buttons as the bar is resized flickerstreak@7: function ReBar.prototype:DragSizeButtons( elapsed ) flickerstreak@7: self.updateTime = self.updateTime - elapsed flickerstreak@7: if self.updateTime <= 0 then flickerstreak@7: self.updateTime = DRAG_UPDATE_RATE flickerstreak@7: flickerstreak@7: local w, h, sz, r, c, sp = self:GetLayout() flickerstreak@1: flickerstreak@7: local newSzW = math.floor((w - c*sp)/c) flickerstreak@7: local newSzH = math.floor((h - r*sp)/r) flickerstreak@7: flickerstreak@7: self.config.size = math.max(12, math.min(newSzW, newSzH)) flickerstreak@1: flickerstreak@7: if self.config.size ~= sz then flickerstreak@7: self:LayoutButtons() flickerstreak@7: self:UpdateResizeTooltip() flickerstreak@7: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: -- change the spacing of buttons as the bar is resized flickerstreak@7: function ReBar.prototype:DragSizeSpacing( elapsed ) flickerstreak@7: self.updateTime = self.updateTime - elapsed flickerstreak@7: if self.updateTime <= 0 then flickerstreak@7: self.updateTime = DRAG_UPDATE_RATE flickerstreak@7: flickerstreak@7: local w, h, sz, r, c, sp = self:GetLayout() flickerstreak@1: flickerstreak@7: local newSpW = math.floor((w - c*sz)/c) flickerstreak@7: local newSpH = math.floor((h - r*sz)/r) flickerstreak@1: flickerstreak@7: self.config.spacing = math.max(0, math.min(newSpW, newSpH)) flickerstreak@1: flickerstreak@7: if self.config.spacing ~= sp then flickerstreak@7: self:LayoutButtons() flickerstreak@7: self:UpdateResizeTooltip() flickerstreak@7: end flickerstreak@1: end flickerstreak@1: end flickerstreak@1: flickerstreak@1: flickerstreak@1: -- update the drag tooltip to indicate current sizes flickerstreak@1: function ReBar.prototype:UpdateResizeTooltip() flickerstreak@1: GameTooltipTextRight4:SetText(self.config.size) flickerstreak@1: GameTooltipTextRight5:SetText(self.config.spacing) flickerstreak@1: GameTooltip:Show() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:ShowTooltip() flickerstreak@1: GameTooltip:SetOwner(self.barFrame, "ANCHOR_TOPRIGHT") flickerstreak@7: GameTooltip:AddLine(self.config.btnConfig.subtype.." Bar "..self.barID..(self.config.visible and "" or " (Hidden)")) flickerstreak@1: GameTooltip:AddLine("Drag to move") flickerstreak@1: GameTooltip:AddLine("Shift-drag for sticky mode") flickerstreak@1: GameTooltip:AddLine("Right-click for options") flickerstreak@1: GameTooltip:Show() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:ShowButtonResizeTooltip(point) flickerstreak@1: GameTooltip:SetOwner(self.barFrame, "ANCHOR_"..point) flickerstreak@1: GameTooltip:AddLine("Drag to resize buttons") flickerstreak@1: GameTooltip:AddLine("Right-click-drag") flickerstreak@1: GameTooltip:AddLine("to change spacing") flickerstreak@1: GameTooltip:AddDoubleLine("Size: ", "0") flickerstreak@1: GameTooltip:AddDoubleLine("Spacing: ", "0") flickerstreak@1: self:UpdateResizeTooltip() flickerstreak@1: end flickerstreak@1: flickerstreak@1: function ReBar.prototype:ShowBarResizeTooltip(point) flickerstreak@1: GameTooltip:SetOwner(self.barFrame, "ANCHOR_"..point) flickerstreak@1: GameTooltip:AddLine("Drag to add/remove buttons") flickerstreak@1: GameTooltip:Show() flickerstreak@1: end flickerstreak@8: