--[[ Configator - A library to help you create a gui config Version: 5.9.4961 (WhackyWallaby) Revision: $Id: Configator.lua 278 2010-10-12 08:15:00Z brykrys $ URL: http://auctioneeraddon.com/dl/ License: This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Additional: Regardless of any other conditions, you may freely use this code within the World of Warcraft game client. -------------------------------------------------------------------------- USAGE: Stub Configator = LibStub:GetLibrary("Configator") Call myCfg = Configator:Create(setterFunc, getterFunc) Call tabId = myCfg:AddTab(TabName) Call myCfg:AddControl(tabId, controlType, leftPct, ...) Wait for callbacks on your getters and setters Your setter will be called with (variableName, value) for you to set Your getter will be called with (variableName) for your to return the current value The AddControl function's ... varies depending on the controlType: "Header" == text "Subhead" == text "Checkbox" == level, setting, label "Slider", "TinySlider", "WideSlider", "NumeriSlider", "NumeriTiny", "NumeriWide" == level, setting, min, max, step, label, fmtfunc "Text" = level, setting, label "NumberBox", "TinyNumber" == level, setting, minVal, maxVal, label "MoneyFrame", "PinnedMoney" = level, setting, label "ColorSelect", "ColorSelectAlpha" == level, setting, label Settings and configuration system. ]] local LIBRARY_VERSION_MAJOR = "Configator" local LIBRARY_VERSION_MINOR = 26 do -- LibStub -- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info -- LibStub is hereby placed in the Public Domain -- Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS! local LibStub = _G[LIBSTUB_MAJOR] -- Check to see is this version of the stub is obsolete if not LibStub or LibStub.minor < LIBSTUB_MINOR then LibStub = LibStub or {libs = {}, minors = {} } _G[LIBSTUB_MAJOR] = LibStub LibStub.minor = LIBSTUB_MINOR -- LibStub:NewLibrary(major, minor) -- major (string) - the major version of the library -- minor (string or number ) - the minor version of the library -- -- returns nil if a newer or same version of the lib is already present -- returns empty library object or old library object if upgrade is needed function LibStub:NewLibrary(major, minor) assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)") minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.") local oldminor = self.minors[major] if oldminor and oldminor >= minor then return nil end self.minors[major], self.libs[major] = minor, self.libs[major] or {} return self.libs[major], oldminor end -- LibStub:GetLibrary(major, [silent]) -- major (string) - the major version of the library -- silent (boolean) - if true, library is optional, silently return nil if its not found -- -- throws an error if the library can not be found (except silent is set) -- returns the library object if found function LibStub:GetLibrary(major, silent) if not self.libs[major] and not silent then error(("Cannot find a library instance of %q."):format(tostring(major)), 2) end return self.libs[major], self.minors[major] end -- LibStub:IterateLibraries() -- -- Returns an iterator for the currently registered libraries function LibStub:IterateLibraries() return pairs(self.libs) end setmetatable(LibStub, { __call = LibStub.GetLibrary }) end end -- LibStub local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR) if not lib then return end LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/libs/trunk/Configator/Configator.lua $","$Rev: 278 $","5.1.DEV.", 'auctioneer', 'libs') local kit = {} if not lib.frames then lib.frames = {} end if not lib.tmpId then lib.tmpId = 0 end -- Table management functions: local function replicate(source, depth, history) if type(source) ~= "table" then return source end assert(depth==nil or tonumber(depth), "Unknown depth: " .. tostring(depth)) if not depth then depth = 0 history = {} end assert(history, "Have depth but without history") assert(depth < 100, "Structure is too deep") local dest = {} history[source] = dest for k, v in pairs(source) do if type(v) == "table" then if history[v] then dest[k] = history[v] else dest[k] = replicate(v, depth+1, history) end else dest[k] = v end end return dest end local function empty(item) if type(item) ~= 'table' then return end for k,v in pairs(item) do item[k] = nil end end local function fill(item, ...) if type(item) ~= 'table' then return end if (#item > 0) then empty(item) end local n = select('#', ...) for i = 1,n do item[i] = select(i, ...) end end -- End table management functions function lib:CreateAnonName() lib.tmpId = lib.tmpId + 1 return "ConfigatorAnon"..lib.tmpId end local function setFocus(focusType, object, focus) if focusType == "NEXT" then object.nextFocus = focus if (object.isMoneyFrame) then MoneyInputFrame_SetNextFocus(object, focus) end elseif focusType == "PREV" then object.previousFocus = focus if (object.isMoneyFrame) then MoneyInputFrame_SetPreviousFocus(object, focus) end end end function lib:TabLink(frame, el, noHook) if not frame.firstFocus then frame.firstFocus = el setFocus("NEXT", el, el) setFocus("PREV", el, el) else local curLast = frame.lastFocus local first = frame.firstFocus setFocus("NEXT", curLast, el) setFocus("PREV", first, el) setFocus("NEXT", el, first) setFocus("PREV", el, curLast) end frame.lastFocus = el if not noHook and not el.isMoneyFrame then el:SetScript("OnTabPressed", kit.FocusShift) end end local function proxyHide(self) self.gui:Hide() end local function proxyIsShown(self) return self.gui.Backdrop:IsVisible() end function lib:Create(setter, getter, dialogWidth, dialogHeight, gapWidth, gapHeight, topOffset, leftOffset) local id = #(lib.frames) + 1 local name = "ConfigatorDialog_"..id if not dialogWidth then dialogWidth = 800 end if not dialogHeight then dialogHeight = 450 end if not gapWidth then gapWidth = 0 end if not gapHeight then gapHeight = 0 end local gui = CreateFrame("Frame", name, UIParent) table.insert(lib.frames, gui) gui.setter = setter gui.getter = getter gui.dialogWidth = dialogWidth gui.dialogHeight = dialogHeight gui.gapWidth = gapWidth gui.gapHeight = gapHeight gui.topOffset = topOffset gui.leftOffset = leftOffset gui.heightDelta = 0 gui.buttonTop = 0 local top = getter("configator.top") local left = getter("configator.left") if (top and left) then gui:SetPoint("TOPLEFT", "UIParent", "BOTTOMLEFT", left, top) else gui:SetPoint("CENTER", "UIParent", "CENTER") end gui:Hide() gui:SetFrameStrata("HIGH") gui:SetToplevel(true) gui:SetMovable(true) gui:SetWidth(dialogWidth) gui:SetHeight(dialogHeight) gui:EnableMouse(true) gui.Backdrop = CreateFrame("Frame", name.."Backdrop", gui) gui.Backdrop:SetAllPoints(gui) gui.Backdrop:SetBackdrop({ bgFile = "Interface/Tooltips/ChatBubble-Background", edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 32, right = 32, top = 32, bottom = 32 } }) gui.Backdrop:SetBackdropColor(0, 0, 0, 1) -- Create a proxy to see if we should react to global CloseWindow() calls gui.Proxy = CreateFrame("Frame", name.."Proxy", gui) gui.Proxy.gui = gui table.insert(UISpecialFrames, name.."Proxy") -- make frames Esc Sensitive by default gui.Proxy.Hide = proxyHide gui.Proxy.IsShown = proxyIsShown gui.Done = CreateFrame("Button", nil, gui.Backdrop, "OptionsButtonTemplate") gui.Done:SetPoint("BOTTOMRIGHT", gui, "BOTTOMRIGHT", -10, 10) gui.Done:SetScript("OnClick", function() gui:Hide() end) gui.Done:SetText(DONE) gui.DragTop = CreateFrame("Button", nil, gui.Backdrop) gui.DragTop:SetPoint("TOPLEFT", gui.Backdrop, "TOPLEFT", 10,-5) gui.DragTop:SetPoint("TOPRIGHT", gui.Backdrop, "TOPRIGHT", -10,-5) gui.DragTop:SetHeight(6) gui.DragTop:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") gui.DragTop:SetScript("OnMouseDown", function() gui:StartMoving() end) gui.DragTop:SetScript("OnMouseUp", function() gui:StopMovingOrSizing() setter("configator.left", gui:GetLeft()) setter("configator.top", gui:GetTop()) end) gui.DragBottom = CreateFrame("Button", nil, gui.Backdrop) gui.DragBottom:SetPoint("BOTTOMLEFT", gui.Backdrop, "BOTTOMLEFT", 10,5) gui.DragBottom:SetPoint("BOTTOMRIGHT", gui.Backdrop, "BOTTOMRIGHT", -10,5) gui.DragBottom:SetHeight(6) gui.DragBottom:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") gui.DragBottom:SetScript("OnMouseDown", function() gui:StartMoving() end) gui.DragBottom:SetScript("OnMouseUp", function() gui:StopMovingOrSizing() setter("configator.left", gui:GetLeft()) setter("configator.top", gui:GetTop()) end) gui:RegisterEvent("PLAYER_LOGOUT") gui:SetScript("OnEvent", function() gui:SetClampedToScreen(1) setter("configator.left", gui:GetLeft()) setter("configator.top", gui:GetTop()) end) gui.config = { current = "", order = { "" }, tabs = { [""] = {}, }, cats = { [""] = {}, }, } gui.tabs = {} gui.elements = {} for k,v in pairs(kit) do gui[k] = v end return gui end -- this shows the game toolip for a specified link function lib:SetLinkTip(frame, link) if not frame or link == nil then GameTooltip:Hide() return end GameTooltip:SetOwner(frame, "ANCHOR_NONE") GameTooltip:SetHyperlink(link) GameTooltip:ClearAllPoints() GameTooltip:SetPoint("TOPRIGHT", frame, "TOPLEFT", -10, -20) GameTooltip:Show() end -- Create a special tooltip just for us if not lib.tooltip then lib.tooltip = CreateFrame("GameTooltip", "ConfigatorTipTooltip", UIParent, "GameTooltipTemplate") local function hide_tip() lib.tooltip:Hide() end lib.tooltip.fadeInfo = {} function lib:SetTip(frame, ...) local n = select("#", ...) if n == 1 then -- Allow passing of tip lines as a single table local tip = select(1, ...) if type(tip) == "table" then lib:SetTip(frame, unpack(tip)) return end end if not frame or n == 0 then lib.tooltip.fadeInfo.finishedFunc = hide_tip local curAlpha = lib.tooltip:GetAlpha() UIFrameFadeOut(lib.tooltip, 0.25, curAlpha, 0) lib.tooltip:SetAlpha(curAlpha) lib.tooltip.schedule = nil return end if lib.tooltip:GetAlpha() > 0 then -- Speed up this fade UIFrameFadeOut(lib.tooltip, 0.01, 0, 0) lib.tooltip:SetAlpha(0) end lib.tooltip:SetOwner(frame, "ANCHOR_NONE") lib.tooltip:ClearLines() local tip for i=1, n do tip = select(i, ...) lib.tooltip:AddLine(tostring(tip) or "", 1,1,0.5, 1) end lib.tooltip:Show() lib.tooltip:SetAlpha(0) lib.tooltip:SetBackdropColor(0,0,0, 1) lib.tooltip:SetPoint("TOP", frame, "BOTTOM", 10, -5) lib.tooltip.schedule = GetTime() + 1 end lib.tooltip:SetScript("OnUpdate", function() if lib.tooltip.schedule and GetTime() > lib.tooltip.schedule then local curAlpha = lib.tooltip:GetAlpha() UIFrameFadeIn(lib.tooltip, 0.33, curAlpha, 1) lib.tooltip:SetAlpha(curAlpha) -- Tooltips set alpha when they are shown, and UIFrameFadeIn does a :Show() lib.tooltip.schedule = nil end end) lib.tooltip:SetBackdrop({ bgFile = "Interface/Tooltips/ChatBubble-Background", edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 32, right = 32, top = 32, bottom = 32 } }) lib.tooltip:SetBackdropColor(0,0,0.3, 1) lib.tooltip:SetClampedToScreen(true) end -- Create our help window if not lib.help then lib.help = CreateFrame("Frame", "ConfigatorHelpFrame", UIParent) lib.help:SetBackdrop({ bgFile = "Interface/Stationery/StationeryTest1", edgeFile = "Interface/TUTORIALFRAME/TUTORIALFRAMEBORDER", tile = false, tileSize = 32, edgeSize = 32, insets = { left = 4, right = 4, top = 22, bottom = 4 } }) lib.help:SetBackdropColor(0.15,0.15,0.15, 0.98) lib.help:SetToplevel(true) lib.help:SetPoint("CENTER") lib.help:SetWidth(450) lib.help:SetHeight(500) lib.help:SetMovable(true) lib.help:EnableMouse(true) lib.help:SetScript("OnUpdate", function() if lib.help.refresh then lib.help.Update() end end) lib.help:SetScript("OnEvent", function() if lib.help:IsVisible() then lib.help.Update() end end) lib.help:RegisterEvent("UPDATE_FLOATING_CHAT_WINDOWS") lib.help:Hide() lib.help.title = CreateFrame("Button", nil, lib.help) lib.help.title:SetScript("OnMouseDown", function() lib.help:StartMoving() end) lib.help.title:SetScript("OnMouseUp", function() lib.help:StopMovingOrSizing() end) lib.help.title:SetPoint("TOPLEFT", lib.help, "TOPLEFT", 3,-3) lib.help.title:SetPoint("BOTTOMRIGHT", lib.help, "TOPRIGHT", -28,-20) lib.help.title:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") if ( lib.help.title.SetTextFontObject ) then lib.help.title.SetNormalFontObject = lib.help.title.SetTextFontObject end -- WotLK Hack lib.help.title:SetNormalFontObject("GameFontNormal") lib.help.title:SetText("Help Window") lib.help.close = CreateFrame("Button", nil, lib.help, "UIPanelCloseButton") lib.help.close:SetPoint("TOPRIGHT", lib.help, "TOPRIGHT", 3, 5) lib.help.close:SetHeight(32) lib.help.close:SetWidth(32) lib.help.close:SetScript("OnClick", function () lib.help:Hide() end) lib.help.content = CreateFrame("Frame", "ConfigatorHelpContent", lib.help) lib.help.content:SetWidth(420) lib.help.content:SetHeight(100) lib.help.content.totalHeight = 0 lib.help.rows = {} lib.help.fontcache = {} function lib.help:AddHelp(question, answer) lib.help:AddRow(question, 14, 1,0.9,0, 8) lib.help:AddRow(answer, 12, 1,1,1) end function lib.help:AddRow(text, size, r,g,b, pad) if not size then size = 12 end if not (r and g and b) then r,g,b = 1,1,1 end if not pad then pad = 0 end local font local remain = #lib.help.fontcache if remain > 0 then font = lib.help.fontcache[remain] table.remove(lib.help.fontcache) else font = lib.help.content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") end local file = font:GetFont() font:SetFont(file, size) font:SetTextColor(r,g,b,1) font:SetText(text) font.pad = pad table.insert(lib.help.rows, font) font:SetPoint("TOPLEFT", lib.help.content, "TOPLEFT", 5, -5) font:SetPoint("TOPRIGHT", lib.help.content, "TOPRIGHT", -5, -5) font:SetWidth(lib.help.content:GetWidth()-10) font:SetJustifyV("TOP") font:SetJustifyH("LEFT") font:Show() lib.help.refresh = true return n, font, height end function lib.help:ClearAllLines() lib.help.content.totalHeight = 0 for i=#lib.help.rows, 1, -1 do lib.help.rows[i]:Hide() lib.help.rows[i]:ClearAllPoints() table.insert(lib.help.fontcache, lib.help.rows[i]) table.remove(lib.help.rows, i) end lib.help.refresh = true end function lib.help:Update() local height, top = 0, 0 for i = 1, #lib.help.rows do local font = lib.help.rows[i] if i == 1 then top = -5 height = height + 5 else top = -height - 2 height = height + 2 end top = top - font.pad height = height + font.pad font:SetPoint("TOPLEFT", lib.help.content, "TOPLEFT", 5, top) font:SetPoint("TOPRIGHT", lib.help.content, "TOPRIGHT", -5, top) font:SetWidth(lib.help.content:GetWidth()-10) height = height + font:GetHeight() end lib.help.content:SetHeight(height+10) lib.help.scroll:Update() lib.help.refresh = nil end function lib.help:Activate() local qlist = self.qlist local faq = self.faq local faa = self.faa local qid lib.help:ClearAllLines() for i = 1, #qlist do qid = qlist[i] lib.help:AddHelp(faq[qid], faa[qid]) end lib.help:SetFrameStrata(self:GetFrameStrata()) lib.help.refresh = true lib.help:Show() end local PanelScroller = LibStub:GetLibrary("PanelScroller") lib.help.scroll = PanelScroller:Create(lib.CreateAnonName(), lib.help) lib.help.scroll:SetPoint("TOPLEFT", lib.help, "TOPLEFT", 10,-25) lib.help.scroll:SetPoint("BOTTOMRIGHT", lib.help, "BOTTOMRIGHT", -25,5) lib.help.scroll:SetScrollChild(lib.help.content:GetName()) lib.help.scroll:SetScrollBarVisible("HORIZONTAL", "NO") end nConf = lib -- Local function to get the UI object type local function isGuiObject(obj) if not obj then return false end if type(obj) ~= "table" then return false end if not obj[0] or type(obj[0]) ~= "userdata" then return false end if not obj.GetObjectType then return false end return obj:GetObjectType() end function kit:ClickButton(pos) local button = self:GetButton(pos) local tabName = button.tabName local catId = button.catId if button.tabName then self:SelectTab(catId, tabName) elseif button.catId then self:SelectCat(catId) end end function kit:SelectTab(catId, tabName) if self.config.tabs[catId] then local id = self.config.tabs[catId][tabName] if id then self:ActivateTab(id) end end end function kit:SelectCat(catId) if self.config.cats[catId] then self.config.cats[catId].isOpen = not self.config.cats[catId].isOpen self:RegenTabs() end end local buttonKit = {} function buttonKit:SetText(text, active) self.text:SetText(text) if active then self.text:SetTextColor(1,0.8,0.1) else self.text:SetTextColor(0.9,0.9,0.9) end end function buttonKit:SetArrowDirection(direction) -- self.text:ClearAllPoints() local file = self.text:GetFont() if direction == "RIGHT" then self.text:SetPoint("LEFT", self, "LEFT", 25,0) self.text:SetFont(file, 12) self.expand:SetTexCoord(1,1, 0,1, 1,0, 0,0) self.expand:Show() elseif direction == "DOWN" then self.text:SetPoint("LEFT", self, "LEFT", 25,0) self.text:SetFont(file, 12) self.expand:SetTexCoord(0,1, 0,0, 1,1, 1,0) self.expand:Show() else self.text:SetPoint("LEFT", self, "LEFT", 18,0) self.text:SetFont(file, 11) self.expand:Hide() end end function kit:GetButton(pos) if not self.buttons then self.buttons = {} end if self.buttons[pos] then return self.buttons[pos] end -- Create a button for this tab button = CreateFrame("Button", myName, self) button:SetWidth(150) button:SetHeight(13) button:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") button.text = button:CreateFontString(nil, "OVERLAY", "GameFontHighlight") button.text:SetPoint("LEFT", button, "LEFT", 25,0) button.expand = button:CreateTexture(nil, "ARTWORK"); button.expand:SetTexture("Interface\\Minimap\\ROTATING-MINIMAPGUIDEARROW") button.expand:SetPoint("LEFT", button, "LEFT", 0,0) button.expand:SetWidth(32) button.expand:SetHeight(32) button.expand:Hide() for k,v in pairs(buttonKit) do button[k] = v end button:SetScript("OnClick", function() self:ClickButton(pos) end) self.buttons[pos] = button if pos == 1 then button:SetPoint("TOPLEFT", self, "TOPLEFT", 5, self.buttonTop-15) else button:SetPoint("TOPLEFT", self:GetButton(pos-1), "BOTTOMLEFT", 0, 0) end return button end function kit:RenderTabs() assert(isGuiObject(self), "Must be called on a valid object") if (self.config.isZero) then return end if not self.render then self:RegenTabs() end local offset = 0 local total = #self.render local count = math.floor((self.dialogHeight - 40) / 13) for i=1, count do local pos = i + offset local button = self:GetButton(i) if pos <= total then local isCat, catId, tabName = unpack(self.render[pos]) if isCat then local isOpen = self.config.cats[catId].isOpen if isOpen then button:SetArrowDirection("DOWN") else button:SetArrowDirection("RIGHT") end button:SetText(self.config.cats[catId].name or catId) button.catId = catId button.tabName = nil else button:SetArrowDirection("NONE") if catId == self.config.selectedCat and tabName == self.config.selectedTab then button:SetText(tabName, true) else button:SetText(tabName) end button.catId = catId button.tabName = tabName end button:Show() else button:SetText("") button:SetArrowDirection("NONE") button.catId = nil button.tabName = nil button:Hide() end end end function kit:RegenTabs() if not self.render then self.render = {} end local render = self.render empty(render) for pos, catId in ipairs(self.config.order) do if self.config.cats[catId].hasTabs then table.insert(render, {true, catId}) if self.config.cats[catId].isOpen then local list = {} for tabName in pairs(self.config.tabs[catId]) do table.insert(list, tabName) end local sortFunction = nil if not ( self.config.cats[catId].isSorted ) then local tabs = self.config.tabs[catId] sortFunction = function(a, b) if ( tabs[a] < tabs[b] ) then return true end end end table.sort(list, sortFunction) for pos, tabName in ipairs(list) do table.insert(render, {false, catId, tabName}) end end end end self:RenderTabs() end function kit:ZeroFrame() assert(isGuiObject(self), "Must be called on a valid object") assert(self.config.isZero == false, "Cannot zero a frame with tabs") local id = 0 local frame, content local myName = lib.CreateAnonName() frame = CreateFrame("Frame", myName.."Frame", self) content = CreateFrame("Frame", myName.."Content", frame) frame.id = id frame:SetPoint("TOPLEFT", self, "TOPLEFT", 10, -10) frame:SetPoint("BOTTOMRIGHT", self.Done, "TOPRIGHT", 0, 5) frame:SetBackdrop({ bgFile = "Interface/Tooltips/ChatBubble-Background", edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 32, right = 32, top = 32, bottom = 32 } }) frame:SetBackdropColor(0,0,0, 1) content:SetPoint("TOPLEFT", frame, "TOPLEFT", 5,-5) content:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -5,5) self.config = {} self.config.current = "zero" self.config.tabs = {} self.config.tabs.zero = {} self.config.tabs.zero.zero = 0 self.config.cats = {} self.config.cats.zero = {} self.config.isZero = true self.tabs[0] = { nil, frame, content, -- For backwards compatability catId = catId, tabName = tabName, gapWidth = 0, gapHeight = 0, expandGap = 0, leftOffset = 0, topOffset = 0, frame = frame, content = content, scroll = nil, buttonTop = 0, } self.tabs.active = 0 self.config.selectedCat = "zero" self.config.selectedTab = "zero" return 0 end function kit:SetPosition(parent, width, height, left, top) assert(isGuiObject(self), "Must be called on a valid object") parent = parent or UIParent top = top or self.getter("configator.top") left = left or self.getter("configator.left") width = width or self.dialogWidth height = height or self.dialogHeight self.heightDelta = self.dialogHeight - height self:SetParent(parent) self:ClearAllPoints() if (top and left) then self:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", left, top) else self:SetPoint("CENTER", parent, "CENTER") end self:SetWidth(width) self:SetHeight(height) for id, tab in ipairs(self.tabs) do if tab and tab.scroll then local frame = tab.frame local lOfs = frame.leftOffset or 0 local gWidth = frame.gapWidth or 0 local cWidth = width - lOfs - gWidth - 210 tab.content:SetWidth(cWidth) end end local button = self.buttons[1] if button then button:SetPoint("TOPLEFT", self, "TOPLEFT", 5, self.buttonTop-15) end local id = self.tabs.active if id and self.tabs[id] then local tab = self.tabs[id] if tab.expanded then self:ExpandFrame(id) else self:ContractFrame(id) end end end function kit:ShowBackdrop() assert(isGuiObject(self), "Must be called on a valid object") self.Backdrop:Show() end function kit:HideBackdrop() assert(isGuiObject(self), "Must be called on a valid object") self.Backdrop:Hide() end function kit:ToggleExpand(id) assert(isGuiObject(self), "Must be called on a valid object") local tab = self.tabs[id] if not tab.expanded then self:ExpandFrame(id) else self:ContractFrame(id) end end function kit:ExpandFrame(id) assert(isGuiObject(self), "Must be called on a valid object") local tab = self.tabs[id] if tab.gapHeight == 0 then return end tab.frame.fullsize:SetNormalTexture("Interface\\Minimap\\UI-Minimap-ZoomOutButton-Up") tab.frame.fullsize:SetPushedTexture("Interface\\Minimap\\UI-Minimap-ZoomOutButton-Down") tab.frame:SetPoint("BOTTOMRIGHT", self.Done, "TOPRIGHT", 0-tab.gapWidth, 5+tab.expandGap) tab.expanded = true end function kit:ContractFrame(id) assert(isGuiObject(self), "Must be called on a valid object") local tab = self.tabs[id] if tab.gapHeight == 0 then return end tab.frame.fullsize:SetNormalTexture("Interface\\Minimap\\UI-Minimap-ZoomInButton-Up") tab.frame.fullsize:SetPushedTexture("Interface\\Minimap\\UI-Minimap-ZoomInButton-Down") tab.frame:SetPoint("BOTTOMRIGHT", self.Done, "TOPRIGHT", 0-tab.gapWidth, 5+tab.gapHeight-self.heightDelta) tab.expanded = nil end function kit:SetExpandGap(id, gap) assert(isGuiObject(self), "Must be called on a valid object") local tab = self.tabs[id] if tab.gapHeight == 0 then return end tab.expandGap = gap end function kit:AddTab(tabName, catId, gapWidth, gapHeight, topOffset, leftOffset) assert(isGuiObject(self), "Must be called on a valid object") assert(not self.config.isZero, "Cannot add tabs to a zeroed frame") if not catId then catId = self.config.current end if not self.config.tabs[catId] then self:AddCat(catId) end local exists, id = self:GetTabByName(tabName, catId) if exists then return id end local frame, content self.config.isZero = false local myName = lib.CreateAnonName() frame = CreateFrame("Frame", myName.."Frame", self) content = CreateFrame("Frame", myName.."Content", frame) if not gapWidth then gapWidth = self.gapWidth or 0 end if not gapHeight then gapHeight = self.gapHeight or 0 end if not topOffset then topOffset = self.topOffset or 0 end if not leftOffset then leftOffset = self.leftOffset or 0 end local expandGap = 0 if self.expandGap then expandGap = self.expandGap end local tab = { nil, frame, content, -- For backwards compatability catId = catId, tabName = tabName, gapWidth = gapWidth, gapHeight = gapHeight, expandGap = expandGap, topOffset = topOffset, leftOffset = leftOffset, frame = frame, content = content, scroll = nil, expanded = nil, } table.insert(self.tabs, tab) id = table.getn(self.tabs) self.config.tabs[catId][tabName] = id tab.id = id frame.id = id content.id = id self.config.cats[catId].hasTabs = true frame:ClearAllPoints() frame:SetPoint("TOPLEFT", self, "TOPLEFT", 160+leftOffset, -10-topOffset) frame:SetPoint("BOTTOMRIGHT", self.Done, "TOPRIGHT", 0-gapWidth, 5+gapHeight) frame:SetBackdrop({ bgFile = "Interface/Tooltips/ChatBubble-Background", edgeFile = "Interface/Tooltips/ChatBubble-BackDrop", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 32, right = 32, top = 32, bottom = 32 } }) frame:SetBackdropColor(0,0,0, 1) frame:SetFrameLevel(10) frame.fullsize = CreateFrame("Button", nil, frame) frame.fullsize:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", -3, -3) frame.fullsize:SetWidth(22) frame.fullsize:SetHeight(22) frame.fullsize:SetNormalTexture("Interface\\Minimap\\UI-Minimap-ZoomInButton-Up") frame.fullsize:SetPushedTexture("Interface\\Minimap\\UI-Minimap-ZoomInButton-Down") frame.fullsize:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight") frame.fullsize:SetScript("OnClick", function() self:ToggleExpand(id) end) if gapHeight > 0 then frame.fullsize:Show() else frame.fullsize:Hide() end content:SetPoint("TOPLEFT", frame, "TOPLEFT", 5,-5) content:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -5,5) content.gapWidth = gapWidth content.gapHeight = gapHeight self:RegenTabs() if (not self.config.selectedTab) then self:ActivateTab(id) else frame:Hide() end if (self.autoScrollTabs) then self:MakeScrollable(id) end return id end function kit:AddCat(catId, catName, sortedTabs, isOpen) assert(isGuiObject(self), "Must be called on a valid object") assert(not self.config.isZero, "Cannot add categories to a zeroed frame") if self.config.tabs[catId] then return end if sortedTabs == nil then sortedTabs = true end self.config.isZero = false table.insert(self.config.order, catId) self.config.tabs[catId] = { } self.config.cats[catId] = { name = catName, isOpen = isOpen, isSorted = sortedTabs, } self.config.current = catId if not self.config.selectedCat then self.config.selectedCat = catId end self:RegenTabs() end local function anchorPoint(frame, el, last, indent, width, height, yofs) local clearance = 0 if (last and last.clearance) then clearance = last.clearance end el:SetPoint("LEFT", frame, "LEFT", indent or 15, 0) if (width == nil) then el:SetPoint("RIGHT", frame, "RIGHT", -5, 0) elseif (type(width) == "number") then el:SetWidth(width) end if (type(height) == "number") then el:SetHeight(height) end if (last) then el:SetPoint("TOP", last, "BOTTOM", 0, -5 + (yofs or 0) - clearance) else el:SetPoint("TOP", frame, "TOP", 0, -5 - clearance) end end function kit:Unfocus() assert(isGuiObject(self), "Must be called on a valid object") self:Hide() self:ClearFocus() self:Show() end function kit:MouseScroll(direction) assert(isGuiObject(self), "Must be called on a valid object") local step = 1 if self:GetObjectType() == "Slider" then step = self:GetValueStep() end self:SetValue(self:GetValue() - direction*step) end function kit:CaptureKeys() self:EnableKeyboard(true) end function kit:ReleaseKeys() self:EnableKeyboard(false) end function kit:KeyPress(key, ...) local dir = 0 if key == "UP" or key == "RIGHT" then dir = 1 elseif key == "DOWN" or key == "LEFT" then dir = -1 end if dir ~= 0 then local size = 1 if IsShiftKeyDown() then if IsControlKeyDown() then size = 25 else size = 5 end elseif IsControlKeyDown() then size = 10 end if self.GetValue then local myVal = self:GetValue() self:SetValue(myVal + size * dir) return elseif self.GetNumber then local myVal = self:GetNumber() self:SetNumber(myVal + size * dir) return elseif self.GetText then local myVal = tonumber(self:GetText()) if myVal then self:SetText(myVal + size * dir) end return end end if self.slave and self.slave.hasFocus then local script = self.slave:GetScript("OnKeyUp") if script then script(key, ...) end end end function kit:FocusShift(...) assert(isGuiObject(self), "Must be called on a valid object") if (IsShiftKeyDown()) then direction = "previousFocus" else direction = "nextFocus" end local nextobj = self[direction] if nextobj.SetFocus then nextobj:SetFocus() end end function kit:SetControlWidth(width) assert(isGuiObject(self), "Must be called on a valid object") self.scalewidth = width end function kit:MakeScrollable(id) assert(isGuiObject(self), "Must be called on a valid object") if (self.tabs[id].scroll) then return end local frame = self.tabs[id].frame local content = self.tabs[id].content local oldwidth = content:GetWidth() content:ClearAllPoints() content:SetWidth(oldwidth) content:SetHeight(250) local PanelScroller = LibStub:GetLibrary("PanelScroller") local scroll = PanelScroller:Create(lib.CreateAnonName(), frame) scroll:SetPoint("TOPLEFT", frame, "TOPLEFT", 5,-7) scroll:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -25,9) scroll:SetScrollChild(content:GetName()) scroll:UpdateScrollChildRect() self.tabs[id].scroll = scroll self.tabs[id][4] = scroll GSC = scroll end -- this is a helper function for the tooltip addition local function AddTipControl(self, control, tip) if not self or not control or not tip then return end local old_enter = control:GetScript("OnEnter") local old_leave = control:GetScript("OnLeave") local function help_enter(self, ...) if old_enter then old_enter(self, ...) end if tip then lib:SetTip(self, tip) end end local function help_leave(self, ...) if old_enter then old_leave(self, ...) end if tip then lib:SetTip() end end control:SetScript("OnEnter", help_enter) control:SetScript("OnLeave", help_leave) end -- this is a text only tool tip function kit:AddTip(id, tip) assert(isGuiObject(self), "Must be called on a valid object") local control local idType = type(id) if idType == "number" and self.tabs then control = self:GetLast(id) elseif idType == "string" then control = _G[id] elseif idType == "table" and type(id[0]) == "userdata" then control = id end assert(isGuiObject(control), "Usage: ConfigatorGui:AddTip(tabId|controlName|control, tip)") if control.button and isGuiObject(control.button) then control = control.button elseif control.control and isGuiObject(control.control) then control = control.control end --MoneyInput frames need a enter/leave for each sub frame gold/silver/copper if control.gold or control.silver or control.copper then AddTipControl(self, control.gold, tip) AddTipControl(self, control.silver, tip) AddTipControl(self, control.copper, tip) else AddTipControl(self, control, tip) end end -- this will show the game tooltip for the link function kit:AddLinkTip(id, link) assert(isGuiObject(self), "Must be called on a valid object") local control local idType = type(id) if idType == "number" and self.tabs then control = self:GetLast(id) elseif idType == "string" then control = _G[id] elseif idType == "table" and type(id[0]) == "userdata" then control = id end assert(isGuiObject(control), "Usage: ConfigatorGui:AddLinkTip(tabId|controlName|control, link)") -- we would really prefer the text labels over the controls themselves (esp. for sliders) -- ccox - Feb 8, 2009 - but I can't make that work correctly if control.button and isGuiObject(control.button) then control = control.button elseif control.control and isGuiObject(control.control) then control = control.control end local old_enter = control:GetScript("OnEnter") local old_leave = control:GetScript("OnLeave") local function help_enter(self, ...) if old_enter then old_enter(self, ...) end lib:SetLinkTip(self, link) end local function help_leave(self, ...) if old_enter then old_leave(self, ...) end lib:SetLinkTip() end control:SetScript("OnEnter", help_enter) control:SetScript("OnLeave", help_leave) end function kit:AddHelp(id, qid, question, answer) assert(isGuiObject(self), "Must be called on a valid object") local content local idType = type(id) if idType == "number" and self.tabs then content = self.tabs[id][2] elseif idType == "string" then content = _G[id] elseif idType == "table" and type(id[0]) == "userdata" then content = id end assert(isGuiObject(content), "Usage: ConfigatorGui:AddHelp(tabId|controlName|control, question, answer)") assert(question and answer, "Usage: ConfigatorGui:AddHelp(tabId|controlName|control, question, answer)") if question and answer then if content and not content.HelpButton then content.HelpButton = CreateFrame("BUTTON", content:GetName().."HelpButton", content) content.HelpButton:SetPoint("TOPRIGHT", content, "TOPRIGHT", -20,0) content.HelpButton:SetWidth(42) content.HelpButton:SetHeight(42) content.HelpButton:SetNormalTexture("Interface\\TUTORIALFRAME\\TutorialFrame-QuestionMark") content.HelpButton:SetHighlightTexture("Interface\\TUTORIALFRAME\\TutorialFrame-QuestionMark") content.HelpButton:SetScript("OnClick", lib.help.Activate) content.HelpButton.text = content.HelpButton:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") content.HelpButton.text:SetText("Help") content.HelpButton.text:SetPoint("TOP", content.HelpButton, "BOTTOM", 0,5) content.HelpButton.qlist = {} content.HelpButton.faq = {} content.HelpButton.faa = {} end content.HelpButton.faq[qid] = question content.HelpButton.faa[qid] = answer for i = 1, #content.HelpButton.qlist do if content.HelpButton.qlist[i] == qid then return end end table.insert(content.HelpButton.qlist, qid) end end function kit:GetControl( tabId, controlId ) if ( controlId ) then return self.tabs[tabId].frame.ctrls[controlId] else local ctrls = self.tabs[tabId].frame.ctrls return ctrls.last, ctrls.pos end end function kit:AddControl(id, cType, column, ...) assert(isGuiObject(self), "Must be called on a valid object") local frame = self.tabs[id].frame if (not frame.ctrls) then frame.ctrls = { pos = 0 } end local cpos = frame.ctrls.pos + 1 frame.ctrls.pos = cpos local ctrl = { kids = {} } frame.ctrls[cpos] = ctrl local last = frame.ctrls.last local control local kids = ctrl.kids local kpos = 0 local framewidth = frame:GetWidth() - 20 column = (column or 0) * framewidth local colwidth = nil if (self.scalewidth) then colwidth = math.min(framewidth-column, (self.scalewidth or 1) * framewidth) self.scalewidth = nil end local content = self.tabs[id].content local el if (cType == "Header") then el = content:CreateFontString(nil, "OVERLAY", "GameFontNormalHuge") local fontFile = el:GetFont() el:SetFont(fontFile, 15) kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last) local text = ... el:SetText(text) last = el elseif (cType == "Subhead") then el = content:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge") local fontFile = el:GetFont() el:SetFont(fontFile, 13) el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, column+15, colwidth, nil, -10) local text = ... el:SetText(text) last = el elseif (cType == "Note") then local level, width, height, text = ... local indent = 10 * (level or 1) el = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") local fontFile = el:GetFont() el:SetFont(fontFile, 10) el:SetJustifyH("LEFT") el:SetJustifyV("TOP") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, column+indent+15, width, height, nil) el:SetText(text) control = el last = el elseif (cType == "Label") then local level, setting, text = ... local indent = 10 * (level or 1) el = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, column+indent+15, colwidth) el:SetText(text) if (setting) then el.hit = CreateFrame("Button", nil, content) el.hit.parent = el el.hit:SetAlpha(0.3) el.hit:SetPoint("TOPLEFT", el, "TOPLEFT", -2, 2) el.hit:SetPoint("BOTTOMRIGHT", el, "BOTTOMRIGHT", 2, -2) --el.hit:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") el.hit:SetHighlightTexture("Interface\\FriendsFrame\\UI-FriendsFrame-HighlightBar") el.hit.setting = setting el.hit.stype = "Button"; el.hit:SetScript("OnClick", function(...) self:ChangeSetting(...) end) el.hit:Show() end control = el last = el elseif (cType == "Custom") then local level, el = ... local indent = 10 * (level or 1) kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, column + indent + 15, colwidth) control = el last = el elseif (cType == "Text") then local level, setting, label = ... local indent = 10 * (level or 1) -- FontString el = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 10 + column + indent) el:SetText(label) last = el -- Editbox el = CreateFrame("EditBox", lib.CreateAnonName(), content, "InputBoxTemplate") lib:TabLink(frame, el) kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 20 + column + indent, colwidth or 160, 32, 4) el.setting = setting el.stype = "EditBox" el:SetAutoFocus(false) self:GetSetting(el) el:SetScript("OnEditFocusGained", function(...) self.curFocus = el end) el:SetScript("OnEditFocusLost", function(...) self.curFocus = nil self:ChangeSetting(...) end) el:SetScript("OnEscapePressed", kit.Unfocus) el:SetScript("OnEnterPressed", kit.Unfocus) self.elements[setting] = el el.textEl = last control = el last = el elseif (cType == "Selectbox") then local level, list, setting, text = ... local indent = 10 * (level or 1) -- Selectbox local tmpName = lib.CreateAnonName() if (type(list) ~= "function") then local listVar = list if (type(list) == "table") then list = function() return listVar end else list = function() return self.getter(listVar) end end end local SelectBox = LibStub:GetLibrary("SelectBox") el = SelectBox:Create(tmpName, content, 140, function(...) self:ChangeSetting(...) end, list, "Default") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, column+indent - 5, colwidth or 140, 22, 4) el.list = list el.setting = setting el.stype = "SelectBox"; el.clearance = 10 self.elements[setting] = el self:GetSetting(el) control = el last = el elseif (cType == "Button") then local level, setting, text = ... local indent = 10 * (level or 1) -- Button el = CreateFrame("Button", nil, content, "OptionsButtonTemplate") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 10 + column + indent, colwidth or 80, 22, 4) el.setting = setting el.stype = "Button"; el:SetScript("OnClick", function(...) self:ChangeSetting(...) end) el:SetText(text) el:SetWidth(math.max(el:GetWidth(), el:GetFontString():GetStringWidth() + 16)) control = el last = el elseif (cType == "Checkbox") then local level, setting, text, singleLine, maxLabelLength = ... if ( maxLabelLength and maxLabelLength <= 1 ) then maxLabelLength = maxLabelLength * framewidth - 25 end local indent = 10 * (level or 1) -- CheckButton el = CreateFrame("CheckButton", nil, content, "OptionsCheckButtonTemplate") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 10 + column + indent, 22, 22, 4) el.setting = setting el.stype = "CheckButton" self:GetSetting(el) el:SetScript("OnClick", function(...) self:ChangeSetting(...) end) self.elements[setting] = el control = el -- FontString el = control:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el if (colwidth) then colwidth = colwidth - 15 end anchorPoint(content, el, last, 35+column+indent, (colwidth or maxLabelLength), (singleLine and 14)) el:SetText(text) local textWidth = el:GetStringWidth()+25 control:SetHitRectInsets(-2,-textWidth, -2,-2) control.textEl = el last = el elseif (cType == "Slider" or cType == "WideSlider" or cType == "TinySlider" or cType == "NumeriSlider" or cType == "NumeriWide" or cType == "NumeriTiny") then local swidth = colwidth or 140 if (cType == "WideSlider" or cType == "NumeriWide") then swidth = 260 end if (cType == "TinySlider" or cType == "NumeriTiny") then swidth = 80 end local hasNumber = false local nwidth = 0 if (cType == "NumeriSlider" or cType == "NumeriWide" or cType == "NumeriTiny") then hasNumber = true nwidth = 40 end local level, setting, min, max, step, text, fmtfunc = ... local indent = 10 * (level or 1) -- FontString el = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, swidth+nwidth + 20 + column+indent) el:SetText(text) local textElement = el -- Slider local tmpName = lib.CreateAnonName() el = CreateFrame("Slider", tmpName, content, "OptionsSliderTemplate") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 13 + column + indent, swidth, 20, 4) _G[tmpName.."Low"]:SetText("") _G[tmpName.."High"]:SetText("") el.setting = setting el.textFmt = text el.fmtFunc = fmtfunc el.textEl = textElement el.stype = "Slider" el.step = step * 100 el:SetMinMaxValues(min * 100, max * 100) el:SetValueStep(step*100) el:SetHitRectInsets(0,0,0,0) local slave el:EnableMouseWheel(true) el:SetScript("OnEnter", kit.CaptureKeys) el:SetScript("OnLeave", kit.ReleaseKeys) el:SetScript("OnKeyUp", kit.KeyPress) el:EnableKeyboard(false) el:SetScript("OnMouseWheel", kit.MouseScroll) self:GetSetting(el) -- We need to have set or the hasNumber part will always start at 0 if hasNumber then local slaveName = lib.CreateAnonName() slave = CreateFrame("EditBox", slaveName, el, "InputBoxTemplate") lib:TabLink(frame, slave) slave:SetPoint("LEFT", el, "RIGHT", 8,0) slave:SetWidth(40) slave:SetHeight(32) slave:SetScale(0.8) slave:SetScript("OnEditFocusGained", function(...) self.curFocus = slave slave.hasFocus = true end) slave:SetScript("OnEditFocusLost", function(...) local myMin, myMax = slave.minValue, slave.maxValue local myVal = math.min(myMax, math.max(myMin, tonumber(slave:GetNumber()) or 0)) if (el:GetValue() ~= myVal*100) then el:SetValue(myVal*100) end slave:SetNumber((el:GetValue())/100) slave.hasFocus = false self.curFocus = nil end) slave:SetScript("OnEscapePressed", kit.Unfocus) slave:SetScript("OnEnterPressed", kit.Unfocus) slave:SetAutoFocus(false) slave:Show() slave.minValue = min slave.maxValue = max slave.element = el slave:SetNumber((el:GetValue())/100) el.slave = slave end el:SetScript("OnValueChanged", function(...) self:ChangeSetting(...) if (slave) then local myVal = el:GetValue() if slave:GetNumber() ~= myVal/100 then slave:SetNumber(myVal/100) end end end) self.elements[setting] = el control = el last = textElement elseif (cType == "NumberBox" or cType == "TinyNumber") then local level, setting, minVal, maxVal, label = ... local indent = 10 * (level or 1) local defWidth = 80 if (cType == "TinyNumber") then defWidth = 40 end -- FontString el = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 10+column+indent) el:SetText(label) last = el -- Editbox el = CreateFrame("EditBox", lib.CreateAnonName(), content, "InputBoxTemplate") lib:TabLink(frame, el) kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 20+column+indent, colwidth or defWidth, 32, 4) el.setting = setting el.stype = "EditBox" el:SetAutoFocus(false) el:SetNumeric(true); el.minValue = minVal; el.maxValue = maxVal; el.Numeric = true; self:GetSetting(el) el:SetScript("OnEditFocusGained", function(...) self.curFocus = el end) el:SetScript("OnEditFocusLost", function(...) self.curFocus = nil self:ChangeSetting(...) end) el:SetScript("OnEscapePressed", kit.Unfocus) el:SetScript("OnEnterPressed", kit.Unfocus) self.elements[setting] = el el.textEl = last control = el last = el elseif (cType == "MoneyFrame" or cType == "PinnedMoney" or cType == "MoneyFramePinned") then local level, setting, minVal, maxVal, label = ... if (cType == "MoneyFrame") then label, minVal, maxVal = minVal, nil, nil end local indent = 10 * (level or 1) -- FontString if label then el = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 10+column+indent) el:SetText(label) last = el end -- MoneyFrame local frameName = lib.CreateAnonName(); el = CreateFrame("Frame", frameName, content, "MoneyInputFrameTemplate") el.isMoneyFrame = true -- NOTE - ccox - 2007-11-18 -- TabLink is not working correctly, causes an error when tabbing out of copper field in money input frame -- lib:TabLink(frame, el) -- for the time being, set to cycle around the fields of the current money frame MoneyInputFrame_SetPreviousFocus(el, _G[frameName.."Copper"]) MoneyInputFrame_SetNextFocus(el, _G[frameName.."Gold"]) local cur = el local MoneyInputFrame_SetOnValueChangedFunc = MoneyInputFrame_SetOnvalueChangedFunc or MoneyInputFrame_SetOnValueChangedFunc -- WotLK Hack MoneyInputFrame_SetOnValueChangedFunc(el, function() self:ChangeSetting(cur) end); kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 20+column+indent, colwidth or 160, 32, 4) el.frameName = frameName; el.setting = setting; el.stype = cType el.clearance = -10 el.minValue = minVal; el.maxValue = maxVal; self:GetSetting(el) self.elements[setting] = el el.textEl = last control = el last = el elseif (cType == "ColorSelect" or cType == "ColorSelectAlpha") then local level, setting, text = ... local indent = 10 * (level or 1) -- ColorSelect el = CreateFrame("Button", nil, content) kpos = kpos+1 kids[kpos] = el anchorPoint(content, el, last, 10 + column + indent, 26, 22, -2) el.setting = setting el.stype = cType el:SetWidth(24) el:SetHeight(16) el.r, el.g, el.b, el.a = 0,0,0,1 el.bg = el:CreateTexture(nil, "BORDER"); el.bg:SetAllPoints(el) el.bg:SetTexture("Interface\\TargetingFrame\\BarFill2") el.bg:SetAlpha(0.7) el.tex = el:CreateTexture(nil, "ARTWORK"); el.tex:SetAllPoints(el) el.tex:SetTexture(r,g,b,a) self:GetSetting(el) el:SetScript("OnClick", function(obj, ...) local f = ColorPickerFrame f:SetColorRGB(obj.r, obj.g, obj.b); if obj.stype == "ColorSelectAlpha" then f.hasOpacity = true f.opacity = obj.a else f.hasOpacity = false end f.func = function() self:ChangeSetting(obj) end f.opacityFunc = f.func f:SetFrameStrata("TOOLTIP") f:SetToplevel("TRUE") f:Show() end) self.elements[setting] = el control = el -- FontString el = control:CreateFontString(nil, "OVERLAY", "GameFontHighlight") el:SetJustifyH("LEFT") kpos = kpos+1 kids[kpos] = el if (colwidth) then colwidth = colwidth - 15 end anchorPoint(content, el, last, 35+column+indent, maxLabelLength, 16, -2) el:SetText(text) local textWidth = el:GetStringWidth()+25 control:SetHitRectInsets(-2,-textWidth, -2,-2) control.textEl = el last = el end if last ~= control then last.control = control end self:SetLast(id, last) return control end function kit:GetLast(id) assert(isGuiObject(self), "Must be called on a valid object") if (self.tabs[id] and self.tabs[id].frame.ctrls) then return self.tabs[id].frame.ctrls.last end end function kit:SetLast(id, last) assert(isGuiObject(self), "Must be called on a valid object") if (self.tabs[id] and self.tabs[id].frame.ctrls) then self.tabs[id].frame.ctrls.last = last end end function kit:ActivateTab(id) assert(isGuiObject(self), "Must be called on a valid object") id = tonumber(id) if not id then id = self.id end if not self.tabs then self = self:GetParent() end if not self.tabs then return error("Must call ActivateTab from a valid Configator object") end if (self.tabs.active) then self.tabs[self.tabs.active].frame:Hide() end local tab = self.tabs[id] tab.frame:Show() self.tabs.active = id self.config.selectedCat = tab.catId self.config.selectedTab = tab.tabName if not self.config.cats[tab.catId].isOpen then self.config.cats[tab.catId].isOpen = true self:RegenTabs() else self:RenderTabs() end if self.expandOnActivate then self:ExpandFrame(id) end end function kit:GetTabByName(tabName, catId) if not catId then catId = self.config.current or "" end if self.config.tabs[catId] then local id = self.config.tabs[catId][tabName] if id then return self.tabs[id], id end end end function kit:GetTabById(id) if id then return self.tabs[id] end end function kit:Refresh() assert(isGuiObject(self), "Must be called on a valid object") for name, el in pairs(self.elements) do self:GetSetting(el) end end function kit:Resave() assert(isGuiObject(self), "Must be called on a valid object") for name, el in pairs(self.elements) do self:ChangeSetting(el) end end function kit:ClearFocus() if self.curFocus then self.curFocus:ClearFocus() end end function kit:GetSetting(element) assert(isGuiObject(self), "Must be called on a valid object") assert(isGuiObject(element), "You must pass a valid element") local setting = element.setting local value = self.getter(setting) if (element.stype == "CheckButton") then element:SetChecked(value or false) elseif (element.stype == "EditBox") then if (element.Numeric) then oldvalue = value or 0; value = oldvalue; if (element.minValue) then value = math.max( value, element.minValue ); end if (element.maxValue) then value = math.min( value, element.maxValue ); end element:SetNumber(value); -- notify that we have pinned the value if (value ~= oldvalue) then self.setter(setting, value); end else element:SetText(value or "") end elseif (element.stype == "SelectBox") then element.value = value element:UpdateValue() elseif (element.stype == "Button") then elseif (element.stype == "Slider") then value = tonumber(value) or 0 element:SetValue(value*100) if (element.fmtFunc) then element.textEl:SetText(string.format(element.textFmt, element.fmtFunc(value))) else element.textEl:SetText(string.format(element.textFmt, value)) end elseif (element.stype == "MoneyFrame") then MoneyInputFrame_ResetMoney(element) MoneyInputFrame_SetCopper(element, tonumber(value) or 0); elseif (element.stype == "MoneyFramePinned") then oldvalue = tonumber(value) or 0; value = oldvalue; if (element.minValue) then value = math.max( value, element.minValue ); end if (element.maxValue) then value = math.min( value, element.maxValue ); end MoneyInputFrame_ResetMoney(element) MoneyInputFrame_SetCopper( element, value ); -- update with pinned value if (oldvalue ~= value) then self.setter(setting, value); end elseif (element.stype == "ColorSelect" or element.stype == "ColorSelectAlpha") then local r, g, b, a = strsplit(",", tostring(value) or "0,0,0,1") element.r = tonumber(r) or 0 element.g = tonumber(g) or 0 element.b = tonumber(b) or 0 element.a = tonumber(a) or 1 if element.tex then element.tex:SetTexture(element.r,element.g,element.b,element.a) end else value = element:GetValue() end end function kit:ChangeSetting(element, ...) assert(isGuiObject(self), "Must be called on a valid object") assert(isGuiObject(element), "You must pass a valid element") assert(element.stype, "You must pass a valid Configator settings object") local setting = element.setting local value if (element.stype == "CheckButton") then value = element:GetChecked() if (value) then value = true else value = false end elseif (element.stype == "EditBox") then if (element.Numeric) then oldvalue = element:GetNumber() or 0; value = oldvalue; if (element.minValue) then value = math.max( value, element.minValue ); end if (element.maxValue) then value = math.min( value, element.maxValue ); end -- update the text field with pinned value if (oldvalue ~= value) then element:SetNumber(value); end else value = element:GetText() or "" end elseif (element.stype == "SelectBox") then value = select(2, ...) elseif (element.stype == "Button") then value = true elseif (element.stype == "Slider") then value = (element:GetValue())/100 or 0 if (element.fmtFunc) then element.textEl:SetText(string.format(element.textFmt, element.fmtFunc(value))) else element.textEl:SetText(string.format(element.textFmt, value)) end elseif (element.stype == "MoneyFrame") then value = MoneyInputFrame_GetCopper( element ); elseif (element.stype == "MoneyFramePinned") then oldvalue = MoneyInputFrame_GetCopper( element ); value = oldvalue; if (element.minValue) then value = math.max( value, element.minValue ); end if (element.maxValue) then value = math.min( value, element.maxValue ); end -- update with pinned value if (oldvalue ~= value) then MoneyInputFrame_SetCopper( element, value ); end elseif (element.stype == "ColorSelect" or element.stype == "ColorSelectAlpha") then if (ColorPickerFrame:IsVisible()) then local r,g,b = ColorPickerFrame:GetColorRGB() local a = 1 if element.stype == "ColorSelectAlpha" then a = OpacitySliderFrame:GetValue() end element.r = tonumber(r) or 0 element.g = tonumber(g) or 0 element.b = tonumber(b) or 0 element.a = tonumber(a) or 1 end value = ("%0.3f,%0.3f,%0.3f,%0.3f"):format(element.r,element.g,element.b,element.a) if element.tex then element.tex:SetTexture(element.r,element.g,element.b,element.a) end elseif element.GetValue then value = element:GetValue() else return end self.setter(setting, value) end function kit:ColumnCheckboxes(id, cols, options) assert(isGuiObject(self), "Must be called on a valid object") local last, cont, el, setting, text last = self:GetLast(id) local optc = table.getn(options) local rows = math.ceil(optc / cols) local row, col = 0, 0 cont = nil for pos, option in ipairs(options) do setting, text = unpack(option) col = math.floor(row / rows) el = self:AddControl(id, "Checkbox", col/cols, 1, setting, text, true, 1/cols) row = row + 1 if (row % rows == 0) then if (col == 0) then cont = el end self:SetLast(id, last) end end self:SetLast(id, cont) end function kit:SetEscSensitive(setting) assert(isGuiObject(self), "Must be called on a valid object") local name = self:GetName() for i = #UISpecialFrames, 1, -1 do if (name == UISpecialFrames[i]) then table.remove(UISpecialFrames, i) end end if (setting) then table.insert(UISpecialFrames[i], name) end end SlashCmdList["LIB_CONFIGATOR"] = function( msg ) msg = msg:trim():lower() if ( msg == "reset" ) then for _, dialog in pairs(lib.frames) do dialog:ClearAllPoints() dialog:SetPoint("CENTER", "UIParent", "CENTER") end end end SLASH_LIB_CONFIGATOR1 = "/configator";