mirror of
https://github.com/brues-code/TwitchEmotes.git
synced 2026-10-22 05:10:51 +00:00
Initial commit
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
--- AceConfig-3.0 wrapper library.
|
||||
-- Provides an API to register an options table with the config registry,
|
||||
-- as well as associate it with a slash command.
|
||||
-- @class file
|
||||
-- @name AceConfig-3.0
|
||||
-- @release $Id: AceConfig-3.0.lua 969 2010-10-07 02:11:48Z shefki $
|
||||
|
||||
--[[
|
||||
AceConfig-3.0
|
||||
|
||||
Very light wrapper library that combines all the AceConfig subcomponents into one more easily used whole.
|
||||
|
||||
]]
|
||||
|
||||
local MAJOR, MINOR = "AceConfig-3.0", 2
|
||||
local AceConfig = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not AceConfig then return end
|
||||
|
||||
AceConfig.embeds = AceConfig.embeds or {}
|
||||
|
||||
local cfgreg = LibStub("AceConfigRegistry-3.0")
|
||||
local cfgcmd = LibStub("AceConfigCmd-3.0")
|
||||
--TODO: local cfgdlg = LibStub("AceConfigDialog-3.0", true)
|
||||
--TODO: local cfgdrp = LibStub("AceConfigDropdown-3.0", true)
|
||||
|
||||
-- Lua APIs
|
||||
local pcall, error, type, pairs = pcall, error, type, pairs
|
||||
|
||||
-- -------------------------------------------------------------------
|
||||
-- :RegisterOptionsTable(appName, options, slashcmd, persist)
|
||||
--
|
||||
-- - appName - (string) application name
|
||||
-- - options - table or function ref, see AceConfigRegistry
|
||||
-- - slashcmd - slash command (string) or table with commands, or nil to NOT create a slash command
|
||||
|
||||
--- Register a option table with the AceConfig registry.
|
||||
-- You can supply a slash command (or a table of slash commands) to register with AceConfigCmd directly.
|
||||
-- @paramsig appName, options [, slashcmd]
|
||||
-- @param appName The application name for the config table.
|
||||
-- @param options The option table (or a function to generate one on demand). http://www.wowace.com/addons/ace3/pages/ace-config-3-0-options-tables/
|
||||
-- @param slashcmd A slash command to register for the option table, or a table of slash commands.
|
||||
-- @usage
|
||||
-- local AceConfig = LibStub("AceConfig-3.0")
|
||||
-- AceConfig:RegisterOptionsTable("MyAddon", myOptions, {"/myslash", "/my"})
|
||||
function AceConfig:RegisterOptionsTable(appName, options, slashcmd)
|
||||
if self == AceConfig then
|
||||
error([[Usage: RegisterOptionsTable(appName, options[, slashcmd]): 'self' - use your own 'self']], 2)
|
||||
end
|
||||
local ok,msg = pcall(cfgreg.RegisterOptionsTable, self, appName, options)
|
||||
if not ok then error(msg, 2) end
|
||||
|
||||
if slashcmd then
|
||||
if type(slashcmd) == "table" then
|
||||
for _,cmd in pairs(slashcmd) do
|
||||
cfgcmd.CreateChatCommand(self, cmd, appName)
|
||||
end
|
||||
else
|
||||
cfgcmd.CreateChatCommand(self, slashcmd, appName)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceConfig:Embed(target)
|
||||
target["RegisterOptionsTable"] = self["RegisterOptionsTable"]
|
||||
end
|
||||
|
||||
for addon in pairs(AceConfig.embeds) do
|
||||
AceConfig:Embed(addon)
|
||||
end
|
||||
@@ -0,0 +1,8 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Include file="AceConfigRegistry-3.0\AceConfigRegistry-3.0.xml"/>
|
||||
<Include file="AceConfigCmd-3.0\AceConfigCmd-3.0.xml"/>
|
||||
<Include file="AceConfigDialog-3.0\AceConfigDialog-3.0.xml"/>
|
||||
<!--<Include file="AceConfigDropdown-3.0\AceConfigDropdown-3.0.xml"/>-->
|
||||
<Script file="AceConfig-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,863 @@
|
||||
--- AceConfigCmd-3.0 handles access to an options table through the "command line" interface via the ChatFrames.
|
||||
-- @class file
|
||||
-- @name AceConfigCmd-3.0
|
||||
-- @release $Id: AceConfigCmd-3.0.lua 1045 2011-12-09 17:58:40Z nevcairiel $
|
||||
|
||||
--[[
|
||||
AceConfigCmd-3.0
|
||||
|
||||
Handles commandline optionstable access
|
||||
|
||||
REQUIRES: AceConsole-3.0 for command registration (loaded on demand)
|
||||
|
||||
]]
|
||||
|
||||
-- TODO: plugin args
|
||||
|
||||
|
||||
local MAJOR, MINOR = "AceConfigCmd-3.0", 13
|
||||
local AceConfigCmd = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not AceConfigCmd then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local Dispatchers = AceCore.Dispatchers
|
||||
local strtrim = AceCore.strtrim
|
||||
local strsplit = AceCore.strsplit
|
||||
local new, del = AceCore.new, AceCore.del
|
||||
local wipe = AceCore.wipe
|
||||
|
||||
AceConfigCmd.commands = AceConfigCmd.commands or {}
|
||||
AceConfigCmd.embeds = AceConfigCmd.embeds or {}
|
||||
local commands = AceConfigCmd.commands
|
||||
|
||||
local cfgreg = LibStub("AceConfigRegistry-3.0")
|
||||
local AceConsole -- LoD
|
||||
local AceConsoleName = "AceConsole-3.0"
|
||||
|
||||
-- Lua APIs
|
||||
local strbyte, strsub = string.byte, string.sub
|
||||
local strlen, strupper, strlower = string.len, string.upper, string.lower
|
||||
local strfind, strgfind, strgsub = string.find, string.gfind, string.gsub
|
||||
|
||||
local format = string.format
|
||||
local tsort, tinsert, tgetn, tremove = table.sort, table.insert, table.getn, table.remove
|
||||
|
||||
-- WoW APIs
|
||||
local _G = AceCore._G
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: LibStub, SELECTED_CHAT_FRAME, DEFAULT_CHAT_FRAME
|
||||
|
||||
|
||||
local L = setmetatable({}, { -- TODO: replace with proper locale
|
||||
__index = function(self,k) return k end
|
||||
})
|
||||
|
||||
|
||||
|
||||
local function print(msg)
|
||||
(SELECTED_CHAT_FRAME or DEFAULT_CHAT_FRAME):AddMessage(msg)
|
||||
end
|
||||
|
||||
-- constants used by getparam() calls below
|
||||
|
||||
local handlertypes = {["table"]=true}
|
||||
local handlermsg = "expected a table"
|
||||
|
||||
local functypes = {["function"]=true, ["string"]=true}
|
||||
local funcmsg = "expected function or member name"
|
||||
|
||||
local pickfirstset = AceCore.pickfirstset
|
||||
|
||||
-- err() - produce real error() regarding malformed options tables etc
|
||||
|
||||
local function err(info,inputpos,msg )
|
||||
local cmdstr=" "..strsub(info.input, 1, inputpos-1)
|
||||
error(MAJOR..": /" ..info[0] ..cmdstr ..": "..(msg or "malformed options table"), 2)
|
||||
end
|
||||
|
||||
|
||||
-- usererr() - produce chatframe message regarding bad slash syntax etc
|
||||
|
||||
local function usererr(info,inputpos,msg )
|
||||
local cmdstr=strsub(info.input, 1, inputpos-1);
|
||||
print("/" ..info[0] .. " "..cmdstr ..": "..(msg or "malformed options table"))
|
||||
end
|
||||
|
||||
|
||||
-- callmethod() - call a given named method (e.g. "get", "set") with given arguments
|
||||
|
||||
|
||||
|
||||
local function callmethod(info, inputpos, tab, methodtype, argc, a1, a2, a3, a4)
|
||||
local method = info[methodtype]
|
||||
if not method then
|
||||
err(info, inputpos, "'"..methodtype.."': not set")
|
||||
end
|
||||
|
||||
argc = argc or 0
|
||||
info.arg = tab.arg
|
||||
info.option = tab
|
||||
info.type = tab.type
|
||||
|
||||
if type(method)=="function" then
|
||||
return Dispatchers[argc+1](method, info, a1, a2, a3, a4)
|
||||
elseif type(method)=="string" then
|
||||
if type(info.handler[method])~="function" then
|
||||
err(info, inputpos, "'"..methodtype.."': '"..method.."' is not a member function of "..tostring(info.handler))
|
||||
end
|
||||
return Dispatchers[argc+2](info.handler[method], info.handler, info, a1, a2, a3, a4)
|
||||
else
|
||||
assert(false) -- type should have already been checked on read
|
||||
end
|
||||
end
|
||||
|
||||
-- callfunction() - call a given named function (e.g. "name", "desc") with given arguments
|
||||
|
||||
-- Ace3v: the variable arguments are currently unused, so we removed it
|
||||
local function callfunction(info, tab, methodtype)
|
||||
local method = tab[methodtype]
|
||||
|
||||
info.arg = tab.arg
|
||||
info.option = tab
|
||||
info.type = tab.type
|
||||
|
||||
if type(method)=="function" then
|
||||
return method(info)
|
||||
else
|
||||
assert(false) -- type should have already been checked on read
|
||||
end
|
||||
end
|
||||
|
||||
-- do_final() - do the final step (set/execute) along with validation and confirmation
|
||||
-- Ace3v: experimental
|
||||
-- @param argc number of variable arguments
|
||||
local function do_final(info, inputpos, tab, methodtype, argc, a1, a2, a3, a4) -- currently maximum 4 arguments
|
||||
if info.validate then
|
||||
local res = callmethod(info,inputpos,tab,"validate",argc,a1,a2,a3,a4)
|
||||
if type(res)=="string" then
|
||||
usererr(info, inputpos, "'"..strsub(info.input, inputpos).."' - "..res)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- console ignores .confirm
|
||||
|
||||
callmethod(info,inputpos,tab,methodtype,argc,a1,a2,a3,a4)
|
||||
end
|
||||
|
||||
|
||||
-- getparam() - used by handle() to retreive and store "handler", "get", "set", etc
|
||||
|
||||
local function getparam(info, inputpos, tab, depth, paramname, types, errormsg)
|
||||
local old,oldat = info[paramname], info[paramname.."_at"]
|
||||
local val=tab[paramname]
|
||||
if val~=nil then
|
||||
if val==false then
|
||||
val=nil
|
||||
elseif not types[type(val)] then
|
||||
err(info, inputpos, "'" .. paramname.. "' - "..errormsg)
|
||||
end
|
||||
info[paramname] = val
|
||||
info[paramname.."_at"] = depth
|
||||
end
|
||||
return old,oldat
|
||||
end
|
||||
|
||||
|
||||
-- iterateargs(tab) - custom iterator that iterates both t.args and t.plugins.*
|
||||
local function iterateargs(tab)
|
||||
if not tab.plugins then
|
||||
return pairs(tab.args)
|
||||
end
|
||||
|
||||
local argtabkey,argtab=next(tab.plugins)
|
||||
local v
|
||||
|
||||
return function(_, k)
|
||||
while argtab do
|
||||
k,v = next(argtab, k)
|
||||
if k then return k,v end
|
||||
if argtab==tab.args then
|
||||
argtab=nil
|
||||
else
|
||||
argtabkey,argtab = next(tab.plugins, argtabkey)
|
||||
if not argtabkey then
|
||||
argtab=tab.args
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function getValueFromTab(info, inputpos, tab, key)
|
||||
local v = tab[key]
|
||||
if type(v) == "function" or type(v) == "string" then
|
||||
info[key] = v
|
||||
v = callmethod(info, inputpos, tab, key)
|
||||
info[key] = nil
|
||||
end
|
||||
return v
|
||||
end
|
||||
|
||||
local function checkhidden(info, inputpos, tab)
|
||||
if tab.cmdHidden~=nil then
|
||||
return tab.cmdHidden
|
||||
end
|
||||
return getValueFromTab(info, inputpos, tab, "hidden")
|
||||
end
|
||||
|
||||
local function showhelp(info, inputpos, tab, depth, noHead)
|
||||
if not noHead then
|
||||
print("|cff33ff99"..info.appName.."|r: Arguments to |cffffff78/"..info[0].."|r "..strsub(info.input,1,inputpos-1)..":")
|
||||
end
|
||||
|
||||
local sortTbl = new() -- [1..n]=name
|
||||
local refTbl = new() -- [name]=tableref
|
||||
|
||||
for k,v in iterateargs(tab) do
|
||||
if not refTbl[k] then -- a plugin overriding something in .args
|
||||
tinsert(sortTbl, k)
|
||||
refTbl[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
tsort(sortTbl, function(one, two)
|
||||
local o1 = refTbl[one].order or 100
|
||||
local o2 = refTbl[two].order or 100
|
||||
if type(o1) == "function" or type(o1) == "string" then
|
||||
info.order = o1
|
||||
tinsert(info, one)
|
||||
o1 = callmethod(info, inputpos, refTbl[one], "order")
|
||||
tremove(info)
|
||||
info.order = nil
|
||||
end
|
||||
if type(o2) == "function" or type(o1) == "string" then
|
||||
info.order = o2
|
||||
tinsert(info, two)
|
||||
o2 = callmethod(info, inputpos, refTbl[two], "order")
|
||||
tremove(info)
|
||||
info.order = nil
|
||||
end
|
||||
if o1<0 and o2<0 then return o1<o2 end
|
||||
if o2<0 then return true end
|
||||
if o1<0 then return false end
|
||||
if o1==o2 then return tostring(one)<tostring(two) end -- compare names
|
||||
return o1<o2
|
||||
end)
|
||||
|
||||
for i = 1, tgetn(sortTbl) do
|
||||
local k = sortTbl[i]
|
||||
local v = refTbl[k]
|
||||
if not checkhidden(info, inputpos, v) then
|
||||
if v.type ~= "description" and v.type ~= "header" then
|
||||
-- recursively show all inline groups
|
||||
local name, desc = v.name, v.desc
|
||||
if type(name) == "function" then
|
||||
name = callfunction(info, v, 'name')
|
||||
end
|
||||
if type(desc) == "function" then
|
||||
desc = callfunction(info, v, 'desc')
|
||||
end
|
||||
if v.type == "group" and pickfirstset(3, v.cmdInline, v.inline, false) then
|
||||
print(" "..(desc or name)..":")
|
||||
local oldhandler,oldhandler_at = getparam(info, inputpos, v, depth, "handler", handlertypes, handlermsg)
|
||||
showhelp(info, inputpos, v, depth, true)
|
||||
info.handler,info.handler_at = oldhandler,oldhandler_at
|
||||
else
|
||||
local key = strgsub(k, " ", "_")
|
||||
print(" |cffffff78"..key.."|r - "..(desc or name or ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
del(sortTbl) -- Ace3v: release the tables
|
||||
del(refTbl)
|
||||
end
|
||||
|
||||
local function keybindingValidateFunc(text)
|
||||
if text == nil or text == "NONE" then
|
||||
return nil
|
||||
end
|
||||
text = strupper(text)
|
||||
local shift, ctrl, alt
|
||||
local modifier
|
||||
while true do
|
||||
if text == "-" then
|
||||
break
|
||||
end
|
||||
modifier, text = strsplit('-', text, 2)
|
||||
if text then
|
||||
if modifier ~= "SHIFT" and modifier ~= "CTRL" and modifier ~= "ALT" then
|
||||
return false
|
||||
end
|
||||
if modifier == "SHIFT" then
|
||||
if shift then
|
||||
return false
|
||||
end
|
||||
shift = true
|
||||
end
|
||||
if modifier == "CTRL" then
|
||||
if ctrl then
|
||||
return false
|
||||
end
|
||||
ctrl = true
|
||||
end
|
||||
if modifier == "ALT" then
|
||||
if alt then
|
||||
return false
|
||||
end
|
||||
alt = true
|
||||
end
|
||||
else
|
||||
text = modifier
|
||||
break
|
||||
end
|
||||
end
|
||||
if text == "" then
|
||||
return false
|
||||
end
|
||||
if not strfind(text,"^F%d+$") and text ~= "CAPSLOCK" and strlen(text) ~= 1 and (strbyte(text) < 128 or strlen(text) > 4) and not _G["KEY_" .. text] then
|
||||
return false
|
||||
end
|
||||
local s = text
|
||||
if shift then
|
||||
s = "SHIFT-" .. s
|
||||
end
|
||||
if ctrl then
|
||||
s = "CTRL-" .. s
|
||||
end
|
||||
if alt then
|
||||
s = "ALT-" .. s
|
||||
end
|
||||
return s
|
||||
end
|
||||
|
||||
-- handle() - selfrecursing function that processes input->optiontable
|
||||
-- - depth - starts at 0
|
||||
-- - retfalse - return false rather than produce error if a match is not found (used by inlined groups)
|
||||
|
||||
local function handle(info, inputpos, tab, depth, retfalse)
|
||||
|
||||
if not(type(tab)=="table" and type(tab.type)=="string") then err(info,inputpos) end
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Grab hold of handler,set,get,func,etc if set (and remember old ones)
|
||||
-- Note that we do NOT validate if method names are correct at this stage,
|
||||
-- the handler may change before they're actually used!
|
||||
|
||||
local oldhandler,oldhandler_at = getparam(info,inputpos,tab,depth,"handler",handlertypes,handlermsg)
|
||||
local oldset,oldset_at = getparam(info,inputpos,tab,depth,"set",functypes,funcmsg)
|
||||
local oldget,oldget_at = getparam(info,inputpos,tab,depth,"get",functypes,funcmsg)
|
||||
local oldfunc,oldfunc_at = getparam(info,inputpos,tab,depth,"func",functypes,funcmsg)
|
||||
local oldvalidate,oldvalidate_at = getparam(info,inputpos,tab,depth,"validate",functypes,funcmsg)
|
||||
--local oldconfirm,oldconfirm_at = getparam(info,inputpos,tab,depth,"confirm",functypes,funcmsg)
|
||||
|
||||
-------------------------------------------------------------------
|
||||
-- Act according to .type of this table
|
||||
|
||||
if tab.type=="group" then
|
||||
------------ group --------------------------------------------
|
||||
|
||||
if type(tab.args)~="table" then err(info, inputpos) end
|
||||
if tab.plugins and type(tab.plugins)~="table" then err(info,inputpos) end
|
||||
|
||||
-- grab next arg from input
|
||||
local _,nextpos,arg = strfind(info.input, " *([^ ]+) *", inputpos)
|
||||
if not arg then
|
||||
showhelp(info, inputpos, tab, depth)
|
||||
return
|
||||
end
|
||||
nextpos=nextpos+1
|
||||
|
||||
-- loop .args and try to find a key with a matching name
|
||||
for k,v in iterateargs(tab) do
|
||||
if not(type(k)=="string" and type(v)=="table" and type(v.type)=="string") then err(info,inputpos, "options table child '"..tostring(k).."' is malformed") end
|
||||
|
||||
-- is this child an inline group? if so, traverse into it
|
||||
if v.type=="group" and pickfirstset(3, v.cmdInline, v.inline, false) then
|
||||
tinsert(info,k)
|
||||
if handle(info, inputpos, v, depth+1, true)==false then
|
||||
tremove(info)
|
||||
-- wasn't found in there, but that's ok, we just keep looking down here
|
||||
else
|
||||
return -- done, name was found in inline group
|
||||
end
|
||||
-- matching name and not a inline group
|
||||
elseif strlower(arg)==strlower(strgsub(k, " ", "_")) then
|
||||
tinsert(info,k)
|
||||
return handle(info,nextpos,v,depth+1)
|
||||
end
|
||||
end
|
||||
|
||||
-- no match
|
||||
if retfalse then
|
||||
-- restore old infotable members and return false to indicate failure
|
||||
info.handler,info.handler_at = oldhandler,oldhandler_at
|
||||
info.set,info.set_at = oldset,oldset_at
|
||||
info.get,info.get_at = oldget,oldget_at
|
||||
info.func,info.func_at = oldfunc,oldfunc_at
|
||||
info.validate,info.validate_at = oldvalidate,oldvalidate_at
|
||||
--info.confirm,info.confirm_at = oldconfirm,oldconfirm_at
|
||||
return false
|
||||
end
|
||||
|
||||
-- couldn't find the command, display error
|
||||
usererr(info, inputpos, "'"..arg.."' - " .. L["unknown argument"])
|
||||
return
|
||||
end
|
||||
|
||||
local str = strsub(info.input,inputpos);
|
||||
|
||||
if tab.type=="execute" then
|
||||
------------ execute --------------------------------------------
|
||||
do_final(info, inputpos, tab, "func")
|
||||
|
||||
|
||||
|
||||
elseif tab.type=="input" then
|
||||
------------ input --------------------------------------------
|
||||
|
||||
if str=="" and tab.nullable == false then
|
||||
usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"])
|
||||
return
|
||||
end
|
||||
|
||||
local res = true
|
||||
if tab.pattern then
|
||||
if not(type(tab.pattern)=="string") then err(info, inputpos, "'pattern' - expected a string") end
|
||||
if not strfind(str, tab.pattern) then
|
||||
usererr(info, inputpos, "'"..str.."' - " .. L["invalid input"])
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 1, str)
|
||||
|
||||
|
||||
|
||||
elseif tab.type=="toggle" then
|
||||
------------ toggle --------------------------------------------
|
||||
local b
|
||||
local str = strtrim(strlower(str))
|
||||
if str=="" then
|
||||
b = callmethod(info, inputpos, tab, "get")
|
||||
|
||||
if tab.tristate then
|
||||
--cycle in true, nil, false order
|
||||
if b then
|
||||
b = nil
|
||||
elseif b == nil then
|
||||
b = false
|
||||
else
|
||||
b = true
|
||||
end
|
||||
else
|
||||
b = not b
|
||||
end
|
||||
|
||||
elseif str==L["on"] then
|
||||
b = true
|
||||
elseif str==L["off"] then
|
||||
b = false
|
||||
elseif tab.tristate and str==L["default"] then
|
||||
b = nil
|
||||
else
|
||||
if tab.tristate then
|
||||
usererr(info, inputpos, format(L["'%s' - expected 'on', 'off' or 'default', or no argument to toggle."], str))
|
||||
else
|
||||
usererr(info, inputpos, format(L["'%s' - expected 'on' or 'off', or no argument to toggle."], str))
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 1, b)
|
||||
|
||||
|
||||
elseif tab.type=="range" then
|
||||
------------ range --------------------------------------------
|
||||
local str = strtrim(strlower(str))
|
||||
if str == "" then
|
||||
-- TODO: Show current value
|
||||
return
|
||||
end
|
||||
|
||||
local val = tonumber(str)
|
||||
if not val then
|
||||
usererr(info, inputpos, "'"..str.."' - "..L["expected number"])
|
||||
return
|
||||
end
|
||||
|
||||
local step = getValueFromTab(info, inputpos, tab, "step")
|
||||
local min = getValueFromTab(info, inputpos, tab, "min")
|
||||
|
||||
if type(step)=="number" then
|
||||
val = min + math.floor((val-min)/step) * step
|
||||
end
|
||||
|
||||
if type(min)=="number" and val<min then
|
||||
usererr(info, inputpos, val.." - "..format(L["must be equal to or higher than %s"], tostring(min)) )
|
||||
return
|
||||
end
|
||||
|
||||
local max = getValueFromTab(info, inputpos, tab, "max")
|
||||
if type(max)=="number" and val>max then
|
||||
usererr(info, inputpos, val.." - "..format(L["must be equal to or lower than %s"], tostring(max)) )
|
||||
return
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 1, val)
|
||||
|
||||
|
||||
elseif tab.type=="select" then
|
||||
------------ select ------------------------------------
|
||||
local str = strtrim(strlower(str))
|
||||
|
||||
local values = getValueFromTab(info, inputpos, tab, "values")
|
||||
|
||||
if str == "" then
|
||||
-- Ace3v: it is possbile to not have a current value
|
||||
-- we do this only for select but not for multiselect
|
||||
local b = tab.get
|
||||
if type(b) == "function" or type(b) == "string" then
|
||||
b = callmethod(info, inputpos, tab, "get")
|
||||
else
|
||||
b = nil
|
||||
end
|
||||
|
||||
local fmt = "|cffffff78- [%s]|r %s"
|
||||
local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r"
|
||||
print(L["Options for |cffffff78"..info[tgetn(info)].."|r:"])
|
||||
for k, v in pairs(values) do
|
||||
if b == k then
|
||||
print(format(fmt_sel, k, v))
|
||||
else
|
||||
print(format(fmt, k, v))
|
||||
end
|
||||
end
|
||||
if tab.valuesTableDestroyable then del(values) end
|
||||
return
|
||||
end
|
||||
|
||||
local ok
|
||||
for k,v in pairs(values) do
|
||||
if strlower(k)==str then
|
||||
str = k -- overwrite with key (in case of case mismatches)
|
||||
ok = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if tab.valuesTableDestroyable then del(values) end
|
||||
if not ok then
|
||||
usererr(info, inputpos, "'"..str.."' - "..L["unknown selection"])
|
||||
return
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 1, str)
|
||||
|
||||
elseif tab.type=="multiselect" then
|
||||
------------ multiselect -------------------------------------------
|
||||
local str = strtrim(strlower(str))
|
||||
|
||||
local values = getValueFromTab(info, inputpos, tab, "values")
|
||||
|
||||
if str == "" then
|
||||
local fmt = "|cffffff78- [%s]|r %s"
|
||||
local fmt_sel = "|cffffff78- [%s]|r %s |cffff0000*|r"
|
||||
print(L["Options for |cffffff78"..info[tgetn(info)].."|r (multiple possible):"])
|
||||
for k, v in pairs(values) do
|
||||
if callmethod(info, inputpos, tab, "get", 1, k) then
|
||||
print(format(fmt_sel, k, v))
|
||||
else
|
||||
print(format(fmt, k, v))
|
||||
end
|
||||
end
|
||||
if tab.valuesTableDestroyable then del(values) end
|
||||
return
|
||||
end
|
||||
|
||||
--build a table of the selections, checking that they exist
|
||||
--parse for =on =off =default in the process
|
||||
--table will be key = true for options that should toggle, key = [on|off|default] for options to be set
|
||||
local sels = new()
|
||||
for v in strgfind(str, "[^ ]+") do
|
||||
--parse option=on etc
|
||||
local _, _, opt, val = strfind(v, '(.+)=(.+)')
|
||||
--get option if toggling
|
||||
if not opt then
|
||||
opt = v
|
||||
end
|
||||
|
||||
--check that the opt is valid
|
||||
local ok
|
||||
for k,v in pairs(values) do
|
||||
if strlower(k)==opt then
|
||||
opt = k -- overwrite with key (in case of case mismatches)
|
||||
ok = true
|
||||
break
|
||||
end
|
||||
end
|
||||
if tab.valuesTableDestroyable then del(values) end
|
||||
|
||||
if not ok then
|
||||
usererr(info, inputpos, "'"..opt.."' - "..L["unknown selection"])
|
||||
return
|
||||
end
|
||||
|
||||
--check that if val was supplied it is valid
|
||||
if val then
|
||||
if val == L["on"] or val == L["off"] or (tab.tristate and val == L["default"]) then
|
||||
--val is valid insert it
|
||||
sels[opt] = val
|
||||
else
|
||||
if tab.tristate then
|
||||
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on', 'off' or 'default', or no argument to toggle."], v, val))
|
||||
else
|
||||
usererr(info, inputpos, format(L["'%s' '%s' - expected 'on' or 'off', or no argument to toggle."], v, val))
|
||||
end
|
||||
del(sels)
|
||||
return
|
||||
end
|
||||
else
|
||||
-- no val supplied, toggle
|
||||
sels[opt] = true
|
||||
end
|
||||
end
|
||||
|
||||
for opt, val in pairs(sels) do
|
||||
local newval
|
||||
|
||||
if (val == true) then
|
||||
--toggle the option
|
||||
local b = callmethod(info, inputpos, tab, "get", 1, opt)
|
||||
|
||||
if tab.tristate then
|
||||
--cycle in true, nil, false order
|
||||
if b then
|
||||
b = nil
|
||||
elseif b == nil then
|
||||
b = false
|
||||
else
|
||||
b = true
|
||||
end
|
||||
else
|
||||
b = not b
|
||||
end
|
||||
newval = b
|
||||
else
|
||||
--set the option as specified
|
||||
if val==L["on"] then
|
||||
newval = true
|
||||
elseif val==L["off"] then
|
||||
newval = false
|
||||
elseif val==L["default"] then
|
||||
newval = nil
|
||||
end
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 2, opt, newval)
|
||||
end
|
||||
del(sels)
|
||||
|
||||
|
||||
elseif tab.type=="color" then
|
||||
------------ color --------------------------------------------
|
||||
local str = strtrim(strlower(str))
|
||||
if str == "" then
|
||||
--TODO: Show current value
|
||||
return
|
||||
end
|
||||
|
||||
local _, r, g, b, a
|
||||
|
||||
local hasAlpha = getValueFromTab(info, inputpos, tab, 'hasAlpha')
|
||||
|
||||
if hasAlpha then
|
||||
if strlen(str) == 8 and strfind(str, "^%x*$") then
|
||||
--parse a hex string
|
||||
r,g,b,a = tonumber(strsub(str, 1, 2), 16) / 255, tonumber(strsub(str, 3, 4), 16) / 255, tonumber(strsub(str, 5, 6), 16) / 255, tonumber(strsub(str, 7, 8), 16) / 255
|
||||
else
|
||||
--parse seperate values
|
||||
_,_,r,g,b,a = strfind(str, "^([%d%.]+) ([%d%.]+) ([%d%.]+) ([%d%.]+)$")
|
||||
r,g,b,a = tonumber(r), tonumber(g), tonumber(b), tonumber(a)
|
||||
end
|
||||
if not (r and g and b and a) then
|
||||
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBBAA' or 'r g b a'."], str))
|
||||
return
|
||||
end
|
||||
|
||||
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 and a >= 0.0 and a <= 1.0 then
|
||||
--values are valid
|
||||
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 and a >= 0 and a <= 255 then
|
||||
--values are valid 0..255, convert to 0..1
|
||||
r = r / 255
|
||||
g = g / 255
|
||||
b = b / 255
|
||||
a = a / 255
|
||||
else
|
||||
--values are invalid
|
||||
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0..1 or 0..255."], str))
|
||||
end
|
||||
else
|
||||
a = 1.0
|
||||
if strlen(str) == 6 and strfind(str, "^%x*$") then
|
||||
--parse a hex string
|
||||
r,g,b = tonumber(strsub(str, 1, 2), 16) / 255, tonumber(strsub(str, 3, 4), 16) / 255, tonumber(strsub(str, 5, 6), 16) / 255
|
||||
else
|
||||
--parse seperate values
|
||||
_,_,r,g,b = strfind(str, "^([%d%.]+) ([%d%.]+) ([%d%.]+)$")
|
||||
r,g,b = tonumber(r), tonumber(g), tonumber(b)
|
||||
end
|
||||
if not (r and g and b) then
|
||||
usererr(info, inputpos, format(L["'%s' - expected 'RRGGBB' or 'r g b'."], str))
|
||||
return
|
||||
end
|
||||
if r >= 0.0 and r <= 1.0 and g >= 0.0 and g <= 1.0 and b >= 0.0 and b <= 1.0 then
|
||||
--values are valid
|
||||
elseif r >= 0 and r <= 255 and g >= 0 and g <= 255 and b >= 0 and b <= 255 then
|
||||
--values are valid 0..255, convert to 0..1
|
||||
r = r / 255
|
||||
g = g / 255
|
||||
b = b / 255
|
||||
else
|
||||
--values are invalid
|
||||
usererr(info, inputpos, format(L["'%s' - values must all be either in the range 0-1 or 0-255."], str))
|
||||
end
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 4, r,g,b,a)
|
||||
|
||||
elseif tab.type=="keybinding" then
|
||||
------------ keybinding --------------------------------------------
|
||||
local str = strtrim(strlower(str))
|
||||
if str == "" then
|
||||
--TODO: Show current value
|
||||
return
|
||||
end
|
||||
local value = keybindingValidateFunc(strupper(str))
|
||||
if value == false then
|
||||
usererr(info, inputpos, format(L["'%s' - Invalid Keybinding."], str))
|
||||
return
|
||||
end
|
||||
|
||||
do_final(info, inputpos, tab, "set", 1, value)
|
||||
|
||||
elseif tab.type=="description" then
|
||||
------------ description --------------------
|
||||
-- ignore description, GUI config only
|
||||
else
|
||||
err(info, inputpos, "unknown options table item type '"..tostring(tab.type).."'")
|
||||
end
|
||||
end
|
||||
|
||||
--- Handle the chat command.
|
||||
-- This is usually called from a chat command handler to parse the command input as operations on an aceoptions table.\\
|
||||
-- AceConfigCmd uses this function internally when a slash command is registered with `:CreateChatCommand`
|
||||
-- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
|
||||
-- @param appName The application name as given to `:RegisterOptionsTable()`
|
||||
-- @param input The commandline input (as given by the WoW handler, i.e. without the command itself)
|
||||
-- @usage
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("MyAddon", "AceConsole-3.0")
|
||||
-- -- Use AceConsole-3.0 to register a Chat Command
|
||||
-- MyAddon:RegisterChatCommand("mychat", "ChatCommand")
|
||||
--
|
||||
-- -- Show the GUI if no input is supplied, otherwise handle the chat input.
|
||||
-- function MyAddon:ChatCommand(input)
|
||||
-- -- Assuming "MyOptions" is the appName of a valid options table
|
||||
-- if not input or input:trim() == "" then
|
||||
-- LibStub("AceConfigDialog-3.0"):Open("MyOptions")
|
||||
-- else
|
||||
-- LibStub("AceConfigCmd-3.0").HandleCommand(MyAddon, "mychat", "MyOptions", input)
|
||||
-- end
|
||||
-- end
|
||||
-- Ace3v: experimental, user should copy info table if he wanna reuse it outside
|
||||
-- then handler
|
||||
local info_ = {}
|
||||
function AceConfigCmd:HandleCommand(slashcmd, appName, input)
|
||||
|
||||
local optgetter = cfgreg:GetOptionsTable(appName)
|
||||
if not optgetter then
|
||||
error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'appName' - no options table "]]..tostring(appName)..[[" has been registered]], 2)
|
||||
end
|
||||
local options = assert( optgetter("cmd", MAJOR) )
|
||||
|
||||
-- Ace3v: prevent user from using AceConfigCmd as self
|
||||
if self == AceConfigCmd then
|
||||
error([[Usage: HandleCommand("slashcmd", "appName", "input"): 'self' - use your own 'self']], 2)
|
||||
end
|
||||
|
||||
--local info = { -- Don't try to recycle this, it gets handed off to callbacks and whatnot
|
||||
-- [0] = slashcmd,
|
||||
-- appName = appName,
|
||||
-- options = options,
|
||||
-- input = input,
|
||||
-- self = self,
|
||||
-- handler = self,
|
||||
-- uiType = "cmd",
|
||||
-- uiName = MAJOR,
|
||||
--}
|
||||
|
||||
wipe(info_)
|
||||
info_[0] = slashcmd
|
||||
info_.appName = appName
|
||||
info_.options = options
|
||||
info_.input = input
|
||||
info_.self = self
|
||||
info_.handler = self
|
||||
info_.uiType = "cmd"
|
||||
info_.uiName = MAJOR
|
||||
|
||||
handle(info_, 1, options, 0) -- (info, inputpos, table, depth)
|
||||
end
|
||||
|
||||
--- Utility function to create a slash command handler.
|
||||
-- Also registers tab completion with AceTab
|
||||
-- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
|
||||
-- @param appName The application name as given to `:RegisterOptionsTable()`
|
||||
function AceConfigCmd:CreateChatCommand(slashcmd, appName, func)
|
||||
if not AceConsole then
|
||||
AceConsole = LibStub(AceConsoleName)
|
||||
end
|
||||
|
||||
-- Ace3v: prevent user from using AceConfigCmd as self
|
||||
if self == AceConfigCmd then
|
||||
error([[Usage: CreateChatCommand("slashcmd", "appName"[, "func"]): 'self' - use your own 'self']], 2)
|
||||
end
|
||||
|
||||
local t = type(func)
|
||||
|
||||
-- Ace3v: make it possible to call another function
|
||||
local handler
|
||||
if t == "string" then
|
||||
handler = function(input) self[func](self, input, slashcmd, appName) end
|
||||
elseif t ~= "function" then
|
||||
handler = function(input)
|
||||
AceConfigCmd.HandleCommand(self, slashcmd, appName, input) -- upgradable
|
||||
end
|
||||
else
|
||||
handler = func
|
||||
end
|
||||
|
||||
if AceConsole.RegisterChatCommand(self, slashcmd, handler, true) then
|
||||
-- succesfully registered so lets get the command -> app table in
|
||||
commands[slashcmd] = appName
|
||||
end
|
||||
end
|
||||
|
||||
--- Utility function that returns the options table that belongs to a slashcommand.
|
||||
-- Designed to be used for the AceTab interface.
|
||||
-- @param slashcmd The slash command WITHOUT leading slash (only used for error output)
|
||||
-- @return The options table associated with the slash command (or nil if the slash command was not registered)
|
||||
function AceConfigCmd:GetChatCommandOptions(slashcmd)
|
||||
return commands[slashcmd]
|
||||
end
|
||||
|
||||
function AceConfigCmd:Embed(target)
|
||||
target["HandleCommand"] = self["HandleCommand"]
|
||||
target["CreateChatCommand"] = self["CreateChatCommand"]
|
||||
end
|
||||
|
||||
for addon in pairs(AceConfigCmd.embeds) do
|
||||
AceConfigCmd:Embed(addon)
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceConfigCmd-3.0.lua"/>
|
||||
</Ui>
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceConfigDialog-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,352 @@
|
||||
--- AceConfigRegistry-3.0 handles central registration of options tables in use by addons and modules.\\
|
||||
-- Options tables can be registered as raw tables, OR as function refs that return a table.\\
|
||||
-- Such functions receive three arguments: "uiType", "uiName", "appName". \\
|
||||
-- * Valid **uiTypes**: "cmd", "dropdown", "dialog". This is verified by the library at call time. \\
|
||||
-- * The **uiName** field is expected to contain the full name of the calling addon, including version, e.g. "FooBar-1.0". This is verified by the library at call time.\\
|
||||
-- * The **appName** field is the options table name as given at registration time \\
|
||||
--
|
||||
-- :IterateOptionsTables() (and :GetOptionsTable() if only given one argument) return a function reference that the requesting config handling addon must call with valid "uiType", "uiName".
|
||||
-- @class file
|
||||
-- @name AceConfigRegistry-3.0
|
||||
-- @release $Id: AceConfigRegistry-3.0.lua 1139 2016-07-03 07:43:51Z nevcairiel $
|
||||
local MAJOR, MINOR = "AceConfigRegistry-3.0", 16
|
||||
local AceConfigRegistry = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not AceConfigRegistry then return end
|
||||
|
||||
AceConfigRegistry.tables = AceConfigRegistry.tables or {}
|
||||
|
||||
local CallbackHandler = LibStub:GetLibrary("CallbackHandler-1.0")
|
||||
|
||||
if not AceConfigRegistry.callbacks then
|
||||
AceConfigRegistry.callbacks = CallbackHandler:New(AceConfigRegistry)
|
||||
end
|
||||
|
||||
-- Lua APIs
|
||||
local tinsert, tconcat, tgetn = table.insert, table.concat, table.getn
|
||||
local strfind = string.find
|
||||
local type, tostring, pairs = type, tostring, pairs
|
||||
local error, assert = error, assert
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- Validating options table consistency:
|
||||
|
||||
|
||||
AceConfigRegistry.validated = {
|
||||
-- list of options table names ran through :ValidateOptionsTable automatically.
|
||||
-- CLEARED ON PURPOSE, since newer versions may have newer validators
|
||||
cmd = {},
|
||||
dropdown = {},
|
||||
dialog = {},
|
||||
}
|
||||
|
||||
local function err(msg, errlvl, ...)
|
||||
local l = tgetn(arg)
|
||||
local i,j = 1,l
|
||||
while i < j do
|
||||
arg[i], arg[j] = arg[j], arg[i]
|
||||
i = i+1
|
||||
j = j-1
|
||||
end
|
||||
error(MAJOR..":ValidateOptionsTable(): "..tconcat(arg,".")..msg, errlvl+2)
|
||||
end
|
||||
|
||||
local isstring={["string"]=true, _="string"}
|
||||
local isstringfunc={["string"]=true,["function"]=true, _="string or funcref"}
|
||||
local istable={["table"]=true, _="table"}
|
||||
local ismethodtable={["table"]=true,["string"]=true,["function"]=true, _="methodname, funcref or table"}
|
||||
local optstring={["nil"]=true,["string"]=true, _="string"}
|
||||
local optstringfunc={["nil"]=true,["string"]=true,["function"]=true, _="string or funcref"}
|
||||
local optstringnumberfunc={["nil"]=true,["string"]=true,["number"]=true,["function"]=true, _="string, number or funcref"}
|
||||
local optnumber={["nil"]=true,["number"]=true, _="number"}
|
||||
local optmethod={["nil"]=true,["string"]=true,["function"]=true, _="methodname or funcref"}
|
||||
local optmethodfalse={["nil"]=true,["string"]=true,["function"]=true,["boolean"]={[false]=true}, _="methodname, funcref or false"}
|
||||
local optmethodnumber={["nil"]=true,["string"]=true,["function"]=true,["number"]=true, _="methodname, funcref or number"}
|
||||
local optmethodtable={["nil"]=true,["string"]=true,["function"]=true,["table"]=true, _="methodname, funcref or table"}
|
||||
local optmethodbool={["nil"]=true,["string"]=true,["function"]=true,["boolean"]=true, _="methodname, funcref or boolean"}
|
||||
local opttable={["nil"]=true,["table"]=true, _="table"}
|
||||
local optbool={["nil"]=true,["boolean"]=true, _="boolean"}
|
||||
local optboolnumber={["nil"]=true,["boolean"]=true,["number"]=true, _="boolean or number"}
|
||||
|
||||
local basekeys={
|
||||
type=isstring,
|
||||
name=isstringfunc,
|
||||
desc=optstringfunc,
|
||||
descStyle=optstring,
|
||||
order=optmethodnumber,
|
||||
validate=optmethodfalse,
|
||||
confirm=optmethodbool,
|
||||
confirmText=optstring,
|
||||
disabled=optmethodbool,
|
||||
hidden=optmethodbool,
|
||||
guiHidden=optmethodbool,
|
||||
dialogHidden=optmethodbool,
|
||||
dropdownHidden=optmethodbool,
|
||||
cmdHidden=optmethodbool,
|
||||
icon=optstringnumberfunc,
|
||||
iconCoords=optmethodtable,
|
||||
handler=opttable,
|
||||
get=optmethodfalse,
|
||||
set=optmethodfalse,
|
||||
func=optmethodfalse,
|
||||
arg={["*"]=true},
|
||||
width=optstring,
|
||||
}
|
||||
|
||||
local typedkeys={
|
||||
header={},
|
||||
description={
|
||||
image=optstringnumberfunc,
|
||||
imageCoords=optmethodtable,
|
||||
imageHeight=optnumber,
|
||||
imageWidth=optnumber,
|
||||
fontSize=optstringfunc,
|
||||
},
|
||||
group={
|
||||
args=istable,
|
||||
plugins=opttable,
|
||||
inline=optbool,
|
||||
cmdInline=optbool,
|
||||
guiInline=optbool,
|
||||
dropdownInline=optbool,
|
||||
dialogInline=optbool,
|
||||
childGroups=optstring,
|
||||
},
|
||||
execute={
|
||||
image=optstringnumberfunc,
|
||||
imageCoords=optmethodtable,
|
||||
imageHeight=optnumber,
|
||||
imageWidth=optnumber,
|
||||
},
|
||||
input={
|
||||
pattern=optstring,
|
||||
usage=optstring,
|
||||
control=optstring,
|
||||
dialogControl=optstring,
|
||||
dropdownControl=optstring,
|
||||
multiline=optboolnumber,
|
||||
nullable=optbool,
|
||||
},
|
||||
toggle={
|
||||
tristate=optbool,
|
||||
image=optstringnumberfunc,
|
||||
imageCoords=optmethodtable,
|
||||
},
|
||||
tristate={
|
||||
},
|
||||
range={
|
||||
min=optnumber,
|
||||
softMin=optnumber,
|
||||
max=optnumber,
|
||||
softMax=optnumber,
|
||||
step=optnumber,
|
||||
bigStep=optnumber,
|
||||
isPercent=optbool,
|
||||
},
|
||||
select={
|
||||
values=ismethodtable,
|
||||
valuesTableDestroyable=optbool, -- Ace3v: if the values table is generated by AceCore.new
|
||||
style={
|
||||
["nil"]=true,
|
||||
["string"]={dropdown=true,radio=true},
|
||||
_="string: 'dropdown' or 'radio'"
|
||||
},
|
||||
control=optstring,
|
||||
dialogControl=optstring,
|
||||
dropdownControl=optstring,
|
||||
itemControl=optstring,
|
||||
},
|
||||
multiselect={
|
||||
values=ismethodtable,
|
||||
valuesTableDestroyable=optbool, -- Ace3v: if the values table is generated by AceCore.new
|
||||
style=optstring,
|
||||
tristate=optbool,
|
||||
control=optstring,
|
||||
dialogControl=optstring,
|
||||
dropdownControl=optstring,
|
||||
},
|
||||
color={
|
||||
hasAlpha=optmethodbool,
|
||||
},
|
||||
keybinding={
|
||||
-- TODO
|
||||
},
|
||||
}
|
||||
|
||||
local function validateKey(k,errlvl,arg)
|
||||
errlvl=(errlvl or 0)+1
|
||||
if type(k)~="string" then
|
||||
err("["..tostring(k).."] - key is not a string", errlvl, arg)
|
||||
end
|
||||
if strfind(k, "[%c\127]") then
|
||||
err("["..tostring(k).."] - key name contained control characters", errlvl, arg)
|
||||
end
|
||||
end
|
||||
|
||||
local function validateVal(v, oktypes, errlvl, arg)
|
||||
errlvl=(errlvl or 0)+1
|
||||
local isok=oktypes[type(v)] or oktypes["*"]
|
||||
|
||||
if not isok then
|
||||
err(": expected a "..oktypes._..", got '"..tostring(v).."'", errlvl, arg)
|
||||
end
|
||||
if type(isok)=="table" then -- isok was a table containing specific values to be tested for!
|
||||
if not isok[v] then
|
||||
err(": did not expect "..type(v).." value '"..tostring(v).."'", errlvl, arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function validate(options,errlvl,arg)
|
||||
errlvl=(errlvl or 0)+1
|
||||
-- basic consistency
|
||||
if type(options)~="table" then
|
||||
err(": expected a table, got a "..type(options), errlvl, arg)
|
||||
end
|
||||
if type(options.type)~="string" then
|
||||
err(".type: expected a string, got a "..type(options.type), errlvl, arg)
|
||||
end
|
||||
|
||||
-- get type and 'typedkeys' member
|
||||
local tk = typedkeys[options.type]
|
||||
if not tk then
|
||||
err(".type: unknown type '"..options.type.."'", errlvl, arg)
|
||||
end
|
||||
|
||||
-- make sure that all options[] are known parameters
|
||||
for k,v in pairs(options) do
|
||||
if not (tk[k] or basekeys[k]) then
|
||||
err(": unknown parameter", errlvl,tostring(k), arg)
|
||||
end
|
||||
end
|
||||
|
||||
-- verify that required params are there, and that everything is the right type
|
||||
for k,oktypes in pairs(basekeys) do
|
||||
validateVal(options[k], oktypes, errlvl, k, arg)
|
||||
end
|
||||
for k,oktypes in pairs(tk) do
|
||||
validateVal(options[k], oktypes, errlvl, k, arg)
|
||||
end
|
||||
|
||||
-- extra logic for groups
|
||||
if options.type=="group" then
|
||||
for k,v in pairs(options.args) do
|
||||
validateKey(k,errlvl,"args", arg)
|
||||
validate(v, errlvl,k,"args", arg)
|
||||
end
|
||||
if options.plugins then
|
||||
for plugname,plugin in pairs(options.plugins) do
|
||||
if type(plugin)~="table" then
|
||||
err(": expected a table, got '"..tostring(plugin).."'", errlvl,tostring(plugname), "plugins", arg)
|
||||
end
|
||||
for k,v in pairs(plugin) do
|
||||
validateKey(k,errlvl,tostring(plugname),"plugins",arg)
|
||||
validate(v, errlvl,k,tostring(plugname),"plugins",arg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--- Validates basic structure and integrity of an options table \\
|
||||
-- Does NOT verify that get/set etc actually exist, since they can be defined at any depth
|
||||
-- @param options The table to be validated
|
||||
-- @param name The name of the table to be validated (shown in any error message)
|
||||
-- @param errlvl (optional number) error level offset, default 0 (=errors point to the function calling :ValidateOptionsTable)
|
||||
function AceConfigRegistry:ValidateOptionsTable(options,name,errlvl)
|
||||
errlvl=(errlvl or 0)+1
|
||||
name = name or "Optionstable"
|
||||
if not options.name then
|
||||
options.name=name -- bit of a hack, the root level doesn't really need a .name :-/
|
||||
end
|
||||
validate(options,errlvl,name)
|
||||
end
|
||||
|
||||
--- Fires a "ConfigTableChange" callback for those listening in on it, allowing config GUIs to refresh.
|
||||
-- You should call this function if your options table changed from any outside event, like a game event
|
||||
-- or a timer.
|
||||
-- @param appName The application name as given to `:RegisterOptionsTable()`
|
||||
function AceConfigRegistry:NotifyChange(appName)
|
||||
if not AceConfigRegistry.tables[appName] then return end
|
||||
AceConfigRegistry.callbacks:Fire("ConfigTableChange", 1, appName)
|
||||
end
|
||||
|
||||
-- -------------------------------------------------------------------
|
||||
-- Registering and retreiving options tables:
|
||||
|
||||
|
||||
-- validateGetterArgs: helper function for :GetOptionsTable (or, rather, the getter functions returned by it)
|
||||
|
||||
local function validateGetterArgs(uiType, uiName, errlvl)
|
||||
errlvl=(errlvl or 0)+2
|
||||
if uiType~="cmd" and uiType~="dropdown" and uiType~="dialog" then
|
||||
error(MAJOR..": Requesting options table: 'uiType' - invalid configuration UI type, expected 'cmd', 'dropdown' or 'dialog'", errlvl)
|
||||
end
|
||||
if not strfind(uiName, "[A-Za-z]+-[0-9]") then -- Expecting e.g. "MyLib-1.2"
|
||||
error(MAJOR..": Requesting options table: 'uiName' - badly formatted or missing version number. Expected e.g. 'MyLib-1.2'", errlvl)
|
||||
end
|
||||
end
|
||||
|
||||
--- Register an options table with the config registry.
|
||||
-- @param appName The application name as given to `:RegisterOptionsTable()`
|
||||
-- @param options The options table, OR a function reference that generates it on demand. \\
|
||||
-- See the top of the page for info on arguments passed to such functions.
|
||||
-- @param skipValidation Skip options table validation (primarily useful for extremely huge options, with a noticeable slowdown)
|
||||
function AceConfigRegistry:RegisterOptionsTable(appName, options, skipValidation)
|
||||
if type(options)=="table" then
|
||||
if options.type~="group" then -- quick sanity checker
|
||||
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - missing type='group' member in root group", 2)
|
||||
end
|
||||
AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
|
||||
errlvl=(errlvl or 0)+1
|
||||
validateGetterArgs(uiType, uiName, errlvl)
|
||||
if not AceConfigRegistry.validated[uiType][appName] and not skipValidation then
|
||||
AceConfigRegistry:ValidateOptionsTable(options, appName, errlvl) -- upgradable
|
||||
AceConfigRegistry.validated[uiType][appName] = true
|
||||
end
|
||||
return options
|
||||
end
|
||||
elseif type(options)=="function" then
|
||||
AceConfigRegistry.tables[appName] = function(uiType, uiName, errlvl)
|
||||
errlvl=(errlvl or 0)+1
|
||||
validateGetterArgs(uiType, uiName, errlvl)
|
||||
local tab = assert(options(uiType, uiName, appName))
|
||||
if not AceConfigRegistry.validated[uiType][appName] and not skipValidation then
|
||||
AceConfigRegistry:ValidateOptionsTable(tab, appName, errlvl) -- upgradable
|
||||
AceConfigRegistry.validated[uiType][appName] = true
|
||||
end
|
||||
return tab
|
||||
end
|
||||
else
|
||||
error(MAJOR..": RegisterOptionsTable(appName, options): 'options' - expected table or function reference", 2)
|
||||
end
|
||||
end
|
||||
|
||||
--- Returns an iterator of ["appName"]=funcref pairs
|
||||
function AceConfigRegistry:IterateOptionsTables()
|
||||
return pairs(AceConfigRegistry.tables)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
--- Query the registry for a specific options table.
|
||||
-- If only appName is given, a function is returned which you
|
||||
-- can call with (uiType,uiName) to get the table.\\
|
||||
-- If uiType&uiName are given, the table is returned.
|
||||
-- @param appName The application name as given to `:RegisterOptionsTable()`
|
||||
-- @param uiType The type of UI to get the table for, one of "cmd", "dropdown", "dialog"
|
||||
-- @param uiName The name of the library/addon querying for the table, e.g. "MyLib-1.0"
|
||||
function AceConfigRegistry:GetOptionsTable(appName, uiType, uiName)
|
||||
local f = AceConfigRegistry.tables[appName]
|
||||
if not f then
|
||||
return nil
|
||||
end
|
||||
|
||||
if uiType then
|
||||
return f(uiType,uiName,1) -- get the table for us
|
||||
else
|
||||
return f -- return the function
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceConfigRegistry-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,270 @@
|
||||
--- **AceConsole-3.0** provides registration facilities for slash commands.
|
||||
-- You can register slash commands to your custom functions and use the `GetArgs` function to parse them
|
||||
-- to your addons individual needs.
|
||||
--
|
||||
-- **AceConsole-3.0** can be embeded into your addon, either explicitly by calling AceConsole:Embed(MyAddon) or by
|
||||
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||
-- and can be accessed directly, without having to explicitly call AceConsole itself.\\
|
||||
-- It is recommended to embed AceConsole, otherwise you'll have to specify a custom `self` on all calls you
|
||||
-- make into AceConsole.
|
||||
-- @class file
|
||||
-- @name AceConsole-3.0
|
||||
-- @release $Id: AceConsole-3.0.lua 1143 2016-07-11 08:52:03Z nevcairiel $
|
||||
local MAJOR,MINOR = "AceConsole-3.0", 7
|
||||
|
||||
local AceConsole, oldminor = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not AceConsole then return end -- No upgrade needed
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
|
||||
AceConsole.embeds = AceConsole.embeds or {} -- table containing objects AceConsole is embedded in.
|
||||
AceConsole.commands = AceConsole.commands or {} -- table containing commands registered
|
||||
AceConsole.weakcommands = AceConsole.weakcommands or {} -- table containing self, command => func references for weak commands that don't persist through enable/disable
|
||||
|
||||
-- Lua APIs
|
||||
local tinsert, tconcat, tgetn, tsetn = table.insert, table.concat, table.getn, table.setn
|
||||
local tostring = tostring
|
||||
local type, pairs, error = type, pairs, error
|
||||
local format, strfind, strsub = string.format, string.find, string.sub
|
||||
local max = math.max
|
||||
local strupper, strlower = string.upper, string.lower
|
||||
|
||||
-- WoW APIs
|
||||
local _G = AceCore._G
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: DEFAULT_CHAT_FRAME, SlashCmdList, hash_SlashCmdList
|
||||
local Print
|
||||
do
|
||||
local tmp = {}
|
||||
function Print(self, frame, arg)
|
||||
if self ~= AceConsole then
|
||||
tmp[1] = "|cff33ff99"..tostring(self).."|r:"
|
||||
else
|
||||
tmp[1] = ''
|
||||
end
|
||||
if type(arg) == "string" then
|
||||
frame:AddMessage(tmp[1]..arg)
|
||||
else -- arg is table and may contain frame as first element if argument frame is nil
|
||||
local b, e = frame and 1 or 2, tgetn(arg)
|
||||
if e >= b then
|
||||
frame = frame or arg[1]
|
||||
for i=0,e-b do
|
||||
tmp[2+i] = tostring(arg[b+i])
|
||||
end
|
||||
frame:AddMessage(tconcat(tmp," ",1,e-b+2)) -- explicitly, because the length is not affected by assignment
|
||||
end
|
||||
end
|
||||
end
|
||||
end -- Print
|
||||
|
||||
--- Print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
|
||||
-- @paramsig [chatframe ,] ...
|
||||
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
|
||||
-- @param ... List of any values to be printed
|
||||
function AceConsole:Print(...)
|
||||
local frame = arg[1]
|
||||
if type(frame) == "table" and frame.AddMessage then -- Is first argument something with an .AddMessage member?
|
||||
return Print(self, nil, arg)
|
||||
else
|
||||
return Print(self, DEFAULT_CHAT_FRAME, arg)
|
||||
end
|
||||
end
|
||||
|
||||
--- Formatted (using format()) print to DEFAULT_CHAT_FRAME or given ChatFrame (anything with an .AddMessage function)
|
||||
-- @paramsig [chatframe ,] "format"[, ...]
|
||||
-- @param chatframe Custom ChatFrame to print to (or any frame with an .AddMessage function)
|
||||
-- @param format Format string - same syntax as standard Lua format()
|
||||
-- @param ... Arguments to the format string
|
||||
function AceConsole:Printf(a1, ...)
|
||||
local frame, succ, s
|
||||
if type(a1) == "table" and a1.AddMessage then -- Is first argument something with an .AddMessage member?
|
||||
frame, succ, s = a1, pcall(format, unpack(arg))
|
||||
else
|
||||
frame, succ, s = DEFAULT_CHAT_FRAME, pcall(format, a1, unpack(arg))
|
||||
end
|
||||
if not succ then error(s,2) end
|
||||
return Print(self, frame, s)
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--- Register a simple chat command
|
||||
-- @param command Chat command to be registered WITHOUT leading "/"
|
||||
-- @param func Function to call when the slash command is being used (funcref or methodname)
|
||||
-- @param persist if false, the command will be soft disabled/enabled when aceconsole is used as a mixin (default: true)
|
||||
function AceConsole:RegisterChatCommand( command, func, persist )
|
||||
if type(command)~="string" then error([[Usage: AceConsole:RegisterChatCommand(command, func[, persist ]): 'command' - expected a string]], 2) end
|
||||
|
||||
if persist==nil then persist=true end -- I'd rather have my addon's "/addon enable" around if the author screws up. Having some extra slash regged when it shouldnt be isn't as destructive. True is a better default. /Mikk
|
||||
|
||||
local name = "ACECONSOLE_"..strupper(command)
|
||||
|
||||
local t = type(func)
|
||||
|
||||
if t == "string" then
|
||||
-- Ace3v: prevent user from using AceConSole as self
|
||||
if self == AceConsole then
|
||||
error([[Usage: RegisterChatCommand(command, func[, persist]): 'self' - use your own 'self']], 2)
|
||||
end
|
||||
SlashCmdList[name] = function(input, editBox)
|
||||
self[func](self, input, editBox)
|
||||
end
|
||||
elseif t == "function" then
|
||||
SlashCmdList[name] = func
|
||||
else
|
||||
error([[Usage: AceConsole:RegisterChatCommand(command, func[, persist ]): 'func' - expected a string or a function]], 2)
|
||||
end
|
||||
_G["SLASH_"..name.."1"] = "/"..strlower(command)
|
||||
AceConsole.commands[command] = name
|
||||
-- non-persisting commands are registered for enabling disabling
|
||||
if not persist then
|
||||
if not AceConsole.weakcommands[self] then AceConsole.weakcommands[self] = {} end
|
||||
AceConsole.weakcommands[self][command] = func
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Unregister a chatcommand
|
||||
-- @param command Chat command to be unregistered WITHOUT leading "/"
|
||||
function AceConsole:UnregisterChatCommand( command )
|
||||
local name = AceConsole.commands[command]
|
||||
if name then
|
||||
SlashCmdList[name] = nil
|
||||
_G["SLASH_" .. name .. "1"] = nil
|
||||
AceConsole.commands[command] = nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Get an iterator over all Chat Commands registered with AceConsole
|
||||
-- @return Iterator (pairs) over all commands
|
||||
function AceConsole:IterateChatCommands() return pairs(AceConsole.commands) end
|
||||
|
||||
local function nils(n,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if n >= 1 then
|
||||
return nil, nils(n-1,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
elseif not argc or argc == 0 then
|
||||
return
|
||||
else
|
||||
return a1, nils(0,argc-1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
end
|
||||
|
||||
--- Retreive one or more space-separated arguments from a string.
|
||||
-- Treats quoted strings and itemlinks as non-spaced.
|
||||
-- @param str The raw argument string
|
||||
-- @param numargs How many arguments to get (default 1)
|
||||
-- @param startpos Where in the string to start scanning (default 1)
|
||||
-- @return Returns arg1, arg2, ..., nextposition\\
|
||||
-- Missing arguments will be returned as nils. 'nextposition' is returned as 1e9 at the end of the string.
|
||||
function AceConsole:GetArgs(str, numargs, startpos)
|
||||
numargs = numargs or 1
|
||||
startpos = max(startpos or 1, 1)
|
||||
|
||||
local pos=startpos
|
||||
|
||||
-- find start of new arg
|
||||
pos = strfind(str, "[^ ]", pos)
|
||||
if not pos then -- whoops, end of string
|
||||
return nils(numargs, 1, 1e9)
|
||||
end
|
||||
|
||||
if numargs<1 then
|
||||
return pos
|
||||
end
|
||||
|
||||
-- quoted or space separated? find out which pattern to use
|
||||
local delim_or_pipe
|
||||
local ch = strsub(str, pos, pos)
|
||||
if ch=='"' then
|
||||
pos = pos + 1
|
||||
delim_or_pipe='([|"])'
|
||||
elseif ch=="'" then
|
||||
pos = pos + 1
|
||||
delim_or_pipe="([|'])"
|
||||
else
|
||||
delim_or_pipe="([| ])"
|
||||
end
|
||||
|
||||
startpos = pos
|
||||
|
||||
while true do
|
||||
-- find delimiter or hyperlink
|
||||
local ch,_
|
||||
pos,_,ch = strfind(str, delim_or_pipe, pos)
|
||||
|
||||
if not pos then break end
|
||||
|
||||
if ch=="|" then
|
||||
-- some kind of escape
|
||||
|
||||
if strsub(str,pos,pos+1)=="|H" then
|
||||
-- It's a |H....|hhyper link!|h
|
||||
pos=strfind(str, "|h", pos+2) -- first |h
|
||||
if not pos then break end
|
||||
|
||||
pos=strfind(str, "|h", pos+2) -- second |h
|
||||
if not pos then break end
|
||||
elseif strsub(str,pos, pos+1) == "|T" then
|
||||
-- It's a |T....|t texture
|
||||
pos=strfind(str, "|t", pos+2)
|
||||
if not pos then break end
|
||||
end
|
||||
|
||||
pos=pos+2 -- skip past this escape (last |h if it was a hyperlink)
|
||||
|
||||
else
|
||||
-- found delimiter, done with this arg
|
||||
return strsub(str, startpos, pos-1), AceConsole:GetArgs(str, numargs-1, pos+1)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- search aborted, we hit end of string. return it all as one argument. (yes, even if it's an unterminated quote or hyperlink)
|
||||
return strsub(str, startpos), nils(numargs-1, 1, 1e9)
|
||||
end
|
||||
|
||||
|
||||
--- embedding and embed handling
|
||||
|
||||
local mixins = {
|
||||
"Print",
|
||||
"Printf",
|
||||
"RegisterChatCommand",
|
||||
"UnregisterChatCommand",
|
||||
"GetArgs",
|
||||
}
|
||||
|
||||
-- Embeds AceConsole into the target object making the functions from the mixins list available on target:..
|
||||
-- @param target target object to embed AceBucket in
|
||||
function AceConsole:Embed( target )
|
||||
for k, v in pairs( mixins ) do
|
||||
target[v] = self[v]
|
||||
end
|
||||
self.embeds[target] = true
|
||||
return target
|
||||
end
|
||||
|
||||
function AceConsole:OnEmbedEnable( target )
|
||||
if AceConsole.weakcommands[target] then
|
||||
for command, func in pairs( AceConsole.weakcommands[target] ) do
|
||||
target:RegisterChatCommand( command, func, false, true ) -- nonpersisting and silent registry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function AceConsole:OnEmbedDisable( target )
|
||||
if AceConsole.weakcommands[target] then
|
||||
for command, func in pairs( AceConsole.weakcommands[target] ) do
|
||||
target:UnregisterChatCommand( command ) -- TODO: this could potentially unregister a command from another application in case of command conflicts. Do we care?
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for addon in pairs(AceConsole.embeds) do
|
||||
AceConsole:Embed(addon)
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceConsole-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,205 @@
|
||||
local ACECORE_MAJOR, ACECORE_MINOR = "AceCore-3.0", 1
|
||||
local AceCore, oldminor = LibStub:NewLibrary(ACECORE_MAJOR, ACECORE_MINOR)
|
||||
|
||||
if not AceCore then return end -- No upgrade needed
|
||||
|
||||
AceCore._G = AceCore._G or getfenv()
|
||||
local _G = AceCore._G
|
||||
local strsub, strgsub, strfind = string.sub, string.gsub, string.find
|
||||
local tremove, tconcat = table.remove, table.concat
|
||||
local tgetn, tsetn = table.getn, table.setn
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
tsetn(t,0)
|
||||
list[t] = true
|
||||
end
|
||||
|
||||
-- debug
|
||||
function AceCore.listcount()
|
||||
local count = 0
|
||||
for k in list do
|
||||
count = count + 1
|
||||
end
|
||||
return count
|
||||
end
|
||||
end -- AceCore.new, AceCore.del
|
||||
AceCore.new, AceCore.del = new, del
|
||||
|
||||
local function errorhandler(err)
|
||||
return geterrorhandler()(err)
|
||||
end
|
||||
AceCore.errorhandler = errorhandler
|
||||
|
||||
local function CreateSafeDispatcher(argCount)
|
||||
local code = [[
|
||||
local errorhandler = LibStub("AceCore-3.0").errorhandler
|
||||
local method, UP_ARGS
|
||||
local function call()
|
||||
local func, ARGS = method, UP_ARGS
|
||||
method, UP_ARGS = nil, NILS
|
||||
return func(ARGS)
|
||||
end
|
||||
return function(func, ARGS)
|
||||
method, UP_ARGS = func, ARGS
|
||||
return xpcall(call, errorhandler)
|
||||
end
|
||||
]]
|
||||
local c = 4*argCount-1
|
||||
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10,b11,b12,b13,b14,b15,b16,b17,b18,b19,b20"
|
||||
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
|
||||
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,c))
|
||||
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
|
||||
code = strgsub(code, "NILS", string.sub(s,1,c))
|
||||
return assert(loadstring(code, "safecall SafeDispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
local SafeDispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher
|
||||
if not tonumber(argCount) then dbg(debugstack()) end
|
||||
if argCount > 0 then
|
||||
dispatcher = CreateSafeDispatcher(argCount)
|
||||
else
|
||||
dispatcher = function(func) return xpcall(func,errorhandler) end
|
||||
end
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
local function safecall(func,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
-- we check to see if the func is passed is actually a function here and don't error when it isn't
|
||||
-- this safecall is used for optional functions like OnInitialize OnEnable etc. When they are not
|
||||
-- present execution should continue without hinderance
|
||||
if type(func) == "function" then
|
||||
return SafeDispatchers[argc](func,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20)
|
||||
end
|
||||
end
|
||||
AceCore.safecall = safecall
|
||||
|
||||
local function CreateDispatcher(argCount)
|
||||
local code = [[
|
||||
return function(func,ARGS)
|
||||
return func(ARGS)
|
||||
end
|
||||
]]
|
||||
local s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10,a11,a12,a13,a14,a15,a16,a17,a18,a19,a20"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,4*argCount-1))
|
||||
return assert(loadstring(code, "call Dispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
AceCore.Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher
|
||||
if argCount > 0 then
|
||||
dispatcher = CreateDispatcher(argCount)
|
||||
else
|
||||
dispatcher = function(func) return func() end
|
||||
end
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
-- some string functions
|
||||
-- vanilla available string operations:
|
||||
-- sub, gfind, rep, gsub, char, dump, find, upper, len, format, byte, lower
|
||||
-- we will just replace every string.match with string.find in the code
|
||||
function AceCore.strtrim(s)
|
||||
return strgsub(s, "^%s*(.-)%s*$", "%1")
|
||||
end
|
||||
|
||||
local function strsplit(delim, s, n)
|
||||
if n and n < 2 then return s end
|
||||
beg = beg or 1
|
||||
local i,j = string.find(s,delim,beg)
|
||||
if not i then
|
||||
return s, nil
|
||||
end
|
||||
return string.sub(s,1,j-1), strsplit(delim, string.sub(s,j+1), n and n-1 or nil)
|
||||
end
|
||||
AceCore.strsplit = strsplit
|
||||
|
||||
-- Ace3v: fonctions copied from AceHook-2.1
|
||||
local protFuncs = {
|
||||
CameraOrSelectOrMoveStart = true, CameraOrSelectOrMoveStop = true,
|
||||
TurnOrActionStart = true, TurnOrActionStop = true,
|
||||
PitchUpStart = true, PitchUpStop = true,
|
||||
PitchDownStart = true, PitchDownStop = true,
|
||||
MoveBackwardStart = true, MoveBackwardStop = true,
|
||||
MoveForwardStart = true, MoveForwardStop = true,
|
||||
Jump = true, StrafeLeftStart = true,
|
||||
StrafeLeftStop = true, StrafeRightStart = true,
|
||||
StrafeRightStop = true, ToggleMouseMove = true,
|
||||
ToggleRun = true, TurnLeftStart = true,
|
||||
TurnLeftStop = true, TurnRightStart = true,
|
||||
TurnRightStop = true,
|
||||
}
|
||||
|
||||
local function issecurevariable(x)
|
||||
return protFuncs[x] and 1 or nil
|
||||
end
|
||||
AceCore.issecurevariable = issecurevariable
|
||||
|
||||
local function hooksecurefunc(arg1, arg2, arg3)
|
||||
if type(arg1) == "string" then
|
||||
arg1, arg2, arg3 = _G, arg1, arg2
|
||||
end
|
||||
local orig = arg1[arg2]
|
||||
if type(orig) ~= "function" then
|
||||
error("The function "..arg2.." does not exist", 2)
|
||||
end
|
||||
arg1[arg2] = function(...)
|
||||
local tmp = {orig(unpack(arg))}
|
||||
arg3(unpack(arg))
|
||||
return unpack(tmp)
|
||||
end
|
||||
end
|
||||
AceCore.hooksecurefunc = hooksecurefunc
|
||||
|
||||
-- pickfirstset() - picks the first non-nil value and returns it
|
||||
local function pickfirstset(argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if (argc <= 1) or (a1 ~= nil) then
|
||||
return a1
|
||||
else
|
||||
return pickfirstset(argc-1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
end
|
||||
AceCore.pickfirstset = pickfirstset
|
||||
|
||||
local function countargs(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if (a1 == nil) then return 0 end
|
||||
return 1 + countargs(a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
AceCore.countargs = countargs
|
||||
|
||||
-- wipe preserves metatable
|
||||
function AceCore.wipe(t)
|
||||
for k,v in pairs(t) do t[k] = nil end
|
||||
tsetn(t,0)
|
||||
return t
|
||||
end
|
||||
|
||||
function AceCore.truncate(t,e)
|
||||
e = e or tgetn(t)
|
||||
for i=1,e do
|
||||
if t[i] == nil then
|
||||
tsetn(t,i-1)
|
||||
return
|
||||
end
|
||||
end
|
||||
tsetn(t,e)
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceCore-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,809 @@
|
||||
--- **AceGUI-3.0** provides access to numerous widgets which can be used to create GUIs.
|
||||
-- AceGUI is used by AceConfigDialog to create the option GUIs, but you can use it by itself
|
||||
-- to create any custom GUI. There are more extensive examples in the test suite in the Ace3
|
||||
-- stand-alone distribution.
|
||||
--
|
||||
-- **Note**: When using AceGUI-3.0 directly, please do not modify the frames of the widgets directly,
|
||||
-- as any "unknown" change to the widgets will cause addons that get your widget out of the widget pool
|
||||
-- to misbehave. If you think some part of a widget should be modifiable, please open a ticket, and we"ll
|
||||
-- implement a proper API to modify it.
|
||||
-- @usage
|
||||
-- local AceGUI = LibStub("AceGUI-3.0")
|
||||
-- -- Create a container frame
|
||||
-- local f = AceGUI:Create("Frame")
|
||||
-- f:SetCallback("OnClose",function(widget) AceGUI:Release(widget) end)
|
||||
-- f:SetTitle("AceGUI-3.0 Example")
|
||||
-- f:SetStatusText("Status Bar")
|
||||
-- f:SetLayout("Flow")
|
||||
-- -- Create a button
|
||||
-- local btn = AceGUI:Create("Button")
|
||||
-- btn:SetWidth(170)
|
||||
-- btn:SetText("Button !")
|
||||
-- btn:SetCallback("OnClick", function() print("Click!") end)
|
||||
-- -- Add the button to the container
|
||||
-- f:AddChild(btn)
|
||||
-- @class file
|
||||
-- @name AceGUI-3.0
|
||||
-- @release $Id: AceGUI-3.0.lua 1102 2013-10-25 14:15:23Z nevcairiel $
|
||||
local ACEGUI_MAJOR, ACEGUI_MINOR = "AceGUI-3.0", 34
|
||||
local AceGUI, oldminor = LibStub:NewLibrary(ACEGUI_MAJOR, ACEGUI_MINOR)
|
||||
|
||||
if not AceGUI then return end -- No upgrade needed
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local hooksecurefunc = AceCore.hooksecurefunc
|
||||
local safecall = AceCore.safecall
|
||||
|
||||
-- Lua APIs
|
||||
local tconcat, tremove, tinsert, tgetn, tsetn = table.concat, table.remove, table.insert, table.getn, table.setn
|
||||
local pairs, next, type = pairs, next, type
|
||||
local error, assert, loadstring = error, assert, loadstring
|
||||
local setmetatable, rawget, rawset = setmetatable, rawget, rawset
|
||||
local math_max = math.max
|
||||
local strupper, strfmt = string.upper, string.format
|
||||
|
||||
-- WoW APIs
|
||||
local UIParent = UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: geterrorhandler, LibStub
|
||||
|
||||
--local con = LibStub("AceConsole-3.0",true)
|
||||
|
||||
AceGUI.WidgetRegistry = AceGUI.WidgetRegistry or {}
|
||||
AceGUI.LayoutRegistry = AceGUI.LayoutRegistry or {}
|
||||
AceGUI.WidgetBase = AceGUI.WidgetBase or {}
|
||||
AceGUI.WidgetContainerBase = AceGUI.WidgetContainerBase or {}
|
||||
AceGUI.WidgetVersions = AceGUI.WidgetVersions or {}
|
||||
AceGUI.HookedFunctions = AceGUI.HookedFunctions or {}
|
||||
|
||||
-- local upvalues
|
||||
local WidgetRegistry = AceGUI.WidgetRegistry
|
||||
local LayoutRegistry = AceGUI.LayoutRegistry
|
||||
local WidgetVersions = AceGUI.WidgetVersions
|
||||
local HookedFunctions = AceGUI.HookedFunctions
|
||||
|
||||
-- Recycling functions
|
||||
local newWidget, delWidget
|
||||
do
|
||||
AceGUI.objPools = AceGUI.objPools or {}
|
||||
local objPools = AceGUI.objPools
|
||||
--Returns a new instance, if none are available either returns a new table or calls the given contructor
|
||||
function newWidget(type)
|
||||
if not WidgetRegistry[type] then
|
||||
error("Attempt to instantiate unknown widget type", 2)
|
||||
end
|
||||
|
||||
if not objPools[type] then
|
||||
objPools[type] = {}
|
||||
end
|
||||
|
||||
local newObj = next(objPools[type])
|
||||
if not newObj then
|
||||
newObj = WidgetRegistry[type]()
|
||||
newObj.AceGUIWidgetVersion = WidgetVersions[type]
|
||||
else
|
||||
objPools[type][newObj] = nil
|
||||
-- if the widget is older then the latest, don't even try to reuse it
|
||||
-- just forget about it, and grab a new one.
|
||||
if not newObj.AceGUIWidgetVersion or newObj.AceGUIWidgetVersion < WidgetVersions[type] then
|
||||
return newWidget(type)
|
||||
end
|
||||
end
|
||||
return newObj
|
||||
end
|
||||
-- Releases an instance to the Pool
|
||||
function delWidget(obj,type)
|
||||
if not objPools[type] then
|
||||
objPools[type] = {}
|
||||
end
|
||||
if objPools[type][obj] then
|
||||
error("Attempt to Release Widget that is already released", 2)
|
||||
end
|
||||
objPools[type][obj] = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Ace3v: when a container contains many children, we can only use the variable arguments
|
||||
local function _fixlevels(parent, ...)
|
||||
local lv = parent:GetFrameLevel() + 1
|
||||
for i = 1, tgetn(arg) do
|
||||
local child = arg[i]
|
||||
child:SetFrameLevel(lv)
|
||||
_fixlevels(child, child:GetChildren())
|
||||
end
|
||||
end
|
||||
|
||||
local function fixlevels(parent)
|
||||
return _fixlevels(parent, parent:GetChildren())
|
||||
end
|
||||
AceGUI.fixlevels = fixlevels
|
||||
|
||||
-- Ace3v: attention! this function is recursive
|
||||
local function _fixstrata(strata, parent, ...)
|
||||
parent:SetFrameStrata(strata)
|
||||
for i = 1, tgetn(arg) do
|
||||
local child = arg[i]
|
||||
_fixstrata(strata, child, child:GetChildren())
|
||||
end
|
||||
end
|
||||
|
||||
local function fixstrata(strata, parent)
|
||||
return _fixstrata(strata, parent, parent:GetChildren())
|
||||
end
|
||||
AceGUI.fixstrata = fixstrata
|
||||
|
||||
|
||||
-------------------
|
||||
-- API Functions --
|
||||
-------------------
|
||||
|
||||
-- Gets a widget Object
|
||||
|
||||
--- Create a new Widget of the given type.
|
||||
-- This function will instantiate a new widget (or use one from the widget pool), and call the
|
||||
-- OnAcquire function on it, before returning.
|
||||
-- @param type The type of the widget.
|
||||
-- @return The newly created widget.
|
||||
function AceGUI:Create(type)
|
||||
if WidgetRegistry[type] then
|
||||
local widget = newWidget(type)
|
||||
|
||||
if widget.OnAcquire then
|
||||
widget:OnAcquire()
|
||||
else
|
||||
error(strfmt("Widget type %s doesn't supply an OnAcquire Function", type))
|
||||
end
|
||||
|
||||
-- Set the default Layout ("List")
|
||||
safecall(widget.SetLayout, 2, widget, "List")
|
||||
safecall(widget.ResumeLayout, 1, widget)
|
||||
return widget
|
||||
end
|
||||
end
|
||||
|
||||
--- Releases a widget Object.
|
||||
-- This function calls OnRelease on the widget and places it back in the widget pool.
|
||||
-- Any data on the widget is being erased, and the widget will be hidden.\\
|
||||
-- If this widget is a Container-Widget, all of its Child-Widgets will be releases as well.
|
||||
-- @param widget The widget to release
|
||||
function AceGUI:Release(widget)
|
||||
safecall(widget.PauseLayout, 1, widget)
|
||||
widget:Fire("OnRelease")
|
||||
safecall(widget.ReleaseChildren, 1, widget)
|
||||
|
||||
if widget.OnRelease then
|
||||
widget:OnRelease()
|
||||
-- else
|
||||
-- error(strfmt("Widget type %s doesn't supply an OnRelease Function", widget.type))
|
||||
end
|
||||
for k in pairs(widget.userdata) do
|
||||
widget.userdata[k] = nil
|
||||
end
|
||||
for k in pairs(widget.events) do
|
||||
widget.events[k] = nil
|
||||
end
|
||||
widget.width = nil
|
||||
widget.relWidth = nil
|
||||
widget.height = nil
|
||||
widget.relHeight = nil
|
||||
widget.noAutoHeight = nil
|
||||
widget.frame:ClearAllPoints()
|
||||
widget.frame:Hide()
|
||||
widget.frame:SetParent(UIParent)
|
||||
widget.frame.width = nil
|
||||
widget.frame.height = nil
|
||||
if widget.content then
|
||||
widget.content.width = nil
|
||||
widget.content.height = nil
|
||||
end
|
||||
delWidget(widget, widget.type)
|
||||
end
|
||||
|
||||
-----------
|
||||
-- Focus --
|
||||
-----------
|
||||
|
||||
|
||||
--- Called when a widget has taken focus.
|
||||
-- e.g. Dropdowns opening, Editboxes gaining kb focus
|
||||
-- @param widget The widget that should be focused
|
||||
function AceGUI:SetFocus(widget)
|
||||
if self.FocusedWidget and self.FocusedWidget ~= widget then
|
||||
safecall(self.FocusedWidget.ClearFocus, 1, self.FocusedWidget)
|
||||
end
|
||||
self.FocusedWidget = widget
|
||||
end
|
||||
|
||||
|
||||
--- Called when something has happened that could cause widgets with focus to drop it
|
||||
-- e.g. titlebar of a frame being clicked
|
||||
function AceGUI:ClearFocus()
|
||||
if self.FocusedWidget then
|
||||
safecall(self.FocusedWidget.ClearFocus, 1, self.FocusedWidget)
|
||||
self.FocusedWidget = nil
|
||||
end
|
||||
end
|
||||
|
||||
-------------
|
||||
-- Widgets --
|
||||
-------------
|
||||
--[[
|
||||
Widgets must provide the following functions
|
||||
OnAcquire() - Called when the object is acquired, should set everything to a default hidden state
|
||||
|
||||
And the following members
|
||||
frame - the frame or derivitive object that will be treated as the widget for size and anchoring purposes
|
||||
type - the type of the object, same as the name given to :RegisterWidget()
|
||||
|
||||
Widgets contain a table called userdata, this is a safe place to store data associated with the wigdet
|
||||
It will be cleared automatically when a widget is released
|
||||
Placing values directly into a widget object should be avoided
|
||||
|
||||
If the Widget can act as a container for other Widgets the following
|
||||
content - frame or derivitive that children will be anchored to
|
||||
|
||||
The Widget can supply the following Optional Members
|
||||
:OnRelease() - Called when the object is Released, should remove any additional anchors and clear any data
|
||||
:OnWidthSet(width) - Called when the width of the widget is changed
|
||||
:OnHeightSet(height) - Called when the height of the widget is changed
|
||||
Widgets should not use the OnSizeChanged events of thier frame or content members, use these methods instead
|
||||
AceGUI already sets a handler to the event
|
||||
:LayoutFinished(width, height) - called after a layout has finished, the width and height will be the width and height of the
|
||||
area used for controls. These can be nil if the layout used the existing size to layout the controls.
|
||||
|
||||
]]
|
||||
|
||||
--------------------------
|
||||
-- Widget Base Template --
|
||||
--------------------------
|
||||
do
|
||||
local WidgetBase = AceGUI.WidgetBase
|
||||
|
||||
WidgetBase.SetParent = function(self, parent)
|
||||
local frame = self.frame
|
||||
frame:SetParent(nil)
|
||||
frame:SetParent(parent.content)
|
||||
self.parent = parent
|
||||
fixlevels(frame)
|
||||
end
|
||||
|
||||
WidgetBase.SetCallback = function(self, name, func)
|
||||
if type(func) == "function" then
|
||||
self.events[name] = func
|
||||
end
|
||||
end
|
||||
|
||||
WidgetBase.Fire = function(self,name,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
argc = argc or 0
|
||||
local func = self.events[name]
|
||||
if func then
|
||||
local success, ret = safecall(func,argc+3,self,name,argc,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if success then
|
||||
return ret
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
WidgetBase.SetWidth = function(self, width)
|
||||
self.frame:SetWidth(width)
|
||||
self.frame.width = width
|
||||
if self.OnWidthSet then
|
||||
self:OnWidthSet(width)
|
||||
end
|
||||
end
|
||||
|
||||
WidgetBase.SetRelativeWidth = function(self, width)
|
||||
if width <= 0 or width > 1 then
|
||||
error(":SetRelativeWidth(width): Invalid relative width.", 2)
|
||||
end
|
||||
self.relWidth = width
|
||||
self.width = "relative"
|
||||
end
|
||||
|
||||
WidgetBase.SetHeight = function(self, height)
|
||||
self.frame:SetHeight(height)
|
||||
self.frame.height = height
|
||||
if self.OnHeightSet then
|
||||
self:OnHeightSet(height)
|
||||
end
|
||||
end
|
||||
|
||||
--[[ WidgetBase.SetRelativeHeight = function(self, height)
|
||||
if height <= 0 or height > 1 then
|
||||
error(":SetRelativeHeight(height): Invalid relative height.", 2)
|
||||
end
|
||||
self.relHeight = height
|
||||
self.height = "relative"
|
||||
end ]]
|
||||
|
||||
WidgetBase.IsVisible = function(self)
|
||||
return self.frame:IsVisible()
|
||||
end
|
||||
|
||||
WidgetBase.IsShown= function(self)
|
||||
return self.frame:IsShown()
|
||||
end
|
||||
|
||||
WidgetBase.Release = function(self)
|
||||
AceGUI:Release(self)
|
||||
end
|
||||
|
||||
WidgetBase.SetPoint = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
return self.frame:SetPoint(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
|
||||
WidgetBase.ClearAllPoints = function(self)
|
||||
return self.frame:ClearAllPoints()
|
||||
end
|
||||
|
||||
WidgetBase.GetNumPoints = function(self)
|
||||
return self.frame:GetNumPoints()
|
||||
end
|
||||
|
||||
WidgetBase.GetPoint = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
return self.frame:GetPoint(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
|
||||
WidgetBase.GetUserDataTable = function(self)
|
||||
return self.userdata
|
||||
end
|
||||
|
||||
WidgetBase.SetUserData = function(self, key, value)
|
||||
self.userdata[key] = value
|
||||
end
|
||||
|
||||
WidgetBase.GetUserData = function(self, key)
|
||||
return self.userdata[key]
|
||||
end
|
||||
|
||||
WidgetBase.IsFullHeight = function(self)
|
||||
return self.height == "fill"
|
||||
end
|
||||
|
||||
WidgetBase.SetFullHeight = function(self, isFull)
|
||||
if isFull then
|
||||
self.height = "fill"
|
||||
else
|
||||
self.height = nil
|
||||
end
|
||||
end
|
||||
|
||||
WidgetBase.IsFullWidth = function(self)
|
||||
return self.width == "fill"
|
||||
end
|
||||
|
||||
WidgetBase.SetFullWidth = function(self, isFull)
|
||||
if isFull then
|
||||
self.width = "fill"
|
||||
else
|
||||
self.width = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- local function LayoutOnUpdate(this)
|
||||
-- this:SetScript("OnUpdate",nil)
|
||||
-- this.obj:PerformLayout()
|
||||
-- end
|
||||
|
||||
local WidgetContainerBase = AceGUI.WidgetContainerBase
|
||||
|
||||
WidgetContainerBase.PauseLayout = function(self)
|
||||
self.LayoutPaused = true
|
||||
end
|
||||
|
||||
WidgetContainerBase.ResumeLayout = function(self)
|
||||
self.LayoutPaused = nil
|
||||
end
|
||||
|
||||
WidgetContainerBase.PerformLayout = function(self)
|
||||
if self.LayoutPaused then
|
||||
return
|
||||
end
|
||||
safecall(self.LayoutFunc, 2, self.content, self.children)
|
||||
end
|
||||
|
||||
--call this function to layout, makes sure layed out objects get a frame to get sizes etc
|
||||
WidgetContainerBase.DoLayout = function(self)
|
||||
self:PerformLayout()
|
||||
-- if not self.parent then
|
||||
-- self.frame:SetScript("OnUpdate", LayoutOnUpdate)
|
||||
-- end
|
||||
end
|
||||
|
||||
WidgetContainerBase.AddChild = function(self, child, beforeWidget)
|
||||
if beforeWidget then
|
||||
local siblingIndex = 1
|
||||
for _, widget in pairs(self.children) do
|
||||
if widget == beforeWidget then
|
||||
break
|
||||
end
|
||||
siblingIndex = siblingIndex + 1
|
||||
end
|
||||
tinsert(self.children, siblingIndex, child)
|
||||
else
|
||||
tinsert(self.children, child)
|
||||
end
|
||||
child:SetParent(self)
|
||||
child.frame:Show()
|
||||
self:DoLayout()
|
||||
end
|
||||
|
||||
do
|
||||
local args = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
|
||||
WidgetContainerBase.AddChildren = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
args[1] = a1
|
||||
args[2] = a2
|
||||
args[3] = a3
|
||||
args[4] = a4
|
||||
args[5] = a5
|
||||
args[6] = a6
|
||||
args[7] = a7
|
||||
args[8] = a8
|
||||
args[9] = a9
|
||||
args[10] = a10
|
||||
for i = 1,10 do
|
||||
local child = args[i]
|
||||
arg[i] = nil
|
||||
|
||||
if not child then break end
|
||||
tinsert(self.children, child)
|
||||
child:SetParent(self)
|
||||
child.frame:Show()
|
||||
end
|
||||
self:DoLayout()
|
||||
end
|
||||
end -- WidgetContainerBase.AddChildren
|
||||
|
||||
WidgetContainerBase.ReleaseChildren = function(self)
|
||||
local children = self.children
|
||||
for i = 1,tgetn(children) do
|
||||
AceGUI:Release(tremove(children))
|
||||
end
|
||||
end
|
||||
|
||||
WidgetContainerBase.SetParent = function(self, parent)
|
||||
WidgetBase.SetParent(self, parent)
|
||||
|
||||
local lv = self.frame:GetFrameLevel()
|
||||
self.content:SetFrameLevel(lv+1)
|
||||
local children = self.children
|
||||
for i = 1,tgetn(children) do
|
||||
local child = children[i]
|
||||
child:SetParent(self)
|
||||
end
|
||||
end
|
||||
|
||||
WidgetContainerBase.SetLayout = function(self, Layout)
|
||||
self.LayoutFunc = AceGUI:GetLayout(Layout)
|
||||
end
|
||||
|
||||
WidgetContainerBase.SetAutoAdjustHeight = function(self, adjust)
|
||||
if adjust then
|
||||
self.noAutoHeight = nil
|
||||
else
|
||||
self.noAutoHeight = true
|
||||
end
|
||||
end
|
||||
|
||||
local function FrameResize()
|
||||
local self = this.obj
|
||||
if this:GetWidth() and this:GetHeight() then
|
||||
if self.OnWidthSet then
|
||||
self:OnWidthSet(this:GetWidth())
|
||||
end
|
||||
if self.OnHeightSet then
|
||||
self:OnHeightSet(this:GetHeight())
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ContentResize()
|
||||
if this:GetWidth() and this:GetHeight() then
|
||||
this.width = this:GetWidth()
|
||||
this.height = this:GetHeight()
|
||||
this.obj:DoLayout()
|
||||
end
|
||||
end
|
||||
|
||||
setmetatable(WidgetContainerBase, {__index=WidgetBase})
|
||||
|
||||
--One of these function should be called on each Widget Instance as part of its creation process
|
||||
|
||||
--- Register a widget-class as a container for newly created widgets.
|
||||
-- @param widget The widget class
|
||||
function AceGUI:RegisterAsContainer(widget)
|
||||
widget.children = {}
|
||||
widget.userdata = {}
|
||||
widget.events = {}
|
||||
widget.base = WidgetContainerBase
|
||||
widget.content.obj = widget
|
||||
widget.frame.obj = widget
|
||||
widget.content:SetScript("OnSizeChanged", ContentResize)
|
||||
widget.frame:SetScript("OnSizeChanged", FrameResize)
|
||||
setmetatable(widget, {__index = WidgetContainerBase})
|
||||
widget:SetLayout("List")
|
||||
return widget
|
||||
end
|
||||
|
||||
--- Register a widget-class as a widget.
|
||||
-- @param widget The widget class
|
||||
function AceGUI:RegisterAsWidget(widget)
|
||||
widget.userdata = {}
|
||||
widget.events = {}
|
||||
widget.base = WidgetBase
|
||||
widget.frame.obj = widget
|
||||
widget.frame:SetScript("OnSizeChanged", FrameResize)
|
||||
setmetatable(widget, {__index = WidgetBase})
|
||||
return widget
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
------------------
|
||||
-- Widget API --
|
||||
------------------
|
||||
|
||||
--- Registers a widget Constructor, this function returns a new instance of the Widget
|
||||
-- @param Name The name of the widget
|
||||
-- @param Constructor The widget constructor function
|
||||
-- @param Version The version of the widget
|
||||
function AceGUI:RegisterWidgetType(Name, Constructor, Version)
|
||||
assert(type(Constructor) == "function")
|
||||
assert(type(Version) == "number")
|
||||
|
||||
local oldVersion = WidgetVersions[Name]
|
||||
if oldVersion and oldVersion >= Version then return end
|
||||
|
||||
WidgetVersions[Name] = Version
|
||||
WidgetRegistry[Name] = Constructor
|
||||
end
|
||||
|
||||
--- Registers a Layout Function
|
||||
-- @param Name The name of the layout
|
||||
-- @param LayoutFunc Reference to the layout function
|
||||
function AceGUI:RegisterLayout(Name, LayoutFunc)
|
||||
assert(type(LayoutFunc) == "function")
|
||||
if type(Name) == "string" then
|
||||
Name = string.upper(Name)
|
||||
end
|
||||
LayoutRegistry[Name] = LayoutFunc
|
||||
end
|
||||
|
||||
--- Get a Layout Function from the registry
|
||||
-- @param Name The name of the layout
|
||||
function AceGUI:GetLayout(Name)
|
||||
if type(Name) == "string" then
|
||||
Name = strupper(Name)
|
||||
end
|
||||
return LayoutRegistry[Name]
|
||||
end
|
||||
|
||||
AceGUI.counts = AceGUI.counts or {}
|
||||
|
||||
--- A type-based counter to count the number of widgets created.
|
||||
-- This is used by widgets that require a named frame, e.g. when a Blizzard
|
||||
-- Template requires it.
|
||||
-- @param type The widget type
|
||||
function AceGUI:GetNextWidgetNum(type)
|
||||
if not self.counts[type] then
|
||||
self.counts[type] = 0
|
||||
end
|
||||
self.counts[type] = self.counts[type] + 1
|
||||
return self.counts[type]
|
||||
end
|
||||
|
||||
--- Return the number of created widgets for this type.
|
||||
-- In contrast to GetNextWidgetNum, the number is not incremented.
|
||||
-- @param type The widget type
|
||||
function AceGUI:GetWidgetCount(type)
|
||||
return self.counts[type] or 0
|
||||
end
|
||||
|
||||
--- Return the version of the currently registered widget type.
|
||||
-- @param type The widget type
|
||||
function AceGUI:GetWidgetVersion(type)
|
||||
return WidgetVersions[type]
|
||||
end
|
||||
|
||||
-------------
|
||||
-- Layouts --
|
||||
-------------
|
||||
|
||||
--[[
|
||||
A Layout is a func that takes 2 parameters
|
||||
content - the frame that widgets will be placed inside
|
||||
children - a table containing the widgets to layout
|
||||
]]
|
||||
|
||||
-- Very simple Layout, Children are stacked on top of each other down the left side
|
||||
AceGUI:RegisterLayout("List",
|
||||
function(content, children)
|
||||
local height = 0
|
||||
local width = content.width or content:GetWidth() or 0
|
||||
for i = 1, tgetn(children) do
|
||||
local child = children[i]
|
||||
|
||||
local frame = child.frame
|
||||
frame:ClearAllPoints()
|
||||
frame:Show()
|
||||
if i == 1 then
|
||||
frame:SetPoint("TOPLEFT", content)
|
||||
else
|
||||
frame:SetPoint("TOPLEFT", children[i-1].frame, "BOTTOMLEFT")
|
||||
end
|
||||
|
||||
if child.width == "fill" then
|
||||
child:SetWidth(width)
|
||||
frame:SetPoint("RIGHT", content)
|
||||
|
||||
if child.DoLayout then
|
||||
child:DoLayout()
|
||||
end
|
||||
elseif child.width == "relative" then
|
||||
child:SetWidth(width * child.relWidth)
|
||||
|
||||
if child.DoLayout then
|
||||
child:DoLayout()
|
||||
end
|
||||
end
|
||||
|
||||
height = height + (frame.height or frame:GetHeight() or 0)
|
||||
end
|
||||
safecall(content.obj.LayoutFinished, 3, content.obj, nil, height)
|
||||
end)
|
||||
|
||||
-- A single control fills the whole content area
|
||||
AceGUI:RegisterLayout("Fill",
|
||||
function(content, children)
|
||||
if children[1] then
|
||||
children[1]:SetWidth(content:GetWidth() or 0)
|
||||
children[1]:SetHeight(content:GetHeight() or 0)
|
||||
children[1].frame:SetAllPoints(content)
|
||||
children[1].frame:Show()
|
||||
safecall(content.obj.LayoutFinished, 3, content.obj, nil, children[1].frame:GetHeight())
|
||||
end
|
||||
end)
|
||||
|
||||
-- Ace3v: currently only a1 used
|
||||
local layoutrecursionblock = nil
|
||||
local function safelayoutcall(object, func, a1)
|
||||
layoutrecursionblock = true
|
||||
object[func](object, a1)
|
||||
layoutrecursionblock = nil
|
||||
end
|
||||
|
||||
AceGUI:RegisterLayout("Flow",
|
||||
function(content, children)
|
||||
if layoutrecursionblock then return end
|
||||
--used height so far
|
||||
local height = 0
|
||||
--width used in the current row
|
||||
local usedwidth = 0
|
||||
--height of the current row
|
||||
local rowheight = 0
|
||||
local rowoffset = 0
|
||||
local lastrowoffset
|
||||
|
||||
local width = content.width or content:GetWidth() or 0
|
||||
|
||||
--control at the start of the row
|
||||
local rowstart
|
||||
local rowstartoffset
|
||||
local lastrowstart
|
||||
local isfullheight
|
||||
|
||||
local frameoffset
|
||||
local lastframeoffset
|
||||
local oversize
|
||||
for i = 1, tgetn(children) do
|
||||
local child = children[i]
|
||||
oversize = nil
|
||||
local frame = child.frame
|
||||
local frameheight = frame.height or frame:GetHeight() or 0
|
||||
local framewidth = frame.width or frame:GetWidth() or 0
|
||||
lastframeoffset = frameoffset
|
||||
-- HACK: Why did we set a frameoffset of (frameheight / 2) ?
|
||||
-- That was moving all widgets half the widgets size down, is that intended?
|
||||
-- Actually, it seems to be neccessary for many cases, we'll leave it in for now.
|
||||
-- If widgets seem to anchor weirdly with this, provide a valid alignoffset for them.
|
||||
-- TODO: Investigate moar!
|
||||
frameoffset = child.alignoffset or (frameheight / 2)
|
||||
|
||||
if child.width == "relative" then
|
||||
framewidth = width * child.relWidth
|
||||
end
|
||||
|
||||
frame:Show()
|
||||
frame:ClearAllPoints()
|
||||
if i == 1 then
|
||||
-- anchor the first control to the top left
|
||||
frame:SetPoint("TOPLEFT", content)
|
||||
rowheight = frameheight
|
||||
rowoffset = frameoffset
|
||||
rowstart = frame
|
||||
rowstartoffset = frameoffset
|
||||
usedwidth = framewidth
|
||||
if usedwidth > width then
|
||||
oversize = true
|
||||
end
|
||||
else
|
||||
-- if there isn't available width for the control start a new row
|
||||
-- if a control is "fill" it will be on a row of its own full width
|
||||
if usedwidth == 0 or ((framewidth) + usedwidth > width) or child.width == "fill" then
|
||||
if isfullheight then
|
||||
-- a previous row has already filled the entire height, there's nothing we can usefully do anymore
|
||||
-- (maybe error/warn about this?)
|
||||
break
|
||||
end
|
||||
--anchor the previous row, we will now know its height and offset
|
||||
rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3))
|
||||
height = height + rowheight + 3
|
||||
--save this as the rowstart so we can anchor it after the row is complete and we have the max height and offset of controls in it
|
||||
rowstart = frame
|
||||
rowstartoffset = frameoffset
|
||||
rowheight = frameheight
|
||||
rowoffset = frameoffset
|
||||
usedwidth = framewidth
|
||||
if usedwidth > width then
|
||||
oversize = true
|
||||
end
|
||||
-- put the control on the current row, adding it to the width and checking if the height needs to be increased
|
||||
else
|
||||
--handles cases where the new height is higher than either control because of the offsets
|
||||
--math.max(rowheight-rowoffset+frameoffset, frameheight-frameoffset+rowoffset)
|
||||
|
||||
--offset is always the larger of the two offsets
|
||||
rowoffset = math_max(rowoffset, frameoffset)
|
||||
rowheight = math_max(rowheight, rowoffset + (frameheight / 2))
|
||||
|
||||
frame:SetPoint("TOPLEFT", children[i-1].frame, "TOPRIGHT", 0, frameoffset - lastframeoffset)
|
||||
usedwidth = framewidth + usedwidth
|
||||
end
|
||||
end
|
||||
|
||||
if child.width == "fill" then
|
||||
safelayoutcall(child, "SetWidth", width)
|
||||
frame:SetPoint("RIGHT", content)
|
||||
|
||||
usedwidth = 0
|
||||
rowstart = frame
|
||||
rowstartoffset = frameoffset
|
||||
|
||||
if child.DoLayout then
|
||||
child:DoLayout()
|
||||
end
|
||||
rowheight = frame.height or frame:GetHeight() or 0
|
||||
rowoffset = child.alignoffset or (rowheight / 2)
|
||||
rowstartoffset = rowoffset
|
||||
elseif child.width == "relative" then
|
||||
safelayoutcall(child, "SetWidth", width * child.relWidth)
|
||||
|
||||
if child.DoLayout then
|
||||
child:DoLayout()
|
||||
end
|
||||
elseif oversize then
|
||||
if width > 1 then
|
||||
frame:SetPoint("RIGHT", content)
|
||||
end
|
||||
end
|
||||
|
||||
if child.height == "fill" then
|
||||
frame:SetPoint("BOTTOM", content)
|
||||
isfullheight = true
|
||||
end
|
||||
end
|
||||
|
||||
--anchor the last row, if its full height needs a special case since its height has just been changed by the anchor
|
||||
if isfullheight then
|
||||
rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -height)
|
||||
elseif rowstart then
|
||||
rowstart:SetPoint("TOPLEFT", content, "TOPLEFT", 0, -(height + (rowoffset - rowstartoffset) + 3))
|
||||
end
|
||||
|
||||
height = height + rowheight + 3
|
||||
safecall(content.obj.LayoutFinished, 3, content.obj, nil, height)
|
||||
end)
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceGUI-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,138 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
BlizOptionsGroup Container
|
||||
Simple container widget for the integration of AceGUI into the Blizzard Interface Options
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "BlizOptionsGroup", 21
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame = CreateFrame
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
|
||||
local function OnShow(frame)
|
||||
frame.obj:Fire("OnShow")
|
||||
end
|
||||
|
||||
local function OnHide(frame)
|
||||
frame.obj:Fire("OnHide")
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
|
||||
local function okay(frame)
|
||||
frame.obj:Fire("okay")
|
||||
end
|
||||
|
||||
local function cancel(frame)
|
||||
frame.obj:Fire("cancel")
|
||||
end
|
||||
|
||||
local function default(frame)
|
||||
frame.obj:Fire("default")
|
||||
end
|
||||
|
||||
local function refresh(frame)
|
||||
frame.obj:Fire("refresh")
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetName()
|
||||
self:SetTitle()
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
local contentwidth = width - 63
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - 26
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end,
|
||||
|
||||
["SetName"] = function(self, name, parent)
|
||||
self.frame.name = name
|
||||
self.frame.parent = parent
|
||||
end,
|
||||
|
||||
["SetTitle"] = function(self, title)
|
||||
local content = self.content
|
||||
content:ClearAllPoints()
|
||||
if not title or title == "" then
|
||||
content:SetPoint("TOPLEFT", 10, -10)
|
||||
self.label:SetText("")
|
||||
else
|
||||
content:SetPoint("TOPLEFT", 10, -40)
|
||||
self.label:SetText(title)
|
||||
end
|
||||
content:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame")
|
||||
frame:Hide()
|
||||
|
||||
-- support functions for the Blizzard Interface Options
|
||||
frame.okay = okay
|
||||
frame.cancel = cancel
|
||||
frame.default = default
|
||||
frame.refresh = refresh
|
||||
|
||||
frame:SetScript("OnHide", OnHide)
|
||||
frame:SetScript("OnShow", OnShow)
|
||||
|
||||
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
|
||||
label:SetPoint("TOPLEFT", 10, -15)
|
||||
label:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", 10, -45)
|
||||
label:SetJustifyH("LEFT")
|
||||
label:SetJustifyV("TOP")
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, frame)
|
||||
content:SetPoint("TOPLEFT", 10, -10)
|
||||
content:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
|
||||
local widget = {
|
||||
label = label,
|
||||
frame = frame,
|
||||
content = content,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,157 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
DropdownGroup Container
|
||||
Container controlled by a dropdown on the top.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "DropdownGroup", 21
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local assert, pairs, type = assert, pairs, type
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame = CreateFrame
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function SelectedGroup(self, event, _, value)
|
||||
local group = self.parentgroup
|
||||
local status = group.status or group.localstatus
|
||||
status.selected = value
|
||||
self.parentgroup:Fire("OnGroupSelected", 1, value)
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self.dropdown:SetText("")
|
||||
self:SetDropdownWidth(200)
|
||||
self:SetTitle("")
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self.dropdown.list = nil
|
||||
self.status = nil
|
||||
for k in pairs(self.localstatus) do
|
||||
self.localstatus[k] = nil
|
||||
end
|
||||
end,
|
||||
|
||||
["SetTitle"] = function(self, title)
|
||||
self.titletext:SetText(title)
|
||||
self.dropdown.frame:ClearAllPoints()
|
||||
if title and title ~= "" then
|
||||
self.dropdown.frame:SetPoint("TOPRIGHT", -2, 0)
|
||||
else
|
||||
self.dropdown.frame:SetPoint("TOPLEFT", -1, 0)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetGroupList"] = function(self,list,order)
|
||||
self.dropdown:SetList(list,order)
|
||||
end,
|
||||
|
||||
["SetStatusTable"] = function(self, status)
|
||||
assert(type(status) == "table")
|
||||
self.status = status
|
||||
end,
|
||||
|
||||
["SetGroup"] = function(self,group)
|
||||
self.dropdown:SetValue(group)
|
||||
local status = self.status or self.localstatus
|
||||
status.selected = group
|
||||
self:Fire("OnGroupSelected", 1, group)
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
local contentwidth = width - 26
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - 63
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end,
|
||||
|
||||
["LayoutFinished"] = function(self, width, height)
|
||||
self:SetHeight((height or 0) + 63)
|
||||
end,
|
||||
|
||||
["SetDropdownWidth"] = function(self, width)
|
||||
self.dropdown:SetWidth(width)
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local PaneBackdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 5, bottom = 3 }
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame")
|
||||
frame:SetHeight(100)
|
||||
frame:SetWidth(100)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
local titletext = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
titletext:SetPoint("TOPLEFT", 4, -5)
|
||||
titletext:SetPoint("TOPRIGHT", -4, -5)
|
||||
titletext:SetJustifyH("LEFT")
|
||||
titletext:SetHeight(18)
|
||||
|
||||
local dropdown = AceGUI:Create("Dropdown")
|
||||
dropdown.frame:SetParent(frame)
|
||||
dropdown.frame:SetFrameLevel(dropdown.frame:GetFrameLevel() + 2)
|
||||
dropdown:SetCallback("OnValueChanged", SelectedGroup)
|
||||
dropdown.frame:SetPoint("TOPLEFT", -1, 0)
|
||||
dropdown.frame:Show()
|
||||
dropdown:SetLabel("")
|
||||
|
||||
local border = CreateFrame("Frame", nil, frame)
|
||||
border:SetPoint("TOPLEFT", 0, -26)
|
||||
border:SetPoint("BOTTOMRIGHT", 0, 3)
|
||||
border:SetBackdrop(PaneBackdrop)
|
||||
border:SetBackdropColor(0.1,0.1,0.1,0.5)
|
||||
border:SetBackdropBorderColor(0.4,0.4,0.4)
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, border)
|
||||
content:SetPoint("TOPLEFT", 10, -10)
|
||||
content:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
|
||||
local widget = {
|
||||
frame = frame,
|
||||
localstatus = {},
|
||||
titletext = titletext,
|
||||
dropdown = dropdown,
|
||||
border = border,
|
||||
content = content,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
dropdown.parentgroup = widget
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,303 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Frame Container
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Frame", 24
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local wipe = AceCore.wipe
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, assert, type = pairs, assert, type
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: CLOSE
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Button_OnClick()
|
||||
PlaySound("gsTitleOptionExit")
|
||||
this.obj:Hide()
|
||||
end
|
||||
|
||||
local function Frame_OnClose()
|
||||
this.obj:Fire("OnClose")
|
||||
end
|
||||
|
||||
local function Frame_OnMouseDown()
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function Title_OnMouseDown()
|
||||
this:GetParent():StartMoving()
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function MoverSizer_OnMouseUp()
|
||||
local frame = this:GetParent()
|
||||
frame:StopMovingOrSizing()
|
||||
local self = frame.obj
|
||||
local status = self.status or self.localstatus
|
||||
status.width = frame:GetWidth()
|
||||
status.height = frame:GetHeight()
|
||||
status.top = frame:GetTop()
|
||||
status.left = frame:GetLeft()
|
||||
end
|
||||
|
||||
local function SizerSE_OnMouseDown()
|
||||
this:GetParent():StartSizing("BOTTOMRIGHT")
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function SizerS_OnMouseDown()
|
||||
this:GetParent():StartSizing("BOTTOM")
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function SizerE_OnMouseDown()
|
||||
this:GetParent():StartSizing("RIGHT")
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function StatusBar_OnEnter()
|
||||
this.obj:Fire("OnEnterStatusBar")
|
||||
end
|
||||
|
||||
local function StatusBar_OnLeave()
|
||||
this.obj:Fire("OnLeaveStatusBar")
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self.frame:SetParent(UIParent)
|
||||
self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
self:SetTitle()
|
||||
self:SetStatusText()
|
||||
self:ApplyStatus()
|
||||
self:Show()
|
||||
self:EnableResize(true)
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self.status = nil
|
||||
wipe(self.localstatus)
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
local contentwidth = width - 34
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - 57
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end,
|
||||
|
||||
["SetTitle"] = function(self, title)
|
||||
self.titletext:SetText(title)
|
||||
self.titlebg:SetWidth((self.titletext:GetWidth() or 0) + 10)
|
||||
end,
|
||||
|
||||
["SetStatusText"] = function(self, text)
|
||||
self.statustext:SetText(text)
|
||||
end,
|
||||
|
||||
["Hide"] = function(self)
|
||||
self.frame:Hide()
|
||||
end,
|
||||
|
||||
["Show"] = function(self)
|
||||
self.frame:Show()
|
||||
end,
|
||||
|
||||
["EnableResize"] = function(self, state)
|
||||
local func = state and "Show" or "Hide"
|
||||
self.sizer_se[func](self.sizer_se)
|
||||
self.sizer_s[func](self.sizer_s)
|
||||
self.sizer_e[func](self.sizer_e)
|
||||
end,
|
||||
|
||||
-- called to set an external table to store status in
|
||||
["SetStatusTable"] = function(self, status)
|
||||
assert(type(status) == "table")
|
||||
self.status = status
|
||||
self:ApplyStatus()
|
||||
end,
|
||||
|
||||
["ApplyStatus"] = function(self)
|
||||
local status = self.status or self.localstatus
|
||||
local frame = self.frame
|
||||
self:SetWidth(status.width or 700)
|
||||
self:SetHeight(status.height or 500)
|
||||
frame:ClearAllPoints()
|
||||
if status.top and status.left then
|
||||
frame:SetPoint("TOP", UIParent, "BOTTOM", 0, status.top)
|
||||
frame:SetPoint("LEFT", UIParent, "LEFT", status.left, 0)
|
||||
else
|
||||
frame:SetPoint("CENTER", UIParent)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local FrameBackdrop = {
|
||||
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
||||
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
||||
tile = true, tileSize = 32, edgeSize = 32,
|
||||
insets = { left = 8, right = 8, top = 8, bottom = 8 }
|
||||
}
|
||||
|
||||
local PaneBackdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 5, bottom = 3 }
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
frame:EnableMouse(true)
|
||||
frame:SetMovable(true)
|
||||
frame:SetResizable(true)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
frame:SetBackdrop(FrameBackdrop)
|
||||
frame:SetBackdropColor(0, 0, 0, 1)
|
||||
frame:SetMinResize(400, 200)
|
||||
frame:SetToplevel(true)
|
||||
frame:SetScript("OnHide", Frame_OnClose)
|
||||
frame:SetScript("OnMouseDown", Frame_OnMouseDown)
|
||||
|
||||
local closebutton = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
|
||||
closebutton:SetScript("OnClick", Button_OnClick)
|
||||
closebutton:SetPoint("BOTTOMRIGHT", -27, 17)
|
||||
closebutton:SetHeight(20)
|
||||
closebutton:SetWidth(100)
|
||||
closebutton:SetText(CLOSE)
|
||||
|
||||
local statusbg = CreateFrame("Button", nil, frame)
|
||||
statusbg:SetPoint("BOTTOMLEFT", 15, 15)
|
||||
statusbg:SetPoint("BOTTOMRIGHT", -132, 15)
|
||||
statusbg:SetHeight(24)
|
||||
statusbg:SetBackdrop(PaneBackdrop)
|
||||
statusbg:SetBackdropColor(0.1,0.1,0.1)
|
||||
statusbg:SetBackdropBorderColor(0.4,0.4,0.4)
|
||||
statusbg:SetScript("OnEnter", StatusBar_OnEnter)
|
||||
statusbg:SetScript("OnLeave", StatusBar_OnLeave)
|
||||
|
||||
local statustext = statusbg:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
statustext:SetPoint("TOPLEFT", 7, -2)
|
||||
statustext:SetPoint("BOTTOMRIGHT", -7, 2)
|
||||
statustext:SetHeight(20)
|
||||
statustext:SetJustifyH("LEFT")
|
||||
statustext:SetText("")
|
||||
|
||||
local titlebg = frame:CreateTexture(nil, "OVERLAY")
|
||||
titlebg:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
|
||||
titlebg:SetTexCoord(0.31, 0.67, 0, 0.63)
|
||||
titlebg:SetPoint("TOP", 0, 12)
|
||||
titlebg:SetWidth(100)
|
||||
titlebg:SetHeight(40)
|
||||
|
||||
local title = CreateFrame("Frame", nil, frame)
|
||||
title:EnableMouse(true)
|
||||
title:SetScript("OnMouseDown", Title_OnMouseDown)
|
||||
title:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
|
||||
title:SetAllPoints(titlebg)
|
||||
|
||||
local titletext = title:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
titletext:SetPoint("TOP", titlebg, "TOP", 0, -14)
|
||||
|
||||
local titlebg_l = frame:CreateTexture(nil, "OVERLAY")
|
||||
titlebg_l:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
|
||||
titlebg_l:SetTexCoord(0.21, 0.31, 0, 0.63)
|
||||
titlebg_l:SetPoint("RIGHT", titlebg, "LEFT")
|
||||
titlebg_l:SetWidth(30)
|
||||
titlebg_l:SetHeight(40)
|
||||
|
||||
local titlebg_r = frame:CreateTexture(nil, "OVERLAY")
|
||||
titlebg_r:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
|
||||
titlebg_r:SetTexCoord(0.67, 0.77, 0, 0.63)
|
||||
titlebg_r:SetPoint("LEFT", titlebg, "RIGHT")
|
||||
titlebg_r:SetWidth(30)
|
||||
titlebg_r:SetHeight(40)
|
||||
|
||||
-- bottom right sizer
|
||||
local sizer_se = CreateFrame("Frame", nil, frame)
|
||||
sizer_se:SetPoint("BOTTOMRIGHT", 0, 0)
|
||||
sizer_se:SetWidth(25)
|
||||
sizer_se:SetHeight(25)
|
||||
sizer_se:EnableMouse()
|
||||
sizer_se:SetScript("OnMouseDown",SizerSE_OnMouseDown)
|
||||
sizer_se:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
|
||||
|
||||
local line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
|
||||
line1:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
line1:SetTexture("Interface\\Cursor\\Item")
|
||||
line1:SetTexCoord(1, 0, 1, 0)
|
||||
|
||||
local sizer_s = CreateFrame("Frame", nil, frame)
|
||||
sizer_s:SetPoint("BOTTOMRIGHT", -25, 0)
|
||||
sizer_s:SetPoint("BOTTOMLEFT", 0, 0)
|
||||
sizer_s:SetHeight(25)
|
||||
sizer_s:EnableMouse(true)
|
||||
sizer_s:SetScript("OnMouseDown", SizerS_OnMouseDown)
|
||||
sizer_s:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
|
||||
|
||||
local sizer_e = CreateFrame("Frame", nil, frame)
|
||||
sizer_e:SetPoint("BOTTOMRIGHT", 0, 25)
|
||||
sizer_e:SetPoint("TOPRIGHT", 0, 0)
|
||||
sizer_e:SetWidth(25)
|
||||
sizer_e:EnableMouse(true)
|
||||
sizer_e:SetScript("OnMouseDown", SizerE_OnMouseDown)
|
||||
sizer_e:SetScript("OnMouseUp", MoverSizer_OnMouseUp)
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, frame)
|
||||
content:SetPoint("TOPLEFT", 17, -27)
|
||||
content:SetPoint("BOTTOMRIGHT", -17, 40)
|
||||
|
||||
local widget = {
|
||||
localstatus = {},
|
||||
titletext = titletext,
|
||||
statustext = statustext,
|
||||
titlebg = titlebg,
|
||||
sizer_se = sizer_se,
|
||||
sizer_s = sizer_s,
|
||||
sizer_e = sizer_e,
|
||||
content = content,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
closebutton.obj, statusbg.obj = widget, widget
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,103 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
InlineGroup Container
|
||||
Simple container widget that creates a visible "box" with an optional title.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "InlineGroup", 21
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetWidth(300)
|
||||
self:SetHeight(100)
|
||||
self:SetTitle("")
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetTitle"] = function(self,title)
|
||||
self.titletext:SetText(title)
|
||||
end,
|
||||
|
||||
|
||||
["LayoutFinished"] = function(self, width, height)
|
||||
if self.noAutoHeight then return end
|
||||
self:SetHeight((height or 0) + 40)
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
local contentwidth = width - 20
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - 20
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local PaneBackdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 5, bottom = 3 }
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
local titletext = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
titletext:SetPoint("TOPLEFT", 14, 0)
|
||||
titletext:SetPoint("TOPRIGHT", -14, 0)
|
||||
titletext:SetJustifyH("LEFT")
|
||||
titletext:SetHeight(18)
|
||||
|
||||
local border = CreateFrame("Frame", nil, frame)
|
||||
border:SetPoint("TOPLEFT", 0, -17)
|
||||
border:SetPoint("BOTTOMRIGHT", -1, 3)
|
||||
border:SetBackdrop(PaneBackdrop)
|
||||
border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
|
||||
border:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, border)
|
||||
content:SetPoint("TOPLEFT", 10, -10)
|
||||
content:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
|
||||
local widget = {
|
||||
frame = frame,
|
||||
content = content,
|
||||
titletext = titletext,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,213 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
ScrollFrame Container
|
||||
Plain container that scrolls its content and doesn't grow in height.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "ScrollFrame", 24
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local fixlevels = AceGUI.fixlevels
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, assert, type = pairs, assert, type
|
||||
local min, max, floor, abs = math.min, math.max, math.floor, math.abs
|
||||
local format = string.format
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function FixScrollOnUpdate()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this.obj:FixScroll()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function ScrollFrame_OnMouseWheel()
|
||||
this.obj:MoveScroll(arg1)
|
||||
end
|
||||
|
||||
local function ScrollFrame_OnSizeChanged()
|
||||
this:SetScript("OnUpdate", FixScrollOnUpdate)
|
||||
end
|
||||
|
||||
local function ScrollBar_OnScrollValueChanged()
|
||||
this.obj:SetScroll(arg1)
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetScroll(0)
|
||||
self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self.status = nil
|
||||
for k in pairs(self.localstatus) do
|
||||
self.localstatus[k] = nil
|
||||
end
|
||||
self.scrollframe:SetPoint("BOTTOMRIGHT",0,0)
|
||||
self.scrollbar:Hide()
|
||||
self.scrollBarShown = nil
|
||||
self.content.height, self.content.width = nil, nil
|
||||
end,
|
||||
|
||||
["SetScroll"] = function(self, value)
|
||||
local status = self.status or self.localstatus
|
||||
local viewheight = self.scrollframe:GetHeight()
|
||||
local height = self.content:GetHeight()
|
||||
local offset
|
||||
|
||||
if viewheight > height then
|
||||
offset = 0
|
||||
else
|
||||
offset = floor((height - viewheight) / 1000.0 * value)
|
||||
end
|
||||
self.content:ClearAllPoints()
|
||||
self.content:SetPoint("TOPLEFT", 0, offset)
|
||||
self.content:SetPoint("TOPRIGHT", 0, offset)
|
||||
status.offset = offset
|
||||
status.scrollvalue = value
|
||||
end,
|
||||
|
||||
["MoveScroll"] = function(self, value)
|
||||
local status = self.status or self.localstatus
|
||||
local height, viewheight = self.scrollframe:GetHeight(), self.content:GetHeight()
|
||||
|
||||
if self.scrollBarShown then
|
||||
local diff = height - viewheight
|
||||
local delta = 1
|
||||
if value < 0 then
|
||||
delta = -1
|
||||
end
|
||||
self.scrollbar:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
|
||||
end
|
||||
end,
|
||||
|
||||
["FixScroll"] = function(self)
|
||||
|
||||
if self.updateLock then return end
|
||||
self.updateLock = true
|
||||
local status = self.status or self.localstatus
|
||||
local scrollframe, content, scrollbar = self.scrollframe, self.content, self.scrollbar
|
||||
local viewheight, height = self.scrollframe:GetHeight(), self.content:GetHeight()
|
||||
local offset = status.offset or 0
|
||||
local curvalue = scrollbar:GetValue()
|
||||
-- Give us a margin of error of 2 pixels to stop some conditions that i would blame on floating point inaccuracys
|
||||
-- No-one is going to miss 2 pixels at the bottom of the frame, anyhow!
|
||||
|
||||
if height < viewheight + 2 then
|
||||
if self.scrollBarShown then
|
||||
self.scrollBarShown = nil
|
||||
scrollbar:Hide()
|
||||
scrollbar:SetValue(0)
|
||||
scrollframe:SetPoint("BOTTOMRIGHT",0,0)
|
||||
self:DoLayout()
|
||||
end
|
||||
offset = 0
|
||||
else
|
||||
if not self.scrollBarShown then
|
||||
self.scrollBarShown = true
|
||||
scrollbar:Show()
|
||||
scrollframe:SetPoint("BOTTOMRIGHT", -20, 0)
|
||||
self:DoLayout()
|
||||
end
|
||||
local value = (offset / (height - viewheight) * 1000)
|
||||
if value > 1000 then
|
||||
value = 1000
|
||||
offset = height - viewheight
|
||||
end
|
||||
scrollbar:SetValue(value)
|
||||
self:SetScroll(value)
|
||||
end
|
||||
status.offset = offset
|
||||
scrollframe:SetScrollChild(content)
|
||||
content:ClearAllPoints()
|
||||
content:SetPoint("TOPLEFT", 0, offset)
|
||||
content:SetPoint("TOPRIGHT", 0, offset)
|
||||
self.updateLock = nil
|
||||
end,
|
||||
|
||||
["LayoutFinished"] = function(self, width, height)
|
||||
self.content:SetHeight(height or 0 + 20)
|
||||
self.scrollframe:SetScript("OnUpdate", FixScrollOnUpdate)
|
||||
end,
|
||||
|
||||
["SetStatusTable"] = function(self, status)
|
||||
assert(type(status) == "table")
|
||||
self.status = status
|
||||
if not status.scrollvalue then
|
||||
status.scrollvalue = 0
|
||||
end
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
content.width = width
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
content.height = height
|
||||
end
|
||||
}
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
local num = AceGUI:GetNextWidgetNum(Type)
|
||||
|
||||
local scrollframe = CreateFrame("ScrollFrame", nil, frame)
|
||||
scrollframe:SetPoint("TOPLEFT",0,0)
|
||||
scrollframe:SetPoint("BOTTOMRIGHT",0,0)
|
||||
scrollframe:EnableMouseWheel(true)
|
||||
scrollframe:SetScript("OnMouseWheel", ScrollFrame_OnMouseWheel)
|
||||
scrollframe:SetScript("OnSizeChanged", ScrollFrame_OnSizeChanged)
|
||||
|
||||
local scrollbar = CreateFrame("Slider", format("AceConfigDialogScrollFrame%dScrollBar", num), scrollframe, "UIPanelScrollBarTemplate")
|
||||
scrollbar:SetPoint("TOPLEFT", scrollframe, "TOPRIGHT", 4, -16)
|
||||
scrollbar:SetPoint("BOTTOMLEFT", scrollframe, "BOTTOMRIGHT", 4, 16)
|
||||
scrollbar:SetMinMaxValues(0, 1000)
|
||||
scrollbar:SetValueStep(1)
|
||||
scrollbar:SetValue(0)
|
||||
scrollbar:SetWidth(16)
|
||||
scrollbar:Hide()
|
||||
-- set the script as the last step, so it doesn't fire yet
|
||||
scrollbar:SetScript("OnValueChanged", ScrollBar_OnScrollValueChanged)
|
||||
|
||||
local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
|
||||
scrollbg:SetAllPoints(scrollbar)
|
||||
scrollbg:SetTexture(0, 0, 0, 0.4)
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, scrollframe)
|
||||
content:SetPoint("TOPLEFT",0,0)
|
||||
content:SetPoint("TOPRIGHT",0,0)
|
||||
content:SetHeight(400)
|
||||
scrollframe:SetScrollChild(content)
|
||||
|
||||
local widget = {
|
||||
localstatus = { scrollvalue = 0 },
|
||||
scrollframe = scrollframe,
|
||||
scrollbar = scrollbar,
|
||||
content = content,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
scrollframe.obj, scrollbar.obj = widget, widget
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,69 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
SimpleGroup Container
|
||||
Simple container widget that just groups widgets.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "SimpleGroup", 20
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetWidth(300)
|
||||
self:SetHeight(100)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["LayoutFinished"] = function(self, width, height)
|
||||
if self.noAutoHeight then return end
|
||||
self:SetHeight(height or 0)
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
content:SetWidth(width)
|
||||
content.width = width
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
content:SetHeight(height)
|
||||
content.height = height
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, frame)
|
||||
content:SetPoint("TOPLEFT")
|
||||
content:SetPoint("BOTTOMRIGHT")
|
||||
|
||||
local widget = {
|
||||
frame = frame,
|
||||
content = content,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,378 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
TabGroup Container
|
||||
Container that uses tabs on top to switch between groups.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "TabGroup", 35
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local wipe = AceCore.wipe
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, ipairs, assert, type, wipe = pairs, ipairs, assert, type, wipe
|
||||
local tgetn = table.getn
|
||||
local strfmt = string.format
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
local _G = AceCore._G
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: PanelTemplates_TabResize, PanelTemplates_SetDisabledTabState, PanelTemplates_SelectTab, PanelTemplates_DeselectTab
|
||||
|
||||
-- local upvalue storage used by BuildTabs
|
||||
local widths = {}
|
||||
local rowwidths = {}
|
||||
local rowends = {}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function UpdateTabLook(frame)
|
||||
if frame.disabled then
|
||||
PanelTemplates_SetDisabledTabState(frame)
|
||||
frame:SetAlpha(0.5)
|
||||
elseif frame.selected then
|
||||
PanelTemplates_SelectTab(frame)
|
||||
frame:SetAlpha(1)
|
||||
else
|
||||
PanelTemplates_DeselectTab(frame)
|
||||
frame:SetAlpha(0.5)
|
||||
end
|
||||
end
|
||||
|
||||
local function Tab_SetText(tab, text)
|
||||
tab:_SetText(text)
|
||||
local width = tab.obj.frame.width or tab.obj.frame:GetWidth() or 0
|
||||
PanelTemplates_TabResize(0, tab, nil, width)
|
||||
end
|
||||
|
||||
local function Tab_SetSelected(tab, selected)
|
||||
tab.selected = selected
|
||||
UpdateTabLook(tab)
|
||||
end
|
||||
|
||||
local function Tab_SetDisabled(tab, disabled)
|
||||
tab.disabled = disabled
|
||||
UpdateTabLook(tab)
|
||||
end
|
||||
|
||||
local function BuildTabsOnUpdate()
|
||||
local self = this.obj
|
||||
self:BuildTabs()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Tab_OnClick()
|
||||
if not (this.selected or this.disabled) then
|
||||
PlaySound("igCharacterInfoTab")
|
||||
this.obj:SelectTab(this.value)
|
||||
end
|
||||
end
|
||||
|
||||
local function Tab_OnEnter()
|
||||
local self = this.obj
|
||||
self:Fire("OnTabEnter", 2, self.tabs[this.id].value, this)
|
||||
end
|
||||
|
||||
local function Tab_OnLeave()
|
||||
local self = this.obj
|
||||
self:Fire("OnTabLeave", 2, self.tabs[this.id].value, this)
|
||||
end
|
||||
|
||||
local function Tab_OnShow()
|
||||
_G[this:GetName().."HighlightTexture"]:SetWidth(this:GetTextWidth() + 30)
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetTitle()
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self.status = nil
|
||||
for k in pairs(self.localstatus) do
|
||||
self.localstatus[k] = nil
|
||||
end
|
||||
self.tablist = nil
|
||||
for _, tab in pairs(self.tabs) do
|
||||
tab:Hide()
|
||||
end
|
||||
end,
|
||||
|
||||
["CreateTab"] = function(self, id)
|
||||
local tabname = strfmt("AceGUITabGroup%dTab%d", self.num, id)
|
||||
local tab = CreateFrame("Button", tabname, self.border, "TabButtonTemplate")
|
||||
-- Normal texture
|
||||
local texture = getglobal(tabname.."Left")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
|
||||
texture:ClearAllPoints()
|
||||
texture:SetPoint("BOTTOMLEFT", tab,"BOTTOMLEFT")
|
||||
texture:SetPoint("TOPLEFT", tab,"TOPLEFT")
|
||||
texture = getglobal(tabname.."Middle")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
|
||||
texture = getglobal(tabname.."Right")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
|
||||
-- Disabled texture
|
||||
texture = getglobal(tabname.."LeftDisabled")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
|
||||
texture:SetPoint("BOTTOMLEFT", tab,"BOTTOMLEFT")
|
||||
texture:SetPoint("TOPLEFT", tab,"TOPLEFT")
|
||||
texture = getglobal(tabname.."MiddleDisabled")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
|
||||
texture = getglobal(tabname.."RightDisabled")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameTab")
|
||||
|
||||
tab.obj = self
|
||||
tab.id = id
|
||||
|
||||
tab:SetScript("OnClick", Tab_OnClick)
|
||||
tab:SetScript("OnEnter", Tab_OnEnter)
|
||||
tab:SetScript("OnLeave", Tab_OnLeave)
|
||||
tab:SetScript("OnShow", Tab_OnShow)
|
||||
|
||||
tab._SetText = tab.SetText
|
||||
tab.SetText = Tab_SetText
|
||||
tab.SetSelected = Tab_SetSelected
|
||||
tab.SetDisabled = Tab_SetDisabled
|
||||
|
||||
return tab
|
||||
end,
|
||||
|
||||
["SetTitle"] = function(self, text)
|
||||
self.titletext:SetText(text or "")
|
||||
if text and text ~= "" then
|
||||
self.alignoffset = 25
|
||||
else
|
||||
self.alignoffset = 18
|
||||
end
|
||||
self:BuildTabs()
|
||||
end,
|
||||
|
||||
["SetStatusTable"] = function(self, status)
|
||||
assert(type(status) == "table")
|
||||
self.status = status
|
||||
end,
|
||||
|
||||
["SelectTab"] = function(self, value)
|
||||
local status = self.status or self.localstatus
|
||||
local found
|
||||
for i, v in ipairs(self.tabs) do
|
||||
if v.value == value then
|
||||
v:SetSelected(true)
|
||||
found = true
|
||||
else
|
||||
v:SetSelected(false)
|
||||
end
|
||||
end
|
||||
status.selected = value
|
||||
if found then
|
||||
self:Fire("OnGroupSelected",1,value)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetTabs"] = function(self, tabs)
|
||||
self.tablist = tabs
|
||||
self:BuildTabs()
|
||||
end,
|
||||
|
||||
|
||||
["BuildTabs"] = function(self)
|
||||
local hastitle = (self.titletext:GetText() and self.titletext:GetText() ~= "")
|
||||
local status = self.status or self.localstatus
|
||||
local tablist = self.tablist
|
||||
local tabs = self.tabs
|
||||
|
||||
if not tablist then return end
|
||||
|
||||
local width = self.frame.width or self.frame:GetWidth() or 0
|
||||
|
||||
wipe(widths)
|
||||
wipe(rowwidths)
|
||||
wipe(rowends)
|
||||
|
||||
--Place Text into tabs and get thier initial width
|
||||
for i, v in ipairs(tablist) do
|
||||
local tab = tabs[i]
|
||||
if not tab then
|
||||
tab = self:CreateTab(i)
|
||||
tabs[i] = tab
|
||||
end
|
||||
|
||||
tab:Show()
|
||||
if type(v) == "table" then
|
||||
tab:SetText(v.text)
|
||||
tab:SetDisabled(v.disabled)
|
||||
tab.value = v.value
|
||||
elseif type(v) == "string" then
|
||||
tab:SetText(v)
|
||||
tab.value = v
|
||||
end
|
||||
|
||||
widths[i] = tab:GetWidth() - 6 --tabs are anchored 10 pixels from the right side of the previous one to reduce spacing, but add a fixed 4px padding for the text
|
||||
end
|
||||
|
||||
for i = tgetn(tablist)+1, tgetn(tabs), 1 do
|
||||
tabs[i]:Hide()
|
||||
end
|
||||
|
||||
--First pass, find the minimum number of rows needed to hold all tabs and the initial tab layout
|
||||
local numtabs = tgetn(tablist)
|
||||
local numrows = 1
|
||||
local usedwidth = 0
|
||||
|
||||
for i = 1, tgetn(tablist) do
|
||||
--If this is not the first tab of a row and there isn't room for it
|
||||
if usedwidth ~= 0 and (width - usedwidth - widths[i]) < 0 then
|
||||
rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
|
||||
rowends[numrows] = i - 1
|
||||
numrows = numrows + 1
|
||||
usedwidth = 0
|
||||
end
|
||||
usedwidth = usedwidth + widths[i]
|
||||
end
|
||||
rowwidths[numrows] = usedwidth + 10 --first tab in each row takes up an extra 10px
|
||||
rowends[numrows] = tgetn(tablist)
|
||||
|
||||
--Fix for single tabs being left on the last row, move a tab from the row above if applicable
|
||||
if numrows > 1 then
|
||||
--if the last row has only one tab
|
||||
if rowends[numrows-1] == numtabs-1 then
|
||||
--if there are more than 2 tabs in the 2nd last row
|
||||
if (numrows == 2 and rowends[numrows-1] > 2) or (rowends[numrows] - rowends[numrows-1] > 2) then
|
||||
--move 1 tab from the second last row to the last, if there is enough space
|
||||
if (rowwidths[numrows] + widths[numtabs-1]) <= width then
|
||||
rowends[numrows-1] = rowends[numrows-1] - 1
|
||||
rowwidths[numrows] = rowwidths[numrows] + widths[numtabs-1]
|
||||
rowwidths[numrows-1] = rowwidths[numrows-1] - widths[numtabs-1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--anchor the rows as defined and resize tabs to fill thier row
|
||||
local starttab = 1
|
||||
for row, endtab in ipairs(rowends) do
|
||||
local first = true
|
||||
for tabno = starttab, endtab do
|
||||
local tab = tabs[tabno]
|
||||
tab:ClearAllPoints()
|
||||
if first then
|
||||
tab:SetPoint("TOPLEFT", self.frame, "TOPLEFT", 0, -(hastitle and 14 or 7)-(row-1)*20 )
|
||||
first = false
|
||||
else
|
||||
tab:SetPoint("LEFT", tabs[tabno-1], "RIGHT", -10, 0)
|
||||
end
|
||||
end
|
||||
|
||||
-- equal padding for each tab to fill the available width,
|
||||
-- if the used space is above 75% already
|
||||
-- the 18 pixel is the typical width of a scrollbar, so we can have a tab group inside a scrolling frame,
|
||||
-- and not have the tabs jump around funny when switching between tabs that need scrolling and those that don't
|
||||
local padding = 0
|
||||
if not (numrows == 1 and rowwidths[1] < width*0.75 - 18) then
|
||||
padding = (width - rowwidths[row]) / (endtab - starttab+1)
|
||||
end
|
||||
|
||||
for i = starttab, endtab do
|
||||
PanelTemplates_TabResize(padding + 4, tabs[i], nil, width)
|
||||
end
|
||||
starttab = endtab + 1
|
||||
end
|
||||
|
||||
self.borderoffset = (hastitle and 17 or 10)+((numrows)*20)+7
|
||||
self.border:SetPoint("TOPLEFT", 1, -self.borderoffset)
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
local contentwidth = width - 60
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
self:BuildTabs(self)
|
||||
self.frame:SetScript("OnUpdate", BuildTabsOnUpdate)
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - (self.borderoffset + 23)
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end,
|
||||
|
||||
["LayoutFinished"] = function(self, width, height)
|
||||
if self.noAutoHeight then return end
|
||||
self:SetHeight((height or 0) + (self.borderoffset + 23))
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local PaneBackdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 5, bottom = 3 }
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local num = AceGUI:GetNextWidgetNum(Type)
|
||||
local frame = CreateFrame("Frame",nil,UIParent)
|
||||
frame:SetHeight(100)
|
||||
frame:SetWidth(100)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
local titletext = frame:CreateFontString(nil,"OVERLAY","GameFontNormal")
|
||||
titletext:SetPoint("TOPLEFT", 14, 0)
|
||||
titletext:SetPoint("TOPRIGHT", -14, 0)
|
||||
titletext:SetJustifyH("LEFT")
|
||||
titletext:SetHeight(18)
|
||||
titletext:SetText("")
|
||||
|
||||
local border = CreateFrame("Frame", nil, frame)
|
||||
border:SetPoint("TOPLEFT", 1, -27)
|
||||
border:SetPoint("BOTTOMRIGHT", -1, 3)
|
||||
border:SetBackdrop(PaneBackdrop)
|
||||
border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
|
||||
border:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
|
||||
local content = CreateFrame("Frame", nil, border)
|
||||
content:SetPoint("TOPLEFT", 10, -7)
|
||||
content:SetPoint("BOTTOMRIGHT", -10, 7)
|
||||
|
||||
local widget = {
|
||||
num = num,
|
||||
frame = frame,
|
||||
localstatus = {},
|
||||
alignoffset = 18,
|
||||
titletext = titletext,
|
||||
border = border,
|
||||
borderoffset = 27,
|
||||
tabs = {},
|
||||
content = content,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,769 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
TreeGroup Container
|
||||
Container that uses a tree control to switch between groups.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "TreeGroup", 40
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local strsplit = AceCore.strsplit
|
||||
local _G = AceCore._G
|
||||
|
||||
-- Lua APIs
|
||||
local next, pairs, ipairs, assert, type = next, pairs, ipairs, assert, type
|
||||
local math_min, math_max, floor = math.min, math.max, floor
|
||||
local tgetn, tremove, unpack, tconcat = table.getn, table.remove, unpack, table.concat
|
||||
local strfmt = string.format
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: GameTooltip, FONT_COLOR_CODE_CLOSE
|
||||
|
||||
-- Recycling functions
|
||||
local new, del
|
||||
do
|
||||
local pool = setmetatable({},{__mode='k'})
|
||||
function new()
|
||||
local t = next(pool)
|
||||
if t then
|
||||
pool[t] = nil
|
||||
return t
|
||||
else
|
||||
return {}
|
||||
end
|
||||
end
|
||||
function del(t)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
pool[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local DEFAULT_TREE_WIDTH = 175
|
||||
local DEFAULT_TREE_SIZABLE = true
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function GetButtonUniqueValue(line)
|
||||
local parent = line.parent
|
||||
if parent and parent.value then
|
||||
return GetButtonUniqueValue(parent).."\001"..line.value
|
||||
else
|
||||
return line.value
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateButton(button, treeline, selected, canExpand, isExpanded)
|
||||
local self = button.obj
|
||||
local toggle = button.toggle
|
||||
local frame = self.frame
|
||||
local text = treeline.text or ""
|
||||
local icon = treeline.icon
|
||||
local iconCoords = treeline.iconCoords
|
||||
local level = treeline.level
|
||||
local value = treeline.value
|
||||
local uniquevalue = treeline.uniquevalue
|
||||
local disabled = treeline.disabled
|
||||
|
||||
button.treeline = treeline
|
||||
button.value = value
|
||||
button.uniquevalue = uniquevalue
|
||||
if selected then
|
||||
button:LockHighlight()
|
||||
button.selected = true
|
||||
else
|
||||
button:UnlockHighlight()
|
||||
button.selected = false
|
||||
end
|
||||
local normalTexture = button:GetNormalTexture()
|
||||
local line = button.line
|
||||
button.level = level
|
||||
|
||||
if ( level == 1 ) then
|
||||
button.text:SetFontObject("GameFontNormal")
|
||||
button:SetHighlightFontObject("GameFontHighlight")
|
||||
button.text:SetPoint("LEFT", (icon and 16 or 0) + 8, 2)
|
||||
else
|
||||
button.text:SetFontObject("GameFontHighlightSmall")
|
||||
button:SetHighlightFontObject("GameFontHighlightSmall")
|
||||
button.text:SetPoint("LEFT", (icon and 16 or 0) + 8 * level, 2)
|
||||
end
|
||||
|
||||
if disabled then
|
||||
button:EnableMouse(false)
|
||||
button.text:SetText("|cff808080"..text..FONT_COLOR_CODE_CLOSE)
|
||||
else
|
||||
button.text:SetText(text)
|
||||
button:EnableMouse(true)
|
||||
end
|
||||
|
||||
if icon then
|
||||
button.icon:SetTexture(icon)
|
||||
button.icon:SetPoint("LEFT", 8 * level, (level == 1) and 0 or 1)
|
||||
else
|
||||
button.icon:SetTexture(nil)
|
||||
end
|
||||
|
||||
if iconCoords then
|
||||
button.icon:SetTexCoord(unpack(iconCoords))
|
||||
else
|
||||
button.icon:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
|
||||
if canExpand then
|
||||
if not isExpanded then
|
||||
toggle:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-UP")
|
||||
toggle:SetPushedTexture("Interface\\Buttons\\UI-PlusButton-DOWN")
|
||||
else
|
||||
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
|
||||
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
|
||||
end
|
||||
toggle:Show()
|
||||
else
|
||||
toggle:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function ShouldDisplayLevel(tree)
|
||||
local result = false
|
||||
for k, v in ipairs(tree) do
|
||||
if v.children == nil and v.visible ~= false then
|
||||
result = true
|
||||
elseif v.children then
|
||||
result = result or ShouldDisplayLevel(v.children)
|
||||
end
|
||||
if result then return result end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function addLine(self, v, tree, level, parent)
|
||||
local line = new()
|
||||
line.value = v.value
|
||||
line.text = v.text
|
||||
line.icon = v.icon
|
||||
line.iconCoords = v.iconCoords
|
||||
line.disabled = v.disabled
|
||||
line.tree = tree
|
||||
line.level = level
|
||||
line.parent = parent
|
||||
line.visible = v.visible
|
||||
line.uniquevalue = GetButtonUniqueValue(line)
|
||||
if v.children then
|
||||
line.hasChildren = true
|
||||
else
|
||||
line.hasChildren = nil
|
||||
end
|
||||
tinsert(self.lines, line)
|
||||
return line
|
||||
end
|
||||
|
||||
--fire an update after one frame to catch the treeframes height
|
||||
local function FirstFrameUpdate()
|
||||
local self = this.obj
|
||||
this:SetScript("OnUpdate", nil)
|
||||
self:RefreshTree()
|
||||
end
|
||||
|
||||
local BuildUniqueValue
|
||||
do
|
||||
local args = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
|
||||
function BuildUniqueValue(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
args[1] = a1
|
||||
args[2] = a2
|
||||
args[3] = a3
|
||||
args[4] = a4
|
||||
args[5] = a5
|
||||
args[6] = a6
|
||||
args[7] = a7
|
||||
args[8] = a8
|
||||
args[9] = a9
|
||||
args[10] = a10
|
||||
return tconcat(tmp, "\001", 1, tgetn(args))
|
||||
end
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Expand_OnClick()
|
||||
local button = this.button
|
||||
local self = button.obj
|
||||
local status = (self.status or self.localstatus).groups
|
||||
status[button.uniquevalue] = not status[button.uniquevalue]
|
||||
self:RefreshTree()
|
||||
end
|
||||
|
||||
local function Button_OnClick()
|
||||
local self = this.obj
|
||||
self:Fire("OnClick", 2, this.uniquevalue, this.selected)
|
||||
if not this.selected then
|
||||
self:SetSelected(this.uniquevalue)
|
||||
this.selected = true
|
||||
this:LockHighlight()
|
||||
self:RefreshTree()
|
||||
end
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function Button_OnDoubleClick()
|
||||
local self = this.obj
|
||||
local status = self.status or self.localstatus
|
||||
local status = (self.status or self.localstatus).groups
|
||||
status[this.uniquevalue] = not status[this.uniquevalue]
|
||||
self:RefreshTree()
|
||||
end
|
||||
|
||||
local function Button_OnEnter()
|
||||
local self = this.obj
|
||||
self:Fire("OnButtonEnter", 2, this.uniquevalue, this)
|
||||
|
||||
if self.enabletooltips then
|
||||
GameTooltip:SetOwner(this, "ANCHOR_NONE")
|
||||
GameTooltip:SetPoint("LEFT",this,"RIGHT")
|
||||
GameTooltip:SetText(this.text:GetText() or "", 1, .82, 0, true)
|
||||
|
||||
GameTooltip:Show()
|
||||
end
|
||||
end
|
||||
|
||||
local function Button_OnLeave()
|
||||
local self = this.obj
|
||||
self:Fire("OnButtonLeave", 2, this.uniquevalue, this)
|
||||
|
||||
if self.enabletooltips then
|
||||
GameTooltip:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnScrollValueChanged()
|
||||
if this.obj.noupdate then return end
|
||||
local self = this.obj
|
||||
local status = self.status or self.localstatus
|
||||
status.scrollvalue = floor(arg1 + 0.5)
|
||||
self:RefreshTree()
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function Tree_OnSizeChanged()
|
||||
this.obj:RefreshTree()
|
||||
end
|
||||
|
||||
local function Tree_OnMouseWheel()
|
||||
local self = this.obj
|
||||
if self.showscroll then
|
||||
local scrollbar = self.scrollbar
|
||||
local min, max = scrollbar:GetMinMaxValues()
|
||||
local value = scrollbar:GetValue()
|
||||
local newvalue = math_min(max,math_max(min,value - arg1))
|
||||
if value ~= newvalue then
|
||||
scrollbar:SetValue(newvalue)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function Dragger_OnLeave()
|
||||
this:SetBackdropColor(1, 1, 1, 0)
|
||||
end
|
||||
|
||||
local function Dragger_OnEnter()
|
||||
this:SetBackdropColor(1, 1, 1, 0.8)
|
||||
end
|
||||
|
||||
local function Dragger_OnMouseDown()
|
||||
local treeframe = this:GetParent()
|
||||
treeframe:StartSizing("RIGHT")
|
||||
end
|
||||
|
||||
local function Dragger_OnMouseUp()
|
||||
local treeframe = this:GetParent()
|
||||
local self = treeframe.obj
|
||||
local this = treeframe:GetParent()
|
||||
treeframe:StopMovingOrSizing()
|
||||
--treeframe:SetScript("OnUpdate", nil)
|
||||
treeframe:SetUserPlaced(false)
|
||||
--Without this :GetHeight will get stuck on the current height, causing the tree contents to not resize
|
||||
treeframe:SetHeight(0)
|
||||
treeframe:SetPoint("TOPLEFT", this, "TOPLEFT",0,0)
|
||||
treeframe:SetPoint("BOTTOMLEFT", this, "BOTTOMLEFT",0,0)
|
||||
|
||||
local status = self.status or self.localstatus
|
||||
status.treewidth = treeframe:GetWidth()
|
||||
|
||||
treeframe.obj:Fire("OnTreeResize", 1, treeframe:GetWidth())
|
||||
-- recalculate the content width
|
||||
treeframe.obj:OnWidthSet(status.fullwidth)
|
||||
-- update the layout of the content
|
||||
treeframe.obj:DoLayout()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods
|
||||
do
|
||||
local select_args = {nil,nil,nil,nil,nil,nil,nil,nil,nil,nil}
|
||||
methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetTreeWidth(DEFAULT_TREE_WIDTH, DEFAULT_TREE_SIZABLE)
|
||||
self:EnableButtonTooltips(true)
|
||||
self.frame:SetScript("OnUpdate", FirstFrameUpdate)
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self.status = nil
|
||||
for k, v in pairs(self.localstatus) do
|
||||
if k == "groups" then
|
||||
for k2 in pairs(v) do
|
||||
v[k2] = nil
|
||||
end
|
||||
else
|
||||
self.localstatus[k] = nil
|
||||
end
|
||||
end
|
||||
self.localstatus.scrollvalue = 0
|
||||
self.localstatus.treewidth = DEFAULT_TREE_WIDTH
|
||||
self.localstatus.treesizable = DEFAULT_TREE_SIZABLE
|
||||
end,
|
||||
|
||||
["EnableButtonTooltips"] = function(self, enable)
|
||||
self.enabletooltips = enable
|
||||
end,
|
||||
|
||||
["CreateButton"] = function(self)
|
||||
local num = AceGUI:GetNextWidgetNum("TreeGroupButton")
|
||||
|
||||
local button = CreateFrame("Button", strfmt("AceGUI30TreeButton%d", num), self.treeframe)
|
||||
button.obj = self
|
||||
button:SetWidth(175)
|
||||
button:SetHeight(18)
|
||||
|
||||
local toggle = CreateFrame("Button", nil, button)
|
||||
toggle.obj = button
|
||||
button.toggle = toggle
|
||||
toggle:SetWidth(14)
|
||||
toggle:SetHeight(14)
|
||||
toggle:ClearAllPoints()
|
||||
toggle:SetPoint("TOPRIGHT", button, "TOPRIGHT", -6, -1)
|
||||
toggle:SetScript("OnClick", Button_OnClick)
|
||||
toggle:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-UP")
|
||||
toggle:SetPushedTexture("Interface\\Buttons\\UI-MinusButton-DOWN")
|
||||
toggle:SetHighlightTexture("Interface\Buttons\UI-PlusButton-Hilight", "ADD")
|
||||
|
||||
local text = button:CreateFontString()
|
||||
button.text = text
|
||||
text:SetFontObject(GameFontNormal)
|
||||
button:SetHighlightFontObject(GameFontHighlight)
|
||||
text:SetPoint("RIGHT", toggle, "LEFT", -2, 0);
|
||||
text:SetJustifyH("LEFT")
|
||||
|
||||
local highlight = button:CreateTexture(nil, "HIGHLIGHT");
|
||||
button.highlight = highlight
|
||||
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
|
||||
highlight:SetBlendMode("ADD")
|
||||
highlight:SetVertexColor(.196, .388, .8);
|
||||
highlight:ClearAllPoints()
|
||||
highlight:SetPoint("TOPLEFT",0,1)
|
||||
highlight:SetPoint("BOTTOMRIGHT",0,1)
|
||||
|
||||
local icon = button:CreateTexture(nil, "OVERLAY")
|
||||
icon:SetWidth(14)
|
||||
icon:SetHeight(14)
|
||||
button.icon = icon
|
||||
|
||||
button:SetScript("OnClick",Button_OnClick)
|
||||
button:SetScript("OnDoubleClick", Button_OnDoubleClick)
|
||||
button:SetScript("OnEnter",Button_OnEnter)
|
||||
button:SetScript("OnLeave",Button_OnLeave)
|
||||
|
||||
button.toggle.button = button
|
||||
button.toggle:SetScript("OnClick",Expand_OnClick)
|
||||
|
||||
button.text:SetHeight(14) -- Prevents text wrapping
|
||||
|
||||
return button
|
||||
end,
|
||||
|
||||
["SetStatusTable"] = function(self, status)
|
||||
assert(type(status) == "table")
|
||||
self.status = status
|
||||
if not status.groups then
|
||||
status.groups = {}
|
||||
end
|
||||
if not status.scrollvalue then
|
||||
status.scrollvalue = 0
|
||||
end
|
||||
if not status.treewidth then
|
||||
status.treewidth = DEFAULT_TREE_WIDTH
|
||||
end
|
||||
if status.treesizable == nil then
|
||||
status.treesizable = DEFAULT_TREE_SIZABLE
|
||||
end
|
||||
self:SetTreeWidth(status.treewidth,status.treesizable)
|
||||
self:RefreshTree()
|
||||
end,
|
||||
|
||||
--sets the tree to be displayed
|
||||
["SetTree"] = function(self, tree, filter)
|
||||
self.filter = filter
|
||||
if tree then
|
||||
assert(type(tree) == "table")
|
||||
end
|
||||
self.tree = tree
|
||||
self:RefreshTree()
|
||||
end,
|
||||
|
||||
["BuildLevel"] = function(self, tree, level, parent)
|
||||
local groups = (self.status or self.localstatus).groups
|
||||
local hasChildren = self.hasChildren
|
||||
|
||||
for i, v in ipairs(tree) do
|
||||
if v.children then
|
||||
if not self.filter or ShouldDisplayLevel(v.children) then
|
||||
local line = addLine(self, v, tree, level, parent)
|
||||
if groups[line.uniquevalue] then
|
||||
self:BuildLevel(v.children, level+1, line)
|
||||
end
|
||||
end
|
||||
elseif v.visible ~= false or not self.filter then
|
||||
addLine(self, v, tree, level, parent)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["RefreshTree"] = function(self,scrollToSelection)
|
||||
local buttons = self.buttons
|
||||
local lines = self.lines
|
||||
|
||||
for i, v in ipairs(buttons) do
|
||||
v:Hide()
|
||||
end
|
||||
while lines[1] do
|
||||
local t = tremove(lines)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
del(t)
|
||||
end
|
||||
|
||||
if not self.tree then return end
|
||||
--Build the list of visible entries from the tree and status tables
|
||||
local status = self.status or self.localstatus
|
||||
local groupstatus = status.groups
|
||||
local tree = self.tree
|
||||
|
||||
local treeframe = self.treeframe
|
||||
|
||||
status.scrollToSelection = status.scrollToSelection or scrollToSelection -- needs to be cached in case the control hasn't been drawn yet (code bails out below)
|
||||
|
||||
self:BuildLevel(tree, 1)
|
||||
|
||||
local numlines = tgetn(lines)
|
||||
|
||||
local maxlines = (floor(((self.treeframe:GetHeight()or 0) - 20 ) / 18))
|
||||
if maxlines <= 0 then return end
|
||||
|
||||
local first, last
|
||||
|
||||
scrollToSelection = status.scrollToSelection
|
||||
status.scrollToSelection = nil
|
||||
|
||||
if numlines <= maxlines then
|
||||
--the whole tree fits in the frame
|
||||
status.scrollvalue = 0
|
||||
self:ShowScroll(false)
|
||||
first, last = 1, numlines
|
||||
else
|
||||
self:ShowScroll(true)
|
||||
--scrolling will be needed
|
||||
self.noupdate = true
|
||||
self.scrollbar:SetMinMaxValues(0, numlines - maxlines)
|
||||
--check if we are scrolled down too far
|
||||
if numlines - status.scrollvalue < maxlines then
|
||||
status.scrollvalue = numlines - maxlines
|
||||
end
|
||||
self.noupdate = nil
|
||||
first, last = status.scrollvalue+1, status.scrollvalue + maxlines
|
||||
--show selection?
|
||||
if scrollToSelection and status.selected then
|
||||
local show
|
||||
for i,line in ipairs(lines) do -- find the line number
|
||||
if line.uniquevalue==status.selected then
|
||||
show=i
|
||||
end
|
||||
end
|
||||
if not show then
|
||||
-- selection was deleted or something?
|
||||
elseif show>=first and show<=last then
|
||||
-- all good
|
||||
else
|
||||
-- scrolling needed!
|
||||
if show<first then
|
||||
status.scrollvalue = show-1
|
||||
else
|
||||
status.scrollvalue = show-maxlines
|
||||
end
|
||||
first, last = status.scrollvalue+1, status.scrollvalue + maxlines
|
||||
end
|
||||
end
|
||||
if self.scrollbar:GetValue() ~= status.scrollvalue then
|
||||
self.scrollbar:SetValue(status.scrollvalue)
|
||||
end
|
||||
end
|
||||
|
||||
local buttonnum = 1
|
||||
for i = first, last do
|
||||
local line = lines[i]
|
||||
local button = buttons[buttonnum]
|
||||
if not button then
|
||||
button = self:CreateButton()
|
||||
|
||||
buttons[buttonnum] = button
|
||||
button:SetParent(treeframe)
|
||||
button:SetFrameLevel(treeframe:GetFrameLevel()+1)
|
||||
button:ClearAllPoints()
|
||||
if buttonnum == 1 then
|
||||
if self.showscroll then
|
||||
button:SetPoint("TOPRIGHT", -22, -10)
|
||||
button:SetPoint("TOPLEFT", 0, -10)
|
||||
else
|
||||
button:SetPoint("TOPRIGHT", 0, -10)
|
||||
button:SetPoint("TOPLEFT", 0, -10)
|
||||
end
|
||||
else
|
||||
button:SetPoint("TOPRIGHT", buttons[buttonnum-1], "BOTTOMRIGHT",0,0)
|
||||
button:SetPoint("TOPLEFT", buttons[buttonnum-1], "BOTTOMLEFT",0,0)
|
||||
end
|
||||
end
|
||||
|
||||
UpdateButton(button, line, status.selected == line.uniquevalue, line.hasChildren, groupstatus[line.uniquevalue] )
|
||||
button:Show()
|
||||
buttonnum = buttonnum + 1
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
["SetSelected"] = function(self, value)
|
||||
local status = self.status or self.localstatus
|
||||
if status.selected ~= value then
|
||||
status.selected = value
|
||||
self:Fire("OnGroupSelected", 1, value)
|
||||
end
|
||||
end,
|
||||
|
||||
["Select"] = function(self, uniquevalue, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
self.filter = false
|
||||
local status = self.status or self.localstatus
|
||||
local groups = status.groups
|
||||
select_args[1] = a1
|
||||
select_args[2] = a2
|
||||
select_args[3] = a3
|
||||
select_args[4] = a4
|
||||
select_args[5] = a5
|
||||
select_args[6] = a6
|
||||
select_args[7] = a7
|
||||
select_args[8] = a8
|
||||
select_args[9] = a9
|
||||
select_args[10] = a10
|
||||
for i = 1, tgetn(select_args) do
|
||||
groups[tconcat(select_args, "\001", 1, i)] = true
|
||||
end
|
||||
status.selected = uniquevalue
|
||||
self:RefreshTree(true)
|
||||
self:Fire("OnGroupSelected", 1, uniquevalue)
|
||||
end,
|
||||
|
||||
["SelectByPath"] = function(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
self:Select(BuildUniqueValue(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10), a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end,
|
||||
|
||||
["SelectByValue"] = function(self, uniquevalue)
|
||||
self:Select(uniquevalue, strsplit("\001", uniquevalue))
|
||||
end,
|
||||
|
||||
["ShowScroll"] = function(self, show)
|
||||
self.showscroll = show
|
||||
if show then
|
||||
self.scrollbar:Show()
|
||||
if self.buttons[1] then
|
||||
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",-22,-10)
|
||||
end
|
||||
else
|
||||
self.scrollbar:Hide()
|
||||
if self.buttons[1] then
|
||||
self.buttons[1]:SetPoint("TOPRIGHT", self.treeframe,"TOPRIGHT",0,-10)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
local content = self.content
|
||||
local treeframe = self.treeframe
|
||||
local status = self.status or self.localstatus
|
||||
status.fullwidth = width
|
||||
|
||||
local contentwidth = width - status.treewidth - 20
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
|
||||
local maxtreewidth = math_min(400, width - 50)
|
||||
|
||||
if maxtreewidth > 100 and status.treewidth > maxtreewidth then
|
||||
self:SetTreeWidth(maxtreewidth, status.treesizable)
|
||||
end
|
||||
treeframe:SetMaxResize(maxtreewidth, 1600)
|
||||
end,
|
||||
|
||||
["OnHeightSet"] = function(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - 20
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end,
|
||||
|
||||
["SetTreeWidth"] = function(self, treewidth, resizable)
|
||||
if not resizable then
|
||||
if type(treewidth) == 'number' then
|
||||
resizable = false
|
||||
elseif type(treewidth) == 'boolean' then
|
||||
resizable = treewidth
|
||||
treewidth = DEFAULT_TREE_WIDTH
|
||||
else
|
||||
resizable = false
|
||||
treewidth = DEFAULT_TREE_WIDTH
|
||||
end
|
||||
end
|
||||
self.treeframe:SetWidth(treewidth)
|
||||
self.dragger:EnableMouse(resizable)
|
||||
|
||||
local status = self.status or self.localstatus
|
||||
status.treewidth = treewidth
|
||||
status.treesizable = resizable
|
||||
|
||||
-- recalculate the content width
|
||||
if status.fullwidth then
|
||||
self:OnWidthSet(status.fullwidth)
|
||||
end
|
||||
end,
|
||||
|
||||
["GetTreeWidth"] = function(self)
|
||||
local status = self.status or self.localstatus
|
||||
return status.treewidth or DEFAULT_TREE_WIDTH
|
||||
end,
|
||||
|
||||
["LayoutFinished"] = function(self, width, height)
|
||||
if self.noAutoHeight then return end
|
||||
self:SetHeight((height or 0) + 20)
|
||||
end
|
||||
}
|
||||
end -- method
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local PaneBackdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
|
||||
tile = true, tileSize = 16, edgeSize = 16,
|
||||
insets = { left = 3, right = 3, top = 5, bottom = 3 }
|
||||
}
|
||||
|
||||
local DraggerBackdrop = {
|
||||
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
|
||||
edgeFile = nil,
|
||||
tile = true, tileSize = 16, edgeSize = 0,
|
||||
insets = { left = 3, right = 3, top = 7, bottom = 7 }
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local num = AceGUI:GetNextWidgetNum(Type)
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
|
||||
local treeframe = CreateFrame("Frame", nil, frame)
|
||||
treeframe:SetPoint("TOPLEFT", 0, 0)
|
||||
treeframe:SetPoint("BOTTOMLEFT", 0, 0)
|
||||
treeframe:SetWidth(DEFAULT_TREE_WIDTH)
|
||||
treeframe:EnableMouseWheel(true)
|
||||
treeframe:SetBackdrop(PaneBackdrop)
|
||||
treeframe:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
|
||||
treeframe:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
treeframe:SetResizable(true)
|
||||
treeframe:SetMinResize(100, 1)
|
||||
treeframe:SetMaxResize(400, 1600)
|
||||
treeframe:SetScript("OnUpdate", FirstFrameUpdate)
|
||||
treeframe:SetScript("OnSizeChanged", Tree_OnSizeChanged)
|
||||
treeframe:SetScript("OnMouseWheel", Tree_OnMouseWheel)
|
||||
|
||||
local dragger = CreateFrame("Frame", nil, treeframe)
|
||||
dragger:SetWidth(8)
|
||||
dragger:SetPoint("TOP", treeframe, "TOPRIGHT")
|
||||
dragger:SetPoint("BOTTOM", treeframe, "BOTTOMRIGHT")
|
||||
dragger:SetBackdrop(DraggerBackdrop)
|
||||
dragger:SetBackdropColor(1, 1, 1, 0)
|
||||
dragger:SetScript("OnEnter", Dragger_OnEnter)
|
||||
dragger:SetScript("OnLeave", Dragger_OnLeave)
|
||||
dragger:SetScript("OnMouseDown", Dragger_OnMouseDown)
|
||||
dragger:SetScript("OnMouseUp", Dragger_OnMouseUp)
|
||||
|
||||
local scrollbar = CreateFrame("Slider", strfmt("AceConfigDialogTreeGroup%dScrollBar", num), treeframe, "UIPanelScrollBarTemplate")
|
||||
scrollbar:SetScript("OnValueChanged", nil)
|
||||
scrollbar:SetPoint("TOPRIGHT", -10, -26)
|
||||
scrollbar:SetPoint("BOTTOMRIGHT", -10, 26)
|
||||
scrollbar:SetMinMaxValues(0,0)
|
||||
scrollbar:SetValueStep(1)
|
||||
scrollbar:SetValue(0)
|
||||
scrollbar:SetWidth(16)
|
||||
scrollbar:SetScript("OnValueChanged", OnScrollValueChanged)
|
||||
|
||||
local scrollbg = scrollbar:CreateTexture(nil, "BACKGROUND")
|
||||
scrollbg:SetAllPoints(scrollbar)
|
||||
scrollbg:SetTexture(0,0,0,0.4)
|
||||
|
||||
local border = CreateFrame("Frame",nil,frame)
|
||||
border:SetPoint("TOPLEFT", treeframe, "TOPRIGHT")
|
||||
border:SetPoint("BOTTOMRIGHT", 0, 0)
|
||||
border:SetBackdrop(PaneBackdrop)
|
||||
border:SetBackdropColor(0.1, 0.1, 0.1, 0.5)
|
||||
border:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame", nil, border)
|
||||
content:SetPoint("TOPLEFT", 10, -10)
|
||||
content:SetPoint("BOTTOMRIGHT", -10, 10)
|
||||
|
||||
local widget = {
|
||||
frame = frame,
|
||||
lines = {},
|
||||
levels = {},
|
||||
buttons = {},
|
||||
hasChildren = {},
|
||||
localstatus = { groups = {}, scrollvalue = 0 },
|
||||
filter = false,
|
||||
treeframe = treeframe,
|
||||
dragger = dragger,
|
||||
scrollbar = scrollbar,
|
||||
border = border,
|
||||
content = content,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
treeframe.obj, dragger.obj, scrollbar.obj = widget, widget, widget
|
||||
|
||||
return AceGUI:RegisterAsContainer(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,331 @@
|
||||
local AceGUI = LibStub("AceGUI-3.0")
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, assert, type = pairs, assert, type
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: GameFontNormal
|
||||
|
||||
----------------
|
||||
-- Main Frame --
|
||||
----------------
|
||||
--[[
|
||||
Events :
|
||||
OnClose
|
||||
|
||||
]]
|
||||
do
|
||||
local Type = "Window"
|
||||
local Version = 4
|
||||
|
||||
local function frameOnClose()
|
||||
this.obj:Fire("OnClose")
|
||||
end
|
||||
|
||||
local function closeOnClick()
|
||||
PlaySound("gsTitleOptionExit")
|
||||
this.obj:Hide()
|
||||
end
|
||||
|
||||
local function frameOnMouseDown()
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function titleOnMouseDown()
|
||||
this:GetParent():StartMoving()
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function frameOnMouseUp()
|
||||
local frame = this:GetParent()
|
||||
frame:StopMovingOrSizing()
|
||||
local self = frame.obj
|
||||
local status = self.status or self.localstatus
|
||||
status.width = frame:GetWidth()
|
||||
status.height = frame:GetHeight()
|
||||
status.top = frame:GetTop()
|
||||
status.left = frame:GetLeft()
|
||||
end
|
||||
|
||||
local function sizerseOnMouseDown()
|
||||
this:GetParent():StartSizing("BOTTOMRIGHT")
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function sizersOnMouseDown()
|
||||
this:GetParent():StartSizing("BOTTOM")
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function sizereOnMouseDown()
|
||||
this:GetParent():StartSizing("RIGHT")
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function sizerOnMouseUp()
|
||||
this:GetParent():StopMovingOrSizing()
|
||||
end
|
||||
|
||||
local function SetTitle(self,title)
|
||||
self.titletext:SetText(title)
|
||||
end
|
||||
|
||||
local function SetStatusText(self,text)
|
||||
-- self.statustext:SetText(text)
|
||||
end
|
||||
|
||||
local function Hide(self)
|
||||
self.frame:Hide()
|
||||
end
|
||||
|
||||
local function Show(self)
|
||||
self.frame:Show()
|
||||
end
|
||||
|
||||
local function OnAcquire(self)
|
||||
self.frame:SetParent(UIParent)
|
||||
self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
self:ApplyStatus()
|
||||
self:EnableResize(true)
|
||||
self:Show()
|
||||
end
|
||||
|
||||
local function OnRelease(self)
|
||||
self.status = nil
|
||||
for k in pairs(self.localstatus) do
|
||||
self.localstatus[k] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- called to set an external table to store status in
|
||||
local function SetStatusTable(self, status)
|
||||
assert(type(status) == "table")
|
||||
self.status = status
|
||||
self:ApplyStatus()
|
||||
end
|
||||
|
||||
local function ApplyStatus(self)
|
||||
local status = self.status or self.localstatus
|
||||
local frame = self.frame
|
||||
self:SetWidth(status.width or 700)
|
||||
self:SetHeight(status.height or 500)
|
||||
if status.top and status.left then
|
||||
frame:SetPoint("TOP",UIParent,"BOTTOM",0,status.top)
|
||||
frame:SetPoint("LEFT",UIParent,"LEFT",status.left,0)
|
||||
else
|
||||
frame:SetPoint("CENTER",UIParent,"CENTER")
|
||||
end
|
||||
end
|
||||
|
||||
local function OnWidthSet(self, width)
|
||||
local content = self.content
|
||||
local contentwidth = width - 34
|
||||
if contentwidth < 0 then
|
||||
contentwidth = 0
|
||||
end
|
||||
content:SetWidth(contentwidth)
|
||||
content.width = contentwidth
|
||||
end
|
||||
|
||||
|
||||
local function OnHeightSet(self, height)
|
||||
local content = self.content
|
||||
local contentheight = height - 57
|
||||
if contentheight < 0 then
|
||||
contentheight = 0
|
||||
end
|
||||
content:SetHeight(contentheight)
|
||||
content.height = contentheight
|
||||
end
|
||||
|
||||
local function EnableResize(self, state)
|
||||
local func = state and "Show" or "Hide"
|
||||
self.sizer_se[func](self.sizer_se)
|
||||
self.sizer_s[func](self.sizer_s)
|
||||
self.sizer_e[func](self.sizer_e)
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame",nil,UIParent)
|
||||
local self = {}
|
||||
self.type = "Window"
|
||||
|
||||
self.Hide = Hide
|
||||
self.Show = Show
|
||||
self.SetTitle = SetTitle
|
||||
self.OnRelease = OnRelease
|
||||
self.OnAcquire = OnAcquire
|
||||
self.SetStatusText = SetStatusText
|
||||
self.SetStatusTable = SetStatusTable
|
||||
self.ApplyStatus = ApplyStatus
|
||||
self.OnWidthSet = OnWidthSet
|
||||
self.OnHeightSet = OnHeightSet
|
||||
self.EnableResize = EnableResize
|
||||
|
||||
self.localstatus = {}
|
||||
|
||||
self.frame = frame
|
||||
frame.obj = self
|
||||
frame:SetWidth(700)
|
||||
frame:SetHeight(500)
|
||||
frame:SetPoint("CENTER",UIParent,"CENTER",0,0)
|
||||
frame:EnableMouse()
|
||||
frame:SetMovable(true)
|
||||
frame:SetResizable(true)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
frame:SetScript("OnMouseDown", frameOnMouseDown)
|
||||
|
||||
frame:SetScript("OnHide",frameOnClose)
|
||||
frame:SetMinResize(240,240)
|
||||
frame:SetToplevel(true)
|
||||
|
||||
local titlebg = frame:CreateTexture(nil, "BACKGROUND")
|
||||
titlebg:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Title-Background]])
|
||||
titlebg:SetPoint("TOPLEFT", 9, -6)
|
||||
titlebg:SetPoint("BOTTOMRIGHT", frame, "TOPRIGHT", -28, -24)
|
||||
|
||||
local dialogbg = frame:CreateTexture(nil, "BACKGROUND")
|
||||
dialogbg:SetTexture([[Interface\Tooltips\UI-Tooltip-Background]])
|
||||
dialogbg:SetPoint("TOPLEFT", 8, -24)
|
||||
dialogbg:SetPoint("BOTTOMRIGHT", -6, 8)
|
||||
dialogbg:SetVertexColor(0, 0, 0, .75)
|
||||
|
||||
local topleft = frame:CreateTexture(nil, "BORDER")
|
||||
topleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
topleft:SetWidth(64)
|
||||
topleft:SetHeight(64)
|
||||
topleft:SetPoint("TOPLEFT")
|
||||
topleft:SetTexCoord(0.501953125, 0.625, 0, 1)
|
||||
|
||||
local topright = frame:CreateTexture(nil, "BORDER")
|
||||
topright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
topright:SetWidth(64)
|
||||
topright:SetHeight(64)
|
||||
topright:SetPoint("TOPRIGHT")
|
||||
topright:SetTexCoord(0.625, 0.75, 0, 1)
|
||||
|
||||
local top = frame:CreateTexture(nil, "BORDER")
|
||||
top:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
top:SetHeight(64)
|
||||
top:SetPoint("TOPLEFT", topleft, "TOPRIGHT")
|
||||
top:SetPoint("TOPRIGHT", topright, "TOPLEFT")
|
||||
top:SetTexCoord(0.25, 0.369140625, 0, 1)
|
||||
|
||||
local bottomleft = frame:CreateTexture(nil, "BORDER")
|
||||
bottomleft:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
bottomleft:SetWidth(64)
|
||||
bottomleft:SetHeight(64)
|
||||
bottomleft:SetPoint("BOTTOMLEFT")
|
||||
bottomleft:SetTexCoord(0.751953125, 0.875, 0, 1)
|
||||
|
||||
local bottomright = frame:CreateTexture(nil, "BORDER")
|
||||
bottomright:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
bottomright:SetWidth(64)
|
||||
bottomright:SetHeight(64)
|
||||
bottomright:SetPoint("BOTTOMRIGHT")
|
||||
bottomright:SetTexCoord(0.875, 1, 0, 1)
|
||||
|
||||
local bottom = frame:CreateTexture(nil, "BORDER")
|
||||
bottom:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
bottom:SetHeight(64)
|
||||
bottom:SetPoint("BOTTOMLEFT", bottomleft, "BOTTOMRIGHT")
|
||||
bottom:SetPoint("BOTTOMRIGHT", bottomright, "BOTTOMLEFT")
|
||||
bottom:SetTexCoord(0.376953125, 0.498046875, 0, 1)
|
||||
|
||||
local left = frame:CreateTexture(nil, "BORDER")
|
||||
left:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
left:SetWidth(64)
|
||||
left:SetPoint("TOPLEFT", topleft, "BOTTOMLEFT")
|
||||
left:SetPoint("BOTTOMLEFT", bottomleft, "TOPLEFT")
|
||||
left:SetTexCoord(0.001953125, 0.125, 0, 1)
|
||||
|
||||
local right = frame:CreateTexture(nil, "BORDER")
|
||||
right:SetTexture([[Interface\PaperDollInfoFrame\UI-GearManager-Border]])
|
||||
right:SetWidth(64)
|
||||
right:SetPoint("TOPRIGHT", topright, "BOTTOMRIGHT")
|
||||
right:SetPoint("BOTTOMRIGHT", bottomright, "TOPRIGHT")
|
||||
right:SetTexCoord(0.1171875, 0.2421875, 0, 1)
|
||||
|
||||
local close = CreateFrame("Button", nil, frame, "UIPanelCloseButton")
|
||||
close:SetPoint("TOPRIGHT", 2, 1)
|
||||
close:SetScript("OnClick", closeOnClick)
|
||||
self.closebutton = close
|
||||
close.obj = self
|
||||
|
||||
local titletext = frame:CreateFontString(nil, "ARTWORK")
|
||||
titletext:SetFontObject(GameFontNormal)
|
||||
titletext:SetPoint("TOPLEFT", 12, -8)
|
||||
titletext:SetPoint("TOPRIGHT", -32, -8)
|
||||
self.titletext = titletext
|
||||
|
||||
local title = CreateFrame("Button", nil, frame)
|
||||
title:SetPoint("TOPLEFT", titlebg)
|
||||
title:SetPoint("BOTTOMRIGHT", titlebg)
|
||||
title:EnableMouse()
|
||||
title:SetScript("OnMouseDown",titleOnMouseDown)
|
||||
title:SetScript("OnMouseUp", frameOnMouseUp)
|
||||
self.title = title
|
||||
|
||||
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 line1 = sizer_se:CreateTexture(nil, "BACKGROUND")
|
||||
self.line1 = line1
|
||||
line1:SetWidth(14)
|
||||
line1:SetHeight(14)
|
||||
line1:SetPoint("BOTTOMRIGHT", -8, 8)
|
||||
line1:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
|
||||
local x = 0.1 * 14/17
|
||||
line1:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
|
||||
|
||||
local line2 = sizer_se:CreateTexture(nil, "BACKGROUND")
|
||||
self.line2 = line2
|
||||
line2:SetWidth(8)
|
||||
line2:SetHeight(8)
|
||||
line2:SetPoint("BOTTOMRIGHT", -8, 8)
|
||||
line2:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
|
||||
local x = 0.1 * 8/17
|
||||
line2:SetTexCoord(0.05 - x, 0.5, 0.05, 0.5 + x, 0.05, 0.5 - x, 0.5 + x, 0.5)
|
||||
|
||||
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
|
||||
|
||||
--Container Support
|
||||
local content = CreateFrame("Frame",nil,frame)
|
||||
self.content = content
|
||||
content.obj = self
|
||||
content:SetPoint("TOPLEFT",frame,"TOPLEFT",12,-32)
|
||||
content:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-12,13)
|
||||
|
||||
AceGUI:RegisterAsContainer(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type,Constructor,Version)
|
||||
end
|
||||
@@ -0,0 +1,106 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Button Widget
|
||||
Graphical Button.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Button", 23
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local _G = AceCore._G
|
||||
local PlaySound, CreateFrame, UIParent = PlaySound, CreateFrame, UIParent
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
-- arg1 is the button for OnClick event
|
||||
local function Button_OnClick()
|
||||
AceGUI:ClearFocus()
|
||||
PlaySound("igMainMenuOption")
|
||||
this.obj:Fire("OnClick", 1, arg1)
|
||||
end
|
||||
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
-- restore default values
|
||||
self:SetHeight(24)
|
||||
self:SetWidth(200)
|
||||
self:SetDisabled(false)
|
||||
self:SetAutoWidth(false)
|
||||
self:SetText()
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetText"] = function(self, text)
|
||||
self.text:SetText(text)
|
||||
if self.autoWidth then
|
||||
self:SetWidth(self.text:GetStringWidth() + 30)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetAutoWidth"] = function(self, autoWidth)
|
||||
self.autoWidth = autoWidth
|
||||
if self.autoWidth then
|
||||
self:SetWidth(self.text:GetStringWidth() + 30)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.frame:Disable()
|
||||
else
|
||||
self.frame:Enable()
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local name = "AceGUI30Button" .. AceGUI:GetNextWidgetNum(Type)
|
||||
local frame = CreateFrame("Button", name, UIParent, "UIPanelButtonTemplate2")
|
||||
frame:Hide()
|
||||
|
||||
frame:EnableMouse(true)
|
||||
frame:SetScript("OnClick", Button_OnClick)
|
||||
frame:SetScript("OnEnter", Control_OnEnter)
|
||||
frame:SetScript("OnLeave", Control_OnLeave)
|
||||
|
||||
local text = frame:GetFontString()
|
||||
text:ClearAllPoints()
|
||||
text:SetPoint("TOPLEFT", 15, -1)
|
||||
text:SetPoint("BOTTOMRIGHT", -15, 1)
|
||||
text:SetJustifyV("MIDDLE")
|
||||
|
||||
local widget = {
|
||||
text = text,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,296 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Checkbox Widget
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "CheckBox", 22
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: SetDesaturation, GameFontHighlight
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function AlignImage(self)
|
||||
local img = self.image:GetTexture()
|
||||
self.text:ClearAllPoints()
|
||||
if not img then
|
||||
self.text:SetPoint("LEFT", self.checkbg, "RIGHT")
|
||||
self.text:SetPoint("RIGHT",0,0)
|
||||
else
|
||||
self.text:SetPoint("LEFT", self.image,"RIGHT", 1, 0)
|
||||
self.text:SetPoint("RIGHT",0,0)
|
||||
end
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function CheckBox_OnMouseDown()
|
||||
local self = this.obj
|
||||
if not self.disabled then
|
||||
if self.image:GetTexture() then
|
||||
self.text:SetPoint("LEFT", self.image,"RIGHT", 2, -1)
|
||||
else
|
||||
self.text:SetPoint("LEFT", self.checkbg, "RIGHT", 1, -1)
|
||||
end
|
||||
end
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function CheckBox_OnMouseUp()
|
||||
|
||||
local self = this.obj
|
||||
if not self.disabled then
|
||||
self:ToggleChecked()
|
||||
|
||||
if self.checked then
|
||||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||||
else -- for both nil and false (tristate)
|
||||
PlaySound("igMainMenuOptionCheckBoxOff")
|
||||
end
|
||||
|
||||
self:Fire("OnValueChanged", 1, self.checked)
|
||||
AlignImage(self)
|
||||
end
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetType()
|
||||
self:SetValue(false)
|
||||
self:SetTriState(nil)
|
||||
-- height is calculated from the width and required space for the description
|
||||
self:SetWidth(200)
|
||||
self:SetImage()
|
||||
self:SetDisabled(nil)
|
||||
self:SetDescription(nil)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
if self.desc then
|
||||
self.desc:SetWidth(width - 30)
|
||||
if self.desc:GetText() and self.desc:GetText() ~= "" then
|
||||
self:SetHeight(28 + self.desc:GetHeight())
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.frame:Disable()
|
||||
self.text:SetTextColor(0.5, 0.5, 0.5)
|
||||
SetDesaturation(self.check, true)
|
||||
if self.desc then
|
||||
self.desc:SetTextColor(0.5, 0.5, 0.5)
|
||||
end
|
||||
else
|
||||
self.frame:Enable()
|
||||
self.text:SetTextColor(1, 1, 1)
|
||||
if self.tristate and self.checked == nil then
|
||||
SetDesaturation(self.check, true)
|
||||
else
|
||||
SetDesaturation(self.check, false)
|
||||
end
|
||||
if self.desc then
|
||||
self.desc:SetTextColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["SetValue"] = function(self,value)
|
||||
local check = self.check
|
||||
self.checked = value
|
||||
if value then
|
||||
SetDesaturation(self.check, false)
|
||||
self.check:Show()
|
||||
else
|
||||
--Nil is the unknown tristate value
|
||||
if self.tristate and value == nil then
|
||||
SetDesaturation(self.check, true)
|
||||
self.check:Show()
|
||||
else
|
||||
SetDesaturation(self.check, false)
|
||||
self.check:Hide()
|
||||
end
|
||||
end
|
||||
self:SetDisabled(self.disabled)
|
||||
end,
|
||||
|
||||
["GetValue"] = function(self)
|
||||
return self.checked
|
||||
end,
|
||||
|
||||
["SetTriState"] = function(self, enabled)
|
||||
self.tristate = enabled
|
||||
self:SetValue(self:GetValue())
|
||||
end,
|
||||
|
||||
["SetType"] = function(self, type)
|
||||
local checkbg = self.checkbg
|
||||
local check = self.check
|
||||
local highlight = self.highlight
|
||||
|
||||
local size
|
||||
if type == "radio" then
|
||||
size = 16
|
||||
checkbg:SetTexture("Interface\\Buttons\\UI-RadioButton")
|
||||
checkbg:SetTexCoord(0, 0.25, 0, 1)
|
||||
check:SetTexture("Interface\\Buttons\\UI-RadioButton")
|
||||
check:SetTexCoord(0.25, 0.5, 0, 1)
|
||||
check:SetBlendMode("ADD")
|
||||
highlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
|
||||
highlight:SetTexCoord(0.5, 0.75, 0, 1)
|
||||
else
|
||||
size = 24
|
||||
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")
|
||||
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
|
||||
highlight:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
|
||||
checkbg:SetHeight(size)
|
||||
checkbg:SetWidth(size)
|
||||
end,
|
||||
|
||||
["ToggleChecked"] = function(self)
|
||||
local value = self:GetValue()
|
||||
if self.tristate then
|
||||
--cycle in true, nil, false order
|
||||
if value then
|
||||
self:SetValue(nil)
|
||||
elseif value == nil then
|
||||
self:SetValue(false)
|
||||
else
|
||||
self:SetValue(true)
|
||||
end
|
||||
else
|
||||
self:SetValue(not self:GetValue())
|
||||
end
|
||||
end,
|
||||
|
||||
["SetLabel"] = function(self, label)
|
||||
self.text:SetText(label)
|
||||
end,
|
||||
|
||||
["SetDescription"] = function(self, desc)
|
||||
if desc then
|
||||
if not self.desc then
|
||||
local desc = self.frame:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
||||
desc:ClearAllPoints()
|
||||
desc:SetPoint("TOPLEFT", self.checkbg, "TOPRIGHT", 5, -21)
|
||||
desc:SetWidth(self.frame.width - 30)
|
||||
desc:SetJustifyH("LEFT")
|
||||
desc:SetJustifyV("TOP")
|
||||
self.desc = desc
|
||||
end
|
||||
self.desc:Show()
|
||||
--self.text:SetFontObject(GameFontNormal)
|
||||
self.desc:SetText(desc)
|
||||
self:SetHeight(28 + self.desc:GetHeight())
|
||||
else
|
||||
if self.desc then
|
||||
self.desc:SetText("")
|
||||
self.desc:Hide()
|
||||
end
|
||||
--self.text:SetFontObject(GameFontHighlight)
|
||||
self:SetHeight(24)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetImage"] = function(self, path, a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
local image = self.image
|
||||
image:SetTexture(path)
|
||||
|
||||
if image:GetTexture() then
|
||||
if a4 or a8 then
|
||||
image:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
else
|
||||
image:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
end
|
||||
AlignImage(self)
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Button", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
frame:EnableMouse(true)
|
||||
frame:SetScript("OnEnter", Control_OnEnter)
|
||||
frame:SetScript("OnLeave", Control_OnLeave)
|
||||
frame:SetScript("OnMouseDown", CheckBox_OnMouseDown)
|
||||
frame:SetScript("OnMouseUp", CheckBox_OnMouseUp)
|
||||
|
||||
local checkbg = frame:CreateTexture(nil, "ARTWORK")
|
||||
checkbg:SetWidth(24)
|
||||
checkbg:SetHeight(24)
|
||||
checkbg:SetPoint("TOPLEFT",0,0)
|
||||
checkbg:SetTexture("Interface\\Buttons\\UI-CheckBox-Up")
|
||||
|
||||
local check = frame:CreateTexture(nil, "OVERLAY")
|
||||
check:SetAllPoints(checkbg)
|
||||
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
|
||||
|
||||
local text = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
|
||||
text:SetJustifyH("LEFT")
|
||||
text:SetHeight(18)
|
||||
text:SetPoint("LEFT", checkbg, "RIGHT")
|
||||
text:SetPoint("RIGHT",0,0)
|
||||
|
||||
local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
|
||||
highlight:SetTexture("Interface\\Buttons\\UI-CheckBox-Highlight")
|
||||
highlight:SetBlendMode("ADD")
|
||||
highlight:SetAllPoints(checkbg)
|
||||
|
||||
local image = frame:CreateTexture(nil, "OVERLAY")
|
||||
image:SetHeight(16)
|
||||
image:SetWidth(16)
|
||||
image:SetPoint("LEFT", checkbg, "RIGHT", 1, 0)
|
||||
|
||||
local widget = {
|
||||
checkbg = checkbg,
|
||||
check = check,
|
||||
text = text,
|
||||
highlight = highlight,
|
||||
image = image,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,187 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
ColorPicker Widget
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "ColorPicker", 23
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: ShowUIPanel, HideUIPanel, ColorPickerFrame, OpacitySliderFrame
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function ColorCallback(self, r, g, b, a, isAlpha)
|
||||
if not self.HasAlpha then
|
||||
a = 1
|
||||
end
|
||||
self:SetColor(r, g, b, a)
|
||||
if ColorPickerFrame:IsVisible() then
|
||||
--colorpicker is still open
|
||||
self:Fire("OnValueChanged", 4, r, g, b, a)
|
||||
else
|
||||
--colorpicker is closed, color callback is first, ignore it,
|
||||
--alpha callback is the final call after it closes so confirm now
|
||||
if isAlpha then
|
||||
self:Fire("OnValueConfirmed", 4, r, g, b, a)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function ColorSwatch_OnClick()
|
||||
HideUIPanel(ColorPickerFrame)
|
||||
local self = this.obj
|
||||
if not self.disabled then
|
||||
ColorPickerFrame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
ColorPickerFrame:SetClampedToScreen(true)
|
||||
|
||||
ColorPickerFrame.func = function()
|
||||
local r, g, b = ColorPickerFrame:GetColorRGB()
|
||||
local a = 1 - OpacitySliderFrame:GetValue()
|
||||
ColorCallback(self, r, g, b, a)
|
||||
end
|
||||
|
||||
ColorPickerFrame.hasOpacity = self.HasAlpha
|
||||
ColorPickerFrame.opacityFunc = function()
|
||||
local r, g, b = ColorPickerFrame:GetColorRGB()
|
||||
local a = 1 - OpacitySliderFrame:GetValue()
|
||||
ColorCallback(self, r, g, b, a, true)
|
||||
end
|
||||
|
||||
local r, g, b, a = self.r, self.g, self.b, self.a
|
||||
if self.HasAlpha then
|
||||
ColorPickerFrame.opacity = 1 - (a or 0)
|
||||
end
|
||||
ColorPickerFrame:SetColorRGB(r, g, b)
|
||||
|
||||
ColorPickerFrame.cancelFunc = function()
|
||||
ColorCallback(self, r, g, b, a, true)
|
||||
end
|
||||
|
||||
ShowUIPanel(ColorPickerFrame)
|
||||
end
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetHeight(24)
|
||||
self:SetWidth(200)
|
||||
self:SetHasAlpha(false)
|
||||
self:SetColor(0, 0, 0, 1)
|
||||
self:SetDisabled(nil)
|
||||
self:SetLabel(nil)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetLabel"] = function(self, text)
|
||||
self.text:SetText(text)
|
||||
end,
|
||||
|
||||
["SetColor"] = function(self, r, g, b, a)
|
||||
self.r = r
|
||||
self.g = g
|
||||
self.b = b
|
||||
self.a = a or 1
|
||||
self.colorSwatch:SetVertexColor(r, g, b, a)
|
||||
end,
|
||||
|
||||
["SetHasAlpha"] = function(self, HasAlpha)
|
||||
self.HasAlpha = HasAlpha
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if self.disabled then
|
||||
self.frame:Disable()
|
||||
self.text:SetTextColor(0.5, 0.5, 0.5)
|
||||
else
|
||||
self.frame:Enable()
|
||||
self.text:SetTextColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Button", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
frame:EnableMouse(true)
|
||||
frame:SetScript("OnEnter", Control_OnEnter)
|
||||
frame:SetScript("OnLeave", Control_OnLeave)
|
||||
frame:SetScript("OnClick", ColorSwatch_OnClick)
|
||||
|
||||
local colorSwatch = frame:CreateTexture(nil, "OVERLAY")
|
||||
colorSwatch:SetWidth(19)
|
||||
colorSwatch:SetHeight(19)
|
||||
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
|
||||
colorSwatch:SetPoint("LEFT",0,0)
|
||||
|
||||
local texture = frame:CreateTexture(nil, "BACKGROUND")
|
||||
texture:SetWidth(16)
|
||||
texture:SetHeight(16)
|
||||
texture:SetTexture(1, 1, 1)
|
||||
texture:SetPoint("CENTER", colorSwatch)
|
||||
texture:Show()
|
||||
|
||||
local checkers = frame:CreateTexture(nil, "BACKGROUND")
|
||||
checkers:SetWidth(14)
|
||||
checkers:SetHeight(14)
|
||||
checkers:SetTexture("Tileset\\Generic\\Checkers")
|
||||
checkers:SetTexCoord(.25, 0, 0.5, .25)
|
||||
checkers:SetDesaturated(true)
|
||||
checkers:SetVertexColor(1, 1, 1, 0.75)
|
||||
checkers:SetPoint("CENTER", colorSwatch)
|
||||
checkers:Show()
|
||||
|
||||
local text = frame:CreateFontString(nil,"OVERLAY","GameFontHighlight")
|
||||
text:SetHeight(24)
|
||||
text:SetJustifyH("LEFT")
|
||||
text:SetTextColor(1, 1, 1)
|
||||
text:SetPoint("LEFT", colorSwatch, "RIGHT", 2, 0)
|
||||
text:SetPoint("RIGHT",0,0)
|
||||
|
||||
--local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
|
||||
--highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
--highlight:SetBlendMode("ADD")
|
||||
--highlight:SetAllPoints(frame)
|
||||
|
||||
local widget = {
|
||||
colorSwatch = colorSwatch,
|
||||
text = text,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,457 @@
|
||||
--[[ $Id: AceGUIWidget-DropDown-Items.lua 1137 2016-05-15 10:57:36Z nevcairiel $ ]]--
|
||||
|
||||
local AceGUI = LibStub("AceGUI-3.0")
|
||||
|
||||
local IsLegion = false
|
||||
|
||||
-- Lua APIs
|
||||
local assert = assert
|
||||
local tgetn = table.getn
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame = CreateFrame
|
||||
|
||||
local fixlevels = AceGUI.fixlevels
|
||||
local fixstrata = AceGUI.fixstrata
|
||||
|
||||
-- ItemBase is the base "class" for all dropdown items.
|
||||
-- Each item has to use ItemBase.Create(widgetType) to
|
||||
-- create an initial 'self' value.
|
||||
-- ItemBase will add common functions and ui event handlers.
|
||||
-- Be sure to keep basic usage when you override functions.
|
||||
|
||||
local ItemBase = {
|
||||
-- NOTE: The ItemBase version is added to each item's version number
|
||||
-- to ensure proper updates on ItemBase changes.
|
||||
-- Use at least 1000er steps.
|
||||
version = 1000,
|
||||
counter = 0,
|
||||
}
|
||||
|
||||
function ItemBase.Frame_OnEnter()
|
||||
local self = this.obj
|
||||
|
||||
if self.useHighlight then
|
||||
self.highlight:Show()
|
||||
end
|
||||
self:Fire("OnEnter")
|
||||
|
||||
if self.specialOnEnter then
|
||||
self.specialOnEnter(self)
|
||||
end
|
||||
end
|
||||
|
||||
function ItemBase.Frame_OnLeave()
|
||||
local self = this.obj
|
||||
|
||||
self.highlight:Hide()
|
||||
self:Fire("OnLeave")
|
||||
|
||||
if self.specialOnLeave then
|
||||
self.specialOnLeave(self)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported, AceGUI callback
|
||||
function ItemBase.OnAcquire(self)
|
||||
self.frame:SetToplevel(true)
|
||||
self.frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
end
|
||||
|
||||
-- exported, AceGUI callback
|
||||
function ItemBase.OnRelease(self)
|
||||
self:SetDisabled(false)
|
||||
self.pullout = nil
|
||||
self.frame:SetParent(nil)
|
||||
self.frame:ClearAllPoints()
|
||||
self.frame:Hide()
|
||||
end
|
||||
|
||||
-- exported
|
||||
-- NOTE: this is called by a Dropdown-Pullout.
|
||||
-- Do not call this method directly
|
||||
function ItemBase.SetPullout(self, pullout)
|
||||
self.pullout = pullout
|
||||
|
||||
self.frame:SetParent(nil)
|
||||
local itemFrame = pullout.itemFrame
|
||||
self.frame:SetParent(itemFrame)
|
||||
self.parent = itemFrame
|
||||
fixlevels(itemFrame)
|
||||
end
|
||||
|
||||
-- exported
|
||||
function ItemBase.SetText(self, text)
|
||||
self.text:SetText(text or "")
|
||||
end
|
||||
|
||||
-- exported
|
||||
function ItemBase.GetText(self)
|
||||
return self.text:GetText()
|
||||
end
|
||||
|
||||
-- exported
|
||||
function ItemBase.SetPoint(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
self.frame:SetPoint(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end
|
||||
|
||||
-- exported
|
||||
function ItemBase.Show(self)
|
||||
self.frame:Show()
|
||||
end
|
||||
|
||||
-- exported
|
||||
function ItemBase.Hide(self)
|
||||
self.frame:Hide()
|
||||
end
|
||||
|
||||
-- exported
|
||||
function ItemBase.SetDisabled(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.useHighlight = false
|
||||
self.text:SetTextColor(.5, .5, .5)
|
||||
else
|
||||
self.useHighlight = true
|
||||
self.text:SetTextColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
-- NOTE: this is called by a Dropdown-Pullout.
|
||||
-- Do not call this method directly
|
||||
function ItemBase.SetOnLeave(self, func)
|
||||
self.specialOnLeave = func
|
||||
end
|
||||
|
||||
-- exported
|
||||
-- NOTE: this is called by a Dropdown-Pullout.
|
||||
-- Do not call this method directly
|
||||
function ItemBase.SetOnEnter(self, func)
|
||||
self.specialOnEnter = func
|
||||
end
|
||||
|
||||
function ItemBase.Create(type)
|
||||
-- NOTE: Most of the following code is copied from AceGUI-3.0/Dropdown widget
|
||||
local count = AceGUI:GetNextWidgetNum(type)
|
||||
local frame = CreateFrame("Button", "AceGUI30DropDownItem"..count)
|
||||
local self = {}
|
||||
self.frame = frame
|
||||
frame.obj = self
|
||||
self.type = type
|
||||
|
||||
self.useHighlight = true
|
||||
|
||||
frame:SetHeight(17)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
local text = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
|
||||
text:SetTextColor(1,1,1)
|
||||
text:SetJustifyH("LEFT")
|
||||
text:SetPoint("TOPLEFT",frame,"TOPLEFT",18,0)
|
||||
text:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",-8,0)
|
||||
self.text = text
|
||||
|
||||
local highlight = frame:CreateTexture(nil, "OVERLAY")
|
||||
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
||||
highlight:SetBlendMode("ADD")
|
||||
highlight:SetHeight(14)
|
||||
highlight:ClearAllPoints()
|
||||
highlight:SetPoint("RIGHT",frame,"RIGHT",-3,0)
|
||||
highlight:SetPoint("LEFT",frame,"LEFT",5,0)
|
||||
highlight:Hide()
|
||||
self.highlight = highlight
|
||||
|
||||
local check = frame:CreateTexture("OVERLAY")
|
||||
check:SetWidth(16)
|
||||
check:SetHeight(16)
|
||||
check:SetPoint("LEFT",frame,"LEFT",3,-1)
|
||||
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
|
||||
check:Hide()
|
||||
self.check = check
|
||||
|
||||
local sub = frame:CreateTexture("OVERLAY")
|
||||
sub:SetWidth(16)
|
||||
sub:SetHeight(16)
|
||||
sub:SetPoint("RIGHT",frame,"RIGHT",-3,-1)
|
||||
sub:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
|
||||
sub:Hide()
|
||||
self.sub = sub
|
||||
|
||||
frame:SetScript("OnEnter", ItemBase.Frame_OnEnter)
|
||||
frame:SetScript("OnLeave", ItemBase.Frame_OnLeave)
|
||||
|
||||
self.OnAcquire = ItemBase.OnAcquire
|
||||
self.OnRelease = ItemBase.OnRelease
|
||||
|
||||
self.SetPullout = ItemBase.SetPullout
|
||||
self.GetText = ItemBase.GetText
|
||||
self.SetText = ItemBase.SetText
|
||||
self.SetDisabled = ItemBase.SetDisabled
|
||||
|
||||
self.SetPoint = ItemBase.SetPoint
|
||||
self.Show = ItemBase.Show
|
||||
self.Hide = ItemBase.Hide
|
||||
|
||||
self.SetOnLeave = ItemBase.SetOnLeave
|
||||
self.SetOnEnter = ItemBase.SetOnEnter
|
||||
|
||||
return self
|
||||
end
|
||||
|
||||
-- Register a dummy LibStub library to retrieve the ItemBase, so other addons can use it.
|
||||
local IBLib = LibStub:NewLibrary("AceGUI-3.0-DropDown-ItemBase", ItemBase.version)
|
||||
if IBLib then
|
||||
IBLib.GetItemBase = function() return ItemBase end
|
||||
end
|
||||
|
||||
--[[
|
||||
Template for items:
|
||||
|
||||
-- Item:
|
||||
--
|
||||
do
|
||||
local widgetType = "Dropdown-Item-"
|
||||
local widgetVersion = 1
|
||||
|
||||
local function Constructor()
|
||||
local self = ItemBase.Create(widgetType)
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
|
||||
end
|
||||
--]]
|
||||
|
||||
-- Item: Header
|
||||
-- A single text entry.
|
||||
-- Special: Different text color and no highlight
|
||||
do
|
||||
local widgetType = "Dropdown-Item-Header"
|
||||
local widgetVersion = 1
|
||||
|
||||
local function OnEnter(this)
|
||||
local self = this.obj
|
||||
self:Fire("OnEnter")
|
||||
|
||||
if self.specialOnEnter then
|
||||
self.specialOnEnter(self)
|
||||
end
|
||||
end
|
||||
|
||||
local function OnLeave()
|
||||
local self = this.obj
|
||||
self:Fire("OnLeave")
|
||||
|
||||
if self.specialOnLeave then
|
||||
self.specialOnLeave(self)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported, override
|
||||
local function SetDisabled(self, disabled)
|
||||
ItemBase.SetDisabled(self, disabled)
|
||||
if not disabled then
|
||||
self.text:SetTextColor(1, 1, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local self = ItemBase.Create(widgetType)
|
||||
|
||||
self.SetDisabled = SetDisabled
|
||||
|
||||
self.frame:SetScript("OnEnter", OnEnter)
|
||||
self.frame:SetScript("OnLeave", OnLeave)
|
||||
|
||||
self.text:SetTextColor(1, 1, 0)
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
|
||||
end
|
||||
|
||||
-- Item: Execute
|
||||
-- A simple button
|
||||
do
|
||||
local widgetType = "Dropdown-Item-Execute"
|
||||
local widgetVersion = 1
|
||||
|
||||
local function Frame_OnClick(this, button)
|
||||
local self = this.obj
|
||||
if self.disabled then return end
|
||||
self:Fire("OnClick")
|
||||
if self.pullout then
|
||||
self.pullout:Close()
|
||||
end
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local self = ItemBase.Create(widgetType)
|
||||
|
||||
self.frame:SetScript("OnClick", Frame_OnClick)
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
|
||||
end
|
||||
|
||||
-- Item: Toggle
|
||||
-- Some sort of checkbox for dropdown menus.
|
||||
-- Does not close the pullout on click.
|
||||
do
|
||||
local widgetType = "Dropdown-Item-Toggle"
|
||||
local widgetVersion = 3
|
||||
|
||||
local function UpdateToggle(self)
|
||||
if self.value then
|
||||
self.check:Show()
|
||||
else
|
||||
self.check:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnRelease(self)
|
||||
ItemBase.OnRelease(self)
|
||||
self:SetValue(nil)
|
||||
end
|
||||
|
||||
local function Frame_OnClick()
|
||||
local self = this.obj
|
||||
if self.disabled then return end
|
||||
self.value = not self.value
|
||||
if self.value then
|
||||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||||
else
|
||||
PlaySound("igMainMenuOptionCheckBoxOff")
|
||||
end
|
||||
UpdateToggle(self)
|
||||
self:Fire("OnValueChanged", 1, self.value)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetValue(self, value)
|
||||
self.value = value
|
||||
UpdateToggle(self)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function GetValue(self)
|
||||
return self.value
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local self = ItemBase.Create(widgetType)
|
||||
|
||||
self.frame:SetScript("OnClick", Frame_OnClick)
|
||||
|
||||
self.SetValue = SetValue
|
||||
self.GetValue = GetValue
|
||||
self.OnRelease = OnRelease
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
|
||||
end
|
||||
|
||||
-- Item: Menu
|
||||
-- Shows a submenu on mouse over
|
||||
-- Does not close the pullout on click
|
||||
do
|
||||
local widgetType = "Dropdown-Item-Menu"
|
||||
local widgetVersion = 2
|
||||
|
||||
local function OnEnter()
|
||||
local self = this.obj
|
||||
self:Fire("OnEnter")
|
||||
|
||||
if self.specialOnEnter then
|
||||
self.specialOnEnter(self)
|
||||
end
|
||||
|
||||
self.highlight:Show()
|
||||
|
||||
if not self.disabled and self.submenu then
|
||||
self.submenu:Open("TOPLEFT", self.frame, "TOPRIGHT", self.pullout:GetRightBorderWidth(), 0, self.frame:GetFrameLevel() + 100)
|
||||
end
|
||||
end
|
||||
|
||||
local function OnHide()
|
||||
local self = this.obj
|
||||
if self.submenu then
|
||||
self.submenu:Close()
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetMenu(self, menu)
|
||||
assert(menu.type == "Dropdown-Pullout")
|
||||
self.submenu = menu
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function CloseMenu(self)
|
||||
self.submenu:Close()
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local self = ItemBase.Create(widgetType)
|
||||
|
||||
self.sub:Show()
|
||||
|
||||
self.frame:SetScript("OnEnter", OnEnter)
|
||||
self.frame:SetScript("OnHide", OnHide)
|
||||
|
||||
self.SetMenu = SetMenu
|
||||
self.CloseMenu = CloseMenu
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
|
||||
end
|
||||
|
||||
-- Item: Separator
|
||||
-- A single line to separate items
|
||||
do
|
||||
local widgetType = "Dropdown-Item-Separator"
|
||||
local widgetVersion = 2
|
||||
|
||||
-- exported, override
|
||||
local function SetDisabled(self, disabled)
|
||||
ItemBase.SetDisabled(self, disabled)
|
||||
self.useHighlight = false
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local self = ItemBase.Create(widgetType)
|
||||
|
||||
self.SetDisabled = SetDisabled
|
||||
|
||||
local line = self.frame:CreateTexture(nil, "OVERLAY")
|
||||
line:SetHeight(1)
|
||||
line:SetTexture(.5, .5, .5)
|
||||
|
||||
line:SetPoint("LEFT", self.frame, "LEFT", 10, 0)
|
||||
line:SetPoint("RIGHT", self.frame, "RIGHT", -10, 0)
|
||||
|
||||
self.text:Hide()
|
||||
|
||||
self.useHighlight = false
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion + ItemBase.version)
|
||||
end
|
||||
@@ -0,0 +1,726 @@
|
||||
--[[ $Id: AceGUIWidget-DropDown.lua 1116 2014-10-12 08:15:46Z nevcairiel $ ]]--
|
||||
local AceGUI = LibStub("AceGUI-3.0")
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
|
||||
-- Lua APIs
|
||||
local min, max, floor = math.min, math.max, math.floor
|
||||
local pairs, ipairs, type = pairs, ipairs, type
|
||||
local tsort, tinsert, tgetn, tsetn = table.sort, table.insert, table.getn, table.setn
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local UIParent, CreateFrame = UIParent, CreateFrame
|
||||
local _G = AceCore._G
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: CLOSE
|
||||
|
||||
local fixlevels = AceGUI.fixlevels
|
||||
local fixstrata = AceGUI.fixstrata
|
||||
|
||||
do
|
||||
local widgetType = "Dropdown-Pullout"
|
||||
local widgetVersion = 3
|
||||
|
||||
--[[ Static data ]]--
|
||||
|
||||
local backdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
||||
edgeSize = 32,
|
||||
tileSize = 32,
|
||||
tile = true,
|
||||
insets = { left = 11, right = 12, top = 12, bottom = 11 },
|
||||
}
|
||||
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 = 3, bottom = 3 }
|
||||
}
|
||||
|
||||
local defaultWidth = 200
|
||||
local defaultMaxHeight = 600
|
||||
|
||||
--[[ UI Event Handlers ]]--
|
||||
|
||||
-- HACK: This should be no part of the pullout, but there
|
||||
-- is no other 'clean' way to response to any item-OnEnter
|
||||
-- Used to close Submenus when an other item is entered
|
||||
local function OnEnter(item)
|
||||
local self = item.pullout
|
||||
for k, v in ipairs(self.items) do
|
||||
if v.CloseMenu and v ~= item then
|
||||
v:CloseMenu()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- See the note in Constructor() for each scroll related function
|
||||
local function OnMouseWheel()
|
||||
this.obj:MoveScroll(arg1)
|
||||
end
|
||||
|
||||
local function OnScrollValueChanged()
|
||||
this.obj:SetScroll(arg1)
|
||||
end
|
||||
|
||||
local function OnSizeChanged()
|
||||
this.obj:FixScroll()
|
||||
end
|
||||
|
||||
--[[ Exported methods ]]--
|
||||
|
||||
-- exported
|
||||
local function SetScroll(self, value)
|
||||
local status = self.scrollStatus
|
||||
local frame, child = self.scrollFrame, self.itemFrame
|
||||
local height, viewheight = frame:GetHeight(), child:GetHeight()
|
||||
|
||||
local offset
|
||||
if height > viewheight then
|
||||
offset = 0
|
||||
else
|
||||
offset = floor((viewheight - height) / 1000 * value)
|
||||
end
|
||||
child:ClearAllPoints()
|
||||
child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
|
||||
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", self.slider:IsShown() and -12 or 0, offset)
|
||||
status.offset = offset
|
||||
status.scrollvalue = value
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function MoveScroll(self, value)
|
||||
local status = self.scrollStatus
|
||||
local frame, child = self.scrollFrame, self.itemFrame
|
||||
local height, viewheight = frame:GetHeight(), child:GetHeight()
|
||||
|
||||
if height > viewheight then
|
||||
self.slider:Hide()
|
||||
else
|
||||
self.slider:Show()
|
||||
local diff = height - viewheight
|
||||
local delta = 1
|
||||
if value < 0 then
|
||||
delta = -1
|
||||
end
|
||||
self.slider:SetValue(min(max(status.scrollvalue + delta*(1000/(diff/45)),0), 1000))
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function FixScroll(self)
|
||||
local status = self.scrollStatus
|
||||
local frame, child = self.scrollFrame, self.itemFrame
|
||||
local height, viewheight = frame:GetHeight(), child:GetHeight()
|
||||
local offset = status.offset or 0
|
||||
|
||||
if viewheight < height then
|
||||
self.slider:Hide()
|
||||
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, offset)
|
||||
self.slider:SetValue(0)
|
||||
else
|
||||
self.slider:Show()
|
||||
local value = (offset / (viewheight - height) * 1000)
|
||||
if value > 1000 then value = 1000 end
|
||||
self.slider:SetValue(value)
|
||||
self:SetScroll(value)
|
||||
if value < 1000 then
|
||||
child:ClearAllPoints()
|
||||
child:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, offset)
|
||||
child:SetPoint("TOPRIGHT", frame, "TOPRIGHT", -12, offset)
|
||||
status.offset = offset
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- exported, AceGUI callback
|
||||
local function OnAcquire(self)
|
||||
self.frame:SetParent(UIParent)
|
||||
--self.itemFrame:SetToplevel(true)
|
||||
end
|
||||
|
||||
-- exported, AceGUI callback
|
||||
local function OnRelease(self)
|
||||
self:Clear()
|
||||
self.frame:ClearAllPoints()
|
||||
self.frame:Hide()
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function AddItem(self, item)
|
||||
tinsert(self.items, item)
|
||||
|
||||
local h = tgetn(self.items) * 16
|
||||
self.itemFrame:SetHeight(h)
|
||||
self.frame:SetHeight(min(h + 34, self.maxHeight)) -- +34: 20 for scrollFrame placement (10 offset) and +14 for item placement
|
||||
|
||||
item.frame:SetPoint("LEFT", self.itemFrame, "LEFT")
|
||||
item.frame:SetPoint("RIGHT", self.itemFrame, "RIGHT")
|
||||
|
||||
item:SetPullout(self)
|
||||
item:SetOnEnter(OnEnter)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function Open(self, point, relFrame, relPoint, x, y)
|
||||
local items = self.items
|
||||
local frame = self.frame
|
||||
local itemFrame = self.itemFrame
|
||||
|
||||
frame:SetPoint(point, relFrame, relPoint, x, y)
|
||||
|
||||
|
||||
local height = 8
|
||||
for i, item in pairs(items) do
|
||||
if i == 1 then
|
||||
item:SetPoint("TOP", itemFrame, "TOP", 0, -2)
|
||||
else
|
||||
item:SetPoint("TOP", items[i-1].frame, "BOTTOM", 0, 1)
|
||||
end
|
||||
|
||||
item:Show()
|
||||
|
||||
height = height + 16
|
||||
end
|
||||
itemFrame:SetHeight(height)
|
||||
fixstrata("TOOLTIP", frame)
|
||||
frame:Show()
|
||||
self:Fire("OnOpen")
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function Close(self)
|
||||
self.frame:Hide()
|
||||
self:Fire("OnClose")
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function Clear(self)
|
||||
local items = self.items
|
||||
for i, item in pairs(items) do
|
||||
AceGUI:Release(item)
|
||||
items[i] = nil
|
||||
end
|
||||
tsetn(items,0)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function IterateItems(self)
|
||||
return ipairs(self.items)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetHideOnLeave(self, val)
|
||||
self.hideOnLeave = val
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetMaxHeight(self, height)
|
||||
self.maxHeight = height or defaultMaxHeight
|
||||
if self.frame:GetHeight() > height then
|
||||
self.frame:SetHeight(height)
|
||||
elseif (self.itemFrame:GetHeight() + 34) < height then
|
||||
self.frame:SetHeight(self.itemFrame:GetHeight() + 34) -- see :AddItem
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function GetRightBorderWidth(self)
|
||||
return 6 + (self.slider:IsShown() and 12 or 0)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function GetLeftBorderWidth(self)
|
||||
return 6
|
||||
end
|
||||
|
||||
--[[ Constructor ]]--
|
||||
|
||||
local function Constructor()
|
||||
local count = AceGUI:GetNextWidgetNum(widgetType)
|
||||
local frame = CreateFrame("Frame", "AceGUI30Pullout"..count, UIParent)
|
||||
local self = {}
|
||||
self.count = count
|
||||
self.type = widgetType
|
||||
self.frame = frame
|
||||
frame.obj = self
|
||||
|
||||
self.OnAcquire = OnAcquire
|
||||
self.OnRelease = OnRelease
|
||||
|
||||
self.AddItem = AddItem
|
||||
self.Open = Open
|
||||
self.Close = Close
|
||||
self.Clear = Clear
|
||||
self.IterateItems = IterateItems
|
||||
self.SetHideOnLeave = SetHideOnLeave
|
||||
|
||||
self.SetScroll = SetScroll
|
||||
self.MoveScroll = MoveScroll
|
||||
self.FixScroll = FixScroll
|
||||
|
||||
self.SetMaxHeight = SetMaxHeight
|
||||
self.GetRightBorderWidth = GetRightBorderWidth
|
||||
self.GetLeftBorderWidth = GetLeftBorderWidth
|
||||
|
||||
self.items = {}
|
||||
|
||||
self.scrollStatus = {
|
||||
scrollvalue = 0,
|
||||
}
|
||||
|
||||
self.maxHeight = defaultMaxHeight
|
||||
|
||||
frame:SetBackdrop(backdrop)
|
||||
frame:SetBackdropColor(0, 0, 0)
|
||||
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
frame:SetClampedToScreen(true)
|
||||
frame:SetWidth(defaultWidth)
|
||||
frame:SetHeight(self.maxHeight)
|
||||
--frame:SetToplevel(true)
|
||||
|
||||
-- NOTE: The whole scroll frame code is copied from the AceGUI-3.0 widget ScrollFrame
|
||||
local scrollFrame = CreateFrame("ScrollFrame", nil, frame)
|
||||
local itemFrame = CreateFrame("Frame", nil, scrollFrame)
|
||||
|
||||
self.scrollFrame = scrollFrame
|
||||
self.itemFrame = itemFrame
|
||||
|
||||
scrollFrame.obj = self
|
||||
itemFrame.obj = self
|
||||
|
||||
local slider = CreateFrame("Slider", "AceGUI30PulloutScrollbar"..count, scrollFrame)
|
||||
slider:SetOrientation("VERTICAL")
|
||||
slider:SetHitRectInsets(0, 0, -10, 0)
|
||||
slider:SetBackdrop(sliderBackdrop)
|
||||
slider:SetWidth(8)
|
||||
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
|
||||
slider:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
self.slider = slider
|
||||
slider.obj = self
|
||||
|
||||
scrollFrame:SetScrollChild(itemFrame)
|
||||
scrollFrame:SetPoint("TOPLEFT", frame, "TOPLEFT", 6, -12)
|
||||
scrollFrame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -6, 12)
|
||||
scrollFrame:EnableMouseWheel(true)
|
||||
scrollFrame:SetScript("OnMouseWheel", OnMouseWheel)
|
||||
scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
|
||||
scrollFrame:SetToplevel(true)
|
||||
scrollFrame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
itemFrame:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 0, 0)
|
||||
itemFrame:SetPoint("TOPRIGHT", scrollFrame, "TOPRIGHT", -12, 0)
|
||||
itemFrame:SetHeight(400)
|
||||
itemFrame:SetToplevel(true)
|
||||
itemFrame:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
|
||||
slider:SetPoint("TOPLEFT", scrollFrame, "TOPRIGHT", -16, 0)
|
||||
slider:SetPoint("BOTTOMLEFT", scrollFrame, "BOTTOMRIGHT", -16, 0)
|
||||
slider:SetScript("OnValueChanged", OnScrollValueChanged)
|
||||
slider:SetMinMaxValues(0, 1000)
|
||||
slider:SetValueStep(1)
|
||||
slider:SetValue(0)
|
||||
|
||||
scrollFrame:Show()
|
||||
itemFrame:Show()
|
||||
slider:Hide()
|
||||
|
||||
self:FixScroll()
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
|
||||
end
|
||||
|
||||
do
|
||||
local widgetType = "Dropdown"
|
||||
local widgetVersion = 30
|
||||
|
||||
--[[ Static data ]]--
|
||||
|
||||
--[[ UI event handler ]]--
|
||||
|
||||
local function Control_OnEnter()
|
||||
this.obj.button:LockHighlight()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj.button:UnlockHighlight()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function Dropdown_OnHide()
|
||||
local self = this.obj
|
||||
if self.open then
|
||||
self.pullout:Close()
|
||||
end
|
||||
end
|
||||
|
||||
local function Dropdown_TogglePullout()
|
||||
local self = this.obj
|
||||
PlaySound("igMainMenuOptionCheckBoxOn") -- missleading name, but the Blizzard code uses this sound
|
||||
if self.open then
|
||||
self.open = nil
|
||||
self.pullout:Close()
|
||||
AceGUI:ClearFocus()
|
||||
else
|
||||
self.open = true
|
||||
self.pullout:SetWidth(self.pulloutWidth or self.frame:GetWidth())
|
||||
self.pullout:Open("TOPLEFT", self.frame, "BOTTOMLEFT", 0, self.label:IsShown() and -2 or 0)
|
||||
AceGUI:SetFocus(self)
|
||||
end
|
||||
end
|
||||
|
||||
local function OnPulloutOpen(this)
|
||||
local self = this.userdata.obj
|
||||
local value = self.value
|
||||
|
||||
if not self.multiselect then
|
||||
for i, item in this:IterateItems() do
|
||||
item:SetValue(item.userdata.value == value)
|
||||
end
|
||||
end
|
||||
|
||||
self.open = true
|
||||
self:Fire("OnOpened")
|
||||
end
|
||||
|
||||
local function OnPulloutClose(this)
|
||||
local self = this.userdata.obj
|
||||
self.open = nil
|
||||
self:Fire("OnClosed")
|
||||
end
|
||||
|
||||
local function ShowMultiText(self)
|
||||
local text
|
||||
for i, widget in self.pullout:IterateItems() do
|
||||
if widget.type == "Dropdown-Item-Toggle" then
|
||||
if widget:GetValue() then
|
||||
if text then
|
||||
text = text..", "..widget:GetText()
|
||||
else
|
||||
text = widget:GetText()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self:SetText(text)
|
||||
end
|
||||
|
||||
local function OnItemValueChanged(this, event, _, checked)
|
||||
local self = this.userdata.obj
|
||||
|
||||
if self.multiselect then
|
||||
self:Fire("OnValueChanged", 2, this.userdata.value, checked)
|
||||
ShowMultiText(self)
|
||||
else
|
||||
if checked then
|
||||
self:SetValue(this.userdata.value)
|
||||
self:Fire("OnValueChanged", 1, this.userdata.value)
|
||||
this:SetValue(false)
|
||||
else
|
||||
self:SetValue(nil)
|
||||
self:Fire("OnValueChanged", 1, nil)
|
||||
this:SetValue(true)
|
||||
end
|
||||
if self.open then
|
||||
self.pullout:Close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[ Exported methods ]]--
|
||||
|
||||
-- exported, AceGUI callback
|
||||
local function OnAcquire(self)
|
||||
local pullout = AceGUI:Create("Dropdown-Pullout")
|
||||
self.pullout = pullout
|
||||
pullout.userdata.obj = self
|
||||
pullout:SetCallback("OnClose", OnPulloutClose)
|
||||
pullout:SetCallback("OnOpen", OnPulloutOpen)
|
||||
self.pullout.frame:SetFrameLevel(self.frame:GetFrameLevel() + 1)
|
||||
local frame = self.pullout.frame
|
||||
fixlevels(frame)
|
||||
|
||||
self:SetHeight(44)
|
||||
self:SetWidth(200)
|
||||
self:SetLabel()
|
||||
self:SetPulloutWidth(nil)
|
||||
end
|
||||
|
||||
-- exported, AceGUI callback
|
||||
local function OnRelease(self)
|
||||
if self.open then
|
||||
self.pullout:Close()
|
||||
end
|
||||
AceGUI:Release(self.pullout)
|
||||
self.pullout = nil
|
||||
|
||||
self:SetText("")
|
||||
self:SetDisabled(false)
|
||||
self:SetMultiselect(false)
|
||||
|
||||
self.value = nil
|
||||
self.list = nil
|
||||
self.open = nil
|
||||
self.hasClose = nil
|
||||
|
||||
self.frame:ClearAllPoints()
|
||||
self.frame:Hide()
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetDisabled(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.text:SetTextColor(0.5,0.5,0.5)
|
||||
self.button:Disable()
|
||||
self.button_cover:Disable()
|
||||
self.label:SetTextColor(0.5,0.5,0.5)
|
||||
else
|
||||
self.button:Enable()
|
||||
self.button_cover:Enable()
|
||||
self.label:SetTextColor(1,.82,0)
|
||||
self.text:SetTextColor(1,1,1)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function ClearFocus(self)
|
||||
if self.open then
|
||||
self.pullout:Close()
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetText(self, text)
|
||||
self.text:SetText(text or "")
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetLabel(self, text)
|
||||
if text and text ~= "" then
|
||||
self.label:SetText(text)
|
||||
self.label:Show()
|
||||
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,-14)
|
||||
self:SetHeight(40)
|
||||
self.alignoffset = 26
|
||||
else
|
||||
self.label:SetText("")
|
||||
self.label:Hide()
|
||||
self.dropdown:SetPoint("TOPLEFT",self.frame,"TOPLEFT",-15,0)
|
||||
self:SetHeight(26)
|
||||
self.alignoffset = 12
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetValue(self, value)
|
||||
if self.list then
|
||||
self:SetText(self.list[value] or "")
|
||||
end
|
||||
self.value = value
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function GetValue(self)
|
||||
return self.value
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetItemValue(self, item, value)
|
||||
if not self.multiselect then return end
|
||||
for i, widget in self.pullout:IterateItems() do
|
||||
if widget.userdata.value == item then
|
||||
if widget.SetValue then
|
||||
widget:SetValue(value)
|
||||
end
|
||||
end
|
||||
end
|
||||
ShowMultiText(self)
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetItemDisabled(self, item, disabled)
|
||||
for i, widget in self.pullout:IterateItems() do
|
||||
if widget.userdata.value == item then
|
||||
widget:SetDisabled(disabled)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function AddListItem(self, value, text, itemType)
|
||||
if not itemType then itemType = "Dropdown-Item-Toggle" end
|
||||
local exists = AceGUI:GetWidgetVersion(itemType)
|
||||
if not exists then error(("The given item type, %q, does not exist within AceGUI-3.0"):format(tostring(itemType)), 2) end
|
||||
|
||||
local item = AceGUI:Create(itemType)
|
||||
item:SetText(text)
|
||||
item.userdata.obj = self
|
||||
item.userdata.value = value
|
||||
item:SetCallback("OnValueChanged", OnItemValueChanged)
|
||||
self.pullout:AddItem(item)
|
||||
end
|
||||
|
||||
local function AddCloseButton(self)
|
||||
if not self.hasClose then
|
||||
local close = AceGUI:Create("Dropdown-Item-Execute")
|
||||
close:SetText(CLOSE)
|
||||
self.pullout:AddItem(close)
|
||||
self.hasClose = true
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local sortlist = {}
|
||||
local function SetList(self, list, order, itemType)
|
||||
self.list = list
|
||||
self.pullout:Clear()
|
||||
self.hasClose = nil
|
||||
if not list then return end
|
||||
|
||||
if type(order) ~= "table" then
|
||||
for v in pairs(list) do
|
||||
tinsert(sortlist, v)
|
||||
end
|
||||
tsort(sortlist)
|
||||
|
||||
for i, key in ipairs(sortlist) do
|
||||
AddListItem(self, key, list[key], itemType)
|
||||
sortlist[i] = nil
|
||||
end
|
||||
tsetn(sortlist,0)
|
||||
else
|
||||
for i, key in ipairs(order) do
|
||||
AddListItem(self, key, list[key], itemType)
|
||||
end
|
||||
end
|
||||
if self.multiselect then
|
||||
ShowMultiText(self)
|
||||
AddCloseButton(self)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function AddItem(self, value, text, itemType)
|
||||
if self.list then
|
||||
self.list[value] = text
|
||||
AddListItem(self, value, text, itemType)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function SetMultiselect(self, multi)
|
||||
self.multiselect = multi
|
||||
if multi then
|
||||
ShowMultiText(self)
|
||||
AddCloseButton(self)
|
||||
end
|
||||
end
|
||||
|
||||
-- exported
|
||||
local function GetMultiselect(self)
|
||||
return self.multiselect
|
||||
end
|
||||
|
||||
local function SetPulloutWidth(self, width)
|
||||
self.pulloutWidth = width
|
||||
end
|
||||
|
||||
--[[ Constructor ]]--
|
||||
|
||||
local function Constructor()
|
||||
local count = AceGUI:GetNextWidgetNum(widgetType)
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
local dropdown = CreateFrame("Frame", "AceGUI30DropDown"..count, frame, "UIDropDownMenuTemplate")
|
||||
|
||||
local self = {}
|
||||
self.type = widgetType
|
||||
self.frame = frame
|
||||
self.dropdown = dropdown
|
||||
self.count = count
|
||||
frame.obj = self
|
||||
dropdown.obj = self
|
||||
|
||||
self.OnRelease = OnRelease
|
||||
self.OnAcquire = OnAcquire
|
||||
|
||||
self.ClearFocus = ClearFocus
|
||||
|
||||
self.SetText = SetText
|
||||
self.SetValue = SetValue
|
||||
self.GetValue = GetValue
|
||||
self.SetList = SetList
|
||||
self.SetLabel = SetLabel
|
||||
self.SetDisabled = SetDisabled
|
||||
self.AddItem = AddItem
|
||||
self.SetMultiselect = SetMultiselect
|
||||
self.GetMultiselect = GetMultiselect
|
||||
self.SetItemValue = SetItemValue
|
||||
self.SetItemDisabled = SetItemDisabled
|
||||
self.SetPulloutWidth = SetPulloutWidth
|
||||
|
||||
self.alignoffset = 26
|
||||
|
||||
frame:SetScript("OnHide",Dropdown_OnHide)
|
||||
|
||||
dropdown:ClearAllPoints()
|
||||
dropdown:SetPoint("TOPLEFT",frame,"TOPLEFT",-15,0)
|
||||
dropdown:SetPoint("BOTTOMRIGHT",frame,"BOTTOMRIGHT",17,0)
|
||||
dropdown:SetScript("OnHide", nil)
|
||||
|
||||
local left = _G[dropdown:GetName() .. "Left"]
|
||||
local middle = _G[dropdown:GetName() .. "Middle"]
|
||||
local right = _G[dropdown:GetName() .. "Right"]
|
||||
|
||||
middle:ClearAllPoints()
|
||||
right:ClearAllPoints()
|
||||
|
||||
middle:SetPoint("LEFT", left, "RIGHT", 0, 0)
|
||||
middle:SetPoint("RIGHT", right, "LEFT", 0, 0)
|
||||
right:SetPoint("TOPRIGHT", dropdown, "TOPRIGHT", 0, 17)
|
||||
|
||||
local button = _G[dropdown:GetName() .. "Button"]
|
||||
self.button = button
|
||||
button.obj = self
|
||||
button:SetScript("OnEnter",Control_OnEnter)
|
||||
button:SetScript("OnLeave",Control_OnLeave)
|
||||
button:SetScript("OnClick",Dropdown_TogglePullout)
|
||||
|
||||
local button_cover = CreateFrame("BUTTON",nil,self.frame)
|
||||
self.button_cover = button_cover
|
||||
button_cover.obj = self
|
||||
button_cover:SetPoint("TOPLEFT",self.frame,"BOTTOMLEFT",0,25)
|
||||
button_cover:SetPoint("BOTTOMRIGHT",self.frame,"BOTTOMRIGHT")
|
||||
button_cover:SetScript("OnEnter",Control_OnEnter)
|
||||
button_cover:SetScript("OnLeave",Control_OnLeave)
|
||||
button_cover:SetScript("OnClick",Dropdown_TogglePullout)
|
||||
|
||||
local text = _G[dropdown:GetName() .. "Text"]
|
||||
self.text = text
|
||||
text.obj = self
|
||||
text:ClearAllPoints()
|
||||
text:SetPoint("RIGHT", right, "RIGHT" ,-43, 2)
|
||||
text:SetPoint("LEFT", left, "LEFT", 25, 2)
|
||||
|
||||
local label = frame:CreateFontString(nil,"OVERLAY","GameFontNormalSmall")
|
||||
label:SetPoint("TOPLEFT",frame,"TOPLEFT",0,0)
|
||||
label:SetPoint("TOPRIGHT",frame,"TOPRIGHT",0,0)
|
||||
label:SetJustifyH("LEFT")
|
||||
label:SetHeight(18)
|
||||
label:Hide()
|
||||
self.label = label
|
||||
|
||||
AceGUI:RegisterAsWidget(self)
|
||||
return self
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(widgetType, Constructor, widgetVersion)
|
||||
end
|
||||
@@ -0,0 +1,367 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
EditBox Widget
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "EditBox", 26
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local hooksecurefunc = AceCore.hooksecurefunc
|
||||
local _G = AceCore._G
|
||||
local GetCursorInfo = _G.GetCursorInfo
|
||||
|
||||
-- Lua APIs
|
||||
local tostring, pairs = tostring, pairs
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local GetCursorInfo, ClearCursor, GetSpellInfo = GetCursorInfo, ClearCursor, GetSpellInfo
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
local strlen = string.len
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: AceGUIEditBoxInsertLink, ChatFontNormal, OKAY
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
if not AceGUIEditBoxInsertLink then
|
||||
-- upgradeable hook
|
||||
hooksecurefunc("BankFrameItemButtonGeneric_OnClick",
|
||||
function(button)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
|
||||
return _G.AceGUIEditBoxInsertLink(GetContainerItemLink(BANK_CONTAINER, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("ContainerFrameItemButton_OnClick",
|
||||
function(button, ignoreModifiers)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
|
||||
return _G.AceGUIEditBoxInsertLink(GetContainerItemLink(this:GetParent():GetID(), this:GetID()))
|
||||
end
|
||||
end)
|
||||
|
||||
hooksecurefunc("KeyRingItemButton_OnClick",
|
||||
function(button)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
|
||||
return _G.AceGUIEditBoxInsertLink(GetContainerItemLink(KEYRING_CONTAINER, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("LootFrameItem_OnClick",
|
||||
function(button)
|
||||
if button == "LeftButton" and IsShiftKeyDown() then
|
||||
return _G.AceGUIEditBoxInsertLink(GetLootSlotLink(this.slot))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("SetItemRef",
|
||||
function(link, text, button)
|
||||
if IsShiftKeyDown() then
|
||||
if strsub(link,1,6) == "player" then
|
||||
local name = strsub(link,8)
|
||||
if name and (strlen(name) > 0) then
|
||||
return _G.AceGUIEditBoxInsertLink(name)
|
||||
end
|
||||
else
|
||||
return _G.AceGUIEditBoxInsertLink(text)
|
||||
end
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("MerchantItemButton_OnClick",
|
||||
function(button, ignoreModifiers)
|
||||
if MerchantFrame.selectedTab == 1 and button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
|
||||
return _G.AceGUIEditBoxInsertLink(GetMerchantItemLink(this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("PaperDollItemSlotButton_OnClick",
|
||||
function(button, ignoreModifiers)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
|
||||
return _G.AceGUIEditBoxInsertLink(GetInventoryItemLink("player", this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestItem_OnClick",
|
||||
function()
|
||||
if IsShiftKeyDown() and this.rewardType ~= "spell" then
|
||||
return _G.AceGUIEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestRewardItem_OnClick",
|
||||
function()
|
||||
if IsShiftKeyDown() and this.rewardType ~= "spell" then
|
||||
return _G.AceGUIEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestLogTitleButton_OnClick",
|
||||
function(button)
|
||||
if IsShiftKeyDown() and (not this.isHeader) then
|
||||
return _G.AceGUIEditBoxInsertLink(gsub(this:GetText(), " *(.*)", "%1"))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestLogRewardItem_OnClick",
|
||||
function()
|
||||
if IsShiftKeyDown() and this.rewardType ~= "spell" then
|
||||
return _G.AceGUIEditBoxInsertLink(GetQuestLogItemLink(this.type, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("SpellButton_OnClick",
|
||||
function(drag)
|
||||
local id = SpellBook_GetSpellID(this:GetID())
|
||||
if id <= MAX_SPELLS and (not drag) and IsShiftKeyDown() then
|
||||
local spellName, subSpellName = GetSpellName(id, SpellBookFrame.bookType)
|
||||
if spellName and not IsSpellPassive(id, SpellBookFrame.bookType) then
|
||||
if subSpellName and (strlen(subSpellName) > 0) then
|
||||
_G.AceGUIEditBoxInsertLink(spellName.."("..subSpellName..")");
|
||||
else
|
||||
_G.AceGUIEditBoxInsertLink(spellName);
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function _G.AceGUIEditBoxInsertLink(text)
|
||||
for i = 1, AceGUI:GetWidgetCount(Type) do
|
||||
local editbox = _G["AceGUI-3.0EditBox"..i]
|
||||
if editbox and editbox:IsVisible() and editbox.hasfocus then
|
||||
editbox:Insert(text)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ShowButton(self)
|
||||
if not self.disablebutton then
|
||||
self.button:Show()
|
||||
self.editbox:SetTextInsets(0, 20, 3, 3)
|
||||
end
|
||||
end
|
||||
|
||||
local function HideButton(self)
|
||||
self.button:Hide()
|
||||
self.editbox:SetTextInsets(0, 0, 3, 3)
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function Frame_OnShowFocus()
|
||||
this.obj.editbox:SetFocus()
|
||||
this:SetScript("OnShow", nil)
|
||||
end
|
||||
|
||||
local function EditBox_OnEscapePressed()
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function EditBox_OnEnterPressed()
|
||||
local self = this.obj
|
||||
local value = this:GetText()
|
||||
local cancel = self:Fire("OnEnterPressed", 1, value)
|
||||
if not cancel then
|
||||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||||
HideButton(self)
|
||||
end
|
||||
end
|
||||
|
||||
local function EditBox_OnReceiveDrag()
|
||||
if not GetCursorInfo then return end
|
||||
local self = this.obj
|
||||
local type, id, info = GetCursorInfo()
|
||||
if type == "item" then
|
||||
self:SetText(info)
|
||||
self:Fire("OnEnterPressed", 1, info)
|
||||
ClearCursor()
|
||||
elseif type == "spell" then
|
||||
local spell, rank = GetSpellName(id, info)
|
||||
if rank ~= "" then spell = spell.."("..rank..")" end
|
||||
self:SetText(spell)
|
||||
self:Fire("OnEnterPressed", 1, spell)
|
||||
ClearCursor()
|
||||
elseif type == "macro" then
|
||||
local name = GetMacroInfo(id)
|
||||
self:SetText(name)
|
||||
self:Fire("OnEnterPressed", 1, name)
|
||||
ClearCursor()
|
||||
end
|
||||
HideButton(self)
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
|
||||
local function EditBox_OnTextChanged()
|
||||
local self = this.obj
|
||||
local value = this:GetText()
|
||||
if tostring(value) ~= tostring(self.lasttext) then
|
||||
self:Fire("OnTextChanged", 1, value)
|
||||
self.lasttext = value
|
||||
ShowButton(self)
|
||||
end
|
||||
end
|
||||
|
||||
local function EditBox_OnFocusGained()
|
||||
this.hasfocus = true
|
||||
AceGUI:SetFocus(this.obj)
|
||||
end
|
||||
|
||||
local function EditBox_OnFocusLost()
|
||||
this.hasfocus = nil
|
||||
end
|
||||
|
||||
local function Button_OnClick()
|
||||
local editbox = this.obj.editbox
|
||||
editbox:ClearFocus()
|
||||
this = editbox -- Ace3v: this is kinda hack here
|
||||
EditBox_OnEnterPressed()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
-- height is controlled by SetLabel
|
||||
self:SetWidth(200)
|
||||
self:SetDisabled(false)
|
||||
self:SetLabel()
|
||||
self:SetText()
|
||||
self:DisableButton(false)
|
||||
self:SetMaxLetters(0)
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self:ClearFocus()
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.editbox:EnableMouse(false)
|
||||
self.editbox:ClearFocus()
|
||||
self.editbox:SetTextColor(0.5,0.5,0.5)
|
||||
self.label:SetTextColor(0.5,0.5,0.5)
|
||||
else
|
||||
self.editbox:EnableMouse(true)
|
||||
self.editbox:SetTextColor(1,1,1)
|
||||
self.label:SetTextColor(1,.82,0)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetText"] = function(self, text)
|
||||
self.lasttext = text or ""
|
||||
self.editbox:SetText(text or "")
|
||||
self.editbox:HighlightText(0)
|
||||
HideButton(self)
|
||||
end,
|
||||
|
||||
["GetText"] = function(self, text)
|
||||
return self.editbox:GetText()
|
||||
end,
|
||||
|
||||
["SetLabel"] = function(self, text)
|
||||
if text and text ~= "" then
|
||||
self.label:SetText(text)
|
||||
self.label:Show()
|
||||
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,-18)
|
||||
self:SetHeight(44)
|
||||
self.alignoffset = 30
|
||||
else
|
||||
self.label:SetText("")
|
||||
self.label:Hide()
|
||||
self.editbox:SetPoint("TOPLEFT",self.frame,"TOPLEFT",7,0)
|
||||
self:SetHeight(26)
|
||||
self.alignoffset = 12
|
||||
end
|
||||
end,
|
||||
|
||||
["DisableButton"] = function(self, disabled)
|
||||
self.disablebutton = disabled
|
||||
if disabled then
|
||||
HideButton(self)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetMaxLetters"] = function (self, num)
|
||||
self.editbox:SetMaxLetters(num or 0)
|
||||
end,
|
||||
|
||||
["ClearFocus"] = function(self)
|
||||
self.editbox:ClearFocus()
|
||||
self.frame:SetScript("OnShow", nil)
|
||||
end,
|
||||
|
||||
["SetFocus"] = function(self)
|
||||
self.editbox:SetFocus()
|
||||
if not self.frame:IsShown() then
|
||||
self.frame:SetScript("OnShow", Frame_OnShowFocus)
|
||||
end
|
||||
end,
|
||||
|
||||
["HighlightText"] = function(self, from, to)
|
||||
self.editbox:HighlightText(from, to)
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local num = AceGUI:GetNextWidgetNum(Type)
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
local editbox = CreateFrame("EditBox", "AceGUI-3.0EditBox"..num, frame, "InputBoxTemplate")
|
||||
editbox:SetAutoFocus(false)
|
||||
editbox:SetFontObject(ChatFontNormal)
|
||||
editbox:SetScript("OnEnter", Control_OnEnter)
|
||||
editbox:SetScript("OnLeave", Control_OnLeave)
|
||||
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
|
||||
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
|
||||
editbox:SetScript("OnTextChanged", EditBox_OnTextChanged)
|
||||
editbox:SetScript("OnReceiveDrag", EditBox_OnReceiveDrag)
|
||||
editbox:SetScript("OnMouseDown", EditBox_OnReceiveDrag)
|
||||
editbox:SetScript("OnEditFocusGained", EditBox_OnFocusGained)
|
||||
editbox:SetScript("OnEditFocusLost", EditBox_OnFocusLost)
|
||||
editbox:SetTextInsets(0, 0, 3, 3)
|
||||
editbox:SetMaxLetters(256)
|
||||
editbox:SetPoint("BOTTOMLEFT", 6, 0)
|
||||
editbox:SetPoint("BOTTOMRIGHT", 0, 0)
|
||||
editbox:SetHeight(19)
|
||||
|
||||
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
label:SetPoint("TOPLEFT", 0, -2)
|
||||
label:SetPoint("TOPRIGHT", 0, -2)
|
||||
label:SetJustifyH("LEFT")
|
||||
label:SetHeight(18)
|
||||
|
||||
local button = CreateFrame("Button", nil, editbox, "UIPanelButtonTemplate")
|
||||
button:SetWidth(40)
|
||||
button:SetHeight(20)
|
||||
button:SetPoint("RIGHT", -2, 0)
|
||||
button:SetText(OKAY)
|
||||
button:SetScript("OnClick", Button_OnClick)
|
||||
button:Hide()
|
||||
|
||||
local widget = {
|
||||
alignoffset = 30,
|
||||
editbox = editbox,
|
||||
label = label,
|
||||
button = button,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
editbox.obj, button.obj = widget, widget
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,78 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Heading Widget
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Heading", 20
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetText()
|
||||
self:SetFullWidth()
|
||||
self:SetHeight(18)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetText"] = function(self, text)
|
||||
self.label:SetText(text or "")
|
||||
if text and text ~= "" then
|
||||
self.left:SetPoint("RIGHT", self.label, "LEFT", -5, 0)
|
||||
self.right:Show()
|
||||
else
|
||||
self.left:SetPoint("RIGHT", -3, 0)
|
||||
self.right:Hide()
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontNormal")
|
||||
label:SetPoint("TOP",0,0)
|
||||
label:SetPoint("BOTTOM",0,0)
|
||||
label:SetJustifyH("CENTER")
|
||||
|
||||
local left = frame:CreateTexture(nil, "BACKGROUND")
|
||||
left:SetHeight(8)
|
||||
left:SetPoint("LEFT", 3, 0)
|
||||
left:SetPoint("RIGHT", label, "LEFT", -5, 0)
|
||||
left:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
|
||||
left:SetTexCoord(0.81, 0.94, 0.5, 1)
|
||||
|
||||
local right = frame:CreateTexture(nil, "BACKGROUND")
|
||||
right:SetHeight(8)
|
||||
right:SetPoint("RIGHT", -3, 0)
|
||||
right:SetPoint("LEFT", label, "RIGHT", 5, 0)
|
||||
right:SetTexture("Interface\\Tooltips\\UI-Tooltip-Border")
|
||||
right:SetTexCoord(0.81, 0.94, 0.5, 1)
|
||||
|
||||
local widget = {
|
||||
label = label,
|
||||
left = left,
|
||||
right = right,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,139 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Icon Widget
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Icon", 21
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, print = pairs, print
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function Button_OnClick()
|
||||
this.obj:Fire("OnClick", 1, arg1)
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetHeight(110)
|
||||
self:SetWidth(110)
|
||||
self:SetLabel()
|
||||
self:SetImage(nil)
|
||||
self:SetImageSize(64, 64)
|
||||
self:SetDisabled(false)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetLabel"] = function(self, text)
|
||||
if text and text ~= "" then
|
||||
self.label:Show()
|
||||
self.label:SetText(text)
|
||||
self:SetHeight(self.image:GetHeight() + 25)
|
||||
else
|
||||
self.label:Hide()
|
||||
self:SetHeight(self.image:GetHeight() + 10)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetImage"] = function(self, path, a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
local image = self.image
|
||||
image:SetTexture(path)
|
||||
|
||||
if image:GetTexture() then
|
||||
if a4 or a8 then
|
||||
image:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
else
|
||||
image:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
["SetImageSize"] = function(self, width, height)
|
||||
self.image:SetWidth(width)
|
||||
self.image:SetHeight(height)
|
||||
--self.frame:SetWidth(width + 30)
|
||||
if self.label:IsShown() then
|
||||
self:SetHeight(height + 25)
|
||||
else
|
||||
self:SetHeight(height + 10)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.frame:Disable()
|
||||
self.label:SetTextColor(0.5, 0.5, 0.5)
|
||||
self.image:SetVertexColor(0.5, 0.5, 0.5, 0.5)
|
||||
else
|
||||
self.frame:Enable()
|
||||
self.label:SetTextColor(1, 1, 1)
|
||||
self.image:SetVertexColor(1, 1, 1, 1)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Button", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
frame:EnableMouse(true)
|
||||
frame:SetScript("OnEnter", Control_OnEnter)
|
||||
frame:SetScript("OnLeave", Control_OnLeave)
|
||||
frame:SetScript("OnClick", Button_OnClick)
|
||||
|
||||
local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlight")
|
||||
label:SetPoint("BOTTOMLEFT")
|
||||
label:SetPoint("BOTTOMRIGHT")
|
||||
label:SetJustifyH("CENTER")
|
||||
label:SetJustifyV("TOP")
|
||||
label:SetHeight(18)
|
||||
|
||||
local image = frame:CreateTexture(nil, "BACKGROUND")
|
||||
image:SetWidth(64)
|
||||
image:SetHeight(64)
|
||||
image:SetPoint("TOP", 0, -5)
|
||||
|
||||
local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
|
||||
highlight:SetAllPoints(image)
|
||||
highlight:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight")
|
||||
highlight:SetTexCoord(0, 1, 0.23, 0.77)
|
||||
highlight:SetBlendMode("ADD")
|
||||
|
||||
local widget = {
|
||||
label = label,
|
||||
image = image,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
widget.SetText = function(self,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) print("AceGUI-3.0-Icon: SetText is deprecated! Use SetLabel instead!"); self:SetLabel(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10) end
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,100 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
InteractiveLabel Widget
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "InteractiveLabel", 20
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: GameFontHighlightSmall
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function Label_OnClick()
|
||||
this.obj:Fire("OnClick", 1, arg1)
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:LabelOnAcquire()
|
||||
self:SetHighlight()
|
||||
self:SetHighlightTexCoord()
|
||||
self:SetDisabled(false)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetHighlight"] = function(self, a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
self.highlight:SetTexture(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
end,
|
||||
|
||||
["SetHighlightTexCoord"] = function(self, a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
if a4 or a8 then
|
||||
self.highlight:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
else
|
||||
self.highlight:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self,disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.frame:EnableMouse(false)
|
||||
self.label:SetTextColor(0.5, 0.5, 0.5)
|
||||
else
|
||||
self.frame:EnableMouse(true)
|
||||
self.label:SetTextColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
-- create a Label type that we will hijack
|
||||
local label = AceGUI:Create("Label")
|
||||
|
||||
local frame = label.frame
|
||||
frame:EnableMouse(true)
|
||||
frame:SetScript("OnEnter", Control_OnEnter)
|
||||
frame:SetScript("OnLeave", Control_OnLeave)
|
||||
frame:SetScript("OnMouseDown", Label_OnClick)
|
||||
|
||||
local highlight = frame:CreateTexture(nil, "HIGHLIGHT")
|
||||
highlight:SetTexture(nil)
|
||||
highlight:SetAllPoints()
|
||||
highlight:SetBlendMode("ADD")
|
||||
|
||||
label.highlight = highlight
|
||||
label.type = Type
|
||||
label.LabelOnAcquire = label.OnAcquire
|
||||
for method, func in pairs(methods) do
|
||||
label[method] = func
|
||||
end
|
||||
|
||||
return label
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Keybinding Widget
|
||||
Set Keybindings in the Config UI.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Keybinding", 25
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown = IsShiftKeyDown, IsControlKeyDown, IsAltKeyDown
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: NOT_BOUND
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function Keybinding_OnHide()
|
||||
local self = this.obj
|
||||
this:EnableKeyboard(false)
|
||||
this:EnableMouseWheel(false)
|
||||
self.msgframe:Hide()
|
||||
this:UnlockHighlight()
|
||||
self.waitingForKey = nil
|
||||
end
|
||||
|
||||
local ignoreKeys = {
|
||||
["BUTTON1"] = true, ["BUTTON2"] = true,
|
||||
["UNKNOWN"] = true,
|
||||
["SHIFT"] = true, ["CTRL"] = true, ["ALT"] = true,
|
||||
}
|
||||
local function Keybinding_OnKeyDown()
|
||||
local self = this.obj
|
||||
if self.waitingForKey then
|
||||
local keyPressed = arg1
|
||||
if keyPressed == "ESCAPE" then
|
||||
keyPressed = ""
|
||||
else
|
||||
if ignoreKeys[keyPressed] 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
|
||||
|
||||
this:EnableKeyboard(false)
|
||||
this:EnableMouseWheel(false)
|
||||
self.msgframe:Hide()
|
||||
this:UnlockHighlight()
|
||||
self.waitingForKey = nil
|
||||
|
||||
if not self.disabled then
|
||||
self:SetKey(keyPressed)
|
||||
self:Fire("OnKeyChanged", 1, keyPressed)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function Keybinding_OnMouseDown()
|
||||
getglobal(this:GetName().."Left"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
|
||||
getglobal(this:GetName().."Middle"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
|
||||
getglobal(this:GetName().."Right"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Down");
|
||||
end
|
||||
|
||||
local function Keybinding_OnMouseUp()
|
||||
getglobal(this:GetName().."Left"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
|
||||
getglobal(this:GetName().."Middle"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
|
||||
getglobal(this:GetName().."Right"):SetTexture("Interface\\Buttons\\UI-Panel-Button-Up");
|
||||
local self = this.obj
|
||||
if MouseIsOver(this) and not self.disabled then
|
||||
if self.waitingForKey then
|
||||
if arg1 ~= "LeftButton" and arg1 ~= "RightButton" then
|
||||
Keybinding_OnKeyDown()
|
||||
end
|
||||
this:EnableKeyboard(false)
|
||||
this:EnableMouseWheel(false)
|
||||
self.msgframe:Hide()
|
||||
this:UnlockHighlight()
|
||||
self.waitingForKey = nil
|
||||
else
|
||||
this:EnableKeyboard(true)
|
||||
this:EnableMouseWheel(true)
|
||||
self.msgframe:Show()
|
||||
this:LockHighlight()
|
||||
self.waitingForKey = true
|
||||
end
|
||||
end
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function Keybinding_OnMouseWheel()
|
||||
if arg1 >= 0 then
|
||||
arg1 = "MOUSEWHEELUP"
|
||||
else
|
||||
arg1 = "MOUSEWHEELDOWN"
|
||||
end
|
||||
Keybinding_OnKeyDown()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetWidth(200)
|
||||
self:SetLabel("")
|
||||
self:SetKey("")
|
||||
self.waitingForKey = nil
|
||||
self.msgframe:Hide()
|
||||
self:SetDisabled(false)
|
||||
self.button:EnableKeyboard(false)
|
||||
self.button:EnableMouseWheel(false)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.button:Disable()
|
||||
self.label:SetTextColor(0.5,0.5,0.5)
|
||||
else
|
||||
self.button:Enable()
|
||||
self.label:SetTextColor(1,1,1)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetKey"] = function(self, key)
|
||||
if (key or "") == "" then
|
||||
self.button:SetText(NOT_BOUND)
|
||||
self.text:SetFontObject("GameFontNormal")
|
||||
else
|
||||
self.button:SetText(key)
|
||||
self.text:SetFontObject("GameFontHighlight")
|
||||
end
|
||||
end,
|
||||
|
||||
["GetKey"] = function(self)
|
||||
local key = self.button:GetText()
|
||||
if key == NOT_BOUND then
|
||||
key = nil
|
||||
end
|
||||
return key
|
||||
end,
|
||||
|
||||
["SetLabel"] = function(self, label)
|
||||
self.label:SetText(label or "")
|
||||
if (label or "") == "" then
|
||||
self.alignoffset = nil
|
||||
self:SetHeight(24)
|
||||
else
|
||||
self.alignoffset = 30
|
||||
self:SetHeight(44)
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
|
||||
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 }
|
||||
}
|
||||
|
||||
local function keybindingMsgFixWidth()
|
||||
this:SetWidth(this.msg:GetWidth() + 10)
|
||||
this:SetScript("OnUpdate", nil)
|
||||
end
|
||||
|
||||
local function Constructor()
|
||||
local name = "AceGUI30KeybindingButton" .. AceGUI:GetNextWidgetNum(Type)
|
||||
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
local button = CreateFrame("Button", name, frame, "UIPanelButtonTemplate2")
|
||||
|
||||
button:EnableMouse(true)
|
||||
button:EnableMouseWheel(false)
|
||||
button:SetScript("OnEnter", Control_OnEnter)
|
||||
button:SetScript("OnLeave", Control_OnLeave)
|
||||
|
||||
button:SetScript("OnKeyDown", Keybinding_OnKeyDown)
|
||||
button:RegisterForClicks("AnyDown","AnyUp")
|
||||
-- Ace3v: RegisterForClicks means OnClick will not be triggered, so use OnKeyDown and OnKeyUp
|
||||
button:SetScript("OnMouseDown", Keybinding_OnMouseDown)
|
||||
button:SetScript("OnMouseUp", Keybinding_OnMouseUp)
|
||||
button:SetScript("OnMouseWheel", Keybinding_OnMouseWheel)
|
||||
button:SetScript("OnHide", Keybinding_OnHide)
|
||||
button:SetPoint("BOTTOMLEFT",0,0)
|
||||
button:SetPoint("BOTTOMRIGHT",0,0)
|
||||
button:SetHeight(24)
|
||||
button:EnableKeyboard(false)
|
||||
|
||||
local text = button:GetFontString()
|
||||
text:SetPoint("LEFT", 7, 0)
|
||||
text:SetPoint("RIGHT", -7, 0)
|
||||
|
||||
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
|
||||
label:SetPoint("TOPLEFT",0,0)
|
||||
label:SetPoint("TOPRIGHT",0,0)
|
||||
label:SetJustifyH("CENTER")
|
||||
label:SetHeight(18)
|
||||
|
||||
local msgframe = CreateFrame("Frame", nil, UIParent)
|
||||
msgframe:SetHeight(30)
|
||||
msgframe:SetBackdrop(ControlBackdrop)
|
||||
msgframe:SetBackdropColor(0,0,0)
|
||||
msgframe:SetFrameStrata("FULLSCREEN_DIALOG")
|
||||
msgframe:SetFrameLevel(1000)
|
||||
msgframe:SetToplevel(true)
|
||||
|
||||
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", 5, -5)
|
||||
msgframe:SetScript("OnUpdate", keybindingMsgFixWidth)
|
||||
msgframe:SetPoint("BOTTOM", button, "TOP")
|
||||
msgframe:Hide()
|
||||
|
||||
local widget = {
|
||||
button = button,
|
||||
label = label,
|
||||
msgframe = msgframe,
|
||||
frame = frame,
|
||||
alignoffset = 30,
|
||||
type = Type,
|
||||
text = text
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
button.obj = widget
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,165 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Label Widget
|
||||
Displays text and optionally an icon.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Label", 23
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local max, pairs = math.max, pairs
|
||||
|
||||
-- WoW APIs
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: GameFontHighlightSmall
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
|
||||
local function UpdateImageAnchor(self)
|
||||
if self.resizing then return end
|
||||
local frame = self.frame
|
||||
local width = frame.width or frame:GetWidth() or 0
|
||||
local image = self.image
|
||||
local label = self.label
|
||||
local height
|
||||
|
||||
label:ClearAllPoints()
|
||||
image:ClearAllPoints()
|
||||
|
||||
if self.imageshown then
|
||||
local imagewidth = image:GetWidth()
|
||||
if (width - imagewidth) < 200 or (label:GetText() or "") == "" then
|
||||
-- image goes on top centered when less than 200 width for the text, or if there is no text
|
||||
image:SetPoint("TOP",0,0)
|
||||
label:SetPoint("TOP", image, "BOTTOM")
|
||||
label:SetPoint("LEFT",0,0)
|
||||
label:SetWidth(width)
|
||||
height = image:GetHeight() + label:GetHeight()
|
||||
else
|
||||
-- image on the left
|
||||
image:SetPoint("TOPLEFT",0,0)
|
||||
if image:GetHeight() > label:GetHeight() then
|
||||
label:SetPoint("LEFT", image, "RIGHT", 4, 0)
|
||||
else
|
||||
label:SetPoint("TOPLEFT", image, "TOPRIGHT", 4, 0)
|
||||
end
|
||||
label:SetWidth(width - imagewidth - 4)
|
||||
height = max(image:GetHeight(), label:GetHeight())
|
||||
end
|
||||
else
|
||||
-- no image shown
|
||||
label:SetPoint("TOPLEFT",0,0)
|
||||
label:SetWidth(width)
|
||||
height = label:GetHeight()
|
||||
end
|
||||
|
||||
self.resizing = true
|
||||
frame:SetHeight(height)
|
||||
frame.height = height
|
||||
self.resizing = nil
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
-- set the flag to stop constant size updates
|
||||
self.resizing = true
|
||||
-- height is set dynamically by the text and image size
|
||||
self:SetWidth(200)
|
||||
self:SetText()
|
||||
self:SetImage(nil)
|
||||
self:SetImageSize(16, 16)
|
||||
self:SetColor()
|
||||
self:SetFontObject()
|
||||
|
||||
-- reset the flag
|
||||
self.resizing = nil
|
||||
-- run the update explicitly
|
||||
UpdateImageAnchor(self)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["OnWidthSet"] = function(self, width)
|
||||
UpdateImageAnchor(self)
|
||||
end,
|
||||
|
||||
["SetText"] = function(self, text)
|
||||
self.label:SetText(text)
|
||||
UpdateImageAnchor(self)
|
||||
end,
|
||||
|
||||
["SetColor"] = function(self, r, g, b)
|
||||
if not (r and g and b) then
|
||||
r, g, b = 1, 1, 1
|
||||
end
|
||||
self.label:SetVertexColor(r, g, b)
|
||||
end,
|
||||
|
||||
["SetImage"] = function(self, path, a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
local image = self.image
|
||||
image:SetTexture(path)
|
||||
|
||||
if image:GetTexture() then
|
||||
self.imageshown = true
|
||||
if a4 or a8 then
|
||||
image:SetTexCoord(a1,a2,a3,a4,a5,a6,a7,a8)
|
||||
else
|
||||
image:SetTexCoord(0, 1, 0, 1)
|
||||
end
|
||||
else
|
||||
self.imageshown = nil
|
||||
end
|
||||
UpdateImageAnchor(self)
|
||||
end,
|
||||
|
||||
["SetFont"] = function(self, font, height, flags)
|
||||
self.label:SetFont(font, height, flags)
|
||||
end,
|
||||
|
||||
["SetFontObject"] = function(self, font)
|
||||
self:SetFont((font or GameFontHighlightSmall):GetFont())
|
||||
end,
|
||||
|
||||
["SetImageSize"] = function(self, width, height)
|
||||
self.image:SetWidth(width)
|
||||
self.image:SetHeight(height)
|
||||
UpdateImageAnchor(self)
|
||||
end,
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
local label = frame:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall")
|
||||
label:SetJustifyH("LEFT")
|
||||
label:SetJustifyV("TOP")
|
||||
|
||||
local image = frame:CreateTexture(nil, "BACKGROUND")
|
||||
|
||||
-- create widget
|
||||
local widget = {
|
||||
label = label,
|
||||
image = image,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,475 @@
|
||||
local Type, Version = "MultiLineEditBox", 28
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local hooksecurefunc = AceCore.hooksecurefunc
|
||||
|
||||
-- Lua APIs
|
||||
local strfmt = string.format
|
||||
local pairs = pairs
|
||||
|
||||
-- WoW APIs
|
||||
local GetCursorInfo, GetSpellInfo, ClearCursor = GetCursorInfo, GetSpellInfo, ClearCursor
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
local _G = AceCore._G
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: ACCEPT, ChatFontNormal
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
if not AceGUIMultiLineEditBoxInsertLink then
|
||||
-- upgradeable hook
|
||||
hooksecurefunc("BankFrameItemButtonGeneric_OnClick",
|
||||
function(button)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetContainerItemLink(BANK_CONTAINER, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("ContainerFrameItemButton_OnClick",
|
||||
function(button, ignoreModifiers)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetContainerItemLink(this:GetParent():GetID(), this:GetID()))
|
||||
end
|
||||
end)
|
||||
|
||||
hooksecurefunc("KeyRingItemButton_OnClick",
|
||||
function(button)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not this.isBag then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetContainerItemLink(KEYRING_CONTAINER, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("LootFrameItem_OnClick",
|
||||
function(button)
|
||||
if button == "LeftButton" and IsShiftKeyDown() then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetLootSlotLink(this.slot))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("SetItemRef",
|
||||
function(link, text, button)
|
||||
if IsShiftKeyDown() then
|
||||
if strsub(link,1,6) == "player" then
|
||||
local name = strsub(link,8)
|
||||
if name and (strlen(name) > 0) then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(name)
|
||||
end
|
||||
else
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(text)
|
||||
end
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("MerchantItemButton_OnClick",
|
||||
function(button, ignoreModifiers)
|
||||
if MerchantFrame.selectedTab == 1 and button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetMerchantItemLink(this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("PaperDollItemSlotButton_OnClick",
|
||||
function(button, ignoreModifiers)
|
||||
if button == "LeftButton" and IsShiftKeyDown() and not ignoreModifiers then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetInventoryItemLink("player", this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestItem_OnClick",
|
||||
function()
|
||||
if IsShiftKeyDown() and this.rewardType ~= "spell" then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestRewardItem_OnClick",
|
||||
function()
|
||||
if IsShiftKeyDown() and this.rewardType ~= "spell" then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetQuestItemLink(this.type, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestLogTitleButton_OnClick",
|
||||
function(button)
|
||||
if IsShiftKeyDown() and (not this.isHeader) then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(gsub(this:GetText(), " *(.*)", "%1"))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("QuestLogRewardItem_OnClick",
|
||||
function()
|
||||
if IsShiftKeyDown() and this.rewardType ~= "spell" then
|
||||
return _G.AceGUIMultiLineEditBoxInsertLink(GetQuestLogItemLink(this.type, this:GetID()))
|
||||
end
|
||||
end)
|
||||
hooksecurefunc("SpellButton_OnClick",
|
||||
function(drag)
|
||||
local id = SpellBook_GetSpellID(this:GetID())
|
||||
if id <= MAX_SPELLS and (not drag) and IsShiftKeyDown() then
|
||||
local spellName, subSpellName = GetSpellName(id, SpellBookFrame.bookType)
|
||||
if spellName and not IsSpellPassive(id, SpellBookFrame.bookType) then
|
||||
if subSpellName and (strlen(subSpellName) > 0) then
|
||||
_G.AceGUIMultiLineEditBoxInsertLink(spellName.."("..subSpellName..")");
|
||||
else
|
||||
_G.AceGUIMultiLineEditBoxInsertLink(spellName);
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
function _G.AceGUIMultiLineEditBoxInsertLink(text)
|
||||
for i = 1, AceGUI:GetWidgetCount(Type) do
|
||||
local editbox = _G[strfmt("MultiLineEditBox%uEdit",i)]
|
||||
if editbox and editbox:IsVisible() and editbox.hasfocus then
|
||||
editbox:Insert(text)
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function Layout(self)
|
||||
self:SetHeight(self.numlines * 14 + (self.disablebutton and 19 or 41) + self.labelHeight)
|
||||
|
||||
if self.labelHeight == 0 then
|
||||
self.scrollBar:SetPoint("TOP", self.frame, "TOP", 0, -23)
|
||||
else
|
||||
self.scrollBar:SetPoint("TOP", self.label, "BOTTOM", 0, -19)
|
||||
end
|
||||
|
||||
if self.disablebutton then
|
||||
self.scrollBar:SetPoint("BOTTOM", self.frame, "BOTTOM", 0, 21)
|
||||
self.scrollBG:SetPoint("BOTTOMLEFT", 0, 4)
|
||||
else
|
||||
self.scrollBar:SetPoint("BOTTOM", self.button, "TOP", 0, 18)
|
||||
self.scrollBG:SetPoint("BOTTOMLEFT", self.button, "TOPLEFT")
|
||||
end
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function OnClick() -- Button
|
||||
local self = this.obj
|
||||
self.editBox:ClearFocus()
|
||||
if not self:Fire("OnEnterPressed", 1, self.editBox:GetText()) then
|
||||
self.button:Disable()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnCursorChanged() -- EditBox
|
||||
|
||||
local self, y = this.obj.scrollFrame, -arg2
|
||||
local offset = self:GetVerticalScroll()
|
||||
if y < offset then
|
||||
self:SetVerticalScroll(y)
|
||||
else
|
||||
y = y + arg4 - self:GetHeight()
|
||||
if y > offset then
|
||||
self:SetVerticalScroll(y)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnEditFocusLost() -- EditBox
|
||||
this.hasfocus = nil
|
||||
this:HighlightText(0, 0)
|
||||
this.obj:Fire("OnEditFocusLost")
|
||||
end
|
||||
|
||||
local function OnEnter() -- EditBox / ScrollFrame
|
||||
local self = this.obj
|
||||
if not self.entered then
|
||||
self.entered = true
|
||||
self:Fire("OnEnter")
|
||||
end
|
||||
end
|
||||
|
||||
local function OnLeave() -- EditBox / ScrollFrame
|
||||
local self = this.obj
|
||||
if self.entered then
|
||||
self.entered = nil
|
||||
self:Fire("OnLeave")
|
||||
end
|
||||
end
|
||||
|
||||
local function OnMouseUp() -- ScrollFrame
|
||||
local self = this.obj.editBox
|
||||
self:SetFocus()
|
||||
local n = self:GetNumLetters()
|
||||
self:HighlightText(n,n)
|
||||
end
|
||||
|
||||
local function OnReceiveDrag() -- EditBox / ScrollFrame
|
||||
if not GetCursorInfo then return end
|
||||
local type, id, info = GetCursorInfo()
|
||||
if type == "spell" then
|
||||
local spell, rank = GetSpellName(id, info)
|
||||
if rank ~= "" then spell = spell.."("..rank..")" end
|
||||
info = spell
|
||||
elseif type ~= "item" then
|
||||
return
|
||||
end
|
||||
ClearCursor()
|
||||
local self = this.obj
|
||||
local editBox = self.editBox
|
||||
if not this.hasfocus then
|
||||
this.hasfocus = true
|
||||
editBox:SetFocus()
|
||||
local n = editBox:GetNumLetters()
|
||||
editBox:HighlightText(n,n)
|
||||
end
|
||||
editBox:Insert(info)
|
||||
self.button:Enable()
|
||||
end
|
||||
|
||||
local function OnSizeChanged() -- ScrollFrame
|
||||
this.obj.editBox:SetWidth(arg1)
|
||||
end
|
||||
|
||||
local function OnTextChanged() -- EditBox
|
||||
local self = this.obj
|
||||
local value = this:GetText()
|
||||
if tostring(value) ~= tostring(self.lasttext) then
|
||||
self:Fire("OnTextChanged", 1, value)
|
||||
self.lasttext = value
|
||||
self.button:Enable()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnTextSet() -- EditBox
|
||||
this:HighlightText(0, 0)
|
||||
this.obj.button:Disable()
|
||||
end
|
||||
|
||||
local function OnVerticalScroll() -- ScrollFrame
|
||||
local self = this.obj
|
||||
local editBox = self.editBox
|
||||
editBox:SetHitRectInsets(0, 0, arg1, editBox:GetHeight() - arg1 - this:GetHeight())
|
||||
|
||||
self.scrollFrame:SetScrollChild(self.editBox)
|
||||
self.editBox:SetPoint("TOPLEFT",0,arg1)
|
||||
self.editBox:SetPoint("TOPRIGHT",0,arg1)
|
||||
end
|
||||
|
||||
local function OnShowFocus()
|
||||
this.obj.editBox:SetFocus()
|
||||
this:SetScript("OnShow", nil)
|
||||
end
|
||||
|
||||
local function OnEditFocusGained()
|
||||
this.hasfocus = true
|
||||
AceGUI:SetFocus(this.obj)
|
||||
this.obj:Fire("OnEditFocusGained")
|
||||
end
|
||||
|
||||
local function OnEscapePressed() -- EditBox
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self.editBox:SetText("")
|
||||
self:SetDisabled(false)
|
||||
self:SetWidth(200)
|
||||
self:DisableButton(false)
|
||||
self:SetNumLines()
|
||||
self.entered = nil
|
||||
self:SetMaxLetters(0)
|
||||
end,
|
||||
|
||||
["OnRelease"] = function(self)
|
||||
self:ClearFocus()
|
||||
end,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
local editBox = self.editBox
|
||||
if disabled then
|
||||
editBox:ClearFocus()
|
||||
editBox:EnableMouse(false)
|
||||
editBox:SetTextColor(0.5, 0.5, 0.5)
|
||||
self.label:SetTextColor(0.5, 0.5, 0.5)
|
||||
self.scrollFrame:EnableMouse(false)
|
||||
self.button:Disable()
|
||||
else
|
||||
editBox:EnableMouse(true)
|
||||
editBox:SetTextColor(1, 1, 1)
|
||||
self.label:SetTextColor(1, 0.82, 0)
|
||||
self.scrollFrame:EnableMouse(true)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetLabel"] = function(self, text)
|
||||
if text and text ~= "" then
|
||||
self.label:SetText(text)
|
||||
if self.labelHeight ~= 10 then
|
||||
self.labelHeight = 10
|
||||
self.label:Show()
|
||||
end
|
||||
elseif self.labelHeight ~= 0 then
|
||||
self.labelHeight = 0
|
||||
self.label:Hide()
|
||||
end
|
||||
Layout(self)
|
||||
end,
|
||||
|
||||
["SetNumLines"] = function(self, value)
|
||||
if not value or value < 4 then
|
||||
value = 4
|
||||
end
|
||||
self.numlines = value
|
||||
Layout(self)
|
||||
end,
|
||||
|
||||
["SetText"] = function(self, text)
|
||||
self.lasttext = text or ""
|
||||
self.editBox:SetText(text or "")
|
||||
self.editBox:HighlightText(0)
|
||||
self.button:Disable()
|
||||
end,
|
||||
|
||||
["GetText"] = function(self)
|
||||
return self.editBox:GetText()
|
||||
end,
|
||||
|
||||
["SetMaxLetters"] = function (self, num)
|
||||
self.editBox:SetMaxLetters(num or 0)
|
||||
end,
|
||||
|
||||
["DisableButton"] = function(self, disabled)
|
||||
self.disablebutton = disabled
|
||||
if disabled then
|
||||
self.button:Hide()
|
||||
else
|
||||
self.button:Show()
|
||||
end
|
||||
Layout(self)
|
||||
end,
|
||||
|
||||
["ClearFocus"] = function(self)
|
||||
self.editBox:ClearFocus()
|
||||
self.frame:SetScript("OnShow", nil)
|
||||
end,
|
||||
|
||||
["SetFocus"] = function(self)
|
||||
self.editBox:SetFocus()
|
||||
if not self.frame:IsShown() then
|
||||
self.frame:SetScript("OnShow", OnShowFocus)
|
||||
end
|
||||
end,
|
||||
|
||||
["HighlightText"] = function(self, from, to)
|
||||
self.editBox:HighlightText(from, to)
|
||||
end,
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
local backdrop = {
|
||||
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
|
||||
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]], edgeSize = 16,
|
||||
insets = { left = 4, right = 3, top = 4, bottom = 3 }
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
frame:Hide()
|
||||
|
||||
local widgetNum = AceGUI:GetNextWidgetNum(Type)
|
||||
|
||||
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
label:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, -4)
|
||||
label:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, -4)
|
||||
label:SetJustifyH("LEFT")
|
||||
label:SetText(ACCEPT)
|
||||
label:SetHeight(10)
|
||||
|
||||
local button = CreateFrame("Button", strfmt("%s%dButton", Type, widgetNum), frame, "UIPanelButtonTemplate")
|
||||
button:SetPoint("BOTTOMLEFT", 0, 4)
|
||||
button:SetHeight(22)
|
||||
button:SetWidth(label:GetStringWidth() + 24)
|
||||
button:SetText(ACCEPT)
|
||||
button:SetScript("OnClick", OnClick)
|
||||
button:Disable()
|
||||
|
||||
local text = button:GetFontString()
|
||||
text:ClearAllPoints()
|
||||
text:SetPoint("TOPLEFT", button, "TOPLEFT", 5, -5)
|
||||
text:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -5, 1)
|
||||
text:SetJustifyV("MIDDLE")
|
||||
|
||||
local scrollBG = CreateFrame("Frame", nil, frame)
|
||||
scrollBG:SetBackdrop(backdrop)
|
||||
scrollBG:SetBackdropColor(0, 0, 0)
|
||||
scrollBG:SetBackdropBorderColor(0.4, 0.4, 0.4)
|
||||
|
||||
local scrollFrame = CreateFrame("ScrollFrame", strfmt("%s%dScrollFrame", Type, widgetNum), frame, "UIPanelScrollFrameTemplate")
|
||||
|
||||
local scrollBar = _G[scrollFrame:GetName() .. "ScrollBar"]
|
||||
scrollBar:ClearAllPoints()
|
||||
scrollBar:SetPoint("TOP", label, "BOTTOM", 0, -19)
|
||||
scrollBar:SetPoint("BOTTOM", button, "TOP", 0, 18)
|
||||
scrollBar:SetPoint("RIGHT", frame, "RIGHT")
|
||||
|
||||
scrollBG:SetPoint("TOPRIGHT", scrollBar, "TOPLEFT", 0, 19)
|
||||
scrollBG:SetPoint("BOTTOMLEFT", button, "TOPLEFT")
|
||||
|
||||
scrollFrame:SetPoint("TOPLEFT", scrollBG, "TOPLEFT", 5, -6)
|
||||
scrollFrame:SetPoint("BOTTOMRIGHT", scrollBG, "BOTTOMRIGHT", -4, 4)
|
||||
scrollFrame:SetScript("OnEnter", OnEnter)
|
||||
scrollFrame:SetScript("OnLeave", OnLeave)
|
||||
scrollFrame:SetScript("OnMouseUp", OnMouseUp)
|
||||
scrollFrame:SetScript("OnReceiveDrag", OnReceiveDrag)
|
||||
scrollFrame:SetScript("OnSizeChanged", OnSizeChanged)
|
||||
local old = scrollFrame:GetScript("OnVerticalScroll");
|
||||
if old then
|
||||
scrollFrame:SetScript("OnVerticalScroll", function()
|
||||
old()
|
||||
OnVerticalScroll()
|
||||
end)
|
||||
else
|
||||
scrollFrame:SetScript("OnVerticalScroll", OnVerticalScroll)
|
||||
end
|
||||
|
||||
local editBox = CreateFrame("EditBox", strfmt("%s%dEdit", Type, widgetNum), scrollFrame)
|
||||
editBox:SetFontObject(ChatFontNormal)
|
||||
editBox:SetMultiLine(true)
|
||||
editBox:EnableMouse(true)
|
||||
editBox:SetAutoFocus(false)
|
||||
--editBox:SetCountInvisibleLetters(false)
|
||||
editBox:SetScript("OnCursorChanged", OnCursorChanged)
|
||||
editBox:SetScript("OnEditFocusLost", OnEditFocusLost)
|
||||
editBox:SetScript("OnEnter", OnEnter)
|
||||
editBox:SetScript("OnEscapePressed", OnEscapePressed)
|
||||
editBox:SetScript("OnLeave", OnLeave)
|
||||
editBox:SetScript("OnMouseDown", OnReceiveDrag)
|
||||
editBox:SetScript("OnReceiveDrag", OnReceiveDrag)
|
||||
editBox:SetScript("OnTextChanged", OnTextChanged)
|
||||
editBox:SetScript("OnTextSet", OnTextSet)
|
||||
editBox:SetScript("OnEditFocusGained", OnEditFocusGained)
|
||||
|
||||
-- Ace3v: the orders are important here
|
||||
scrollFrame:SetScrollChild(editBox)
|
||||
editBox:SetPoint("TOPLEFT",0,0)
|
||||
editBox:SetPoint("TOPRIGHT",0,0)
|
||||
|
||||
local widget = {
|
||||
button = button,
|
||||
editBox = editBox,
|
||||
frame = frame,
|
||||
label = label,
|
||||
labelHeight = 10,
|
||||
numlines = 4,
|
||||
scrollBar = scrollBar,
|
||||
scrollBG = scrollBG,
|
||||
scrollFrame = scrollFrame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
button.obj, editBox.obj, scrollFrame.obj = widget, widget, widget
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type, Constructor, Version)
|
||||
@@ -0,0 +1,285 @@
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Slider Widget
|
||||
Graphical Slider, like, for Range values.
|
||||
-------------------------------------------------------------------------------]]
|
||||
local Type, Version = "Slider", 21
|
||||
local AceGUI = LibStub and LibStub("AceGUI-3.0", true)
|
||||
if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end
|
||||
|
||||
-- Lua APIs
|
||||
local min, max, floor = math.min, math.max, math.floor
|
||||
local tonumber, pairs = tonumber, pairs
|
||||
|
||||
-- WoW APIs
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame, UIParent = CreateFrame, UIParent
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: GameFontHighlightSmall
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Support functions
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function UpdateText(self)
|
||||
local value = self.value or 0
|
||||
if self.ispercent then
|
||||
self.editbox:SetText(("%s%%"):format(floor(value * 1000 + 0.5) / 10))
|
||||
else
|
||||
self.editbox:SetText(floor(value * 100 + 0.5) / 100)
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateLabels(self)
|
||||
local min, max = (self.min or 0), (self.max or 100)
|
||||
if self.ispercent then
|
||||
self.lowtext:SetFormattedText("%s%%", (min * 100))
|
||||
self.hightext:SetFormattedText("%s%%", (max * 100))
|
||||
else
|
||||
self.lowtext:SetText(min)
|
||||
self.hightext:SetText(max)
|
||||
end
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Scripts
|
||||
-------------------------------------------------------------------------------]]
|
||||
local function Control_OnEnter()
|
||||
this.obj:Fire("OnEnter")
|
||||
end
|
||||
|
||||
local function Control_OnLeave()
|
||||
this.obj:Fire("OnLeave")
|
||||
end
|
||||
|
||||
local function Frame_OnMouseDown()
|
||||
this.obj.slider:EnableMouseWheel(true)
|
||||
AceGUI:ClearFocus()
|
||||
end
|
||||
|
||||
local function Slider_OnValueChanged()
|
||||
local self = this.obj
|
||||
if not this.setup then
|
||||
local newvalue = this:GetValue()
|
||||
if self.step and self.step > 0 then
|
||||
local min_value = self.min or 0
|
||||
newvalue = floor((newvalue - min_value) / self.step + 0.5) * self.step + min_value
|
||||
end
|
||||
if newvalue ~= self.value and not self.disabled then
|
||||
self.value = newvalue
|
||||
self:Fire("OnValueChanged", 1, newvalue)
|
||||
end
|
||||
if self.value then
|
||||
UpdateText(self)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function Slider_OnMouseUp()
|
||||
local self = this.obj
|
||||
self:Fire("OnMouseUp", 1, self.value)
|
||||
end
|
||||
|
||||
local function Slider_OnMouseWheel()
|
||||
local self = this.obj
|
||||
if not self.disabled then
|
||||
local value = self.value
|
||||
if arg1 > 0 then
|
||||
value = min(value + (self.step or 1), self.max)
|
||||
else
|
||||
value = max(value - (self.step or 1), self.min)
|
||||
end
|
||||
self.slider:SetValue(value)
|
||||
end
|
||||
end
|
||||
|
||||
local function EditBox_OnEscapePressed()
|
||||
this:ClearFocus()
|
||||
end
|
||||
|
||||
local function EditBox_OnEnterPressed()
|
||||
local self = this.obj
|
||||
local value = this:GetText()
|
||||
if self.ispercent then
|
||||
value = value:gsub('%%', '')
|
||||
value = tonumber(value) / 100
|
||||
else
|
||||
value = tonumber(value)
|
||||
end
|
||||
|
||||
if value then
|
||||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||||
self.slider:SetValue(value)
|
||||
self:Fire("OnMouseUp", 1, value)
|
||||
end
|
||||
end
|
||||
|
||||
local function EditBox_OnEnter()
|
||||
this:SetBackdropBorderColor(0.5, 0.5, 0.5, 1)
|
||||
end
|
||||
|
||||
local function EditBox_OnLeave()
|
||||
this:SetBackdropBorderColor(0.3, 0.3, 0.3, 0.8)
|
||||
end
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Methods
|
||||
-------------------------------------------------------------------------------]]
|
||||
local methods = {
|
||||
["OnAcquire"] = function(self)
|
||||
self:SetWidth(200)
|
||||
self:SetHeight(44)
|
||||
self:SetDisabled(false)
|
||||
self:SetIsPercent(nil)
|
||||
self:SetSliderValues(0,100,1)
|
||||
self:SetValue(0)
|
||||
self.slider:EnableMouseWheel(false)
|
||||
end,
|
||||
|
||||
-- ["OnRelease"] = nil,
|
||||
|
||||
["SetDisabled"] = function(self, disabled)
|
||||
self.disabled = disabled
|
||||
if disabled then
|
||||
self.slider:EnableMouse(false)
|
||||
self.label:SetTextColor(.5, .5, .5)
|
||||
self.hightext:SetTextColor(.5, .5, .5)
|
||||
self.lowtext:SetTextColor(.5, .5, .5)
|
||||
--self.valuetext:SetTextColor(.5, .5, .5)
|
||||
self.editbox:SetTextColor(.5, .5, .5)
|
||||
self.editbox:EnableMouse(false)
|
||||
self.editbox:ClearFocus()
|
||||
else
|
||||
self.slider:EnableMouse(true)
|
||||
self.label:SetTextColor(1, .82, 0)
|
||||
self.hightext:SetTextColor(1, 1, 1)
|
||||
self.lowtext:SetTextColor(1, 1, 1)
|
||||
--self.valuetext:SetTextColor(1, 1, 1)
|
||||
self.editbox:SetTextColor(1, 1, 1)
|
||||
self.editbox:EnableMouse(true)
|
||||
end
|
||||
end,
|
||||
|
||||
["SetValue"] = function(self, value)
|
||||
self.slider.setup = true
|
||||
self.slider:SetValue(value)
|
||||
self.value = value
|
||||
UpdateText(self)
|
||||
self.slider.setup = nil
|
||||
end,
|
||||
|
||||
["GetValue"] = function(self)
|
||||
return self.value
|
||||
end,
|
||||
|
||||
["SetLabel"] = function(self, text)
|
||||
self.label:SetText(text)
|
||||
end,
|
||||
|
||||
["SetSliderValues"] = function(self, min, max, step)
|
||||
local frame = self.slider
|
||||
frame.setup = true
|
||||
self.min = min
|
||||
self.max = max
|
||||
self.step = step
|
||||
frame:SetMinMaxValues(min or 0,max or 100)
|
||||
UpdateLabels(self)
|
||||
frame:SetValueStep(step or 1)
|
||||
if self.value then
|
||||
frame:SetValue(self.value)
|
||||
end
|
||||
frame.setup = nil
|
||||
end,
|
||||
|
||||
["SetIsPercent"] = function(self, value)
|
||||
self.ispercent = value
|
||||
UpdateLabels(self)
|
||||
UpdateText(self)
|
||||
end
|
||||
}
|
||||
|
||||
--[[-----------------------------------------------------------------------------
|
||||
Constructor
|
||||
-------------------------------------------------------------------------------]]
|
||||
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 }
|
||||
}
|
||||
|
||||
local ManualBackdrop = {
|
||||
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
|
||||
tile = true, edgeSize = 1, tileSize = 5,
|
||||
}
|
||||
|
||||
local function Constructor()
|
||||
local frame = CreateFrame("Frame", nil, UIParent)
|
||||
|
||||
frame:EnableMouse(true)
|
||||
frame:SetScript("OnMouseDown", Frame_OnMouseDown)
|
||||
|
||||
local label = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
label:SetPoint("TOPLEFT",0,0)
|
||||
label:SetPoint("TOPRIGHT",0,0)
|
||||
label:SetJustifyH("CENTER",0,0)
|
||||
label:SetHeight(15)
|
||||
|
||||
local slider = CreateFrame("Slider", nil, frame)
|
||||
slider:SetOrientation("HORIZONTAL")
|
||||
slider:SetHeight(15)
|
||||
slider:SetHitRectInsets(0, 0, -10, 0)
|
||||
slider:SetBackdrop(SliderBackdrop)
|
||||
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Horizontal")
|
||||
slider:SetPoint("TOP", label, "BOTTOM")
|
||||
slider:SetPoint("LEFT", 3, 0)
|
||||
slider:SetPoint("RIGHT", -3, 0)
|
||||
slider:SetValue(0)
|
||||
slider:SetScript("OnValueChanged",Slider_OnValueChanged)
|
||||
slider:SetScript("OnEnter", Control_OnEnter)
|
||||
slider:SetScript("OnLeave", Control_OnLeave)
|
||||
slider:SetScript("OnMouseUp", Slider_OnMouseUp)
|
||||
slider:SetScript("OnMouseWheel", Slider_OnMouseWheel)
|
||||
|
||||
local lowtext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
lowtext:SetPoint("TOPLEFT", slider, "BOTTOMLEFT", 2, 3)
|
||||
|
||||
local hightext = slider:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
||||
hightext:SetPoint("TOPRIGHT", slider, "BOTTOMRIGHT", -2, 3)
|
||||
|
||||
local editbox = CreateFrame("EditBox", nil, frame)
|
||||
editbox:SetAutoFocus(false)
|
||||
editbox:SetFontObject(GameFontHighlightSmall)
|
||||
editbox:SetPoint("TOP", slider, "BOTTOM")
|
||||
editbox:SetHeight(14)
|
||||
editbox:SetWidth(70)
|
||||
editbox:SetJustifyH("CENTER")
|
||||
editbox:EnableMouse(true)
|
||||
editbox:SetBackdrop(ManualBackdrop)
|
||||
editbox:SetBackdropColor(0, 0, 0, 0.5)
|
||||
editbox:SetBackdropBorderColor(0.3, 0.3, 0.30, 0.80)
|
||||
editbox:SetScript("OnEnter", EditBox_OnEnter)
|
||||
editbox:SetScript("OnLeave", EditBox_OnLeave)
|
||||
editbox:SetScript("OnEnterPressed", EditBox_OnEnterPressed)
|
||||
editbox:SetScript("OnEscapePressed", EditBox_OnEscapePressed)
|
||||
|
||||
local widget = {
|
||||
label = label,
|
||||
slider = slider,
|
||||
lowtext = lowtext,
|
||||
hightext = hightext,
|
||||
editbox = editbox,
|
||||
alignoffset = 25,
|
||||
frame = frame,
|
||||
type = Type
|
||||
}
|
||||
for method, func in pairs(methods) do
|
||||
widget[method] = func
|
||||
end
|
||||
slider.obj, editbox.obj = widget, widget
|
||||
|
||||
return AceGUI:RegisterAsWidget(widget)
|
||||
end
|
||||
|
||||
AceGUI:RegisterWidgetType(Type,Constructor,Version)
|
||||
@@ -0,0 +1,27 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<!-- Container -->
|
||||
<Script file="AceGUIContainer-BlizOptionsGroup.lua"/>
|
||||
<Script file="AceGUIContainer-DropDownGroup.lua"/>
|
||||
<Script file="AceGUIContainer-Frame.lua"/>
|
||||
<Script file="AceGUIContainer-InlineGroup.lua"/>
|
||||
<Script file="AceGUIContainer-ScrollFrame.lua"/>
|
||||
<Script file="AceGUIContainer-SimpleGroup.lua"/>
|
||||
<Script file="AceGUIContainer-TabGroup.lua"/>
|
||||
<Script file="AceGUIContainer-TreeGroup.lua"/>
|
||||
<Script file="AceGUIContainer-Window.lua"/>
|
||||
<!-- Widgets -->
|
||||
<Script file="AceGUIWidget-Button.lua"/>
|
||||
<Script file="AceGUIWidget-CheckBox.lua"/>
|
||||
<Script file="AceGUIWidget-ColorPicker.lua"/>
|
||||
<Script file="AceGUIWidget-DropDown.lua"/>
|
||||
<Script file="AceGUIWidget-DropDown-Items.lua"/>
|
||||
<Script file="AceGUIWidget-EditBox.lua"/>
|
||||
<Script file="AceGUIWidget-Heading.lua"/>
|
||||
<Script file="AceGUIWidget-Icon.lua"/>
|
||||
<Script file="AceGUIWidget-InteractiveLabel.lua"/>
|
||||
<Script file="AceGUIWidget-Keybinding.lua"/>
|
||||
<Script file="AceGUIWidget-Label.lua"/>
|
||||
<Script file="AceGUIWidget-MultiLineEditBox.lua"/>
|
||||
<Script file="AceGUIWidget-Slider.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,540 @@
|
||||
--- **AceHook-3.0** offers safe Hooking/Unhooking of functions, methods and frame scripts.
|
||||
-- Using AceHook-3.0 is recommended when you need to unhook your hooks again, so the hook chain isn't broken
|
||||
-- when you manually restore the original function.
|
||||
--
|
||||
-- **AceHook-3.0** can be embeded into your addon, either explicitly by calling AceHook:Embed(MyAddon) or by
|
||||
-- specifying it as an embeded library in your AceAddon. All functions will be available on your addon object
|
||||
-- and can be accessed directly, without having to explicitly call AceHook itself.\\
|
||||
-- It is recommended to embed AceHook, otherwise you'll have to specify a custom `self` on all calls you
|
||||
-- make into AceHook.
|
||||
-- @class file
|
||||
-- @name AceHook-3.0
|
||||
-- @release $Id: AceHook-3.0.lua 1118 2014-10-12 08:21:54Z nevcairiel $
|
||||
local ACEHOOK_MAJOR, ACEHOOK_MINOR = "AceHook-3.0", 8
|
||||
local AceHook, oldminor = LibStub:NewLibrary(ACEHOOK_MAJOR, ACEHOOK_MINOR)
|
||||
|
||||
if not AceHook then return end -- No upgrade needed
|
||||
|
||||
local AceCore = LibStub("AceCore-3.0")
|
||||
local new, del = AceCore.new, AceCore.del
|
||||
|
||||
AceHook.embeded = AceHook.embeded or {}
|
||||
AceHook.registry = AceHook.registry or setmetatable({}, {__index = function(tbl, key) tbl[key] = {} return tbl[key] end })
|
||||
AceHook.handlers = AceHook.handlers or {}
|
||||
AceHook.actives = AceHook.actives or {}
|
||||
AceHook.scripts = AceHook.scripts or {}
|
||||
AceHook.onceSecure = AceHook.onceSecure or {}
|
||||
AceHook.hooks = AceHook.hooks or {}
|
||||
|
||||
-- local upvalues
|
||||
local registry = AceHook.registry
|
||||
local handlers = AceHook.handlers
|
||||
local actives = AceHook.actives
|
||||
local scripts = AceHook.scripts
|
||||
local onceSecure = AceHook.onceSecure
|
||||
|
||||
-- Lua APIs
|
||||
local pairs, next, type = pairs, next, type
|
||||
local format = string.format
|
||||
local assert, error = assert, error
|
||||
|
||||
-- WoW APIs
|
||||
local hooksecurefunc, issecurevariable = AceCore.hooksecurefunc, AceCore.issecurevariable
|
||||
|
||||
local _G = AceCore._G
|
||||
|
||||
local protectedScripts = {
|
||||
OnClick = true,
|
||||
}
|
||||
|
||||
-- upgrading of embeded is done at the bottom of the file
|
||||
|
||||
local mixins = {
|
||||
"Hook", "SecureHook",
|
||||
"HookScript", "SecureHookScript",
|
||||
"Unhook", "UnhookAll",
|
||||
"IsHooked",
|
||||
"RawHook", "RawHookScript"
|
||||
}
|
||||
|
||||
-- AceHook:Embed( target )
|
||||
-- target (object) - target object to embed AceHook in
|
||||
--
|
||||
-- Embeds AceEevent into the target object making the functions from the mixins list available on target:..
|
||||
function AceHook:Embed( target )
|
||||
for k, v in pairs( mixins ) do
|
||||
target[v] = self[v]
|
||||
end
|
||||
self.embeded[target] = true
|
||||
-- inject the hooks table safely
|
||||
target.hooks = target.hooks or {}
|
||||
return target
|
||||
end
|
||||
|
||||
-- AceHook:OnEmbedDisable( target )
|
||||
-- target (object) - target object that is being disabled
|
||||
--
|
||||
-- Unhooks all hooks when the target disables.
|
||||
-- this method should be called by the target manually or by an addon framework
|
||||
function AceHook:OnEmbedDisable( target )
|
||||
target:UnhookAll()
|
||||
end
|
||||
|
||||
-- failsafe means even the hooking method fails, the original method will
|
||||
-- always be called
|
||||
local function createHook(self, handler, orig, secure, failsafe)
|
||||
local uid
|
||||
local method = type(handler) == "string"
|
||||
-- Ace3v: when this function called in the "hook" function, we have
|
||||
-- failsafe = not (raw or secure), so the first check condition
|
||||
-- is same as "if failsafe" or "if not raw and not secure"
|
||||
if failsafe and not secure then
|
||||
-- failsafe hook creation
|
||||
uid = function(...)
|
||||
if actives[uid] then
|
||||
if method then
|
||||
self[handler](self,unpack(arg))
|
||||
else
|
||||
handler(unpack(arg))
|
||||
end
|
||||
end
|
||||
return orig(unpack(arg))
|
||||
end
|
||||
-- /failsafe hook
|
||||
else
|
||||
-- all other hooks
|
||||
uid = function(...)
|
||||
if actives[uid] then
|
||||
if method then
|
||||
return self[handler](self,unpack(arg))
|
||||
else
|
||||
return handler(unpack(arg))
|
||||
end
|
||||
elseif not secure then -- backup on non secure
|
||||
return orig(unpack(arg))
|
||||
end
|
||||
end
|
||||
-- /hook
|
||||
end
|
||||
return uid
|
||||
end
|
||||
|
||||
local function donothing() end
|
||||
|
||||
-- @param self
|
||||
-- @param obj nil or a frame object, use with script = true
|
||||
-- @param method string, the name of a global function or the name of an object method
|
||||
-- @param handler function, or string when it's a method of 'self', if nil then will use the same value as method
|
||||
-- @param script boolean, must be true, if the hooked object is a frame
|
||||
-- @param secure boolean, if hooking a secure script, if true the original script will be called first, else later
|
||||
-- @param raw boolean, if raw hooking (replacing the original method)
|
||||
-- @param forceSecure boolean, if forcing to hook a secure method
|
||||
-- @param usage
|
||||
local function hook(self, obj, method, handler, script, secure, raw, forceSecure, usage)
|
||||
if not handler then handler = method end
|
||||
|
||||
-- These asserts make sure AceHooks's devs play by the rules.
|
||||
assert(not script or type(script) == "boolean")
|
||||
assert(not secure or type(secure) == "boolean")
|
||||
assert(not raw or type(raw) == "boolean")
|
||||
assert(not forceSecure or type(forceSecure) == "boolean")
|
||||
assert(usage)
|
||||
|
||||
-- Error checking Battery!
|
||||
if obj and type(obj) ~= "table" then
|
||||
error(format("%s: 'object' - nil or table expected got %s", usage, type(obj)), 3)
|
||||
end
|
||||
if type(method) ~= "string" then
|
||||
error(format("%s: 'method' - string expected got %s", usage, type(method)), 3)
|
||||
end
|
||||
if type(handler) ~= "string" and type(handler) ~= "function" then
|
||||
error(format("%s: 'handler' - nil, string, or function expected got %s", usage, type(handler)), 3)
|
||||
end
|
||||
if type(handler) == "string" and type(self[handler]) ~= "function" then
|
||||
error(format("%s: 'handler' - Handler specified does not exist at self[handler]", usage), 3)
|
||||
end
|
||||
if script then
|
||||
if not obj or not obj.GetScript or not obj:HasScript(method) then
|
||||
error(format("%s: You can only hook a script on a frame object", usage), 3)
|
||||
end
|
||||
if not secure and obj.IsProtected and obj:IsProtected() and protectedScripts[method] then
|
||||
error(format("Cannot hook secure script %q; Use SecureHookScript(obj, method, [handler]) instead.", method), 3)
|
||||
end
|
||||
else
|
||||
local issecure
|
||||
if obj then
|
||||
issecure = onceSecure[obj] and onceSecure[obj][method] or issecurevariable(obj, method)
|
||||
else
|
||||
issecure = onceSecure[method] or issecurevariable(method)
|
||||
end
|
||||
if issecure then
|
||||
if forceSecure then
|
||||
if obj then
|
||||
onceSecure[obj] = onceSecure[obj] or {}
|
||||
onceSecure[obj][method] = true
|
||||
else
|
||||
onceSecure[method] = true
|
||||
end
|
||||
elseif not secure then
|
||||
error(format("%s: Attempt to hook secure function %s. Use `SecureHook' or add `true' to the argument list to override.", usage, method), 3)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local uid
|
||||
if obj then
|
||||
uid = registry[self][obj] and registry[self][obj][method]
|
||||
else
|
||||
uid = registry[self][method]
|
||||
end
|
||||
|
||||
if uid then
|
||||
if actives[uid] then
|
||||
-- Only two sane choices exist here. We either a) error 100% of the time or b) always unhook and then hook
|
||||
-- choice b would likely lead to odd debuging conditions or other mysteries so we're going with a.
|
||||
error(format("Attempting to rehook already active hook %s.", method))
|
||||
end
|
||||
|
||||
if handlers[uid] == handler then -- turn on a decative hook, note enclosures break this ability, small memory leak
|
||||
actives[uid] = true
|
||||
return
|
||||
elseif obj then -- is there any reason not to call unhook instead of doing the following several lines?
|
||||
if self.hooks and self.hooks[obj] then
|
||||
self.hooks[obj][method] = nil
|
||||
end
|
||||
registry[self][obj][method] = nil
|
||||
else
|
||||
if self.hooks then
|
||||
self.hooks[method] = nil
|
||||
end
|
||||
registry[self][method] = nil
|
||||
end
|
||||
handlers[uid], actives[uid], scripts[uid] = nil, nil, nil
|
||||
uid = nil
|
||||
end
|
||||
|
||||
local orig
|
||||
if script then
|
||||
-- Sometimes there is not a original function for a script.
|
||||
orig = obj:GetScript(method) or donothing
|
||||
elseif obj then
|
||||
orig = obj[method]
|
||||
else
|
||||
orig = _G[method]
|
||||
end
|
||||
|
||||
if not orig then
|
||||
error(format("%s: Attempting to hook a non existing target", usage), 3)
|
||||
end
|
||||
|
||||
uid = createHook(self, handler, orig, secure, not (raw or secure))
|
||||
|
||||
if obj then
|
||||
registry[self][obj] = registry[self][obj] or new()
|
||||
registry[self][obj][method] = uid
|
||||
|
||||
if not secure then
|
||||
self.hooks[obj] = self.hooks[obj] or new()
|
||||
self.hooks[obj][method] = orig
|
||||
end
|
||||
|
||||
if script then
|
||||
if not secure then
|
||||
obj:SetScript(method, uid)
|
||||
else
|
||||
obj:SetScript(method, function(...)
|
||||
local tmp = {orig(unpack(arg))}
|
||||
uid(unpack(arg))
|
||||
return unpack(tmp)
|
||||
end)
|
||||
--obj:HookScript(method, uid) -- Ace3v: vanilla frame has no HookScript
|
||||
end
|
||||
else
|
||||
if not secure then
|
||||
obj[method] = uid
|
||||
else
|
||||
hooksecurefunc(obj, method, uid)
|
||||
end
|
||||
end
|
||||
else
|
||||
registry[self][method] = uid
|
||||
|
||||
if not secure then
|
||||
_G[method] = uid
|
||||
self.hooks[method] = orig
|
||||
else
|
||||
hooksecurefunc(method, uid)
|
||||
end
|
||||
end
|
||||
|
||||
actives[uid], handlers[uid], scripts[uid] = true, handler, script and true or nil
|
||||
end
|
||||
|
||||
--- Hook a function or a method on an object.
|
||||
-- The hook created will be a "safe hook", that means that your handler will be called
|
||||
-- before the hooked function ("Pre-Hook"), and you don't have to call the original function yourself,
|
||||
-- however you cannot stop the execution of the function, or modify any of the arguments/return values.\\
|
||||
-- This type of hook is typically used if you need to know if some function got called, and don't want to modify it.
|
||||
-- @paramsig [object], method, [handler], [hookSecure]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
-- @param hookSecure If true, AceHook will allow hooking of secure functions.
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook ActionButton_UpdateHotkeys, overwriting the secure status
|
||||
-- self:Hook("ActionButton_UpdateHotkeys", true)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:ActionButton_UpdateHotkeys(button, type)
|
||||
-- print(button:GetName() .. " is updating its HotKey")
|
||||
-- end
|
||||
function AceHook:Hook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure, object = object, method, handler, nil
|
||||
end
|
||||
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, false, false, hookSecure or false, "Usage: Hook([object], method, [handler], [hookSecure])")
|
||||
end
|
||||
|
||||
--- RawHook a function or a method on an object.
|
||||
-- The hook created will be a "raw hook", that means that your handler will completly replace
|
||||
-- the original function, and your handler has to call the original function (or not, depending on your intentions).\\
|
||||
-- The original function will be stored in `self.hooks[object][method]` or `self.hooks[functionName]` respectively.\\
|
||||
-- This type of hook can be used for all purposes, and is usually the most common case when you need to modify arguments
|
||||
-- or want to control execution of the original function.
|
||||
-- @paramsig [object], method, [handler], [hookSecure]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
-- @param hookSecure If true, AceHook will allow hooking of secure functions.
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook ActionButton_UpdateHotkeys, overwriting the secure status
|
||||
-- self:RawHook("ActionButton_UpdateHotkeys", true)
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:ActionButton_UpdateHotkeys(button, type)
|
||||
-- if button:GetName() == "MyButton" then
|
||||
-- -- do stuff here
|
||||
-- else
|
||||
-- self.hooks.ActionButton_UpdateHotkeys(button, type)
|
||||
-- end
|
||||
-- end
|
||||
function AceHook:RawHook(object, method, handler, hookSecure)
|
||||
if type(object) == "string" then
|
||||
method, handler, hookSecure, object = object, method, handler, nil
|
||||
end
|
||||
|
||||
if handler == true then
|
||||
handler, hookSecure = nil, true
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, false, true, hookSecure or false, "Usage: RawHook([object], method, [handler], [hookSecure])")
|
||||
end
|
||||
|
||||
--- SecureHook a function or a method on an object.
|
||||
-- This function is a wrapper around the `hooksecurefunc` function in the WoW API. Using AceHook
|
||||
-- extends the functionality of secure hooks, and adds the ability to unhook once the hook isn't
|
||||
-- required anymore, or the addon is being disabled.\\
|
||||
-- Secure Hooks should be used if the secure-status of the function is vital to its function,
|
||||
-- and taint would block execution. Secure Hooks are always called after the original function was called
|
||||
-- ("Post Hook"), and you cannot modify the arguments, return values or control the execution.
|
||||
-- @paramsig [object], method, [handler]
|
||||
-- @param object The object to hook a method from
|
||||
-- @param method If object was specified, the name of the method, or the name of the function to hook.
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked function)
|
||||
function AceHook:SecureHook(object, method, handler)
|
||||
if type(object) == "string" then
|
||||
method, handler, object = object, method, nil
|
||||
end
|
||||
|
||||
hook(self, object, method, handler, false, true, false, false, "Usage: SecureHook([object], method, [handler])")
|
||||
end
|
||||
|
||||
--- Hook a script handler on a frame.
|
||||
-- The hook created will be a "safe hook", that means that your handler will be called
|
||||
-- before the hooked script ("Pre-Hook"), and you don't have to call the original function yourself,
|
||||
-- however you cannot stop the execution of the function, or modify any of the arguments/return values.\\
|
||||
-- This is the frame script equivalent of the :Hook safe-hook. It would typically be used to be notified
|
||||
-- when a certain event happens to a frame.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook the OnShow of FriendsFrame
|
||||
-- self:HookScript(FriendsFrame, "OnShow", "FriendsFrameOnShow")
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:FriendsFrameOnShow(frame)
|
||||
-- print("The FriendsFrame was shown!")
|
||||
-- end
|
||||
function AceHook:HookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, false, false, false, "Usage: HookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- RawHook a script handler on a frame.
|
||||
-- The hook created will be a "raw hook", that means that your handler will completly replace
|
||||
-- the original script, and your handler has to call the original script (or not, depending on your intentions).\\
|
||||
-- The original script will be stored in `self.hooks[frame][script]`.\\
|
||||
-- This type of hook can be used for all purposes, and is usually the most common case when you need to modify arguments
|
||||
-- or want to control execution of the original script.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
-- @usage
|
||||
-- -- create an addon with AceHook embeded
|
||||
-- MyAddon = LibStub("AceAddon-3.0"):NewAddon("HookDemo", "AceHook-3.0")
|
||||
--
|
||||
-- function MyAddon:OnEnable()
|
||||
-- -- Hook the OnShow of FriendsFrame
|
||||
-- self:RawHookScript(FriendsFrame, "OnShow", "FriendsFrameOnShow")
|
||||
-- end
|
||||
--
|
||||
-- function MyAddon:FriendsFrameOnShow(frame)
|
||||
-- -- Call the original function
|
||||
-- self.hooks[frame].OnShow(frame)
|
||||
-- -- Do our processing
|
||||
-- -- .. stuff
|
||||
-- end
|
||||
function AceHook:RawHookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, false, true, false, "Usage: RawHookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- SecureHook a script handler on a frame.
|
||||
-- This function is a wrapper around the `frame:HookScript` function in the WoW API. Using AceHook
|
||||
-- extends the functionality of secure hooks, and adds the ability to unhook once the hook isn't
|
||||
-- required anymore, or the addon is being disabled.\\
|
||||
-- Secure Hooks should be used if the secure-status of the function is vital to its function,
|
||||
-- and taint would block execution. Secure Hooks are always called after the original function was called
|
||||
-- ("Post Hook"), and you cannot modify the arguments, return values or control the execution.
|
||||
-- @paramsig frame, script, [handler]
|
||||
-- @param frame The Frame to hook the script on
|
||||
-- @param script The script to hook
|
||||
-- @param handler The handler for the hook, a funcref or a method name. (Defaults to the name of the hooked script)
|
||||
function AceHook:SecureHookScript(frame, script, handler)
|
||||
hook(self, frame, script, handler, true, true, false, false, "Usage: SecureHookScript(object, method, [handler])")
|
||||
end
|
||||
|
||||
--- Unhook from the specified function, method or script.
|
||||
-- @paramsig [obj], method
|
||||
-- @param obj The object or frame to unhook from
|
||||
-- @param method The name of the method, function or script to unhook from.
|
||||
function AceHook:Unhook(obj, method)
|
||||
local usage = "Usage: Unhook([obj], method)"
|
||||
if type(obj) == "string" then
|
||||
method, obj = obj, nil
|
||||
end
|
||||
|
||||
if obj and type(obj) ~= "table" then
|
||||
error(format("%s: 'obj' - expecting nil or table got %s", usage, type(obj)), 2)
|
||||
end
|
||||
if type(method) ~= "string" then
|
||||
error(format("%s: 'method' - expeting string got %s", usage, type(method)), 2)
|
||||
end
|
||||
|
||||
local uid
|
||||
if obj then
|
||||
uid = registry[self][obj] and registry[self][obj][method]
|
||||
else
|
||||
uid = registry[self][method]
|
||||
end
|
||||
|
||||
if not uid or not actives[uid] then
|
||||
-- Declining to error on an unneeded unhook since the end effect is the same and this would just be annoying.
|
||||
return false
|
||||
end
|
||||
|
||||
actives[uid], handlers[uid] = nil, nil
|
||||
|
||||
if obj then
|
||||
local tmp = registry[self][obj]
|
||||
tmp[method] = nil
|
||||
if not next(tmp) then
|
||||
del(tmp)
|
||||
registry[self][obj] = nil
|
||||
end
|
||||
|
||||
-- if the hook reference doesnt exist, then its a secure hook, just bail out and dont do any unhooking
|
||||
if not self.hooks[obj] or not self.hooks[obj][method] then return true end
|
||||
|
||||
if scripts[uid] and obj:GetScript(method) == uid then -- unhooks scripts
|
||||
obj:SetScript(method, self.hooks[obj][method] ~= donothing and self.hooks[obj][method] or nil)
|
||||
scripts[uid] = nil
|
||||
elseif obj and self.hooks[obj] and self.hooks[obj][method] and obj[method] == uid then -- unhooks methods
|
||||
obj[method] = self.hooks[obj][method]
|
||||
end
|
||||
|
||||
tmp = self.hooks[obj]
|
||||
tmp[method] = nil
|
||||
if not next(tmp) then
|
||||
del(tmp)
|
||||
self.hooks[obj] = nil
|
||||
end
|
||||
else
|
||||
registry[self][method] = nil
|
||||
|
||||
-- if self.hooks[method] doesn't exist, then this is a SecureHook, just bail out
|
||||
if not self.hooks[method] then return true end
|
||||
|
||||
if self.hooks[method] and _G[method] == uid then -- unhooks functions
|
||||
_G[method] = self.hooks[method]
|
||||
end
|
||||
|
||||
self.hooks[method] = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--- Unhook all existing hooks for this addon.
|
||||
function AceHook:UnhookAll()
|
||||
for key, value in pairs(registry[self]) do
|
||||
if type(key) == "table" then
|
||||
for method in pairs(value) do
|
||||
self:Unhook(key, method)
|
||||
end
|
||||
else
|
||||
self:Unhook(key)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--- Check if the specific function, method or script is already hooked.
|
||||
-- @paramsig [obj], method
|
||||
-- @param obj The object or frame to unhook from
|
||||
-- @param method The name of the method, function or script to unhook from.
|
||||
function AceHook:IsHooked(obj, method)
|
||||
-- we don't check if registry[self] exists, this is done by evil magicks in the metatable
|
||||
if type(obj) == "string" then
|
||||
if registry[self][obj] and actives[registry[self][obj]] then
|
||||
return true, handlers[registry[self][obj]]
|
||||
end
|
||||
else
|
||||
if registry[self][obj] and registry[self][obj][method] and actives[registry[self][obj][method]] then
|
||||
return true, handlers[registry[self][obj][method]]
|
||||
end
|
||||
end
|
||||
|
||||
return false, nil
|
||||
end
|
||||
|
||||
--- Upgrade our old embeded
|
||||
for target, v in pairs( AceHook.embeded ) do
|
||||
AceHook:Embed( target )
|
||||
end
|
||||
@@ -0,0 +1,4 @@
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.blizzard.com/wow/ui/
|
||||
..\FrameXML\UI.xsd">
|
||||
<Script file="AceHook-3.0.lua"/>
|
||||
</Ui>
|
||||
@@ -0,0 +1,280 @@
|
||||
--[[ $Id: CallbackHandler-1.0.lua 1131 2015-06-04 07:29:24Z nevcairiel $ ]]
|
||||
local MAJOR, MINOR = "CallbackHandler-1.0", 6
|
||||
local CallbackHandler = LibStub:NewLibrary(MAJOR, MINOR)
|
||||
|
||||
if not CallbackHandler then return end -- No upgrade needed
|
||||
|
||||
-- Lua APIs
|
||||
local tconcat, tinsert, tgetn, tsetn = table.concat, table.insert, table.getn, table.setn
|
||||
local assert, error, loadstring = assert, error, loadstring
|
||||
local setmetatable, rawset, rawget = setmetatable, rawset, rawget
|
||||
local next, pairs, type, tostring = next, pairs, type, tostring
|
||||
local strgsub = string.gsub
|
||||
|
||||
local new, del
|
||||
do
|
||||
local list = setmetatable({}, {__mode = "k"})
|
||||
function new()
|
||||
local t = next(list)
|
||||
if not t then
|
||||
return {}
|
||||
end
|
||||
list[t] = nil
|
||||
return t
|
||||
end
|
||||
|
||||
function del(t)
|
||||
setmetatable(t, nil)
|
||||
for k in pairs(t) do
|
||||
t[k] = nil
|
||||
end
|
||||
tsetn(t,0)
|
||||
list[t] = true
|
||||
end
|
||||
end
|
||||
|
||||
local meta = {__index = function(tbl, key) rawset(tbl, key, new()) return tbl[key] end}
|
||||
|
||||
-- Global vars/functions that we don't upvalue since they might get hooked, or upgraded
|
||||
-- List them here for Mikk's FindGlobals script
|
||||
-- GLOBALS: geterrorhandler
|
||||
|
||||
local function errorhandler(err)
|
||||
return geterrorhandler()(err)
|
||||
end
|
||||
CallbackHandler.errorhandler = errorhandler
|
||||
|
||||
local function CreateDispatcher(argCount)
|
||||
local code = [[
|
||||
local xpcall, errorhandler = xpcall, LibStub("CallbackHandler-1.0").errorhandler
|
||||
local method, UP_ARGS
|
||||
local function call()
|
||||
local func, ARGS = method, UP_ARGS
|
||||
method, UP_ARGS = nil, NILS
|
||||
return func(ARGS)
|
||||
end
|
||||
return function(handlers, ARGS)
|
||||
local index
|
||||
index, method = next(handlers)
|
||||
if not method then return end
|
||||
repeat
|
||||
UP_ARGS = ARGS
|
||||
xpcall(call, errorhandler)
|
||||
index, method = next(handlers, index)
|
||||
until not method
|
||||
end
|
||||
]]
|
||||
local c = 4*argCount-1
|
||||
local s = "b01,b02,b03,b04,b05,b06,b07,b08,b09,b10"
|
||||
code = strgsub(code, "UP_ARGS", string.sub(s,1,c))
|
||||
s = "a01,a02,a03,a04,a05,a06,a07,a08,a09,a10"
|
||||
code = strgsub(code, "ARGS", string.sub(s,1,c))
|
||||
s = "nil,nil,nil,nil,nil,nil,nil,nil,nil,nil"
|
||||
code = strgsub(code, "NILS", string.sub(s,1,c))
|
||||
return assert(loadstring(code, "safecall Dispatcher["..tostring(argCount).."]"))()
|
||||
end
|
||||
|
||||
local Dispatchers = setmetatable({}, {__index=function(self, argCount)
|
||||
local dispatcher = CreateDispatcher(argCount)
|
||||
rawset(self, argCount, dispatcher)
|
||||
return dispatcher
|
||||
end})
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
-- CallbackHandler:New
|
||||
--
|
||||
-- target - target object to embed public APIs in
|
||||
-- RegisterName - name of the callback registration API, default "RegisterCallback"
|
||||
-- UnregisterName - name of the callback unregistration API, default "UnregisterCallback"
|
||||
-- UnregisterAllName - name of the API to unregister all callbacks, default "UnregisterAllCallbacks". false == don't publish this API.
|
||||
function CallbackHandler:New(target, RegisterName, UnregisterName, UnregisterAllName)
|
||||
|
||||
RegisterName = RegisterName or "RegisterCallback"
|
||||
UnregisterName = UnregisterName or "UnregisterCallback"
|
||||
if UnregisterAllName==nil then -- false is used to indicate "don't want this method"
|
||||
UnregisterAllName = "UnregisterAllCallbacks"
|
||||
end
|
||||
|
||||
-- we declare all objects and exported APIs inside this closure to quickly gain access
|
||||
-- to e.g. function names, the "target" parameter, etc
|
||||
|
||||
|
||||
-- Create the registry object
|
||||
local events = setmetatable({}, meta)
|
||||
local registry = { recurse=0, events=events }
|
||||
|
||||
-- registry:Fire() - fires the given event/message into the registry
|
||||
function registry:Fire(eventname, argc, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
if not rawget(events, eventname) or not next(events[eventname]) then return end
|
||||
local oldrecurse = registry.recurse
|
||||
registry.recurse = oldrecurse + 1
|
||||
|
||||
argc = argc or 0
|
||||
Dispatchers[argc+1](events[eventname], eventname, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)
|
||||
|
||||
registry.recurse = oldrecurse
|
||||
|
||||
if registry.insertQueue and oldrecurse==0 then
|
||||
-- Something in one of our callbacks wanted to register more callbacks; they got queued
|
||||
for eventname,callbacks in pairs(registry.insertQueue) do
|
||||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
|
||||
for self,func in pairs(callbacks) do
|
||||
events[eventname][self] = func
|
||||
-- fire OnUsed callback?
|
||||
if first and registry.OnUsed then
|
||||
registry.OnUsed(registry, target, eventname)
|
||||
first = nil
|
||||
end
|
||||
end
|
||||
del(callbacks)
|
||||
end
|
||||
del(registry.insertQueue)
|
||||
registry.insertQueue = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Registration of a callback, handles:
|
||||
-- self["method"], leads to self["method"](self, ...)
|
||||
-- self with function ref, leads to functionref(...)
|
||||
-- "addonId" (instead of self) with function ref, leads to functionref(...)
|
||||
-- all with an optional arg, which, if present, gets passed as first argument (after self if present)
|
||||
target[RegisterName] = function(self, eventname, method, ...)
|
||||
if type(eventname) ~= "string" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'eventname' - string expected.", 2)
|
||||
end
|
||||
|
||||
method = method or eventname
|
||||
|
||||
local first = not rawget(events, eventname) or not next(events[eventname]) -- test for empty before. not test for one member after. that one member may have been overwritten.
|
||||
|
||||
if type(method) ~= "string" and type(method) ~= "function" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'method' - string or function expected.", 2)
|
||||
end
|
||||
|
||||
local regfunc
|
||||
local a1 = arg[1]
|
||||
|
||||
if type(method) == "string" then
|
||||
-- self["method"] calling style
|
||||
if type(self) ~= "table" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): self was not a table?", 2)
|
||||
elseif self==target then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): do not use Library:"..RegisterName.."(), use your own 'self'.", 2)
|
||||
elseif type(self[method]) ~= "function" then
|
||||
error("Usage: "..RegisterName.."(eventname, method[, arg]): 'method' - method '"..tostring(method).."' not found on 'self'.", 2)
|
||||
end
|
||||
|
||||
if tgetn(arg) >= 1 then
|
||||
regfunc = function (...) return self[method](self,a1,unpack(arg)) end
|
||||
else
|
||||
regfunc = function (...) return self[method](self,unpack(arg)) end
|
||||
end
|
||||
else
|
||||
-- function ref with self=object or self="addonId"
|
||||
if type(self)~="table" and type(self)~="string" then
|
||||
error("Usage: "..RegisterName.."(self or addonId, eventname, method[, arg]): 'self or addonId': table or string expected.", 2)
|
||||
end
|
||||
|
||||
if tgetn(arg) >= 1 then
|
||||
regfunc = function (...) return method(a1, unpack(arg)) end
|
||||
else
|
||||
regfunc = method
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if events[eventname][self] or registry.recurse<1 then
|
||||
-- if registry.recurse<1 then
|
||||
-- we're overwriting an existing entry, or not currently recursing. just set it.
|
||||
events[eventname][self] = regfunc
|
||||
-- fire OnUsed callback?
|
||||
if registry.OnUsed and first then
|
||||
registry.OnUsed(registry, target, eventname)
|
||||
end
|
||||
else
|
||||
-- we're currently processing a callback in this registry, so delay the registration of this new entry!
|
||||
-- yes, we're a bit wasteful on garbage, but this is a fringe case, so we're picking low implementation overhead over garbage efficiency
|
||||
registry.insertQueue = registry.insertQueue or setmetatable(new(),meta)
|
||||
registry.insertQueue[eventname][self] = regfunc
|
||||
end
|
||||
end
|
||||
|
||||
-- Unregister a callback
|
||||
target[UnregisterName] = function(self, eventname)
|
||||
if not self or self==target then
|
||||
error("Usage: "..UnregisterName.."(eventname): bad 'self'", 2)
|
||||
end
|
||||
if type(eventname) ~= "string" then
|
||||
error("Usage: "..UnregisterName.."(eventname): 'eventname' - string expected.", 2)
|
||||
end
|
||||
if rawget(events, eventname) and events[eventname][self] then
|
||||
events[eventname][self] = nil
|
||||
|
||||
-- Fire OnUnused callback?
|
||||
if registry.OnUnused and not next(events[eventname]) then
|
||||
registry.OnUnused(registry, target, eventname)
|
||||
end
|
||||
|
||||
if rawget(events, eventname) and not next(events[eventname]) then
|
||||
del(events[eventname])
|
||||
events[eventname] = nil
|
||||
end
|
||||
end
|
||||
if registry.insertQueue and rawget(registry.insertQueue, eventname) and registry.insertQueue[eventname][self] then
|
||||
registry.insertQueue[eventname][self] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- OPTIONAL: Unregister all callbacks for given selfs/addonIds
|
||||
if UnregisterAllName then
|
||||
target[UnregisterAllName] = function(a1,a2,a3,a4,a5,a6,a7,a8,a9,a10)
|
||||
if not a1 then
|
||||
error("Usage: "..UnregisterAllName.."([whatFor]): missing 'self' or 'addonId' to unregister events for.", 2)
|
||||
end
|
||||
if a1 == target then
|
||||
error("Usage: "..UnregisterAllName.."([whatFor]): supply a meaningful 'self' or 'addonId'", 2)
|
||||
end
|
||||
|
||||
-- use our registry table as argument table
|
||||
registry[1] = a1
|
||||
registry[2] = a2
|
||||
registry[3] = a3
|
||||
registry[4] = a4
|
||||
registry[5] = a5
|
||||
registry[6] = a6
|
||||
registry[7] = a7
|
||||
registry[8] = a8
|
||||
registry[9] = a9
|
||||
registry[10] = a10
|
||||
for i=1,10 do
|
||||
local self = registry[i]
|
||||
registry[i] = nil
|
||||
if self then
|
||||
if registry.insertQueue then
|
||||
for eventname, callbacks in pairs(registry.insertQueue) do
|
||||
if callbacks[self] then
|
||||
callbacks[self] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
for eventname, callbacks in pairs(events) do
|
||||
if callbacks[self] then
|
||||
callbacks[self] = nil
|
||||
-- Fire OnUnused callback?
|
||||
if registry.OnUnused and not next(callbacks) then
|
||||
registry.OnUnused(registry, target, eventname)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return registry
|
||||
end
|
||||
|
||||
-- CallbackHandler purposefully does NOT do explicit embedding. Nor does it
|
||||
-- try to upgrade old implicit embeds since the system is selfcontained and
|
||||
-- relies on closures to work.
|
||||
|
||||
@@ -0,0 +1,487 @@
|
||||
|
||||
-----------------------------------------------------------------------
|
||||
-- LibDBIcon-1.0
|
||||
--
|
||||
-- Allows addons to easily create a lightweight minimap icon as an alternative to heavier LDB displays.
|
||||
--
|
||||
|
||||
local DBICON10 = "LibDBIcon-1.0"
|
||||
local DBICON10_MINOR = 44 -- Bump on changes
|
||||
if not LibStub then error(DBICON10 .. " requires LibStub.") end
|
||||
local ldb = LibStub("LibDataBroker-1.1", true)
|
||||
if not ldb then error(DBICON10 .. " requires LibDataBroker-1.1.") end
|
||||
local lib = LibStub:NewLibrary(DBICON10, DBICON10_MINOR)
|
||||
if not lib then return end
|
||||
|
||||
LibStub("AceHook-3.0"):Embed(lib)
|
||||
lib.objects = lib.objects or {}
|
||||
lib.callbackRegistered = lib.callbackRegistered or nil
|
||||
lib.callbacks = lib.callbacks or LibStub("CallbackHandler-1.0"):New(lib)
|
||||
lib.notCreated = lib.notCreated or {}
|
||||
lib.radius = lib.radius or 5
|
||||
lib.tooltip = lib.tooltip or CreateFrame("GameTooltip", "LibDBIconTooltip", UIParent, "GameTooltipTemplate")
|
||||
local next, Minimap = next, Minimap
|
||||
local tgetn = table.getn
|
||||
local isDraggingButton = false
|
||||
|
||||
function lib:IconCallback(event, name, key, value)
|
||||
if lib.objects[name] then
|
||||
if key == "icon" then
|
||||
lib.objects[name].icon:SetTexture(value)
|
||||
elseif key == "iconCoords" then
|
||||
lib.objects[name].icon:UpdateCoord()
|
||||
elseif key == "iconR" then
|
||||
local _, g, b = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(value, g, b)
|
||||
elseif key == "iconG" then
|
||||
local r, _, b = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(r, value, b)
|
||||
elseif key == "iconB" then
|
||||
local r, g = lib.objects[name].icon:GetVertexColor()
|
||||
lib.objects[name].icon:SetVertexColor(r, g, value)
|
||||
end
|
||||
end
|
||||
end
|
||||
if not lib.callbackRegistered then
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__icon", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconCoords", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconR", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconG", "IconCallback")
|
||||
ldb.RegisterCallback(lib, "LibDataBroker_AttributeChanged__iconB", "IconCallback")
|
||||
lib.callbackRegistered = true
|
||||
end
|
||||
|
||||
local function getAnchors(frame)
|
||||
local x, y = frame:GetCenter()
|
||||
if not x or not y then return "CENTER" end
|
||||
local hhalf = (x > UIParent:GetWidth()*2/3) and "RIGHT" or (x < UIParent:GetWidth()/3) and "LEFT" or ""
|
||||
local vhalf = (y > UIParent:GetHeight()/2) and "TOP" or "BOTTOM"
|
||||
return vhalf..hhalf, frame, (vhalf == "TOP" and "BOTTOM" or "TOP")..hhalf
|
||||
end
|
||||
|
||||
local function onEnter()
|
||||
if isDraggingButton then return end
|
||||
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
|
||||
local obj = this.dataObject
|
||||
if obj.OnTooltipShow then
|
||||
lib.tooltip:SetOwner(this, "ANCHOR_NONE")
|
||||
lib.tooltip:SetPoint(getAnchors(this))
|
||||
obj.OnTooltipShow(lib.tooltip)
|
||||
lib.tooltip:Show()
|
||||
elseif obj.OnEnter then
|
||||
obj.OnEnter()
|
||||
end
|
||||
end
|
||||
|
||||
local function onLeave()
|
||||
lib.tooltip:Hide()
|
||||
|
||||
if not isDraggingButton then
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local obj = this.dataObject
|
||||
if obj.OnLeave then
|
||||
obj.OnLeave()
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local onDragStart, updatePosition
|
||||
|
||||
do
|
||||
local minimapShapes = {
|
||||
["ROUND"] = {true, true, true, true},
|
||||
["SQUARE"] = {false, false, false, false},
|
||||
["CORNER-TOPLEFT"] = {false, false, false, true},
|
||||
["CORNER-TOPRIGHT"] = {false, false, true, false},
|
||||
["CORNER-BOTTOMLEFT"] = {false, true, false, false},
|
||||
["CORNER-BOTTOMRIGHT"] = {true, false, false, false},
|
||||
["SIDE-LEFT"] = {false, true, false, true},
|
||||
["SIDE-RIGHT"] = {true, false, true, false},
|
||||
["SIDE-TOP"] = {false, false, true, true},
|
||||
["SIDE-BOTTOM"] = {true, true, false, false},
|
||||
["TRICORNER-TOPLEFT"] = {false, true, true, true},
|
||||
["TRICORNER-TOPRIGHT"] = {true, false, true, true},
|
||||
["TRICORNER-BOTTOMLEFT"] = {true, true, false, true},
|
||||
["TRICORNER-BOTTOMRIGHT"] = {true, true, true, false},
|
||||
}
|
||||
|
||||
local rad, cos, sin, sqrt, max, min = math.rad, math.cos, math.sin, math.sqrt, math.max, math.min
|
||||
function updatePosition(button, position)
|
||||
local angle = rad(position or 225)
|
||||
local x, y, q = cos(angle), sin(angle), 1
|
||||
if x < 0 then q = q + 1 end
|
||||
if y > 0 then q = q + 2 end
|
||||
local minimapShape = GetMinimapShape and GetMinimapShape() or "ROUND"
|
||||
local quadTable = minimapShapes[minimapShape]
|
||||
local w = (Minimap:GetWidth() / 2) + lib.radius
|
||||
local h = (Minimap:GetHeight() / 2) + lib.radius
|
||||
if quadTable[q] then
|
||||
x, y = x*w, y*h
|
||||
else
|
||||
local diagRadiusW = sqrt(2*(w)^2)-10
|
||||
local diagRadiusH = sqrt(2*(h)^2)-10
|
||||
x = max(-w, min(x*diagRadiusW, w))
|
||||
y = max(-h, min(y*diagRadiusH, h))
|
||||
end
|
||||
button:SetPoint("CENTER", Minimap, "CENTER", x, y)
|
||||
end
|
||||
end
|
||||
|
||||
local function onClick( )
|
||||
if this.dataObject.OnClick then
|
||||
this.dataObject.OnClick( this, arg1)
|
||||
end
|
||||
end
|
||||
|
||||
local function onMouseDown()
|
||||
this.isMouseDown = true
|
||||
this.icon:UpdateCoord()
|
||||
end
|
||||
|
||||
local function onMouseUp()
|
||||
this.isMouseDown = false
|
||||
this.icon:UpdateCoord()
|
||||
end
|
||||
|
||||
local fmod = function(x, y)
|
||||
return x - math.floor(x / y) * y
|
||||
end
|
||||
|
||||
do
|
||||
local deg, atan2 = math.deg, math.atan2
|
||||
local function onUpdate()
|
||||
local mx, my = Minimap:GetCenter()
|
||||
local px, py = GetCursorPosition()
|
||||
local scale = Minimap:GetEffectiveScale()
|
||||
px, py = px / scale, py / scale
|
||||
local pos = 225
|
||||
if this.db then
|
||||
pos = fmod(deg(atan2(py - my, px - mx)) , 360)
|
||||
this.db.minimapPos = pos
|
||||
else
|
||||
pos = fmod(deg(atan2(py - my, px - mx)) , 360)
|
||||
this.minimapPos = pos
|
||||
end
|
||||
updatePosition(this, pos)
|
||||
end
|
||||
|
||||
function onDragStart()
|
||||
this:LockHighlight()
|
||||
this.isMouseDown = true
|
||||
this.icon:UpdateCoord()
|
||||
this:SetScript("OnUpdate", onUpdate)
|
||||
isDraggingButton = true
|
||||
lib.tooltip:Hide()
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function onDragStop()
|
||||
this:SetScript("OnUpdate", nil)
|
||||
this.isMouseDown = false
|
||||
this.icon:UpdateCoord()
|
||||
this:UnlockHighlight()
|
||||
isDraggingButton = false
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
--button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local defaultCoords = {0, 1, 0, 1}
|
||||
local function updateCoord(self)
|
||||
local coords = self:GetParent().dataObject.iconCoords or defaultCoords
|
||||
local deltaX, deltaY = 0, 0
|
||||
if not self:GetParent().isMouseDown then
|
||||
deltaX = (coords[2] - coords[1]) * 0.05
|
||||
deltaY = (coords[4] - coords[3]) * 0.05
|
||||
end
|
||||
self:SetTexCoord(coords[1] + deltaX, coords[2] - deltaX, coords[3] + deltaY, coords[4] - deltaY)
|
||||
end
|
||||
|
||||
local function createButton(name, object, db)
|
||||
local button = CreateFrame("Button", "LibDBIcon10_"..name, Minimap)
|
||||
button.dataObject = object
|
||||
button.db = db
|
||||
button:SetFrameStrata("MEDIUM")
|
||||
button:SetWidth(31)
|
||||
button:SetHeight(31)
|
||||
button:SetFrameLevel(8)
|
||||
button:SetFrameStrata("HIGH")
|
||||
button:SetFrameLevel(7)
|
||||
button:EnableMouse(true)
|
||||
--button:EnableMouseWheel(true)
|
||||
button:SetMovable(true)
|
||||
button:RegisterForClicks("LeftButtonUp","RightButtonUp")
|
||||
button:RegisterForDrag("LeftButton")
|
||||
button:SetHighlightTexture("Interface\\Minimap\\UI-Minimap-ZoomButton-Highlight")
|
||||
local overlay = button:CreateTexture(nil, "OVERLAY")
|
||||
overlay:SetWidth(53)
|
||||
overlay:SetHeight(53)
|
||||
overlay:SetTexture("Interface\\Minimap\\MiniMap-TrackingBorder")
|
||||
overlay:SetPoint("TOPLEFT",0,0)
|
||||
local background = button:CreateTexture(nil, "BACKGROUND")
|
||||
background:SetWidth(20)
|
||||
background:SetHeight(20)
|
||||
background:SetTexture("Interface\\Minimap\\UI-Minimap-Background")
|
||||
background:SetPoint("TOPLEFT", 7, -5)
|
||||
local icon = button:CreateTexture(nil, "ARTWORK")
|
||||
icon:SetWidth(17)
|
||||
icon:SetHeight(17)
|
||||
icon:SetTexture(object.icon)
|
||||
icon:SetPoint("TOPLEFT", 7, -6)
|
||||
button.icon = icon
|
||||
icon.parent = button
|
||||
button.isMouseDown = false
|
||||
|
||||
local r, g, b = icon:GetVertexColor()
|
||||
icon:SetVertexColor(object.iconR or r, object.iconG or g, object.iconB or b)
|
||||
|
||||
icon.UpdateCoord = updateCoord
|
||||
icon:UpdateCoord()
|
||||
|
||||
button:SetScript("OnEnter", onEnter)
|
||||
button:SetScript("OnLeave", onLeave)
|
||||
button:SetScript("OnClick", onClick)
|
||||
if not db or not db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
button:SetScript("OnMouseDown", onMouseDown)
|
||||
button:SetScript("OnMouseUp", onMouseUp)
|
||||
--[[
|
||||
button.fadeOut = button:CreateAnimationGroup()
|
||||
local animOut = button.fadeOut:CreateAnimation("Alpha")
|
||||
animOut:SetOrder(1)
|
||||
animOut:SetDuration(0.2)
|
||||
animOut:SetFromAlpha(1)
|
||||
animOut:SetToAlpha(0)
|
||||
animOut:SetStartDelay(1)
|
||||
button.fadeOut:SetToFinalAlpha(true)
|
||||
]]
|
||||
lib.objects[name] = button
|
||||
|
||||
if lib.loggedIn then
|
||||
updatePosition(button, db and db.minimapPos)
|
||||
if not db or not db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
lib.callbacks:Fire("LibDBIcon_IconCreated", button, name) -- Fire 'Icon Created' callback
|
||||
end
|
||||
|
||||
-- We could use a metatable.__index on lib.objects, but then we'd create
|
||||
-- the icons when checking things like :IsRegistered, which is not necessary.
|
||||
local function check(name)
|
||||
if lib.notCreated[name] then
|
||||
createButton(name, lib.notCreated[name][1], lib.notCreated[name][2])
|
||||
lib.notCreated[name] = nil
|
||||
end
|
||||
end
|
||||
|
||||
-- Wait a bit with the initial positioning to let any GetMinimapShape addons
|
||||
-- load up.
|
||||
if not lib.loggedIn then
|
||||
local f = CreateFrame("Frame")
|
||||
f:SetScript("OnEvent", function()
|
||||
for _, button in next, lib.objects do
|
||||
updatePosition(button, button.db and button.db.minimapPos)
|
||||
if not button.db or not button.db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
end
|
||||
lib.loggedIn = true
|
||||
this:SetScript("OnEvent", nil)
|
||||
end)
|
||||
f:RegisterEvent("PLAYER_LOGIN")
|
||||
end
|
||||
|
||||
local function getDatabase(name)
|
||||
return lib.notCreated[name] and lib.notCreated[name][2] or lib.objects[name].db
|
||||
end
|
||||
|
||||
function lib:Register(name, object, db)
|
||||
if not object.icon then error("Can't register LDB objects without icons set!") end
|
||||
if lib.objects[name] or lib.notCreated[name] then error(DBICON10.. ": Object '".. name .."' is already registered.") end
|
||||
if not db or not db.hide then
|
||||
createButton(name, object, db)
|
||||
else
|
||||
lib.notCreated[name] = {object, db}
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Lock(name)
|
||||
if not lib:IsRegistered(name) then return end
|
||||
if lib.objects[name] then
|
||||
lib.objects[name]:SetScript("OnDragStart", nil)
|
||||
lib.objects[name]:SetScript("OnDragStop", nil)
|
||||
end
|
||||
local db = getDatabase(name)
|
||||
if db then
|
||||
db.lock = true
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Unlock(name)
|
||||
if not lib:IsRegistered(name) then return end
|
||||
if lib.objects[name] then
|
||||
lib.objects[name]:SetScript("OnDragStart", onDragStart)
|
||||
lib.objects[name]:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
local db = getDatabase(name)
|
||||
if db then
|
||||
db.lock = nil
|
||||
end
|
||||
end
|
||||
|
||||
function lib:Hide(name)
|
||||
if not lib.objects[name] then return end
|
||||
lib.objects[name]:Hide()
|
||||
end
|
||||
|
||||
function lib:Show(name)
|
||||
check(name)
|
||||
local button = lib.objects[name]
|
||||
if button then
|
||||
button:Show()
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
end
|
||||
end
|
||||
|
||||
function lib:IsRegistered(name)
|
||||
return (lib.objects[name] or lib.notCreated[name]) and true or false
|
||||
end
|
||||
|
||||
function lib:Refresh(name, db)
|
||||
check(name)
|
||||
local button = lib.objects[name]
|
||||
if db then
|
||||
button.db = db
|
||||
end
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
if not button.db or not button.db.hide then
|
||||
button:Show()
|
||||
else
|
||||
button:Hide()
|
||||
end
|
||||
if not button.db or not button.db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
else
|
||||
button:SetScript("OnDragStart", nil)
|
||||
button:SetScript("OnDragStop", nil)
|
||||
end
|
||||
end
|
||||
|
||||
function lib:GetMinimapButton(name)
|
||||
return lib.objects[name]
|
||||
end
|
||||
|
||||
do
|
||||
local function OnMinimapEnter()
|
||||
if isDraggingButton then return end
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
local function OnMinimapLeave()
|
||||
if isDraggingButton then return end
|
||||
for _, button in next, lib.objects do
|
||||
if button.showOnMouseover then
|
||||
-- button.fadeOut:Play()
|
||||
end
|
||||
end
|
||||
end
|
||||
lib:HookScript(Minimap, "OnEnter", OnMinimapEnter)
|
||||
lib:HookScript(Minimap, "OnLeave", OnMinimapLeave)
|
||||
|
||||
function lib:ShowOnEnter(name, value)
|
||||
local button = lib.objects[name]
|
||||
if button then
|
||||
if value then
|
||||
button.showOnMouseover = true
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(0)
|
||||
else
|
||||
button.showOnMouseover = false
|
||||
-- button.fadeOut:Stop()
|
||||
button:SetAlpha(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib:GetButtonList()
|
||||
local t = {}
|
||||
for name in next, lib.objects do
|
||||
tinsert(t, name)
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
function lib:SetButtonRadius(radius)
|
||||
if type(radius) == "number" then
|
||||
lib.radius = radius
|
||||
for _, button in next, lib.objects do
|
||||
updatePosition(button, button.db and button.db.minimapPos or button.minimapPos)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function lib:SetButtonToPosition(button, position)
|
||||
updatePosition(lib.objects[button] or button, position)
|
||||
end
|
||||
|
||||
-- Upgrade!
|
||||
for name, button in next, lib.objects do
|
||||
local db = getDatabase(name)
|
||||
if not db or not db.lock then
|
||||
button:SetScript("OnDragStart", onDragStart)
|
||||
button:SetScript("OnDragStop", onDragStop)
|
||||
end
|
||||
button:SetScript("OnEnter", onEnter)
|
||||
button:SetScript("OnLeave", onLeave)
|
||||
button:SetScript("OnClick", onClick)
|
||||
button:SetScript("OnMouseDown", onMouseDown)
|
||||
button:SetScript("OnMouseUp", onMouseUp)
|
||||
--[[
|
||||
if not button.fadeOut then -- Upgrade to 39
|
||||
button.fadeOut = button:CreateAnimationGroup()
|
||||
local animOut = button.fadeOut:CreateAnimation("Alpha")
|
||||
animOut:SetOrder(1)
|
||||
animOut:SetDuration(0.2)
|
||||
animOut:SetFromAlpha(1)
|
||||
animOut:SetToAlpha(0)
|
||||
animOut:SetStartDelay(1)
|
||||
button.fadeOut:SetToFinalAlpha(true)
|
||||
end
|
||||
]]
|
||||
end
|
||||
lib:SetButtonRadius(lib.radius) -- Upgrade to 40
|
||||
@@ -0,0 +1,33 @@
|
||||
-- LibStub is a simple versioning stub meant for use in Libraries. http://www.wowace.com/wiki/LibStub for more info
|
||||
-- LibStub is hereby placed in the Public Domain Credits: Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
||||
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2 -- NEVER MAKE THIS AN SVN REVISION! IT NEEDS TO BE USABLE IN ALL REPOS!
|
||||
local _G = getfenv()
|
||||
local strfind, strfmt = string.find, string.format
|
||||
local LibStub = _G[LIBSTUB_MAJOR]
|
||||
|
||||
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
||||
LibStub = LibStub or { libs = {}, minors = {} }
|
||||
_G[LIBSTUB_MAJOR] = LibStub
|
||||
LibStub.minor = LIBSTUB_MINOR
|
||||
|
||||
function LibStub:NewLibrary(major, minor)
|
||||
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
||||
local _,_,num = strfind(minor, "(%d+)")
|
||||
minor = assert(tonumber(num), "Minor version must either be a number or contain a number.")
|
||||
|
||||
local oldminor = self.minors[major]
|
||||
if oldminor and oldminor >= minor then return nil end
|
||||
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
||||
return self.libs[major], oldminor
|
||||
end
|
||||
|
||||
function LibStub:GetLibrary(major, silent)
|
||||
if not self.libs[major] and not silent then
|
||||
error(strfmt("Cannot find a library instance of %q.", tostring(major)), 2)
|
||||
end
|
||||
return self.libs[major], self.minors[major]
|
||||
end
|
||||
|
||||
function LibStub:IterateLibraries() return pairs(self.libs) end
|
||||
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
||||
end
|
||||
@@ -0,0 +1,33 @@
|
||||
tag v1.1.4
|
||||
ddb0519a000c69ddf3a28c3f9fe2e62bb3fd00c5
|
||||
Tekkub <tekkub@gmail.com>
|
||||
2008-11-06 22:03:04 -0700
|
||||
|
||||
Build 1.1.4
|
||||
|
||||
|
||||
--------------------
|
||||
|
||||
Tekkub:
|
||||
Add pairs and ipairs iters, since we can't use the normal iters on our dataobjs
|
||||
Simplify readme, all docs have been moved into GitHub wiki pages
|
||||
Documentation on how to use LDB data (for display addons)
|
||||
Add StatBlockCore forum link
|
||||
Add link to Fortress thread
|
||||
And rearrange the addon list a bit too
|
||||
Make field lists into nice pretty tables
|
||||
Add list of who is using LDB
|
||||
Always with the typos, I hate my fingers
|
||||
Add tooltiptext and OnTooltipShow to data addon spec
|
||||
Readme rejiggering
|
||||
Add in some documentation on how to push data into LDB
|
||||
Meh, fuck you textile
|
||||
Adding readme
|
||||
Pass current dataobj with attr change callbacks to avoid excessive calls to :GetDataObjectByName
|
||||
Tekkub Stoutwrithe:
|
||||
Make passed dataobj actually work
|
||||
I always forget the 'then'
|
||||
Minor memory optimization
|
||||
- Only hold upvalues to locals in the functions called frequently
|
||||
- Retain the metatable across future lib upgrades (the one in v1 will be lost)
|
||||
Allow caller to pass a pre-populated table to NewDataObject
|
||||
@@ -0,0 +1,90 @@
|
||||
|
||||
assert(LibStub, "LibDataBroker-1.1 requires LibStub")
|
||||
assert(LibStub:GetLibrary("CallbackHandler-1.0", true), "LibDataBroker-1.1 requires CallbackHandler-1.0")
|
||||
|
||||
local lib, oldminor = LibStub:NewLibrary("LibDataBroker-1.1", 4)
|
||||
if not lib then return end
|
||||
oldminor = oldminor or 0
|
||||
|
||||
|
||||
lib.callbacks = lib.callbacks or LibStub:GetLibrary("CallbackHandler-1.0"):New(lib)
|
||||
lib.attributestorage, lib.namestorage, lib.proxystorage = lib.attributestorage or {}, lib.namestorage or {}, lib.proxystorage or {}
|
||||
local attributestorage, namestorage, callbacks = lib.attributestorage, lib.namestorage, lib.callbacks
|
||||
|
||||
if oldminor < 2 then
|
||||
lib.domt = {
|
||||
__metatable = "access denied",
|
||||
__index = function(self, key) return attributestorage[self] and attributestorage[self][key] end,
|
||||
}
|
||||
end
|
||||
|
||||
if oldminor < 3 then
|
||||
lib.domt.__newindex = function(self, key, value)
|
||||
if not attributestorage[self] then attributestorage[self] = {} end
|
||||
if attributestorage[self][key] == value then return end
|
||||
attributestorage[self][key] = value
|
||||
local name = namestorage[self]
|
||||
if not name then return end
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged", name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name, name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged_"..name.."_"..key, name, key, value, self)
|
||||
callbacks:Fire("LibDataBroker_AttributeChanged__"..key, name, key, value, self)
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 2 then
|
||||
function lib:NewDataObject(name, dataobj)
|
||||
if self.proxystorage[name] then return end
|
||||
|
||||
if dataobj then
|
||||
assert(type(dataobj) == "table", "Invalid dataobj, must be nil or a table")
|
||||
self.attributestorage[dataobj] = {}
|
||||
for i,v in pairs(dataobj) do
|
||||
self.attributestorage[dataobj][i] = v
|
||||
dataobj[i] = nil
|
||||
end
|
||||
end
|
||||
dataobj = setmetatable(dataobj or {}, self.domt)
|
||||
self.proxystorage[name], self.namestorage[dataobj] = dataobj, name
|
||||
self.callbacks:Fire("LibDataBroker_DataObjectCreated", name, dataobj)
|
||||
return dataobj
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 1 then
|
||||
function lib:DataObjectIterator()
|
||||
return pairs(self.proxystorage)
|
||||
end
|
||||
|
||||
function lib:GetDataObjectByName(dataobjectname)
|
||||
return self.proxystorage[dataobjectname]
|
||||
end
|
||||
|
||||
function lib:GetNameByDataObject(dataobject)
|
||||
return self.namestorage[dataobject]
|
||||
end
|
||||
end
|
||||
|
||||
if oldminor < 4 then
|
||||
local next = pairs(attributestorage)
|
||||
function lib:pairs(dataobject_or_name)
|
||||
local t = type(dataobject_or_name)
|
||||
assert(t == "string" or t == "table", "Usage: ldb:pairs('dataobjectname') or ldb:pairs(dataobject)")
|
||||
|
||||
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
|
||||
assert(attributestorage[dataobj], "Data object not found")
|
||||
|
||||
return next, attributestorage[dataobj], nil
|
||||
end
|
||||
|
||||
local ipairs_iter = ipairs(attributestorage)
|
||||
function lib:ipairs(dataobject_or_name)
|
||||
local t = type(dataobject_or_name)
|
||||
assert(t == "string" or t == "table", "Usage: ldb:ipairs('dataobjectname') or ldb:ipairs(dataobject)")
|
||||
|
||||
local dataobj = self.proxystorage[dataobject_or_name] or dataobject_or_name
|
||||
assert(attributestorage[dataobj], "Data object not found")
|
||||
|
||||
return ipairs_iter, attributestorage[dataobj], 0
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,13 @@
|
||||
LibDataBroker is a small WoW addon library designed to provide a "MVC":http://en.wikipedia.org/wiki/Model-view-controller interface for use in various addons.
|
||||
LDB's primary goal is to "detach" plugins for TitanPanel and FuBar from the display addon.
|
||||
Plugins can provide data into a simple table, and display addons can receive callbacks to refresh their display of this data.
|
||||
LDB also provides a place for addons to register "quicklaunch" functions, removing the need for authors to embed many large libraries to create minimap buttons.
|
||||
Users who do not wish to be "plagued" by these buttons simply do not install an addon to render them.
|
||||
|
||||
Due to it's simple generic design, LDB can be used for any design where you wish to have an addon notified of changes to a table.
|
||||
|
||||
h2. Links
|
||||
|
||||
* "API documentation":http://github.com/tekkub/libdatabroker-1-1/wikis/api
|
||||
* "Data specifications":http://github.com/tekkub/libdatabroker-1-1/wikis/data-specifications
|
||||
* "Addons using LDB":http://github.com/tekkub/libdatabroker-1-1/wikis/addons-using-ldb
|
||||
Reference in New Issue
Block a user