--[[ Name: Waterfall-1.0 Revision: $Revision: 78661 $ Author(s): Nargiddley (nargiddley@gmail.com) Inspired By: Dewdrop by ckknight Website: http://www.wowace.com/wiki/Waterfall-1.0 Documentation: http://www.wowace.com/wiki/Waterfall-1.0 SVN: http://svn.wowace.com/wowace/trunk/Waterfall-1.0 Description: Gui Configuration Library License: LGPL 2.1 Dependencies: AceOO-2.0 ]] local MAJOR_VERSION = "Waterfall-1.0" local MINOR_VERSION = "$Revision: 78663 $" local CONTROL_LIMIT = 250 local _ local tinsert = table.insert local tremove = table.remove local table_setn = table.setn if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end if not AceLibrary:HasInstance("AceOO-2.0") then error(MAJOR_VERSION .. " requires AceOO-2.0.") end local AceOO = AceLibrary("AceOO-2.0") local Waterfall = {} local currentframe local OPTIONS = "Options" local ARROW = " -> " local ARE_YOU_SURE = "Are you sure you want to %s?" if GetLocale() == "zhCN" then OPTIONS = "选项" ARROW = " -> " ARE_YOU_SURE = "你确定要 %s么?" elseif GetLocale() == "zhTW" then OPTIONS = "選項" ARROW = " > " ARE_YOU_SURE = "你確定要「%s」嗎?" elseif GetLocale() == "koKR" then OPTIONS = "설정" ARE_YOU_SURE = "정말 당신은 `%s'|1을;를; 하시겠습니까?" elseif GetLocale() == "ruRU" then OPTIONS = "Опции" ARE_YOU_SURE = "Вы уверены что вы хотите %s?" end local DEFAULT_CONTROL_WIDTH = 180 --[[ local print = Sea.io.print local function printTable(table,rowname,level) if ( level == nil ) then level = 1; end if ( type(rowname) == "nil" ) then rowname = "ROOT"; elseif ( type(rowname) == "string" ) then rowname = "\""..rowname.."\""; elseif ( type(rowname) ~= "number" ) then rowname = "*"..type(rowname).."*"; end local msg = ""; if ( table == nil ) then Sea.io.print(msg,"[",rowname,"] := nil "); return end if ( type(table) == "table" and level > 0 ) then Sea.io.print (msg,rowname," { "); for k,v in table do if v == nil then Sea.io.print(msg,"[",rowname,"] := nil "); end printTable(v,k,level-1); end Sea.io.print(msg,"}"); elseif (type(table) == "function" ) then Sea.io.print(msg,"[",rowname,"] => {{FunctionPtr*}}"); elseif (type(table) == "userdata" ) then Sea.io.print(msg,"[",rowname,"] => {{UserData}}"); elseif (type(table) == "boolean" ) then local value = "true"; if ( not table ) then value = "false"; end Sea.io.print(msg,"[",rowname,"] => ",value); else Sea.io.print(msg,"[",rowname,"] => ",table); end end ]] --[[ Passing an AceOO class will get an object of that class from the pool You dont need to specify the class when releasing it Passing a string will get a table from a named pool the string must be passed when releasing tables like this --]] local getObj, releaseObj do local objPools = {} function getObj(class,...) if not objPools[class] then objPools[class] = {} end local newObj = tremove(objPools[class]) if not newObj then if type(class) ~= "string" and type(class.new) == "function" then newObj = class:new(unpack(arg)) else newObj = {} end end return newObj end function releaseObj(obj,class) if not class then class = obj.class end if not class then error("You must supply a class to release non AceOO objects") end if not objPools[class] then objPools[class] = {} end if type(obj.CleanUp) == "function" then obj:CleanUp() end tinsert(objPools[class],obj) end end --[[ --borrowed from Dewdrop-2.0 local function new(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) local t = {} if k1 then t[k1] = v1 if k2 then t[k2] = v2 if k3 then t[k3] = v3 if k4 then t[k4] = v4 if k5 then t[k5] = v5 if k6 then t[k6] = v6 if k7 then t[k7] = v7 if k8 then t[k8] = v8 if k9 then t[k9] = v9 if k10 then t[k10] = v10 if k11 then t[k11] = v11 if k12 then t[k12] = v12 if k13 then t[k13] = v13 if k14 then t[k14] = v14 if k15 then t[k15] = v15 if k16 then t[k16] = v16 if k17 then t[k17] = v17 if k18 then t[k18] = v18 if k19 then t[k19] = v19 if k20 then t[k20] = v20 end end end end end end end end end end end end end end end end end end end end return t end local tmp do local t function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) for k in pairs(t) do t[k] = nil end if type(k1) == "table" then for k,v in pairs(k1) do t[k] = v end else if k1 then t[k1] = v1 if k2 then t[k2] = v2 if k3 then t[k3] = v3 if k4 then t[k4] = v4 if k5 then t[k5] = v5 if k6 then t[k6] = v6 if k7 then t[k7] = v7 if k8 then t[k8] = v8 if k9 then t[k9] = v9 if k10 then t[k10] = v10 if k11 then t[k11] = v11 if k12 then t[k12] = v12 if k13 then t[k13] = v13 if k14 then t[k14] = v14 if k15 then t[k15] = v15 if k16 then t[k16] = v16 if k17 then t[k17] = v17 if k18 then t[k18] = v18 if k19 then t[k19] = v19 if k20 then t[k20] = v20 end end end end end end end end end end end end end end end end end end end end end return t end local x = tmp function tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) t = {} tmp = x x = nil return tmp(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) end end local tmp2 do local t function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) for k in pairs(t) do t[k] = nil end if k1 then t[k1] = v1 if k2 then t[k2] = v2 if k3 then t[k3] = v3 if k4 then t[k4] = v4 if k5 then t[k5] = v5 if k6 then t[k6] = v6 if k7 then t[k7] = v7 if k8 then t[k8] = v8 if k9 then t[k9] = v9 if k10 then t[k10] = v10 if k11 then t[k11] = v11 if k12 then t[k12] = v12 if k13 then t[k13] = v13 if k14 then t[k14] = v14 if k15 then t[k15] = v15 if k16 then t[k16] = v16 if k17 then t[k17] = v17 if k18 then t[k18] = v18 if k19 then t[k19] = v19 if k20 then t[k20] = v20 end end end end end end end end end end end end end end end end end end end end return t end local x = tmp2 function tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) t = {} tmp2 = x x = nil return tmp2(k1, v1, k2, v2, k3, v3, k4, v4, k5, v5, k6, v6, k7, v7, k8, v8, k9, v9, k10, v10, k11, v11, k12, v12, k13, v13, k14, v14, k15, v15, k16, v16, k17, v17, k18, v18, k19, v19, k20, v20) end end ]] local function new(...) local t = {} for i = 1, table.getn(arg), 2 do local k = arg[i] if k then t[k] = arg[i+1] else break end end return t end local tmp do local t = {} function tmp(...) for k in pairs(t) do t[k] = nil end table_setn(t,0) for i = 1, table.getn(arg), 2 do local k = arg[i] if k then t[k] = arg[i+1] else break end end return t end end local tmp2 do local t = {} function tmp2(...) for k in pairs(t) do t[k] = nil end table_setn(t,0) for i = 1, table.getn(arg), 2 do local k = arg[i] if k then t[k] = arg[i+1] else break end end return t end end local function getArgs(t, str, num, ...) local x = t[str .. num] if x == nil then return unpack(arg) else return x, getArgs(t, str, num + 1, unpack(arg)) end end local function confirmPopup(message, func, ...) if not StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] then StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] = {} end local t = StaticPopupDialogs["WATERFALL01_CONFIRM_DIALOG"] for k in pairs(t) do t[k] = nil end table_setn(t,0) t.text = message t.button1 = ACCEPT t.button2 = CANCEL t.OnAccept = function() func(unpack(t)) end for i = 1, table.getn(arg) do t[i] = arg[i] end t.timeout = 0 t.whileDead = 1 t.hideOnEscape = 1 StaticPopup_Show("WATERFALL01_CONFIRM_DIALOG") end local function showGameTooltip(this) if this.tooltipTitle or this.tooltipText then GameTooltip_SetDefaultAnchor(GameTooltip, this.frame or this) if this.tooltipTitle then GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1) if this.tooltipText then GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1) end else GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1) end GameTooltip:Show() end end --end of borrowing local function parseList(list) local token, rest = string.match(list,"([^%s]+)%s+(.+)") if not rest then return (tonumber(list) or list) else return (tonumber(token) or token), parseList(rest) end end local function compareOptions(a,b) if not a then return true end if not b then return false end local A, B = a.order or 100, b.order or 100 if A == B then local NameA = a.guiName or a.name or "" local NameB = b.guiName or b.name or "" return string.upper(NameA) < string.upper(NameB) end if A < 0 then if B > 0 then return false end else if B < 0 then return true end end return A < B end function Waterfall:Register(id,...) local settings = new(unpack(arg)) if settings.aceOptions then assert(type(settings.aceOptions) == "table","\"aceOptions\" must be a table") else assert(type(settings.tree) == "table","\"tree\" must be a table") assert(type(settings.children) == "function","\"children\" must be a function") end if not settings.title then settings.title = id end if settings.treeType == "SECTIONS" then settings.hideTreeRoot = true settings.treeLevels = 3 end if self.registry[id] then self:UnRegister(id) end self.registry[id] = settings end function Waterfall:UnRegister(id) self.registry[id] = nil end function Waterfall:IsRegistered(id) return not not self.registry[id] end local function releaseTree(tree) for i, v in ipairs(tree) do tree[i] = nil releaseTree(v) releaseObj(v,"table") end for k, v in pairs(tree) do tree[k] = nil end table_setn(tree,0) end local function checkNegatable(attr,handler,param) local ret if type(attr) == "function" then ret = attr(param) elseif type(attr) == "string" and handler then local neg = string.match(attr,"^~(.+)$") if neg then attr = neg end ret = handler[attr](handler,param) if neg then ret = not ret end else ret = attr end return ret end local mysort = function(a, b) local alpha, bravo = a, b local alpha_order = alpha and alpha.order or 100 local bravo_order = bravo and bravo.order or 100 local alpha_name = alpha and (alpha.guiName or alpha.name) local bravo_name = bravo and (bravo.guiName or bravo.name) if alpha_order == bravo_order then if not alpha_name then return bravo_name elseif not bravo_name then return false else return string.upper(string.gsub(string.gsub(alpha_name,"|c%x%x%x%x%x%x%x%x", ""), "|r", "")) < string.upper(string.gsub(string.gsub(bravo_name,"|c%x%x%x%x%x%x%x%x", ""),"|r", "")) end else if alpha_order < 0 then if bravo_order > 0 then return false end else if bravo_order < 0 then return true end end return alpha_order < bravo_order end end local function buildAceOptionsTree(tree, status, options, numLevels, path, rootHandler) local settings = Waterfall.registry[currentframe.id] local handler = options.handler or rootHandler local bargs = options.args assert(type(bargs) == "table",string.format("args must be a table: Current Path:%s",(path or "."))) local root if path then tree.id = path tree.text = options.guiName or options.name or (options.handler and options.handler.name) or path tree.name = tree.text tree.tooltipTitle = tree.text tree.tooltipText = options.desc tree.order = options.order tree.disabled = checkNegatable(options.disabled, handler, options.passValue) tree.handler = handler tree.icon = options.icon tree.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue) local hasOptions for k, item in pairs(bargs) do if item.type ~= "group" and item.type ~= "heading" then if not checkNegatable(item.wfHidden or item.guiHidden or item.hidden,handler, item.passValue) then hasOptions = true end end end tree.hasOptions = hasOptions else releaseTree(tree) local hasOptions for k, item in pairs(bargs) do if item.type ~= "group" and item.type ~= "heading" then hasOptions = true end end if hasOptions then root = getObj("table") root.text = options.guiName or options.name or (options.handler and options.handler.name) or path root.name = tree.text root.tooltipTitle = tree.text root.tooltipText = options.desc root.order = options.order root.disabled = checkNegatable(options.disabled, handler, options.passValue) root.handler = handler root.icon = options.icon root.hidden = checkNegatable(options.wfHidden or options.guiHidden or options.hidden,handler, options.passValue) root.id = "." root.hasOptions = true if status['.'] == nil then status['.'] = true end tinsert(tree,root) if not settings.hideTreeRoot then tree = root end end settings.treeRootShown = hasOptions and not settings.hideTreeRoot end if not numLevels or numLevels > 1 then for k, item in pairs(bargs) do if item.type == "group" then local child = getObj("table") local nextPath if path then nextPath = path.."."..k else nextPath = k end if numLevels then buildAceOptionsTree(child, status, item,numLevels - 1,nextPath, handler) else buildAceOptionsTree(child, status, item,nil,nextPath, handler) end tinsert(tree,child) end end end table.sort(tree, mysort) -- table.sort(tree,compareOptions) end local function checkAceOptionsPath(path, options, frame) local t = options local validpath for k in string.gfind(path,"[^\.]+") do if tonumber(k) and t.args[tonumber(k)] then k = tonumber(k) end if t.args and t.args[k] then t = t.args[k] if validpath then validpath = validpath..'.'..k else validpath = k end else if frame then frame:SetSelected(validpath, true) end break end end return validpath or '.' end local function feedFromOptionsTree(paneid, options) if not paneid then return end local settings = Waterfall.registry[currentframe.id] paneid = checkAceOptionsPath(paneid, options, settings.frame) local curlevel = 1 local levels = 1 for x in string.gfind(paneid,"[^%.]+") do curlevel = curlevel + 1 end if settings.treeLevels == curlevel then levels = nil end if paneid == "." then Waterfall:FeedAceOptionsTable(options, nil, levels) return options.name or (options.handler and options.handler.name) or OPTIONS else local crumbs if settings.treeRootShown then crumbs = options.name or (options.handler and options.handler.name) or OPTIONS end local t = options Waterfall:FeedAceOptionsTable(options,paneid,levels) for k in string.gfind(paneid,"[^\.]+") do if tonumber(k) and t.args[tonumber(k)] then k = tonumber(k) end if t.args and t.args[k] then t = t.args[k] if crumbs then crumbs = crumbs..ARROW..(t.guiName or t.name or "") else crumbs = (t.guiName or t.name or "") end else Waterfall:error("Invalid path \"%s\" for Aceoptions table",paneid) end end return crumbs end end local old_CloseSpecialWindows function Waterfall:Open(id, pane) if not old_CloseSpecialWindows then old_CloseSpecialWindows = CloseSpecialWindows CloseSpecialWindows = function() local found = old_CloseSpecialWindows() return self:CloseAll() or found end end local info = self.registry[id] if not info then error("You cannot open a waterfall without registering it first") end --already open if info.frame then if pane then info.frame:SetSelected(pane) end return end local frame = getObj(WaterfallFrame) --WaterfallFrame:new() frame:SetID(id) frame.lib = self info.frame = frame if info.title then frame:SetTitle(info.title) end local r,g,b = info.colorR or 1, info.colorG or 0.6, info.colorB or 0 frame:SetColor(r,g,b) if info.aceOptions then if not info.tree then info.tree = {} end if not info.treestatus then info.treestatus = {} end currentframe = frame frame:SetTree(info.tree, info.treestatus, buildAceOptionsTree, info.aceOptions, info.treeLevels) currentframe = nil frame:SetChildren(feedFromOptionsTree, info.aceOptions) if pane then frame.treeview:Collapse() end frame:SetSelected(pane or info.defaultPane or ".") frame.treeview:SetType(info.treeType) else if not info.treestatus then info.treestatus = {} end frame:SetTree(info.tree, info.treestatus) frame:SetChildren(info.children) if pane or info.defaultPane then frame:SetSelected(pane or info.defaultPane) end frame.treeview:SetType(info.treeType) end if info.height then frame:SetHeight(info.height) end if info.width then frame:SetWidth(info.width) end frame:Refresh() frame:Show() end function Waterfall:IsOpen(id) return not not self.registry[id].frame end function Waterfall:Refresh(id) local info = self.registry[id] if not info then error("You can't refresh a waterfall without registering it first") end if info.frame then info.frame:Refresh() end end function Waterfall:SetSize(id, width, height) local info = self.registry[id] if not info then error("You refresh open a waterfall without registering it first") end if info.frame then info.frame:SetWidth(width) info.frame:SetHeight(height) end end local function setCommonAttributes(control,info) control.width = info.width control.height = info.height control.noNewLine = info.noNewLine control.causesRefresh = info.causesRefresh control.fullRefresh = info.fullRefresh control.treeRefresh = info.treeRefresh control.tooltipTitle = info.tooltipTitle control.tooltipText = info.tooltipText control:SetText(info.text or "") end function Waterfall:AddControl(...) currentframe.controlcount = currentframe.controlcount + 1 if not currentframe then error("AddControl must be called from within a children function") end local control local info = self.registry[currentframe.id] local limit = info.controlLimit or CONTROL_LIMIT if currentframe.controlcount > limit then if currentframe.controlcount == limit + 1 then control = getObj(WaterfallLabel) control.r = 1 control.g = 0 control.b = 0 control:SetText(("More than %s Controls in current pane....."):format(limit)) control:Refresh() currentframe:AddControl(control) end return end local info = tmp2(unpack(arg)) if info.type == "label" then control = getObj(WaterfallLabel) setCommonAttributes(control,info) control.justifyH = info.justifyH control.r = info.r control.g = info.g control.b = info.b control:Refresh() elseif info.type == "dragLink" then control = getObj(WaterfallDragLink) setCommonAttributes(control,info) control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control:Refresh() elseif info.type == "linklabel" then control = getObj(WaterfallLinklabel) setCommonAttributes(control,info) control.justifyH = info.justifyH control.r = info.r control.g = info.g control.b = info.b control:SetFunc("link",info.linkFunc,getArgs(info,"linkArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control.confirm = info.confirm control:Refresh() elseif info.type == "button" then control = getObj(WaterfallButton) setCommonAttributes(control,info) control:SetFunc("exec",info.execFunc,getArgs(info,"execArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control.confirm = info.confirm control.link = info.link control:Refresh() elseif info.type == "heading" then control = getObj(WaterfallHeading) control.justifyH = info.justifyH setCommonAttributes(control,info) control:Refresh() elseif info.type == "textbox" then control = getObj(WaterfallTextBox) setCommonAttributes(control,info) control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) control:SetFunc("changed",info.changedFunc,getArgs(info,"changedArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control.toNumber = info.toNumber control.parseList = info.parseList control.setOnTextChanged = info.setOnTextChanged control:SetFunc("validate",info.validateFunc,getArgs(info,"validateArg",1)) control.validateModifies = info.validateModifies control:Refresh() elseif info.type == "dropdown" then control = getObj(WaterfallDropdown) setCommonAttributes(control,info) control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control.parseList = info.parseList control.list = info.list control.columns = info.columns control.strict = info.strict control:Refresh() elseif info.type == "checkbox" then control = getObj(WaterfallCheckBox) setCommonAttributes(control,info) control:SetChecked(info.checked) control.isRadio = info.isRadio control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control:Refresh() elseif info.type == "slider" then control = getObj(WaterfallSlider) setCommonAttributes(control,info) control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) control.min = info.min control.max = info.max control.step = info.step control.bigStep = info.bigStep or info.step control.finalSetOnly = info.finalSetOnly if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control:Refresh() elseif info.type == "color" then control = getObj(WaterfallColorSwatch) setCommonAttributes(control,info) control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control.hasOpacity = info.hasOpacity control.r = info.r control.g = info.g control.b = info.b control.opacity = info.opacity control.hasOpacity = info.hasOpacity control:Refresh() elseif info.type == "keybind" then control = getObj(WaterfallKeybinding) control:SetText("Binding") setCommonAttributes(control,info) control:SetFunc("set",info.setFunc,getArgs(info,"setArg",1)) control:SetFunc("get",info.getFunc,getArgs(info,"getArg",1)) if type(info.disabled) == "function" then control:SetFunc("disabled",info.disabled,getArgs(info,"disabledArg",1)) control.inverseDisabled = info.inverseDisabled else control.disabled = info.disabled end control.keybindingOnly = info.keybindingOnly control.keybindingExcept = info.keybindingExcept control:Refresh() else error("Invalid or missing type") end currentframe:AddControl(control) end local function addDisabled(t,disabled,handler,param) if disabled then if type(disabled) == "function" or type(disabled) == "boolean" then tinsert(t,"disabled") tinsert(t,disabled) if param then tinsert(t,"disabledArg1") tinsert(t,param) end elseif type(disabled) == "string" then local neg = string.match(disabled,"^~(.+)$") if neg then disabled = neg end if handler and handler[disabled] then tinsert(t,"disabled") tinsert(t,handler[disabled]) tinsert(t,"disabledArg1") tinsert(t,handler) if param then tinsert(t,"disabledArg2") tinsert(t,param) end end if neg then tinsert(t,"inverseDisabled") tinsert(t,true) end end end end local function appendArgs(t,name,start,...) for k = 1 , table.getn(arg) do tinsert(t,name.."Arg"..(start + k - 1)) tinsert(t,arg[k]) end end local function addFunction(t,name,func,handler,...) if type(func) == "function" then tinsert(t,name.."Func") tinsert(t,func) appendArgs(t,name,1,unpack(arg)) elseif type(func) == "string" and handler and handler[func] then tinsert(t,name.."Func") tinsert(t,handler[func]) tinsert(t,name.."Arg1") tinsert(t,handler) appendArgs(t,name,2,unpack(arg)) end end local function validateRange(min,max,step,value) value = tonumber(value) if step > 0 then value = math.floor((value - min) / step + 0.5) * step + min end if value >= min and value <= max then return value else return nil end end --[[ Feeds an ace options table into the right pane, must be called within a "children" function root is the root of your options table, this is important if you use any methods for resolving the handler path is the path into the table seperated by "." e.g. "Items.Compress" to feed the table given by root.args.Items.args.Compress maxLevels is how many sub groups to feed as well, 1 is only the current level 2 will feed sub groups etc. nil assumes no limit ]] function Waterfall:FeedAceOptionsTable(root,path,maxLevels) local feedtmp = getObj("table") local feedkeys = getObj("table") local tmpargs = getObj("table") local t local handler = root.handler local groupDisabled = checkNegatable(root.disabled,root.handler,root.passValue) local passSet local passGet if root.pass then passSet = root.set passGet = root.get else passSet = nil passGet = nil end if path then t = root for k in string.gfind(path,"[^\.]+") do if tonumber(k) and t.args[tonumber(k)] then k = tonumber(k) end if t.args and t.args[k] then t = t.args[k] handler = t.handler or handler groupDisabled = checkNegatable(t.disabled,handler,t.passValue) or groupDisabled if t.pass then passSet = t.set passGet = t.get else passSet = nil passGet = nil end else self:error("Invalid path \"%s\" for Aceoptions table",path) end end else t = root end if t.type ~= "group" then self:error("You must feed an ace options group") end for k, v in pairs(t.args) do tinsert(feedtmp, v) feedkeys[v] = k end table.sort(feedtmp, mysort) local lastheader for i, v in ipairs(feedtmp) do --only add headers if they arent immediately followed by a group if lastheader then if v.type ~= "group" then --self:AddControl("type","label","text","","width",80) --self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true) self:AddControl("type","heading","text",lastheader,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER") end lastheader = nil end local hidden if v.guiHidden ~= nil then hidden = v.guiHidden elseif v.wfHidden ~= nil then hidden = v.wfHidden else hidden = v.hidden end hidden = checkNegatable(hidden,v.handler or handler,v.passValue) local name = v.guiName or v.name if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then name = "|cFF808080"..name end if not hidden then if v.type == "toggle" then --self:AddControl("type","label","text","","width",80) tinsert(tmpargs,"type") tinsert(tmpargs,"checkbox") --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) tinsert(tmpargs,"text") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) tinsert(tmpargs,"isRadio") tinsert(tmpargs,not not v.isRadio) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) elseif v.type == "dragLink" then tinsert(tmpargs,"type") tinsert(tmpargs,"dragLink") tinsert(tmpargs,"text") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) tinsert(tmpargs,"tooltipTitle") local tooltipTitle = "" .. tostring(v.itemId) tinsert(tmpargs,tooltipTitle) tinsert(tmpargs,"linkInfo") tinsert(tmpargs,v.linkInfo or {}) tinsert(tmpargs,"icon") tinsert(tmpargs,v.icon or "") tinsert(tmpargs,"iconWidth") tinsert(tmpargs,v.iconWidth or WaterfallDragLink.defaultIconSize) tinsert(tmpargs,"iconHeight") tinsert(tmpargs,v.iconHeight or WaterfallDragLink.defaultIconSize) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler,v.passValue) end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) self:AddControl(unpack(tmpargs)) elseif v.type == "group" then if not maxLevels or maxLevels > 1 then --self:AddControl("type","label","text","","width",80) if checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled then name = "|cFF808080"..name end --self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER","noNewLine",true) self:AddControl("type","heading","text",name,"width",DEFAULT_CONTROL_WIDTH,"justifyH","CENTER") local nextPath local nextMaxLevels if path then nextPath = path.."."..feedkeys[v] else nextPath = feedkeys[v] end if maxLevels then nextMaxLevels = maxLevels - 1 end self:FeedAceOptionsTable(root,nextPath,nextMaxLevels) self:AddControl("type","heading") end elseif v.type == "text" then if type(v.validate) == "table" then if v.multiToggle then self:AddControl("type","heading","text",name) for key, value in pairs(v.validate) do if type(key) == "number" then key = value end self:AddControl("type","label","text","","width",12) tinsert(tmpargs,"type") tinsert(tmpargs,"checkbox") tinsert(tmpargs,"text") tinsert(tmpargs,value) tinsert(tmpargs,"noNewLine") tinsert(tmpargs,true) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,value) if v.passValue then if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v],key) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue,key) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v],key) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue,key) end else if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,feedkeys[v],key) else addFunction(tmpargs,"get",v.get,v.handler or handler,key) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,feedkeys[v],key) else addFunction(tmpargs,"set",v.set,v.handler or handler,key) end end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue,key) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) for i = 0,table.getn(tmpargs) do tmpargs[i] = nil end end else --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") self:AddControl("type","label","text",name) tinsert(tmpargs,"type") tinsert(tmpargs,"dropdown") tinsert(tmpargs,"strict") tinsert(tmpargs,true) --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"list") tinsert(tmpargs,v.validate) tinsert(tmpargs,"columns") tinsert(tmpargs,v.columns) tinsert(tmpargs,"parseList") tinsert(tmpargs, not not v.input) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) end elseif v.validate == "keybinding" then --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") self:AddControl("type","label","text",name) tinsert(tmpargs,"type") tinsert(tmpargs,"keybind") --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end tinsert(tmpargs,"keybindingOnly") tinsert(tmpargs,v.keybindingOnly or false) tinsert(tmpargs,"keybindingExcept") tinsert(tmpargs,v.keybindingExcept or false) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) else --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") self:AddControl("type","label","text",name) tinsert(tmpargs,"type") tinsert(tmpargs,"textbox") --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"parseList") tinsert(tmpargs, not not v.input) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) addFunction(tmpargs,"validate",v.validate,v.handler or handler) self:AddControl(unpack(tmpargs)) end elseif v.type == "execute" then --self:AddControl("type","label","text","","width",80) tinsert(tmpargs,"type") tinsert(tmpargs,"button") --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) tinsert(tmpargs,"text") tinsert(tmpargs,name) tinsert(tmpargs,"confirm") tinsert(tmpargs, v.confirm or false) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) tinsert(tmpargs, "link") tinsert(tmpargs,v.link or false) addFunction(tmpargs,"exec",v.func,v.handler or handler,v.passValue) addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) elseif v.type == "header" then if name and ( checkNegatable(v.disabled,v.handler or handler,v.passValue) or groupDisabled ) then lastheader = "|cFF808080"..name else lastheader = name end elseif v.type == "range" then --self:AddControl("type","label","text",name,"width",80,"justifyH","RIGHT") self:AddControl("type","label","text",name) tinsert(tmpargs,"type") tinsert(tmpargs,"slider") --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) local min,max,step min = v.min or 0 max = v.max or 1 step = v.step or 0 tinsert(tmpargs,"min") tinsert(tmpargs,v.min or 0) tinsert(tmpargs,"max") tinsert(tmpargs,v.max or 1) tinsert(tmpargs,"step") tinsert(tmpargs,v.step or 0) tinsert(tmpargs,"bigStep") tinsert(tmpargs,v.bigStep or v.step or 0) tinsert(tmpargs,"finalSetOnly") tinsert(tmpargs,v.finalSetOnly or false) --if no step is given or there are more than 20 steps possible --local complex = step == 0 or ((max - min) / step) > 20 local complex = true tinsert(tmpargs,"text") tinsert(tmpargs,"") tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) if complex then tinsert(tmpargs,"type") tinsert(tmpargs,"textbox") tinsert(tmpargs,"noNewLine") tinsert(tmpargs,true) tinsert(tmpargs,"toNumber") tinsert(tmpargs,true) tinsert(tmpargs,"validateModifies") tinsert(tmpargs,true) tinsert(tmpargs,"width") tinsert(tmpargs,60) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end addFunction(tmpargs,"validate",validateRange,v.handler or handler,min,max,step) addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"fullRefresh") tinsert(tmpargs,true) self:AddControl(unpack(tmpargs)) end elseif v.type == "color" then --self:AddControl("type","label","text","","width",80) tinsert(tmpargs,"type") tinsert(tmpargs,"color") --tinsert(tmpargs,"noNewLine") --tinsert(tmpargs,true) tinsert(tmpargs,"text") tinsert(tmpargs,name) if passGet and v.get == nil then addFunction(tmpargs,"get",passGet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"get",v.get,v.handler or handler,v.passValue) end if passSet and v.set == nil then addFunction(tmpargs,"set",passSet,v.handler or handler,v.passValue or feedkeys[v]) else addFunction(tmpargs,"set",v.set,v.handler or handler,v.passValue) end addDisabled(tmpargs,groupDisabled or v.disabled,v.handler or handler,v.passValue) tinsert(tmpargs,"tooltipTitle") tinsert(tmpargs,name) tinsert(tmpargs,"tooltipText") tinsert(tmpargs,v.desc or false) tinsert(tmpargs,"hasOpacity") tinsert(tmpargs, not not v.hasAlpha) self:AddControl(unpack(tmpargs)) end for i = 0,table.getn(tmpargs) do tmpargs[i] = nil end table_setn(tmpargs, 0) end end for i = 0,table.getn(feedtmp) do feedtmp[i] = nil end table_setn(feedtmp, 0) for k in pairs(feedkeys) do feedkeys[k] = nil end table_setn(feedkeys, 0) releaseObj(feedtmp,"table") releaseObj(feedkeys,"table") releaseObj(tmpargs,"table") end function Waterfall:Close(id) local info = self.registry[id] if not info then error("You cannot close a waterfall without registering it first") end local frame = info.frame if frame then local OnClose = info.OnClose if type(OnClose) == "function" then OnClose(id) end releaseObj(frame) end info.frame = nil end function Waterfall:CloseAll() local closed for id, v in pairs(self.registry) do if v.frame then self:Close(id) closed = true end end return closed end ----------------- -- Gui Classes -- ----------------- local FrameBackdrop = { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 16, insets = { left = 3, right = 3, top = 30, bottom = 3 } } local PaneBackdrop = { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 16, insets = { left = 3, right = 3, top = 23, bottom = 3 } } local ControlBackdrop = { bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", tile = true, tileSize = 16, edgeSize = 16, insets = { left = 3, right = 3, top = 3, bottom = 3 } } -- virtual class to contain frame functions local FramePassthrough = AceOO.Class() FramePassthrough.virtual = true function FramePassthrough.prototype:SetPoint(point,relativeTo,relativePoint,xOffset,yOffset) self.frame:SetPoint(point,(relativeTo and relativeTo.frame) or relativeTo,relativePoint,xOffset,yOffset) end function FramePassthrough.prototype:SetAllPoints(frame) self.frame:SetAllPoints(frame.frame or frame) end function FramePassthrough.prototype:SetWidth(...) self.frame:SetWidth(unpack(arg)) end function FramePassthrough.prototype:SetHeight(...) self.frame:SetHeight(unpack(arg)) end function FramePassthrough.prototype:SetMinResize(...) self.frame:SetMinResize(unpack(arg)) end function FramePassthrough.prototype:SetMaxResize(...) self.frame:SetMaxResize(unpack(arg)) end function FramePassthrough.prototype:ClearAllPoints() self.frame:ClearAllPoints() end function FramePassthrough.prototype:Show() self.frame:Show() end function FramePassthrough.prototype:Hide() self.frame:Hide() end function FramePassthrough.prototype:GetHeight() return self.frame:GetHeight() end function FramePassthrough.prototype:GetWidth() return self.frame:GetWidth() end -- Base Class for Gui Controls local WaterfallControl = AceOO.Class(FramePassthrough) WaterfallControl.virtual = true function WaterfallControl.prototype:init(frametype,inherits) WaterfallControl.super.prototype.init(self) local frame = CreateFrame(frametype or "Frame",nil,nil,inherits) self.frame = frame frame.obj = self frame:SetFrameLevel(10) frame:SetFrameStrata("DIALOG") end function WaterfallControl.prototype:SetParent(parent) self.parent = parent self.frame:SetParent(parent.scrollchild) end function WaterfallControl.prototype:Refresh() end --basic cleanup function, usually overridden function WaterfallControl.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self.disabled = nil self:SetFunc("disabled",nil) end function WaterfallControl.prototype:SetFunc(funcType,func,...) self[funcType.."Func"] = func local i = 1 while self[funcType.."Arg"..i] do self[funcType.."Arg"..i] = nil i = i + 1 end for i = 1 , table.getn(arg) do self[funcType.."Arg"..i] = arg[i] end end local function Control_OnEnter() showGameTooltip(this.obj) end local function Control_OnLeave() GameTooltip:Hide() end --the main waterfall frame WaterfallFrame = AceOO.Class(FramePassthrough) --the right side pane with gui components in it WaterfallPane = AceOO.Class(FramePassthrough) --gui components to be placed on the pane WaterfallLabel = AceOO.Class(WaterfallControl) WaterfallLinklabel = AceOO.Class(WaterfallControl) WaterfallCheckBox = AceOO.Class(WaterfallControl) WaterfallDragLink = AceOO.Class(WaterfallControl) WaterfallButton = AceOO.Class(WaterfallControl) WaterfallHeading = AceOO.Class(WaterfallControl) WaterfallTextBox = AceOO.Class(WaterfallControl) WaterfallSlider = AceOO.Class(WaterfallControl) WaterfallColorSwatch = AceOO.Class(WaterfallControl) WaterfallDropdown = AceOO.Class(WaterfallControl) WaterfallKeybinding = AceOO.Class(WaterfallControl) --the left side treeview pane WaterfallTreeView = AceOO.Class(FramePassthrough) WaterfallTreeLine = AceOO.Class(FramePassthrough) WaterfallTreeSection = AceOO.Class(FramePassthrough) --------------------------- -- Waterfall Frame Class -- --------------------------- local function frameOnClose() this.obj.lib:Close(this.obj.id) end local function closeOnClick() this.obj:Hide() end local function frameOnMouseDown() this:GetParent():StartMoving() end local function frameOnMouseUp() this:GetParent():StopMovingOrSizing() end local function sizerseOnMouseDown() this:GetParent():StartSizing("BOTTOMRIGHT") end local function sizersOnMouseDown() this:GetParent():StartSizing("BOTTOM") end local function sizereOnMouseDown() this:GetParent():StartSizing("RIGHT") end local function sizerOnMouseUp() this:GetParent():StopMovingOrSizing() end function WaterfallFrame.prototype:init() WaterfallFrame.super.prototype.init(self) local frame = CreateFrame("Frame",nil,UIParent) self.frame = frame frame.obj = self frame:SetWidth(550) frame:SetHeight(500) frame:SetPoint("CENTER",UIParent,"CENTER",0,0) frame:EnableMouse() frame:SetMovable(true) frame:SetResizable(true) frame:SetMinResize(500,300) frame:SetFrameLevel(1) frame:SetFrameStrata("DIALOG") frame:SetBackdrop(FrameBackdrop) frame:SetBackdropColor(0,0,0) frame:SetScript("OnHide",frameOnClose) local titlebar = frame:CreateTexture(nil,"BACKGROUND") self.titlebar = titlebar local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") self.titlebar2 = titlebar2 local r,g,b = 1, 0.6, 0 self.colorR = r self.colorG = g self.colorB = b titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",3,-4) titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-3,-4) titlebar:SetHeight(13) titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) titlebar2:SetHeight(13) titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) titlebar:SetTexture(r,g,b,1) titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) titlebar2:SetTexture(r,g,b,1) local closebutton = CreateFrame("Button",nil,frame,"UIPanelCloseButton") closebutton:SetScript("OnClick", closeOnClick) closebutton:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-2,-2) self.closebutton = closebutton closebutton.obj = self local title = CreateFrame("Frame",nil,frame) self.title = title title:EnableMouse() title:SetScript("OnMouseDown",frameOnMouseDown) title:SetScript("OnMouseUp", frameOnMouseUp) title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0) title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0) local titletext = title:CreateFontString(nil,"OVERLAY","QuestTitleFont") titletext:SetPoint("TOPLEFT",title,"TOPLEFT",0,0) titletext:SetPoint("TOPRIGHT",title,"TOPRIGHT",0,0) titletext:SetHeight(26) titletext:SetShadowColor(0,0,0) titletext:SetShadowOffset(1,-1) titletext:SetTextColor(1,1,1) self.titletext = titletext local sizer_se = CreateFrame("Frame",nil,frame) sizer_se:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) sizer_se:SetWidth(25) sizer_se:SetHeight(25) sizer_se:EnableMouse() sizer_se:SetScript("OnMouseDown",sizerseOnMouseDown) sizer_se:SetScript("OnMouseUp", sizerOnMouseUp) self.sizer_se = sizer_se local sizer_s = CreateFrame("Frame",nil,frame) sizer_s:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-25,0) sizer_s:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",0,0) sizer_s:SetHeight(25) sizer_s:EnableMouse() sizer_s:SetScript("OnMouseDown",sizersOnMouseDown) sizer_s:SetScript("OnMouseUp", sizerOnMouseUp) self.sizer_s = sizer_s local sizer_e = CreateFrame("Frame",nil,frame) sizer_e:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,25) sizer_e:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) sizer_e:SetWidth(25) sizer_e:EnableMouse() sizer_e:SetScript("OnMouseDown",sizereOnMouseDown) sizer_e:SetScript("OnMouseUp", sizerOnMouseUp) self.sizer_e = sizer_e local treeview = WaterfallTreeView:new(self) self.treeview = treeview treeview:SetPoint("TOPLEFT",frame,"TOPLEFT",11,-32) treeview:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",11,10) treeview:SetWidth(200) treeview:SetMinResize(200,0) treeview:SetMaxResize(400,10000) local mainpane = WaterfallPane:new(self) mainpane:SetPoint("TOPLEFT",treeview.frame,"TOPRIGHT",-3,0) mainpane:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-10,10) self.mainpane = mainpane end function WaterfallFrame.prototype:SetColor(r,g,b) self.colorR, self.colorG, self.colorB = r,g,b local titlebar = self.titlebar local titlebar2 = self.titlebar2 titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) titlebar:SetTexture(r,g,b,1) titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) titlebar2:SetTexture(r,g,b,1) self.mainpane:SetColor(r,g,b) end function WaterfallFrame.prototype:GetColor() return self.colorR, self.colorG, self.colorB end function WaterfallFrame.prototype:Hide() self.frame:Hide() end function WaterfallFrame.prototype:Show() self.frame:Show() end function WaterfallFrame.prototype:SetID(id) self.id = id end function WaterfallFrame.prototype:ChangePane(newpane) self.mainpane:Clear(newpane) self.controlcount = 0 currentframe = self if self.children then local title = self.children(newpane, self.childrenArg) self.mainpane.titletext:SetText(title) end currentframe = nil self.mainpane:DoLayout() self.mainpane.paneid = newpane end function WaterfallFrame.prototype:SetSelected(id, noupdate) self.treeview:SetSelected(id, noupdate) end function WaterfallFrame.prototype:SetTitle(title) self.titletext:SetText(title) end function WaterfallFrame.prototype:SetChildren(children, arg) self.children = children self.childrenArg = arg end function WaterfallFrame.prototype:SetTree(tree, treestatus, func, ...) self.treeview:SetContent(tree, treestatus, func, unpack(arg)) end function WaterfallFrame.prototype:AddControl(control) self.mainpane:AddControl(control) end function WaterfallFrame.prototype:Refresh() self.treeview:Refresh() self:ChangePane(self.treeview:GetSelected()) end function WaterfallFrame.prototype:CleanUp() self.children = nil self.tree = nil self.treeview:CleanUp() self:ChangePane() self:Hide() end function WaterfallFrame.prototype:ReAnchorTree() self.treeview:SetPoint("TOPLEFT",self.frame,"TOPLEFT",11,-32) self.treeview:SetPoint("BOTTOMLEFT",self.frame,"BOTTOMLEFT",11,10) end ------------------------- -- WaterfallPane Class -- ------------------------- local function paneOnSizeChanged() this.obj:SizeChanged() end local function paneOnMouseWheel() this.obj:MoveScroll(arg1) end local function paneOnSizeChanged() this.obj:DoLayout() end local function paneOnScrollValueChanged(self,value) this.obj:SetScroll(arg1) end local numpanes = 0 function WaterfallPane.prototype:init(parent) WaterfallPane.super.prototype.init(self) numpanes = numpanes + 1 self.parent = parent local name = "WaterfallPane"..numpanes local frame = CreateFrame("Frame",name,parent.frame) frame:SetBackdrop(PaneBackdrop) frame:SetBackdropBorderColor(0.6, 0.6, 0.6) frame:SetBackdropColor(0.1, 0.1, 0.1) frame:SetFrameLevel(2) frame:SetFrameStrata("DIALOG") self.frame = frame frame.obj = self frame:SetScript("OnSizeChanged",paneOnSizeChanged) --local title = CreateFrame("Frame",nil,frame) --title:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) --title:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) --title:SetHeight(24) --title:SetBackdrop(PaneBackdrop) --title:SetBackdropColor(0,0,0,0.2) --self.title = title local scrollframe = CreateFrame("ScrollFrame",name.."ScrollFrame",frame) local scrollchild = CreateFrame("Frame",name.."ChildFrame",scrollframe) local scrollbar = CreateFrame("Slider",name.."ScrollBar",scrollframe,"UIPanelScrollBarTemplate") self.scrollframe = scrollframe self.scrollchild = scrollchild self.scrollbar = scrollbar scrollframe:SetScrollChild(scrollchild) scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",9,-29) scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12) scrollframe:EnableMouseWheel(true) scrollframe:SetScript("OnMouseWheel", paneOnMouseWheel) scrollframe:SetScript("OnSizeChanged", paneOnSizeChanged) scrollframe.obj = self --scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) --scrollchild:SetPoint("BOTTOMRIGHT",scrollframe,"BOTTOMRIGHT",0,0) scrollchild:ClearAllPoints() scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) scrollchild:SetWidth(400) scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) scrollbar:SetMinMaxValues(0,1000) scrollbar:SetValueStep(1) scrollbar:SetValue(0) scrollbar:SetWidth(16) scrollbar:SetScript("OnValueChanged", paneOnScrollValueChanged) scrollbar.obj = self self.scrollvalue = 0 local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-5) titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-8,-5) titletext:SetHeight(16) titletext:SetText("") titletext:SetTextColor(1,1,1) local r,g,b = 1, 0.6, 0 self.colorR = r self.colorG = g self.colorB = b local titlebar = frame:CreateTexture(nil,"BACKGROUND") self.titlebar = titlebar local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") self.titlebar2 = titlebar2 titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) titlebar:SetHeight(10) titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) titlebar2:SetHeight(10) titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) titlebar:SetTexture(r,g,b,1) titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) titlebar2:SetTexture(r,g,b,1) --titletext:SetTextColor(1,1,1) self.titletext = titletext self.controls = {} end function WaterfallPane.prototype:GetID() return self.parent.id end function WaterfallPane.prototype:SetScroll(value) local frame, child = self.scrollframe, self.scrollchild local viewheight = frame:GetHeight() local height = child:GetHeight() local width, viewwidth = frame:GetWidth(), child:GetWidth() local offset local xOffset = max(0, (width-viewwidth)/2 ) if viewheight > height then offset = 0 else offset = floor((height - viewheight) / 1000.0 * (value or 0)) end child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset) child.offset = offset self.scrollvalue = value end --[[ horiz scrolling version function WaterfallPane.prototype:SetScroll(value) local frame, child = self.scrollframe, self.scrollchild local viewheight = frame:GetWidth() local height = child:GetWidth() local offset if viewheight > height then offset = 0 else offset = floor((height - viewheight) / 1000.0 * value) end child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0) child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0) child.offset = offset self.scrollvalue = value end ]] function WaterfallPane.prototype:MoveScroll(value) local frame, child = self.scrollframe, self.scrollchild local height, viewheight = frame:GetHeight(), child:GetHeight() if height > viewheight then self.scrollbar:Hide() else self.scrollbar:Show() local diff = height - viewheight local delta = 1 if value < 0 then delta = -1 end self.scrollbar:SetValue(min(max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000)) end end function WaterfallPane.prototype:SetColor(r,g,b) self.colorR, self.colorG, self.colorB = r,g,b local titlebar = self.titlebar local titlebar2 = self.titlebar2 titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) titlebar:SetTexture(r,g,b,1) titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) titlebar2:SetTexture(r,g,b,1) end function WaterfallPane.prototype:Clear(newpane) for i = 1, table.getn(self.controls) do releaseObj(self.controls[i]) self.controls[i] = nil end table_setn(self.controls,0) if self.paneid ~= newpane then self:SetScroll(0) end end function WaterfallPane.prototype:ClearFocus() for i, v in ipairs(self.controls) do if v.class == WaterfallTextBox then v.frame:ClearFocus() end end end function WaterfallPane.prototype:AddControl(control) tinsert(self.controls, control) control:SetParent(self) end --simple one under the other layout for now function WaterfallPane.prototype:DoLayout() local usedheight = 0 local maxwidth = 0 local rowwidth = 1 local rowheight = 1 for i, control in ipairs(self.controls) do control:ClearAllPoints() if i == 1 then control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,0) rowheight = control:GetHeight() rowwidth = control:GetWidth() + 10 else if control.noNewLine then control:SetPoint("TOPLEFT",self.controls[i-1],"TOPRIGHT",0,0) rowheight = max(rowheight,control:GetHeight()) rowwidth = rowwidth + control:GetWidth() else usedheight = usedheight + rowheight maxwidth = max(rowwidth,maxwidth) control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",10,-usedheight) rowheight = control:GetHeight() rowwidth = control:GetWidth() end end control:Show() end maxwidth = max(rowwidth,maxwidth) self.scrollchild:SetHeight(usedheight+rowheight) self.scrollchild:SetWidth(maxwidth+5) self:FixScroll() end --[[ function WaterfallPane.prototype:DoLayout() local colheight = 0 local sectionheight = 0 local sectionwidth = 0 local colwidth = 0 local usedwidth = 0 local frameheight = self.scrollchild:GetHeight() for i, control in ipairs(self.controls) do colheight = colheight + control:GetHeight() colwidth = max(colwidth, control:GetWidth()) control:ClearAllPoints() if colheight <= (frameheight - 10) then if i == 1 then control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4,0) else control:SetPoint("TOPLEFT",self.controls[i-1],"BOTTOMLEFT",0,0) end else usedwidth = usedwidth + colwidth + 6 colwidth = 0 colheight = 0 control:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",4+usedwidth,0) end control:Show() end usedwidth = usedwidth + colwidth self.scrollchild:SetWidth(usedwidth) self:FixScroll() end ]] function WaterfallPane.prototype:SizeChanged() self:FixScroll() end function WaterfallPane.prototype:FixScroll() local frame, child = self.scrollframe, self.scrollchild local height, viewheight = frame:GetHeight(), child:GetHeight() local width, viewwidth = frame:GetWidth(), child:GetWidth() local offset = self.scrollchild.offset if not offset then offset = 0 end local curvalue = self.scrollbar:GetValue() local xOffset = max(0, (width-viewwidth)/2 ) if viewheight < height then self.scrollbar:Hide() --self.scrollbar:SetValue(0) child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,0) child.offset = 0 else self.scrollbar:Show() local value = (offset / (viewheight - height) * 1000) if value > 1000 then value = 1000 end self.scrollbar:SetValue(value) self:SetScroll(value) if value < 1000 then child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",xOffset,offset) child.offset = offset end end end --[[ horiz scrolling version function WaterfallPane.prototype:FixScroll() local frame, child = self.scrollframe, self.scrollchild local height, viewheight = frame:GetWidth(), child:GetWidth() local offset = self.scrollchild.offset if not offset then offset = 0 end local curvalue = self.scrollbar:GetValue() if viewheight < height then self.scrollbar:EnableMouse(false) self.scrollbar:SetValue(0) else self.scrollbar:EnableMouse(true) local value = (offset / (viewheight - height) * 1000) if value > 1000 then value = 1000 end self.scrollbar:SetValue(value) self:SetScroll(value) if value < 1000 then child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",-offset,0) child:SetPoint("BOTTOMLEFT",frame,"BOTTOMLEFT",-offset,0) child.offset = offset end end end ]] function WaterfallPane.prototype:Refresh(fullRefresh) if fullRefresh then self.parent:Refresh() else for i, control in ipairs(self.controls) do control:Refresh() end end self:FixScroll() end ----------------- -- Label Class -- ----------------- function WaterfallLabel.prototype:init() WaterfallLabel.super.prototype.init(self) local frame = self.frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") self.text = text frame.text = text text:SetText("Waterfalllabel") text:SetJustifyH("LEFT") text:SetTextColor(1,1,1) frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) text:SetHeight(16) text:SetPoint("LEFT",frame,"LEFT") end --fontstrings dont return the correct width untill they have been drawn, wait a frame to set the width local function labelFixWidth() if not this.obj.width then this:SetWidth(this.text:GetWidth()+3) end this:SetScript("OnUpdate",nil) end function WaterfallLabel.prototype:SetText(text) self.text:SetText(text) self:Refresh() end function WaterfallLabel.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") end function WaterfallLabel.prototype:Refresh() self.frame:SetHeight(self.height or 16) if self.width then self.text:ClearAllPoints() self.text:SetPoint("LEFT",self.frame,"LEFT") self.text:SetPoint("RIGHT",self.frame,"RIGHT") self:SetWidth(self.width) else self.text:ClearAllPoints() self.text:SetPoint("LEFT",self.frame,"LEFT") self.frame:SetScript("OnUpdate", labelFixWidth) end self.text:SetJustifyH(self.justifyH or "LEFT") self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1) end ----------------- -- Linklabel Class -- ----------------- local function Linklabel_OnEnter() local self = this.obj if not self.disabled then self.highlight:Show() showGameTooltip(self) end end local function Linklabel_OnLeave() this.obj.highlight:Hide() GameTooltip:Hide() end local function linkConfirmFunc(self, refresh, full, func, ...) func(unpack(arg)) if refresh or full then self.parent:Refresh(full) end end local function Linklabel_OnClick() local self = this.obj local func = self.linkFunc local confirm = this.obj.confirm if func then if confirm then if type(confirm) == "string" then confirmPopup(confirm,linkConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1)) else confirmPopup((ARE_YOU_SURE):format(this.obj.text:GetText()),linkConfirmFunc, self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"linkArg",1)) end else func(getArgs(this.obj,"linkArg",1)) end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end end function WaterfallLinklabel.prototype:init() WaterfallLinklabel.super.prototype.init(self, "Button") local frame = self.frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") self.text = text frame.text = text frame:SetScript("OnClick", Linklabel_OnClick) frame:SetScript("OnEnter", Linklabel_OnEnter) frame:SetScript("OnLeave", Linklabel_OnLeave) text:SetText("WaterfallLinklabel") text:SetJustifyH("LEFT") text:SetTextColor(1,1,1) frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) text:SetHeight(16) text:SetPoint("LEFT",frame,"LEFT") local highlight = frame:CreateTexture(nil, "BACKGROUND") self.highlight = highlight highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") highlight:SetBlendMode("ADD") highlight:SetAllPoints(frame) highlight:Hide() frame:EnableMouse() end function WaterfallLinklabel.prototype:SetText(text) self.text:SetText(text) self:Refresh() end function WaterfallLinklabel.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") end function WaterfallLinklabel.prototype:Refresh() self.frame:SetHeight(self.height or 16) if self.width then self.text:ClearAllPoints() self.text:SetPoint("LEFT",self.frame,"LEFT") self.text:SetPoint("RIGHT",self.frame,"RIGHT") self:SetWidth(self.width) else self.text:ClearAllPoints() self.text:SetPoint("LEFT") self.frame:SetScript("OnUpdate", labelFixWidth) end self.text:SetJustifyH(self.justifyH or "LEFT") self.text:SetTextColor(self.r or 1, self.g or 1, self.b or 1) end function WaterfallLinklabel.prototype:SetColor(r,g,b) self.r = r self.g = g self.b = b self:Refresh() end ----------------- -- ColorSwatch Class -- ----------------- local tmpt = setmetatable({}, {mode='v'}) local function ColorSwatch_OnClick() local self = this.obj if not self.disabled then local func = self.setFunc local hasOpacity = self.hasOpacity for k in pairs(tmpt) do tmpt[k] = nil end for i = 1, 1000 do local x = self['setArg'..i] if x == nil then break else tmpt[i] = x end end ColorPickerFrame.func = function() if func then local r,g,b = ColorPickerFrame:GetColorRGB() local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil local n = table.getn(tmpt) tmpt[n+1] = r tmpt[n+2] = g tmpt[n+3] = b tmpt[n+4] = a func(unpack(tmpt)) self:SetColor(r,g,b,a) tmpt[n+1] = nil tmpt[n+2] = nil tmpt[n+3] = nil tmpt[n+4] = nil if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end end ColorPickerFrame.hasOpacity = self.hasOpacity ColorPickerFrame.opacityFunc = ColorPickerFrame.func ColorPickerFrame.opacity = 1 - (self.opacity or 0) ColorPickerFrame:SetColorRGB(self.r, self.g, self.b) local r, g, b, a = self.r, self.g, self.b, self.opacity ColorPickerFrame.cancelFunc = function() if func then local n = table.getn(tmpt) tmpt[n+1] = r tmpt[n+2] = g tmpt[n+3] = b tmpt[n+4] = a func(unpack(tmpt)) self:SetColor(r,g,b,a) for i = 1, n+4 do tmpt[i] = nil end end end ColorPickerFrame:SetFrameStrata(self.frame:GetFrameStrata()) ColorPickerFrame:SetFrameLevel(self.frame:GetParent():GetFrameLevel()+1) for i, child in ipairs({ColorPickerFrame:GetChildren()}) do -- child:SetFrameStrata(self.frame:GetFrameStrata()) child:SetFrameLevel(self.frame:GetParent():GetFrameLevel()+2) end ShowUIPanel(ColorPickerFrame) end end local function ColorSwatch_OnEnter() local self = this.obj if not self.disabled then self.highlight:Show() showGameTooltip(self) end end local function ColorSwatch_OnLeave() this.obj.highlight:Hide() GameTooltip:Hide() end function WaterfallColorSwatch.prototype:init() WaterfallColorSwatch.super.prototype.init(self,"Button") local frame = self.frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") self.text = text text:SetJustifyH("LEFT") text:SetTextColor(1,1,1) frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) text:SetHeight(24) frame:SetScript("OnClick", ColorSwatch_OnClick) frame:SetScript("OnEnter",ColorSwatch_OnEnter) frame:SetScript("OnLeave",ColorSwatch_OnLeave) local colorSwatch = frame:CreateTexture(nil, "OVERLAY") self.colorSwatch = colorSwatch colorSwatch:SetWidth(24) colorSwatch:SetHeight(24) colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch") local texture = frame:CreateTexture(nil, "OVERLAY") colorSwatch.texture = texture texture:SetTexture(1, 1, 1) texture:SetWidth(13.8) texture:SetHeight(13.8) texture:Show() local highlight = frame:CreateTexture(nil, "BACKGROUND") self.highlight = highlight highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") highlight:SetBlendMode("ADD") highlight:SetAllPoints(frame) highlight:Hide() texture:SetPoint("CENTER", colorSwatch, "CENTER") colorSwatch:SetPoint("LEFT", frame, "LEFT", 0, 0) text:SetPoint("LEFT",colorSwatch,"RIGHT",2,0) text:SetPoint("RIGHT",frame,"RIGHT") end function WaterfallColorSwatch.prototype:SetText(text) self.text:SetText(text) end function WaterfallColorSwatch.prototype:SetColor(r,g,b,a) self.r = r self.g = g self.b = b self.opacity = a self:Refresh() end function WaterfallColorSwatch.prototype:Refresh() if self.getFunc then self.r, self.g, self.b, self.opacity = self.getFunc(getArgs(self,"getArg",1)) end if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end self.colorSwatch.texture:SetTexture(self.r,self.g,self.b) if self.disabled then self.text:SetTextColor(0.5,0.5,0.5) else self.text:SetTextColor(1,1,1) end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end end function WaterfallColorSwatch.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetFunc("color",nil) self.r = nil self.g = nil self.b = nil self.hasOpacity = nil self.opacity = nil self.disabled = nil self:SetFunc("disabled",nil) end ----------------- -- Heading Class -- ----------------- function WaterfallHeading.prototype:init() WaterfallHeading.super.prototype.init(self) local frame = self.frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") self.text = text frame.text = text text:SetJustifyH("LEFT") frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) text:SetHeight(24) text:SetPoint("LEFT",frame,"LEFT") end function WaterfallHeading.prototype:SetText(text) self.text:SetText(text) self.frame:SetScript("OnUpdate", labelFixWidth) end function WaterfallHeading.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") end function WaterfallHeading.prototype:Refresh() self.frame:SetHeight(self.height or 16) if self.width then self.text:ClearAllPoints() self.text:SetPoint("LEFT",self.frame,"LEFT") self.text:SetPoint("RIGHT",self.frame,"RIGHT") self:SetWidth(self.width) else self.text:ClearAllPoints() self.text:SetPoint("LEFT",self.frame,"LEFT") self.frame:SetScript("OnUpdate", labelFixWidth) end self.text:SetJustifyH(self.justifyH or "LEFT") end ----------------- -- CheckBox Class -- ----------------- local function CheckBox_OnEnter() local self = this.obj if not self.disabled then self.highlight:Show() showGameTooltip(self) end end local function CheckBox_OnLeave() local self = this.obj if not self.down then self.highlight:Hide() end GameTooltip:Hide() end local function CheckBox_OnMouseUp() local self = this.obj if not self.disabled then self:ToggleChecked() self.text:SetPoint("LEFT",self.check,"RIGHT",0,0) end self.down = nil end local function CheckBox_OnMouseDown() local self = this.obj if not self.disabled then self.text:SetPoint("LEFT",self.check,"RIGHT",1,-1) self.down = true end end function WaterfallCheckBox.prototype:init() WaterfallCheckBox.super.prototype.init(self,"Button") local frame = self.frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") self.text = text frame:SetScript("OnEnter",CheckBox_OnEnter) frame:SetScript("OnLeave",CheckBox_OnLeave) frame:SetScript("OnMouseUp",CheckBox_OnMouseUp) frame:SetScript("OnMouseDown",CheckBox_OnMouseDown) frame:EnableMouse() local checkbg = frame:CreateTexture(nil,"ARTWORK") self.checkbg = checkbg checkbg:SetWidth(20) checkbg:SetHeight(20) checkbg:SetPoint("LEFT",frame,"LEFT",0,0) checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") local check = frame:CreateTexture(nil,"OVERLAY") self.check = check check:SetWidth(20) check:SetHeight(20) check:SetPoint("LEFT",frame,"LEFT",0,0) check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") self.checked = false self:UpdateCheck() local highlight = frame:CreateTexture(nil, "BACKGROUND") self.highlight = highlight highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") highlight:SetBlendMode("ADD") highlight:SetAllPoints(frame) highlight:Hide() text:SetJustifyH("LEFT") text:SetTextColor(1,1,1) frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) text:SetHeight(24) text:SetPoint("LEFT",check,"RIGHT",0,0) end function WaterfallCheckBox.prototype:UpdateTexture() local checkbg = self.checkbg local check = self.check local highlight = self.highlight if self.isRadio then checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton") checkbg:SetTexCoord(0,0.25,0,1) check:SetTexture("Interface\\Buttons\\UI-RadioButton") check:SetTexCoord(0.5,0.75,0,1) check:SetBlendMode("ADD") else checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up") checkbg:SetTexCoord(0,1,0,1) check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") check:SetTexCoord(0,1,0,1) check:SetBlendMode("BLEND") end end function WaterfallCheckBox.prototype:SetText(text) self.text:SetText(text) end function WaterfallCheckBox.prototype:UpdateCheck() if self.checked then self.check:Show() else self.check:Hide() end end function WaterfallCheckBox.prototype:ToggleChecked() self.checked = not self.checked self:UpdateCheck() if self.setFunc then self.setFunc(getArgs(self,"setArg",1,self.checked)) end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) else self:Refresh() end end function WaterfallCheckBox.prototype:SetChecked(value) self.checked = value self:UpdateCheck() end function WaterfallCheckBox.prototype:GetChecked() return self.checked end function WaterfallCheckBox.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.highlight:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetChecked(false) self:SetFunc("disabled",nil) self:SetFunc("get",nil) self:SetFunc("set",nil) self.isRadio = nil end function WaterfallCheckBox.prototype:Refresh() if self.getFunc then local checked = self.getFunc(getArgs(self,"getArg",1)) self:SetChecked(checked) end if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.text:SetTextColor(0.5,0.5,0.5) SetDesaturation(self.check, true) else self.text:SetTextColor(1,1,1) SetDesaturation(self.check, false) end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end self:UpdateTexture() end ----------------- -- DragLink Class -- ----------------- -- Provides an icon and description of a link based on on its linkInfo.itemType, itemId, itemInfo -- Dragging onto the linkIcon sets linkInfo.itemType, linkInfo.itemId, linkInfo.itemInfo -- If linkInfo.itemType == "spell" then linkInfo.spellName, linkInfo.spellRank, linkInfo.spellClass (current player class) are also set -- Dragging between two DragLink objects will swap their contents function Waterfall:GetDraggingObject() return Waterfall.fromObject end function Waterfall:SetDraggingObject(fromObject) Waterfall.fromObject = fromObject end local function DragLinkOnDragStart() local fromObject = this.obj Waterfall:SetDraggingObject(fromObject) end local function DragLinkOnReceiveDrag() local self = this.obj local toObject = this.obj local fromObject = Waterfall:GetDraggingObject() local refreshNeeded = false if (fromObject and fromObject ~= toObject) then --DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag toObject " .. tostring(toObject)) fromObject.itemType, toObject.itemType = toObject.itemType, fromObject.itemType fromObject.itemId, toObject.itemId = toObject.itemId, fromObject.itemId fromObject.itemInfo, toObject.itemInfo = toObject.itemInfo, fromObject.itemInfo fromObject.spellName, toObject.spellName = toObject.spellName, fromObject.spellName fromObject.spellRank, toObject.spellRank = toObject.spellRank, fromObject.spellRank fromObject.spellClass, toObject.spellClass = toObject.spellClass, fromObject.spellClass if fromObject.causesRefresh or fromObject.fullRefresh then fromObject.parent:Refresh(fromObject.fullRefresh) end refreshNeeded = true else local itemType, itemId, itemInfo = GetCursorInfo() --DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo)) if (itemType == "item" or itemType == "spell" or itemType == "macro") then local linkInfo = self.linkInfo linkInfo.itemType = itemType linkInfo.itemId = itemId linkInfo.itemInfo = itemInfo if (itemType == "spell") then local spellName, spellRank = GetSpellName(itemId, itemInfo) linkInfo.spellName = spellName linkInfo.spellRank = spellRank linkInfo.spellClass = WaterfallDragLink.CLASS else linkInfo.spellName = nil linkInfo.spellRank = nil linkInfo.spellClass = nil end --DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag itemType " .. tostring(itemType) .. " itemId " .. tostring(itemId) .. " itemInfo " .. tostring(itemInfo)) --DEFAULT_CHAT_FRAME:AddMessage("DragLinkOnReceiveDrag self.setFunc " .. tostring(self.setFunc) .. " self.disabled " .. tostring(self.disabled)) if (self.setFunc and not self.disabled) then self.setFunc(getArgs(self,"setArg",1,linkInfo)) end refreshNeeded = true ClearCursor() end end if (refreshNeeded) then if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) else self:Refresh() end end Waterfall:SetDraggingObject(nil) end local function DragLinkOnClick() DragLinkOnReceiveDrag() --?? end local function DragLinkOnEnter() local self = this.obj local linkInfo = self.linkInfo if (linkInfo) then if (linkInfo.itemType == "item") then GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") if (linkInfo.itemId) then GameTooltip:SetHyperlink("item:" .. linkInfo.itemId .. ":0:0:0:0:0:0:0") else GameTooltip:SetHyperlink(tostring(linkInfo.itemInfo)) end elseif (linkInfo.itemType == "spell") then if (linkInfo.spellName) then -- Cannot set this without preserving spell book & spell id. Even then may be from different class. So lame. Awaiting a Blizzard API that doesnt suck. -- GameTooltip:SetSpell(linkInfo.spellId, linkInfo.spellTab) GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") GameTooltip:AddLine(linkInfo.spellName, 1, 1, 1) local name, rank, icon, cost, isFunnel, powerType, castTime, minRange, maxRange = GetSpellInfo(linkInfo.spellName) if (name) then if (powerType == 0) then cost = tostring(cost) .. " " .. MANA elseif (powerType == 1) then cost = tostring(cost) .. " " .. RAGE elseif (powerType == 2) then cost = tostring(cost) .. " " .. FOCUS elseif (powerType == 3) then cost = tostring(cost) .. " " .. ENERGY elseif (powerType == 4) then cost = tostring(cost) .. " " .. HAPPINESS end local range = "" if (maxRange) then range = SPELL_RANGE:format(maxRange) end GameTooltip:AddDoubleLine(cost, range, 1, 1, 1, 1, 1, 1) if (castTime and castTime > 0) then GameTooltip:AddLine(SPELL_CAST_TIME_SEC:format(castTime), 1, 1, 1) else GameTooltip:AddLine(SPELL_CAST_TIME_INSTANT, 1, 1, 1) end if (rank) then GameTooltip:AddLine(tostring(rank)) end GameTooltip:Show() end end elseif (linkInfo.itemType == "macro") then if (linkInfo.itemId) then local name, icon, body = GetMacroInfo(linkInfo.itemId) if (name) then GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") GameTooltip:AddLine(tostring(name), 0.2, 0.8, 0.8, 1) GameTooltip:AddTexture(icon) GameTooltip:AddLine(tostring(body), 1, 1, 1, 1) GameTooltip:Show() end end end end end local function DragLinkOnLeave() GameTooltip:Hide() end local function DragLinkGetTexture(self) local linkInfo = self.linkInfo local texture if (linkInfo) then if (linkInfo.itemType == "item") then if (linkInfo.itemId and tonumber(linkInfo.itemId)) then _,_,_,_,_,_,_,_,_,texture = GetItemInfo(tonumber(linkInfo.itemId)) if (texture) then return texture end end elseif (linkInfo.itemType == "spell") then if (linkInfo.spellName) then _, _, texture = GetSpellInfo(linkInfo.spellName) if (texture) then return texture end end elseif (linkInfo.itemType == "macro") then if (linkInfo.itemId) then _, texture = GetMacroInfo(linkInfo.itemId) if (texture) then return texture end end end end return "Interface\\Icons\\INV_Misc_Gift_01" end WaterfallDragLink.defaultIconSize = 36 _, WaterfallDragLink.CLASS = UnitClass("player") -- Use the capitalized english class name function WaterfallDragLink.prototype:init() WaterfallDragLink.super.prototype.init(self, "Button") local frame = self.frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") self.text = text frame:SetScript("OnDragStart", DragLinkOnDragStart) frame:SetScript("OnReceiveDrag", DragLinkOnReceiveDrag) frame:SetScript("OnClick", DragLinkOnClick) frame:SetScript("OnEnter", DragLinkOnEnter) frame:SetScript("OnLeave", DragLinkOnLeave) frame:EnableMouse() frame:RegisterForDrag("LeftButton") frame:RegisterForClicks("LeftButtonUp", "RightButtonUp") local linkIcon = frame:CreateTexture(nil, "OVERLAY") linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize) linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize) linkIcon:SetPoint("LEFT",frame,"LEFT",0,0) linkIcon:SetTexture(DragLinkGetTexture(self)) linkIcon:SetTexCoord(0,1,0,1) linkIcon:Show() self.linkIcon = linkIcon text:SetJustifyH("LEFT") text:SetTextColor(1,1,1) frame:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4) frame:SetWidth(DEFAULT_CONTROL_WIDTH) text:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize + 4) text:SetPoint("LEFT",check,"RIGHT",0,0) end function WaterfallDragLink.prototype:SetText(text) self.text:SetText(text) end function WaterfallDragLink.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetFunc("disabled", nil) self.linkIcon:Hide() end function WaterfallDragLink.prototype:Refresh() if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.frame:Disable() else self.frame:Enable() end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end if (not self.linkIcon) then local linkIcon = self.frame:CreateTexture(nil, "OVERLAY") linkIcon:SetWidth(self.iconWidth or WaterfallDragLink.defaultIconSize) linkIcon:SetHeight(self.iconHeight or WaterfallDragLink.defaultIconSize) linkIcon:SetPoint("TOP", self.frame, "TOP", 0, 0) linkIcon:SetTexCoord(0,1,0,1) linkIcon:Show() self.linkIcon = linkIcon end if self.getFunc then self.linkInfo = self.getFunc(getArgs(self,"getArg",1)) else self:assert(self.linkInfo, "wth?") end self.linkIcon:SetTexture(DragLinkGetTexture(self)) self.linkIcon:Show() if (self.linkInfo.itemId) then self:SetText((self.linkInfo.itemInfo or "") .. " (" .. tostring(self.linkInfo.itemId) .. ")") else self:SetText("") end --DEFAULT_CHAT_FRAME:AddMessage("WaterfallDragLink.prototype:Refresh texture " .. tostring(DragLinkGetTexture(self)) .. " self.linkInfo.itemId " .. tostring(self.linkInfo.itemId) .. " self.linkInfo.itemInfo " .. tostring(self.linkInfo.itemInfo)) end ----------------- -- Button Class -- ----------------- local function buttonConfirmFunc(self, refresh, full, func, ...) func(unpack(arg)) if refresh or full then self.parent:Refresh(full) end end local function Button_OnClick() local self = this.obj local func = self.execFunc local confirm = this.obj.confirm if func then if confirm then if type(confirm) == "string" then confirmPopup(confirm,buttonConfirmFunc,self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1)) else confirmPopup((ARE_YOU_SURE):format(this.obj.frame:GetText()),buttonConfirmFunc, self,self.causesRefresh,self.fullRefresh,func,getArgs(this.obj,"execArg",1)) end else func(getArgs(this.obj,"execArg",1)) end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end end local function Control_OnEnter() if this.obj.link then GameTooltip:SetOwner(this, "ANCHOR_PRESERVE") GameTooltip:SetHyperlink(this.obj.link) else showGameTooltip(this.obj) end end local function Control_OnLeave() GameTooltip:Hide() end function WaterfallButton.prototype:init() WaterfallButton.super.prototype.init(self,"Button","UIPanelButtonTemplate") local frame = self.frame --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") local text = frame:GetFontString() text:SetPoint("LEFT",frame,"LEFT",7,0) text:SetPoint("RIGHT",frame,"RIGHT",-7,0) frame:SetScript("OnClick",Button_OnClick) frame:SetScript("OnEnter",Control_OnEnter) frame:SetScript("OnLeave",Control_OnLeave) frame:EnableMouse() frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) end function WaterfallButton.prototype:SetText(text) self.frame:SetText(text) end function WaterfallButton.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetFunc("disabled",nil) self:SetFunc("exec",nil) end function WaterfallButton.prototype:Refresh() if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.frame:Disable() else self.frame:Enable() end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end end ----------------- -- Keybinding Class -- ----------------- local function Keybinding_OnClick( button) local self = this.obj if button == "LeftButton" or button == "RightButton" then if self.waitingForKey then this:EnableKeyboard(false) self.msgframe:Hide() this:UnlockHighlight() self.waitingForKey = nil else this:EnableKeyboard(true) self.parent:ClearFocus() self.msgframe:Show() this:LockHighlight() self.waitingForKey = true end end end local function Keybinding_OnKeyDown( key) local self = this.obj if self.waitingForKey then local keyPressed = key; if keyPressed == "ESCAPE" then keyPressed = "" else if ( keyPressed == "BUTTON1" or keyPressed == "BUTTON2" ) then return; end if ( keyPressed == "UNKNOWN" ) then return; end if ( keyPressed == "SHIFT" or keyPressed == "CTRL" or keyPressed == "ALT") then return; end if ( keyPressed == "LSHIFT" or keyPressed == "LCTRL" or keyPressed == "LALT") then return; end if ( keyPressed == "RSHIFT" or keyPressed == "RCTRL" or keyPressed == "RALT") then return; end if ( IsShiftKeyDown() ) then keyPressed = "SHIFT-"..keyPressed; end if ( IsControlKeyDown() ) then keyPressed = "CTRL-"..keyPressed; end if ( IsAltKeyDown() ) then keyPressed = "ALT-"..keyPressed; end end if self.setFunc and not self.disabled then local validated = true if keyPressed == "" then keyPressed = nil else if self.keybindingOnly and not self.keybindingOnly[keyPressed] then validated = false end if self.keybindingExcept and self.keybindingExcept[keyPressed] then validated = false end end if validated then self.setFunc(getArgs(self,"setArg",1,keyPressed)) end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end self:Refresh() this:EnableKeyboard(false) self.msgframe:Hide() this:UnlockHighlight() self.waitingForKey = nil end end local function Keybinding_OnMouseDown( button) if ( button == "LeftButton" or button == "RightButton" ) then return elseif ( button == "MiddleButton" ) then button = "BUTTON3"; elseif ( button == "Button4" ) then button = "BUTTON4" elseif ( button == "Button5" ) then button = "BUTTON5" end Keybinding_OnKeyDown( button) end local function keybindingMsgFixWidth() this:SetWidth(this.msg:GetWidth()+10) this:SetScript("OnUpdate",nil) end function WaterfallKeybinding.prototype:init() WaterfallKeybinding.super.prototype.init(self,"Button","UIPanelButtonTemplate") local frame = self.frame --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") local text = frame:GetFontString() text:SetPoint("LEFT",frame,"LEFT",7,0) text:SetPoint("RIGHT",frame,"RIGHT",-7,0) frame:SetScript("OnClick",Keybinding_OnClick) frame:SetScript("OnKeyDown",Keybinding_OnKeyDown) frame:SetScript("OnEnter",Control_OnEnter) frame:SetScript("OnLeave",Control_OnLeave) frame:SetScript("OnMouseDown",Keybinding_OnMouseDown) frame:RegisterForClicks("AnyDown") frame:EnableMouse() frame:SetHeight(24) frame:SetWidth(DEFAULT_CONTROL_WIDTH) local msgframe = CreateFrame("Frame",nil,UIParent) msgframe:SetHeight(30) msgframe:SetBackdrop(ControlBackdrop) msgframe:SetBackdropColor(0,0,0) msgframe:SetFrameStrata("DIALOG") msgframe:SetFrameLevel(20) self.msgframe = msgframe local msg = msgframe:CreateFontString(nil,"OVERLAY","GameFontNormal") msg:SetText("Press a key to bind, ESC to clear the binding or click the button again to cancel") msgframe.msg = msg msg:SetPoint("TOPLEFT",msgframe,"TOPLEFT",5,-5) msgframe:SetScript("OnUpdate", keybindingMsgFixWidth) msgframe:SetPoint("TOP",frame,"BOTTOM",0,-2) msgframe:Hide() end function WaterfallKeybinding.prototype:SetText(text) self.frame:SetText(text) end function WaterfallKeybinding.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetFunc("disabled",nil) self:SetFunc("exec",nil) self.waitingForKey = nil self.msgframe:Hide() end function WaterfallKeybinding.prototype:Refresh() if self.getFunc then local text = self.getFunc(getArgs(self,"getArg",1)) self:SetText(text or "") end if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.frame:Disable() else self.frame:Enable() end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end end ----------------- -- Slider Class -- ----------------- local function Slider_OnValueChanged() local self = this.obj if not this.setup then local func = self.setFunc local newvalue newvalue = this:GetValue() if newvalue ~= self.value and not self.disabled then if self.bigStep > 0 then self.value = math.floor((newvalue - self.min) / self.bigStep + 0.5) * self.bigStep + self.min else self.value = newvalue end if func and not self.isInit then if (self.finalSetOnly) then self.finalSetValue = self.value else func(getArgs(this.obj,"setArg",1,self.value)) end end end if self.value then this.obj.valuetext:SetText(math.floor(self.value*10)/10) end end end local function Slider_OnMouseUp() local self = this.obj if (self.finalSetValue) then local func = self.setFunc if func then func(getArgs(this.obj,"setArg",1,self.finalSetValue)) end end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end local SliderBackdrop = { bgFile = "Interface\\Buttons\\UI-SliderBar-Background", edgeFile = "Interface\\Buttons\\UI-SliderBar-Border", tile = true, tileSize = 8, edgeSize = 8, insets = { left = 3, right = 3, top = 6, bottom = 6 } } function WaterfallSlider.prototype:init() WaterfallSlider.super.prototype.init(self,"Frame") local frame = self.frame self.slider = CreateFrame("Slider",nil,frame) local slider = self.slider slider:SetScript("OnEnter",Control_OnEnter) slider:SetScript("OnLeave",Control_OnLeave) slider:SetScript("OnMouseUp", Slider_OnMouseUp) slider.obj = self slider:SetOrientation("HORIZONTAL") frame:SetWidth(DEFAULT_CONTROL_WIDTH) slider:SetHeight(17) slider:SetHitRectInsets(0,0,-10,-10) slider:SetBackdrop(SliderBackdrop) self.text = slider:CreateFontString(nil,"ARTWORK","GameFontNormalSmall") self.text:SetPoint("TOP",frame,"TOP",0,-2) self.lowtext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") self.lowtext:SetPoint("TOPLEFT",slider,"BOTTOMLEFT",2,3) self.hightext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") self.hightext:SetPoint("TOPRIGHT",slider,"BOTTOMRIGHT",-2,3) self.valuetext = slider:CreateFontString(nil,"ARTWORK","GameFontHighlightSmall") self.valuetext:SetPoint("TOP",slider,"BOTTOM",0,3) slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal") frame:SetWidth(DEFAULT_CONTROL_WIDTH) frame:SetHeight(40) slider:SetPoint("TOP",self.text,"BOTTOM",0,0) slider:SetPoint("LEFT",frame,"LEFT",0,0) slider:SetPoint("RIGHT",frame,"RIGHT",0,0) if self.getFunc then self.isInit = true self.value = self.getFunc(getArgs(self,"getArg",1)) self.isInit = nil end slider:SetValue(self.value or 0) slider:SetScript("OnValueChanged",Slider_OnValueChanged) end function WaterfallSlider.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetFunc("disabled",nil) self:SetFunc("get",nil) self:SetFunc("set",nil) self.min = nil self.max = nil self.step = nil self.value = nil self.parent = nil self.disabled = nil end function WaterfallSlider.prototype:SetText(text) self.text:SetText(text) end function WaterfallSlider.prototype:Refresh() local frame = self.slider frame.setup = true frame:SetMinMaxValues(self.min or 0,self.max or 100) self.lowtext:SetText(self.min or 0) self.hightext:SetText(self.max or 100) frame:SetValueStep(self.step or 1) frame.setup = nil if self.getFunc then local newvalue newvalue = self.getFunc(getArgs(self,"getArg",1)) if newvalue ~= self.value then self.isInit = true frame:SetValue(newvalue) self.isInit = nil end end if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.slider:EnableMouse(false) self.text:SetTextColor(.5,.5,.5) self.hightext:SetTextColor(.5,.5,.5) self.lowtext:SetTextColor(.5,.5,.5) self.valuetext:SetTextColor(.5,.5,.5) else self.slider:EnableMouse(true) self.text:SetTextColor(1,1,1) self.hightext:SetTextColor(1,1,1) self.lowtext:SetTextColor(1,1,1) self.valuetext:SetTextColor(1,1,1) end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end end ------------------- -- TextBox Class -- ------------------- local function TextBox_OnEscapePressed() this:ClearFocus() end local function TextBox_OnEnterPressed() local self = this.obj local value = this:GetText() if self.toNumber then value = tonumber(value) --TODO: same message as a failed validation if not value then return end end if self.setFunc and not self.disabled then local validated = true if type(self.validateFunc) == "function" then validated = self.validateFunc(getArgs(self,"validateArg",1,value)) if validated and self.validateModifies then value = validated local setOnTextChanged = self.setOnTextChanged self.setOnTextChanged = nil this:SetText(value) self.setOnTextChanged = setOnTextChanged end elseif type(self.validateFunc) == "table" then validated = self.validateFunc[value] end --TODO: message for failed validation if validated then if self.parseList then self.setFunc(getArgs(self,"setArg",1,parseList(value))) else self.setFunc(getArgs(self,"setArg",1,value)) end end if self.getFunc == false then self:SetText("") end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end end local function TextBox_OnTextChanged() local self = this.obj local value = this:GetText() if self.setOnTextChanged then TextBox_OnEnterPressed() end if self.changedFunc and not self.disabled then if self.parseList then self.changedFunc(getArgs(self,"changedArg",1,parseList(value))) else self.changedFunc(getArgs(self,"changedArg",1,value)) end end end function WaterfallTextBox.prototype:init() WaterfallTextBox.super.prototype.init(self,"EditBox") local frame = self.frame --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") --self.text = text frame:SetScript("OnEnter",Control_OnEnter) frame:SetScript("OnLeave",Control_OnLeave) frame:SetFontObject(ChatFontNormal) frame:SetScript("OnEscapePressed",TextBox_OnEscapePressed) frame:SetScript("OnEnterPressed",TextBox_OnEnterPressed) frame:SetScript("OnTextChanged",TextBox_OnTextChanged) frame:SetTextInsets(5,5,3,3) frame:SetMaxLetters(256) frame:SetAutoFocus(false) frame:SetHeight(26) frame:SetWidth(DEFAULT_CONTROL_WIDTH) frame:SetBackdrop(ControlBackdrop) frame:SetBackdropColor(0,0,0) end local function UglyScrollLeft() this:HighlightText(0,1); this:Insert(" "..strsub(this:GetText(),1,1)); this:HighlightText(0,1); this:Insert(""); this:SetScript("OnUpdate", nil); end function WaterfallTextBox.prototype:SetText(text) self.frame:SetText(text) self.frame:SetScript("OnUpdate", UglyScrollLeft); end function WaterfallTextBox.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self.setOnTextChanged = nil self:SetText("") self:SetFunc("disabled",nil) self:SetFunc("get",nil) self:SetFunc("set",nil) self:SetFunc("changed",nil) end function WaterfallTextBox.prototype:Refresh() if self.getFunc then local text = self.getFunc(getArgs(self,"getArg",1)) if text then self:SetText(text) end end if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.frame:EnableMouse(false) self.frame:ClearFocus() self.frame:SetTextColor(0.5,0.5,0.5) else self.frame:EnableMouse(true) self.frame:SetTextColor(1,1,1) end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end end -------------------- -- DropDown Class -- -------------------- local function Dropdown_OnEnterPressed() local self = this.obj if self.setFunc and not self.disabled then local ret if self.strict and this.value then ret = this.value else ret = this:GetText() end if self.parseList then self.setFunc(getArgs(self,"setArg",1,parseList(ret))) else self.setFunc(getArgs(self,"setArg",1,ret)) end if self.causesRefresh or self.fullRefresh then self.parent:Refresh(self.fullRefresh) end end end local function Dropdown_TogglePullout() local self = this.obj if self.open then self.open = nil self.pullout:Hide() else self.open = true self:BuildPullout() if self.lines[1] and self.lines[1]:IsShown() then self.pullout:Show() end end end local function Dropdown_OnHide() this.obj.pullout:Hide() end local function Dropdown_LineClicked() local self = this.obj self.open = false self.pullout:Hide() self.editbox:SetText(this.text:GetText()) self.editbox.value = this.value Dropdown_OnEnterPressed(self.editbox) end local function Dropdown_LineEnter() this.highlight:Show() end local function Dropdown_LineLeave() this.highlight:Hide() end function WaterfallDropdown.prototype:init() WaterfallDropdown.super.prototype.init(self,"Frame") local frame = self.frame frame.obj = self --local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") --self.text = text local editbox = CreateFrame("EditBox",nil,frame) self.editbox = editbox editbox.obj = self editbox:SetFontObject(ChatFontNormal) editbox:SetScript("OnEscapePressed",TextBox_OnEscapePressed) editbox:SetScript("OnEnterPressed",Dropdown_OnEnterPressed) frame:SetScript("OnEnter",Control_OnEnter) frame:SetScript("OnLeave",Control_OnLeave) editbox:SetScript("OnEnter",Control_OnEnter) editbox:SetScript("OnLeave",Control_OnLeave) editbox:SetTextInsets(5,5,3,3) editbox:SetMaxLetters(256) editbox:SetAutoFocus(false) editbox:SetBackdrop(ControlBackdrop) editbox:SetBackdropColor(0,0,0) editbox:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0) editbox:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-20,0) local button = CreateFrame("Button",nil,frame) self.button = button button.obj = self button:SetWidth(24) button:SetHeight(24) button:SetScript("OnEnter",Control_OnEnter) button:SetScript("OnLeave",Control_OnLeave) button:SetNormalTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up") button:GetNormalTexture():SetTexCoord(.09,.91,.09,.91) button:SetPushedTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down") button:GetPushedTexture():SetTexCoord(.09,.91,.09,.91) button:SetDisabledTexture("Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled") button:GetDisabledTexture():SetTexCoord(.09,.91,.09,.91) button:SetHighlightTexture("Interface\\Buttons\\UI-Common-MouseHilight", "ADD") button:GetHighlightTexture():SetTexCoord(.09,.91,.09,.91) button:SetPoint("RIGHT",frame,"RIGHT",0,0) button:SetScript("OnClick",Dropdown_TogglePullout) frame:SetHeight(26) frame:SetWidth(DEFAULT_CONTROL_WIDTH) frame:SetScript("OnHide",Dropdown_OnHide) local pullout = CreateFrame("Frame",nil,UIParent) self.pullout = pullout frame:EnableMouse() pullout:SetBackdrop(ControlBackdrop) pullout:SetBackdropColor(0,0,0) pullout:SetFrameStrata("DIALOG") pullout:SetFrameLevel(20) pullout:SetPoint("TOPLEFT",frame,"BOTTOMLEFT",0,0) pullout:SetPoint("TOPRIGHT",frame,"BOTTOMRIGHT",-24,0) pullout:SetClampedToScreen(true) pullout:Hide() self.lines = {} end function WaterfallDropdown.prototype:SetText(text) self.editbox:SetText(text) end function WaterfallDropdown.prototype:CleanUp() self.frame:ClearAllPoints() self.frame:Hide() self.width = nil self.height = nil self.noNewLine = nil self.causesRefresh = nil self.fullRefresh = nil self.treeRefresh = nil self:SetText("") self:SetFunc("disabled",nil) self:SetFunc("get",nil) self:SetFunc("set",nil) self.editbox.value = nil self.columns = nil end function WaterfallDropdown.prototype:Refresh() local list = self.list if self.getFunc then local newval = self.getFunc(getArgs(self,"getArg",1)) if newval then if self.strict then if type(list) == "table" then local value for k, text in pairs(list) do if type(k) == "string" then value = k else value = text end if newval == value then self:SetText(text) self.editbox.value = value break end end elseif type(list) == "function" then for i, text, value in list() do if newval == value then self:SetText(text) self.editbox.value = value break end end end else self.editbox:SetText(newval) self.editbox.value = newval end else self.editbox:SetText("") self.editbox.value = newval end end if self.disabledFunc then self.disabled = self.disabledFunc(getArgs(self,"disabledArg",1)) if self.inverseDisabled then self.disabled = not self.disabled end end if self.disabled then self.editbox:EnableMouse(false) self.editbox:ClearFocus() self.editbox:SetTextColor(0.5,0.5,0.5) self.button:Disable() else self.button:Enable() if self.strict then self.editbox:EnableMouse(false) self.editbox:ClearFocus() self.editbox:SetTextColor(1,1,1) else self.editbox:EnableMouse(true) self.editbox:SetTextColor(1,1,1) end end if self.width then self:SetWidth(self.width) else self:SetWidth(DEFAULT_CONTROL_WIDTH) end end local function div(a,b) return a - math.floor(a/b)*b end local ddsort = {} function WaterfallDropdown.prototype:BuildPullout() local list = self.list local lines = self.lines local totalheight = 10 self:ClearPullout() if type(list) == "table" then for k, v in pairs(list) do tinsert(ddsort,k) end table.sort(ddsort) local columns = tonumber(self.columns) local columnHeight, columnWidth local columnCount = 0 self.pullout:ClearAllPoints() if (columns) then columnHeight = math.floor((table.getn(ddsort)) / columns) + 2 columnWidth = self.pullout:GetWidth() self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH * columns - 24,0) self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0) else self.pullout:SetPoint("TOPLEFT",self.frame,"BOTTOMRIGHT", -1 * DEFAULT_CONTROL_WIDTH - 24,0) self.pullout:SetPoint("TOPRIGHT",self.frame,"BOTTOMRIGHT",-24,0) columnHeight = table.getn(ddsort) + 1 end for i, value in pairs(ddsort) do local text = list[value] if (div((i - 1), (columnHeight - 1)) == 0) then columnCount = columnCount + 1 end if (lines[i]) then self:UpdateLine(lines[i], math.mod(i-1, (columnHeight-1)), columnCount) else lines[i] = self:CreateLine(math.mod(i-1, (columnHeight-1)), columnCount) end if i == 1 then lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) else if (columns and (div((i - 1), columnHeight) == 0)) then lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) else lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0) end end lines[i].text:SetText(text) if type(value) == "string" then lines[i].value = value else lines[i].value = text end if lines[i].value == self.editbox.value and self.getFunc then lines[i].check:Show() else lines[i].check:Hide() end lines[i]:Show() end totalheight = totalheight + 17 * (columnHeight-1) for k in pairs(ddsort) do ddsort[k] = nil end table_setn(ddsort,0) elseif type(list) == "function" then for i, text, value in list() do if not lines[i] then lines[i] = self:CreateLine() if i == 1 then lines[i]:SetPoint("TOP",self.pullout,"TOP",0,-5) else lines[i]:SetPoint("TOP",lines[i-1],"BOTTOM",0,0) end end lines[i].text:SetText(text) lines[i].value = value lines[i]:Show() totalheight = totalheight + 17 end end self.pullout:SetHeight(totalheight) end function WaterfallDropdown.prototype:ClearPullout() if self.lines then for i, line in ipairs(self.lines) do line.text:SetText("") line:Hide() end end self.pullout:SetHeight(10) self.pullout:SetWidth(DEFAULT_CONTROL_WIDTH) end -- For large list, if specified, 0 based row, 1 based column gives a grid. function WaterfallDropdown.prototype:CreateLine(row, column) local frame = CreateFrame("Button",nil,self.pullout) frame.obj = self frame.text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall") frame.text:SetTextColor(1,1,1) frame.text:SetJustifyH("LEFT") frame:SetFrameLevel(25) frame:SetFrameStrata("DIALOG") local highlight = frame:CreateTexture(nil, "OVERLAY") frame.highlight = highlight highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") highlight:SetBlendMode("ADD") local check = frame:CreateTexture("OVERLAY") frame.check = check check:SetWidth(16) check:SetHeight(16) check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check") frame:SetScript("OnClick",Dropdown_LineClicked) frame:SetScript("OnEnter",Dropdown_LineEnter) frame:SetScript("OnLeave",Dropdown_LineLeave) return self:UpdateLine(frame, row, column) end function WaterfallDropdown.prototype:UpdateLine(frame, row, column) if (column) then frame:SetPoint("TOPLEFT",self.pullout,"TOPLEFT",6 + (column - 1) * DEFAULT_CONTROL_WIDTH,-5 - row*17) frame:SetPoint("BOTTOMRIGHT",self.pullout,"TOPLEFT",-6 + column * DEFAULT_CONTROL_WIDTH,-5 - (row+1)*17) else frame:SetPoint("LEFT",self.pullout,"LEFT",6,0) frame:SetPoint("RIGHT",self.pullout,"RIGHT",-6,0) end frame.text:SetPoint("TOPLEFT",frame,"TOPLEFT",16,0) frame.text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) frame.highlight:SetAllPoints(frame) frame.highlight:Hide() frame.check:SetPoint("LEFT",frame,"LEFT",0,0) return frame end ----------------------------- -- WaterfallTreeView Class -- ----------------------------- local function treeviewOnSizeChanged() this.obj:SizeChanged() end local function treeviewOnMouseWheel() this.obj:MoveScroll(arg1) end local function treeviewOnScrollValueChanged() this.obj:SetScroll(arg1) end local function treeviewSizerOnMouseUp() this:GetParent():StopMovingOrSizing() this.obj.parent:ReAnchorTree() end function WaterfallTreeView.prototype:init(parent) WaterfallTreeView.super.prototype.init(self) --Parent WaterfallFrame Object self.parent = parent local frame = CreateFrame("Frame",nil,parent.frame) frame.obj = self local scrollframe = CreateFrame("ScrollFrame",nil,frame) local scrollchild = CreateFrame("Frame",nil,scrollframe) local scrollbar = CreateFrame("Slider",nil,scrollframe,"UIPanelScrollBarTemplate") frame:SetBackdrop(ControlBackdrop) frame:SetBackdropBorderColor(0.6, 0.6, 0.6) frame:SetBackdropColor(0.1, 0.1, 0.1) frame:SetScript("OnSizeChanged", treeviewOnSizeChanged) frame:SetFrameLevel(2) frame:SetFrameStrata("DIALOG") frame:SetResizable(true) self.scrollframe = scrollframe self.scrollchild = scrollchild self.frame = frame self.scrollbar = scrollbar scrollframe:SetScrollChild(scrollchild) scrollframe:SetPoint("TOPLEFT",frame,"TOPLEFT",8,-12) scrollframe:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-28,12) scrollframe:EnableMouseWheel(true) scrollframe:SetScript("OnMouseWheel", treeviewOnMouseWheel) scrollframe.obj = self scrollchild:SetPoint("TOPLEFT",scrollframe,"TOPLEFT",0,0) scrollchild:SetPoint("TOPRIGHT",scrollframe,"TOPRIGHT",0,0) scrollchild:SetHeight(400) scrollbar:SetPoint("TOPLEFT",scrollframe,"TOPRIGHT",0,-16) scrollbar:SetPoint("BOTTOMLEFT",scrollframe,"BOTTOMRIGHT",0,16) scrollbar:SetMinMaxValues(0,1000) scrollbar:SetValueStep(1) scrollbar:SetValue(0) scrollbar:SetWidth(16) scrollbar:SetScript("OnValueChanged", treeviewOnScrollValueChanged) self.scrollvalue = 0 scrollchild.lines = {} self.linetables = {} self.sections = {} scrollbar.obj = self local sizer = CreateFrame("Frame",nil,frame) sizer:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0) sizer:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",0,0) sizer:SetWidth(7) sizer:EnableMouse() sizer:SetScript("OnMouseDown",sizereOnMouseDown) sizer:SetScript("OnMouseUp", treeviewSizerOnMouseUp) self.sizer = sizer sizer.obj = self self:Refresh() end function WaterfallTreeView.prototype:SetScroll(value) local frame, child = self.scrollframe, self.scrollchild local viewheight = frame:GetHeight() local height = child:GetHeight() local offset if viewheight > height then offset = 0 else offset = floor((height - viewheight) / 1000.0 * value) end child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) child.offset = offset self.scrollvalue = value end function WaterfallTreeView.prototype:SizeChanged() self:FixScroll() self.parent:ReAnchorTree() end function WaterfallTreeView.prototype:MoveScroll(value) local frame, child = self.scrollframe, self.scrollchild local height, viewheight = frame:GetHeight(), child:GetHeight() if height > viewheight then self.scrollbar:Hide() else self.scrollbar:Show() local diff = height - viewheight local delta = 1 if value < 0 then delta = -1 end self.scrollbar:SetValue(math.min(math.max(self.scrollvalue + delta*(1000/(diff/45)),0), 1000)) end end function WaterfallTreeView.prototype:FixScroll() if self.noFixScroll then return end local frame, child = self.scrollframe, self.scrollchild local height, viewheight = frame:GetHeight(), child:GetHeight() local offset = self.scrollchild.offset if not offset then offset = 0 end local curvalue = self.scrollbar:GetValue() if viewheight < height then self.scrollbar:Hide() self.scrollbar:SetValue(0) else self.scrollbar:Show() local value = (offset / (viewheight - height) * 1000) if value > 1000 then value = 1000 end self.scrollbar:SetValue(value) self:SetScroll(value) if value < 1000 then child:ClearAllPoints() child:SetPoint("TOPLEFT",frame,"TOPLEFT",0,offset) child:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,offset) child.offset = offset end end end function WaterfallTreeView.prototype:ToggleExpand(id) self.status[id] = not self.status[id] self:Refresh() end function WaterfallTreeView.prototype:Refresh(noupdate) self.noFixScroll = true self:Clear() if not self.contents then return end if self.contentsFunc and not noupdate then self.parent.controlcount = 0 currentframe = self.parent self.contentsFunc(self.contents, self.status, getArgs(self, "contentsArg", 1)) currentframe = nil end if self.type == "SECTIONS" then for i, section in ipairs(self.contents) do if not section.hidden then self:AddSection(i,section) end end local height = 0 for i, section in ipairs(self.sections) do height = height + section:GetHeight() end self.scrollchild:SetHeight(height+3) else local level = 0 local index = 1 for i, line in ipairs(self.contents) do if not line.hidden then index = self:AddLine(index,line,level) end end self.scrollchild:SetHeight(index*16) end self.noFixScroll = nil self:FixScroll() end local function isSectionSelected(section, selected) if not selected then return end if section.id == selected then return true end for i,v in ipairs(section.lines) do if i <= section.numlines then if v.id == selected then return true end end end end function WaterfallTreeView.prototype:Collapse() for k in pairs(self.status) do self.status[k] = false end end function WaterfallTreeView.prototype:AddSection(index,line) local sections = self.sections local id = line.id if not sections[index] then sections[index] = getObj(WaterfallTreeSection,self) local section = sections[index] section:SetParent(self) if index == 1 then section:SetPoint("TOPLEFT",self.scrollchild,"TOPLEFT",0,0) section:SetPoint("TOPRIGHT",self.scrollchild,"TOPRIGHT",0,0) else section:SetPoint("TOPLEFT",sections[index-1],"BOTTOMLEFT",0,0) section:SetPoint("TOPRIGHT",sections[index-1],"BOTTOMRIGHT",0,0) end end local section = sections[index] section:SetText(line.text) section:SetID(line.id) section:SetTooltipText(line.tooltipTitle,line.tooltipText) if self.status[id] == nil then self.status[id] = not not line.isOpen end local handler = line.handler local disabled = checkNegatable(line.disabled,handler) section.disabled = disabled section:SetColor(self.parent:GetColor()) if line.hasOptions then section:AddLine(line.text,line.id,line.id == self.selected,disabled,line.tooltipTitle,line.tooltipText) end local firstid for i, item in ipairs(line) do if not item.hidden then if not firstid then firstid = item.id end local itemdisabled = checkNegatable(item.disabled,handler) section:AddLine(item.text,item.id,item.id == self.selected,itemdisabled or disabled,item.tooltipTitle,item.tooltipText) end end if self.selected == section.id and not line.hasOptions then self:SetSelected(firstid) return end if isSectionSelected(section, self.selected) then self.status[section.id] = true end section:SetExpanded(self.status[line.id] and not disabled) section:Show() end function WaterfallTreeView.prototype:Clear() for i, frame in ipairs(self.scrollchild.lines) do releaseObj(frame) self.scrollchild.lines[i] = nil end for k in pairs(self.linetables) do self.linetables[k] = nil end for i, section in ipairs(self.sections) do releaseObj(section) self.sections[i] = nil end self.numlines = 0 self.scrollchild:SetHeight(1) self:FixScroll() end function WaterfallTreeView.prototype:ClearSelection() self.selected = nil end function WaterfallTreeView.prototype:GetSelected() return self.selected end function WaterfallTreeView.prototype:SetSelected(lineid, noupdate) self.selected = lineid self:Refresh(noupdate) if not noupdate then self.parent:ChangePane(lineid) end end function WaterfallTreeView.prototype:AddLine(index,line,level,disabled) local lines = self.scrollchild.lines local text, hasChildren, isSelected, isOpen text = line.text or "" hasChildren = table.getn(line) > 0 local id = line.id if self.status[id] == nil then self.status[id] = not not line.isOpen end isOpen = self.status[id] and not disabled isSelected = self.selected == id if not lines[index] then lines[index] = getObj(WaterfallTreeLine) lines[index]:SetParent(self) end local disabled if type(line.disabled) == "function" then disabled = line.disabled() else disabled = line.disabled end lines[index]:Setup(index,id,text,level,hasChildren,isSelected,isOpen,disabled) lines[index]:SetTooltipText(line.tooltipTitle,line.tooltipText) lines[index]:SetIcon(line.icon) self.linetables[index] = line index = index + 1 if isOpen then for i, childline in ipairs(line) do if not childline.hidden then index = self:AddLine(index,childline,level+1) end end end return index end function WaterfallTreeView.prototype:SetType(type) if not type then self.type = "TREE" else self.type = type end end function WaterfallTreeView.prototype:SetContent(contents, status, func, ...) self.contents = contents self.status = status self.contentsFunc = func local i = 1 while self["contentsArg"..i] do self["contentsArg"..i] = nil i = i + 1 end for i = 1 , table.getn(arg) do self["contentsArg"..i] = arg[i] end self:Refresh() self:FixScroll() end function WaterfallTreeView.prototype:CleanUp() self.contents = nil self.selected = nil self.contentsFunc = nil local i = 1 while self["contentsArg"..i] do self["contentsArg"..i] = nil i = i + 1 end self.selected = nil self:Refresh() end -------------------- -- WaterfallTreeLine Class -- -------------------- local function TreeLine_OnEnter() if not this.obj.disabled then this.highlight:SetAlpha(1) this.highlight:Show() showGameTooltip(this.obj) end end local function TreeLine_OnLeave() if not this.obj.isSelected then this.highlight:Hide() else this.highlight:SetAlpha(0.7) end GameTooltip:Hide() end local function TreeLine_OnClick() if not this.obj.disabled then this.obj.parent:SetSelected(this.obj.id) end end local function TreeLine_OnExpandClick() if not this.obj.disabled then this.obj.parent:ToggleExpand(this.obj.id) end end function WaterfallTreeLine.prototype:init() WaterfallTreeLine.super.prototype.init(self) local frame = CreateFrame("Button",nil,nil) self.frame = frame local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") text:SetJustifyH("LEFT") text:SetTextColor(1,1,1) frame:SetHeight(16) frame:EnableMouse(true) frame:SetScript("OnEnter",TreeLine_OnEnter) frame:SetScript("OnLeave",TreeLine_OnLeave) frame:SetScript("OnClick",TreeLine_OnClick) frame:SetFrameLevel(10) frame:SetFrameStrata("DIALOG") local highlight = frame:CreateTexture(nil, "BACKGROUND") highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") highlight:SetBlendMode("ADD") highlight:SetAllPoints(frame) highlight:Hide() local expand = CreateFrame("Button",nil,frame) expand.obj = self expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up") expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down") expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled") expand:SetHighlightTexture("Interface\\Buttons\\UI-PlusButton-Hilight") expand:SetScript("OnClick",TreeLine_OnExpandClick) expand:SetFrameLevel(21) expand:SetWidth(16) expand:SetHeight(16) local icon = frame:CreateTexture(nil,"OVERLAY") icon:SetWidth(16) icon:SetHeight(16) icon:SetPoint("LEFT",expand,"RIGHT",1,0) self.icon = icon expand:SetPoint("LEFT",frame,"LEFT",0,0) text:SetPoint("LEFT",icon,"RIGHT",2,0) text:SetPoint("RIGHT",frame,"RIGHT",0,0) frame.obj = self frame.text = text frame.highlight = highlight frame.expand = expand self.expand = expand self.highlight = highlight self.text = text end function WaterfallTreeLine.prototype:UpdateExpandButton() local expand = self.expand if self.hasChildren then if self.isOpen then expand:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up") expand:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-Down") expand:SetDisabledTexture("Interface\\Buttons\\UI-MinusButton-Disabled") expand:Enable() expand:SetWidth(16) else if self.disabled then expand:Disable() else expand:Enable() end expand:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up") expand:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-Down") expand:SetDisabledTexture("Interface\\Buttons\\UI-PlusButton-Disabled") expand:SetWidth(16) end else expand:SetNormalTexture(nil) expand:SetPushedTexture(nil) expand:SetDisabledTexture(nil) expand:Disable() expand:SetWidth(16) end end function WaterfallTreeLine.prototype:CleanUp() self:ClearAllPoints() self:Hide() self:SetText("") self.disabled = nil self.handler = nil self.name = nil end function WaterfallTreeLine.prototype:Setup(index, id, text, level, hasChildren, isSelected, isOpen, disabled) self.level = level self.hasChildren = hasChildren self.isSelected = isSelected self.isOpen = isOpen and not disabled self.index = index self.id = id self.disabled = disabled self:UpdateExpandButton() self.expand:SetPoint("LEFT",self.frame,"LEFT",level*8,0) self:SetText(text) if disabled then self.text:SetTextColor(0.5,0.5,0.5) else self.text:SetTextColor(1,1,1) end self.frame:SetPoint("TOPLEFT",self.parent.scrollchild,"TOPLEFT",0,-(index-1)*16) self.frame:SetPoint("TOPRIGHT",self.parent.scrollchild,"TOPRIGHT",0,-(index-1)*16) if self.isSelected then self.highlight:Show() self.highlight:SetAlpha(0.7) else self.highlight:Hide() end self.frame:Show() end function WaterfallTreeLine.prototype:SetIcon(iconpath) local icon = self.icon if iconpath then icon:SetTexture(iconpath) icon:SetWidth(16) else icon:SetTexture(nil) icon:SetWidth(1) end end function WaterfallTreeLine.prototype:SetText(text) self.text:SetText(text) end function WaterfallTreeLine.prototype:SetTooltipText(title,text) self.tooltipTitle = title self.tooltipText = text end function WaterfallTreeLine.prototype:SetParent(parent) self.parent = parent self.frame.parent = parent self.frame:SetParent(parent.scrollchild) end ----------------------- -- TreeSection Class -- ----------------------- local function SectionTitle_OnClick() local self = this.obj local parent = self.parent if not self.disabled then local selected = self.parent.selected local lines = self.lines if self.expanded then --search before changing pane, since it will release and reinit this section local found for i = 1, self.numlines do if lines[i].id == selected then found = true end end --if expanded close -- if one of our children is selected then clear the pane if found then parent.selected = nil parent:ToggleExpand(self.id) parent:SetSelected(nil) else parent:ToggleExpand(self.id) end else --save before changing pane, since it will release and reinit this section local newid if lines[1] then newid = lines[1].id end parent:ToggleExpand(self.id) if newid and not parent.selected then parent:SetSelected(newid) end end end end local function SectionTitle_OnEnter() local self = this.obj if not self.disabled then self.hover = true self:UpdateColor() end showGameTooltip(self) end local function SectionTitle_OnLeave() local self = this.obj self.hover = false self:UpdateColor() GameTooltip:Hide() end local function SectionItem_OnEnter() if not this.disabled then this.highlight:SetAlpha(1) this.highlight:Show() showGameTooltip() end end local function SectionItem_OnClick() if not this.disabled then this.obj.parent:SetSelected(this.id) end end local function SectionItem_OnLeave() if not this.isSelected then this.highlight:Hide() else this.highlight:SetAlpha(0.7) end GameTooltip:Hide() end function WaterfallTreeSection.prototype:init(parent) WaterfallTreeSection.super.prototype.init(self) self.parent = parent local frame = CreateFrame("Frame",nil,parent.scrollchild) frame.obj = self self.frame = frame frame:SetBackdrop(PaneBackdrop) frame:SetBackdropBorderColor(0.6, 0.6, 0.6) frame:SetBackdropColor(0.1, 0.1, 0.1) frame:SetFrameLevel(10) frame:SetFrameStrata("DIALOG") local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal") self.titletext = titletext titletext:SetPoint("TOPLEFT",frame,"TOPLEFT",0,-5) titletext:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,-5) titletext:SetHeight(16) titletext:SetText("Section") titletext:SetTextColor(1,1,1) local arrow = frame:CreateTexture(nil,"OVERLAY") self.arrow = arrow arrow:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-6,-4) arrow:SetWidth(20) arrow:SetHeight(20) arrow:SetTexture("Interface\\MiniMap\\ROTATING-MINIMAPARROW") local r,g,b = 1, 0.6, 0 self.colorR = r self.colorG = g self.colorB = b local titlebar = frame:CreateTexture(nil,"BACKGROUND") self.titlebar = titlebar local titlebar2 = frame:CreateTexture(nil,"BACKGROUND") self.titlebar2 = titlebar2 titlebar:SetPoint("TOPLEFT",frame,"TOPLEFT",4,-4) titlebar:SetPoint("TOPRIGHT",frame,"TOPRIGHT",-4,-4) titlebar:SetHeight(10) titlebar2:SetPoint("TOPLEFT",titlebar,"BOTTOMLEFT",0,0) titlebar2:SetPoint("TOPRIGHT",titlebar,"BOTTOMRIGHT",0,0) titlebar2:SetHeight(10) titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) titlebar:SetTexture(r,g,b,1) titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) titlebar2:SetTexture(r,g,b,1) local title = CreateFrame("Button",nil,frame) self.title = title title:EnableMouse() title.obj = self title:SetScript("OnClick", SectionTitle_OnClick) title:SetScript("OnEnter", SectionTitle_OnEnter) title:SetScript("OnLeave", SectionTitle_OnLeave) title:SetPoint("TOPLEFT",titlebar,"TOPLEFT",0,0) title:SetPoint("BOTTOMRIGHT",titlebar2,"BOTTOMRIGHT",0,0) self.lines = {} self.numlines = 0 frame:SetWidth(100) frame:SetHeight(100) self.expanded = true frame:SetPoint("CENTER",UIParent,"CENTER") end function WaterfallTreeSection.prototype:SetParent(parent) self.parent = parent self.frame:SetParent(parent.scrollchild) end function WaterfallTreeSection.prototype:UpdateColor() local r,g,b self.arrow:Show() if self.disabled then r,g,b = 0.5,0.5,0.5 self.arrow:Hide() elseif self.hover then r,g,b = self.colorR+((1-self.colorR)/2), self.colorG+((1-self.colorG)/2), self.colorB+((1-self.colorB)/2) else r,g,b = self.colorR, self.colorG, self.colorB end local titlebar = self.titlebar local titlebar2 = self.titlebar2 titlebar:SetGradientAlpha("VERTICAL",r*0.6,g*0.6,b*0.6,1,r,g,b,1) titlebar:SetTexture(r,g,b,1) titlebar2:SetGradientAlpha("VERTICAL",r*0.9,g*0.9,b*0.9,1,r*0.6,g*0.6,b*0.6,1) titlebar2:SetTexture(r,g,b,1) end function WaterfallTreeSection.prototype:SetColor(r,g,b) if r and g and b then self.colorR, self.colorG, self.colorB = r,g,b end self:UpdateColor() end function WaterfallTreeSection.prototype:ToggleExpand() self:SetExpanded(not self.expanded) end function WaterfallTreeSection.prototype:SetText(text) self.titletext:SetText(text) end function WaterfallTreeSection.prototype:SetTooltipText(title,text) self.tooltipTitle = title self.tooltipText = text end function WaterfallTreeSection.prototype:SetID(id) self.id = id end function WaterfallTreeSection.prototype:AddLine(text,id,selected,disabled,tooltipTitle,tooltipText) local numlines = self.numlines + 1 self.numlines = numlines local lines = self.lines if not lines[numlines] then self:CreateLine(numlines) end local line = lines[numlines] line.text:SetText(text) line.id = id line.disabled = disabled line.isSelected = selected line.tooltipTitle = tooltipTitle line.tooltipText = tooltipText if selected then line.highlight:SetAlpha(0.7) line.highlight:Show() else line.highlight:Hide() end if disabled then line.text:SetTextColor(0.5,0.5,0.5) else line.text:SetTextColor(1,1,1) end line:Show() self:FixHeight() end function WaterfallTreeSection.prototype:SetExpanded(expanded) self.expanded = expanded local lines = self.lines if self.expanded then for i = 1, self.numlines do lines[i]:Show() end self.arrow:SetTexCoord(.18,.85,.15,.82) else for i = 1, table.getn(lines) do lines[i]:Hide() end self.arrow:SetTexCoord(.85,.18,.82,.15) end self:FixHeight() end function WaterfallTreeSection.prototype:FixHeight() if self.expanded then self.frame:SetHeight(33 + self.numlines * 18) else self.frame:SetHeight(28) end end function WaterfallTreeSection.prototype:CreateLine(index) local frame = self.frame self.lines[index] = CreateFrame("Button",nil,frame) local line = self.lines[index] line.obj = self line:SetScript("OnEnter",SectionItem_OnEnter) line:SetScript("OnLeave",SectionItem_OnLeave) line:SetScript("OnClick",SectionItem_OnClick) line:SetPoint("LEFT",frame,"LEFT",5,0) line:SetPoint("RIGHT",frame,"RIGHT",-5,0) line:SetHeight(16) if index == 1 then line:SetPoint("TOP",frame,"TOP",0,-28) else line:SetPoint("TOP",self.lines[index-1],"BOTTOM",0,-2) end local text = line:CreateFontString(nil,"OVERLAY","GameFontNormal") line.text = text text:SetAllPoints(line) text:SetTextColor(1,1,1) local highlight = line:CreateTexture(nil, "BACKGROUND") line.highlight = highlight highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight") highlight:SetBlendMode("ADD") highlight:SetAllPoints(line) highlight:Hide() end function WaterfallTreeSection.prototype:CleanUp() for i = 1, table.getn(self.lines) do self.lines[i]:Hide() end self.numlines = 0 self.frame:Hide() self.frame:ClearAllPoints() self.id = nil self.disabled = nil self.colorR = 1 self.colorG = 0.6 self.colorB = 0 end local function activate(self, oldLib, oldDeactivate) if oldLib and oldLib.registry then self.registry = oldLib.registry else self.registry = {} end if oldDeactivate then oldDeactivate(oldLib) end end AceLibrary:Register(Waterfall, MAJOR_VERSION, MINOR_VERSION, activate) Waterfall = AceLibrary("Waterfall-1.0")