3509 lines
103 KiB
Lua
3509 lines
103 KiB
Lua
--[[
|
|
Name: Dewdrop-2.0
|
|
Revision: $Rev: 326 $
|
|
Author(s): ckknight (ckknight@gmail.com)
|
|
Website: http://ckknight.wowinterface.com/
|
|
Documentation: http://wiki.wowace.com/index.php/Dewdrop-2.0
|
|
SVN: http://svn.wowace.com/root/trunk/DewdropLib/Dewdrop-2.0
|
|
Description: A library to provide a clean dropdown menu interface.
|
|
Dependencies: AceLibrary
|
|
License: LGPL v2.1
|
|
]]
|
|
|
|
local MAJOR_VERSION = "Dewdrop-2.0"
|
|
local MINOR_VERSION = tonumber(strmatch("$Revision: 326 $", "%d+")) + 90000
|
|
|
|
if not AceLibrary then error(MAJOR_VERSION .. " requires AceLibrary") end
|
|
if not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION) then return end
|
|
|
|
local Dewdrop = {}
|
|
|
|
local SharedMedia
|
|
|
|
local CLOSE = "Close"
|
|
local CLOSE_DESC = "Close the menu."
|
|
local VALIDATION_ERROR = "Validation error."
|
|
local USAGE_TOOLTIP = "Usage: %s."
|
|
local RANGE_TOOLTIP = "Note that you can scroll your mouse wheel while over the slider to step by one."
|
|
local RESET_KEYBINDING_DESC = "Hit escape to clear the keybinding."
|
|
local KEY_BUTTON1 = "Left Mouse"
|
|
local KEY_BUTTON2 = "Right Mouse"
|
|
local DISABLED = "Disabled"
|
|
local DEFAULT_CONFIRM_MESSAGE = "Are you sure you want to perform `%s'?"
|
|
|
|
if GetLocale() == "deDE" then
|
|
CLOSE = "Schlie\195\159en"
|
|
CLOSE_DESC = "Men\195\188 schlie\195\159en."
|
|
VALIDATION_ERROR = "Validierungsfehler."
|
|
USAGE_TOOLTIP = "Benutzung: %s."
|
|
RANGE_TOOLTIP = "Beachte das du mit dem Mausrad scrollen kannst solange du mit dem Mauszeiger \195\188ber dem Schieberegler bist, um feinere Spr\195\188nge zu machen."
|
|
RESET_KEYBINDING_DESC = "Escape dr\195\188cken, um die Tastenbelegung zu l\195\182schen."
|
|
KEY_BUTTON1 = "Linke Maustaste"
|
|
KEY_BUTTON2 = "Rechte Maustaste"
|
|
DISABLED = "Deaktiviert"
|
|
DEFAULT_CONFIRM_MESSAGE = "Bist du sicher das du `%s' machen willst?"
|
|
elseif GetLocale() == "koKR" then
|
|
CLOSE = "닫기"
|
|
CLOSE_DESC = "메뉴를 닫습니다."
|
|
VALIDATION_ERROR = "오류 확인."
|
|
USAGE_TOOLTIP = "사용법: %s."
|
|
RANGE_TOOLTIP = "알림 : 슬라이더 위에서 마우스 휠을 사용하면 한단계씩 조절할 수 있습니다."
|
|
RESET_KEYBINDING_DESC = "단축키를 해제하려면 ESC키를 누르세요."
|
|
KEY_BUTTON1 = "왼쪽 마우스"
|
|
KEY_BUTTON2 = "오른쪽 마우스"
|
|
DISABLED = "비활성화됨"
|
|
DEFAULT_CONFIRM_MESSAGE = "정말로 `%s' 실행을 하시겠습니까 ?"
|
|
elseif GetLocale() == "frFR" then
|
|
CLOSE = "Fermer"
|
|
CLOSE_DESC = "Ferme le menu."
|
|
VALIDATION_ERROR = "Erreur de validation."
|
|
USAGE_TOOLTIP = "Utilisation : %s."
|
|
RANGE_TOOLTIP = "Vous pouvez aussi utiliser la molette de la souris pour pour modifier progressivement."
|
|
RESET_KEYBINDING_DESC = "Appuyez sur la touche Echappement pour effacer le raccourci."
|
|
KEY_BUTTON1 = "Clic gauche"
|
|
KEY_BUTTON2 = "Clic droit"
|
|
DISABLED = "D\195\169sactiv\195\169"
|
|
DEFAULT_CONFIRM_MESSAGE = "\195\138tes-vous s\195\187r de vouloir effectuer '%s' ?"
|
|
elseif GetLocale() == "esES" then
|
|
CLOSE = "Cerrar"
|
|
CLOSE_DESC = "Cierra el menú."
|
|
VALIDATION_ERROR = "Error de validación."
|
|
USAGE_TOOLTIP = "Uso: %s."
|
|
RANGE_TOOLTIP = "Puedes desplazarte verticalmente con la rueda del ratón sobre el desplazador."
|
|
RESET_KEYBINDING_DESC = "Pulsa Escape para borrar la asignación de tecla."
|
|
KEY_BUTTON1 = "Clic Izquierdo"
|
|
KEY_BUTTON2 = "Clic Derecho"
|
|
DISABLED = "Desactivado"
|
|
DEFAULT_CONFIRM_MESSAGE = "¿Estás seguro de querer realizar `%s'?"
|
|
elseif GetLocale() == "zhTW" then
|
|
CLOSE = "關閉"
|
|
CLOSE_DESC = "關閉選單。"
|
|
VALIDATION_ERROR = "驗證錯誤。"
|
|
USAGE_TOOLTIP = "用法: %s。"
|
|
RANGE_TOOLTIP = "你可以在捲動條上使用滑鼠滾輪來捲動。"
|
|
RESET_KEYBINDING_DESC = "按Esc鍵清除快捷鍵。"
|
|
KEY_BUTTON1 = "滑鼠左鍵"
|
|
KEY_BUTTON2 = "滑鼠右鍵"
|
|
DISABLED = "停用"
|
|
DEFAULT_CONFIRM_MESSAGE = "是否執行「%s」?"
|
|
elseif GetLocale() == "zhCN" then
|
|
CLOSE = "关闭"
|
|
CLOSE_DESC = "关闭菜单"
|
|
VALIDATION_ERROR = "验证错误."
|
|
USAGE_TOOLTIP = "用法: %s."
|
|
RANGE_TOOLTIP = "你可以在滚动条上使用鼠标滚轮来翻页."
|
|
RESET_KEYBINDING_DESC = "按ESC键清除按键绑定"
|
|
KEY_BUTTON1 = "鼠标左键"
|
|
KEY_BUTTON2 = "鼠标右键"
|
|
DISABLED = "禁用"
|
|
DEFAULT_CONFIRM_MESSAGE = "是否执行'%s'?"
|
|
elseif GetLocale() == "ruRU" then
|
|
CLOSE = "Закрыть"
|
|
CLOSE_DESC = "Закрыть меню."
|
|
VALIDATION_ERROR = "Ошибка проверки данных."
|
|
USAGE_TOOLTIP = "Используйте: %s."
|
|
RANGE_TOOLTIP = "Используйте колесо мыши для прокрутки ползунка."
|
|
RESET_KEYBINDING_DESC = "Нажмите клавишу Escape для очистки клавиши."
|
|
KEY_BUTTON1 = "ЛКМ"
|
|
KEY_BUTTON2 = "ПКМ"
|
|
DISABLED = "Отключено"
|
|
DEFAULT_CONFIRM_MESSAGE = "Вы уверены что вы хотите выполнять `%s'?"
|
|
end
|
|
|
|
Dewdrop.KEY_BUTTON1 = KEY_BUTTON1
|
|
Dewdrop.KEY_BUTTON2 = KEY_BUTTON2
|
|
|
|
local function new(...)
|
|
local t = {}
|
|
for i = 1, select('#', ...), 2 do
|
|
local k = select(i, ...)
|
|
if k then
|
|
t[k] = select(i+1, ...)
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
|
|
local tmp
|
|
do
|
|
local t = {}
|
|
function tmp(...)
|
|
for k in pairs(t) do
|
|
t[k] = nil
|
|
end
|
|
for i = 1, select('#', ...), 2 do
|
|
local k = select(i, ...)
|
|
if k then
|
|
t[k] = select(i+1, ...)
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
end
|
|
local tmp2
|
|
do
|
|
local t = {}
|
|
function tmp2(...)
|
|
for k in pairs(t) do
|
|
t[k] = nil
|
|
end
|
|
for i = 1, select('#', ...), 2 do
|
|
local k = select(i, ...)
|
|
if k then
|
|
t[k] = select(i+1, ...)
|
|
else
|
|
break
|
|
end
|
|
end
|
|
return t
|
|
end
|
|
end
|
|
local levels
|
|
local buttons
|
|
|
|
|
|
-- Secure frame handling:
|
|
-- Rather than using secure buttons in the menu (has problems), we have one
|
|
-- master secureframe that we pop onto menu items on mouseover. This requires
|
|
-- some dark magic with OnLeave etc, but it's not too bad.
|
|
|
|
local eventFrame = CreateFrame("Button")
|
|
local secureFrame
|
|
local createSecureFrame
|
|
|
|
local function secureFrame_Show(self)
|
|
local owner = self.owner
|
|
|
|
if self.secure then -- Leftovers from previos owner, clean up! ("Shouldn't" happen but does..)
|
|
for k,v in pairs(self.secure) do
|
|
self:SetAttribute(k, nil)
|
|
end
|
|
end
|
|
self.secure = owner.secure; -- Grab hold of new secure data
|
|
|
|
local scale = owner:GetEffectiveScale()
|
|
|
|
self:SetPoint("TOPLEFT", nil, "BOTTOMLEFT", owner:GetLeft() * scale, owner:GetTop() * scale)
|
|
self:SetPoint("BOTTOMRIGHT", nil, "BOTTOMLEFT", owner:GetRight() * scale, owner:GetBottom() * scale)
|
|
self:EnableMouse(true)
|
|
for k,v in pairs(self.secure) do
|
|
self:SetAttribute(k, v)
|
|
end
|
|
|
|
secureFrame:SetFrameStrata(owner:GetFrameStrata())
|
|
secureFrame:SetFrameLevel(owner:GetFrameLevel()+1)
|
|
|
|
self:Show()
|
|
end
|
|
|
|
local function secureFrame_Hide(self)
|
|
self:Hide()
|
|
if self.secure then
|
|
for k,v in pairs(self.secure) do
|
|
self:SetAttribute(k, nil)
|
|
end
|
|
end
|
|
self.secure = nil
|
|
end
|
|
|
|
eventFrame:SetScript("OnEvent",
|
|
function(this, event)
|
|
if event=="PLAYER_REGEN_ENABLED" then
|
|
createSecureFrame()
|
|
secureFrame.combat = false
|
|
if not secureFrame:IsShown() and secureFrame.owner then
|
|
secureFrame_Show(secureFrame)
|
|
end
|
|
elseif event=="PLAYER_REGEN_DISABLED" and secureFrame then
|
|
secureFrame.combat = true
|
|
if secureFrame:IsShown() then
|
|
secureFrame_Hide(secureFrame)
|
|
end
|
|
end
|
|
end
|
|
)
|
|
eventFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
|
|
eventFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
|
|
|
|
function createSecureFrame()
|
|
if secureFrame or InCombatLockdown() then return end
|
|
secureFrame = CreateFrame("Button", nil, nil, "SecureActionButtonTemplate")
|
|
secureFrame:Hide()
|
|
|
|
secureFrame:SetScript("OnLeave",
|
|
function(this)
|
|
local owner=this.owner
|
|
this:Deactivate()
|
|
owner:GetScript("OnLeave")(owner)
|
|
end
|
|
)
|
|
|
|
secureFrame:HookScript("OnClick",
|
|
function(this)
|
|
local realthis = this
|
|
this = this.owner
|
|
this:GetScript("OnClick")(this)
|
|
end
|
|
)
|
|
|
|
function secureFrame:IsOwnedBy(frame)
|
|
return self.owner == frame
|
|
end
|
|
|
|
function secureFrame:Activate(owner)
|
|
if self.owner then -- "Shouldn't" happen but apparently it does and I cba to troubleshoot...
|
|
if not self.combat then
|
|
secureFrame_Hide(self)
|
|
end
|
|
end
|
|
self.owner = owner
|
|
if not self.combat then
|
|
secureFrame_Show(self)
|
|
end
|
|
end
|
|
|
|
function secureFrame:Deactivate()
|
|
if not self.combat then
|
|
secureFrame_Hide(self)
|
|
end
|
|
self.owner = nil
|
|
end
|
|
|
|
end
|
|
createSecureFrame()
|
|
-- END secure frame utilities
|
|
|
|
|
|
-- Underline on mouseover - use a single global underline that we move around, no point in creating lots of copies
|
|
local underlineFrame = CreateFrame("Frame", nil)
|
|
underlineFrame.tx = underlineFrame:CreateTexture()
|
|
underlineFrame.tx:SetTexture(1,1,0.5,0.75)
|
|
underlineFrame:SetScript("OnHide", function(this) this:Hide(); end)
|
|
underlineFrame:SetScript("OnShow", function(this) -- change sizing on the fly to catch runtime uiscale changes
|
|
underlineFrame.tx:SetPoint("TOPLEFT", -1, -2/this:GetEffectiveScale())
|
|
underlineFrame.tx:SetPoint("RIGHT", 1,0)
|
|
underlineFrame.tx:SetHeight(0.6 / this:GetEffectiveScale());
|
|
end)
|
|
underlineFrame:SetHeight(1)
|
|
|
|
-- END underline on mouseover
|
|
|
|
|
|
local function GetScaledCursorPosition()
|
|
local x, y = GetCursorPosition()
|
|
local scale = UIParent:GetEffectiveScale()
|
|
return x / scale, y / scale
|
|
end
|
|
|
|
local function StartCounting(self, level)
|
|
for i = level, 1, -1 do
|
|
if levels[i] then
|
|
levels[i].count = 3
|
|
end
|
|
end
|
|
end
|
|
|
|
local function StopCounting(self, level)
|
|
for i = level, 1, -1 do
|
|
if levels[i] then
|
|
levels[i].count = nil
|
|
end
|
|
end
|
|
end
|
|
|
|
local function OnUpdate(self, elapsed)
|
|
for _,level in ipairs(levels) do
|
|
local count = level.count
|
|
if count then
|
|
count = count - elapsed
|
|
if count < 0 then
|
|
level.count = nil
|
|
self:Close(level.num)
|
|
else
|
|
level.count = count
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function CheckDualMonitor(self, frame)
|
|
local ratio = GetScreenWidth() / GetScreenHeight()
|
|
if ratio >= 2.4 and frame:GetRight() > GetScreenWidth() / 2 and frame:GetLeft() < GetScreenWidth() / 2 then
|
|
local offsetx
|
|
if GetCursorPosition() / GetScreenHeight() * 768 < GetScreenWidth() / 2 then
|
|
offsetx = GetScreenWidth() / 2 - frame:GetRight()
|
|
else
|
|
offsetx = GetScreenWidth() / 2 - frame:GetLeft()
|
|
end
|
|
local point, parent, relativePoint, x, y = frame:GetPoint(1)
|
|
frame:SetPoint(point, parent, relativePoint, (x or 0) + offsetx, y or 0)
|
|
end
|
|
end
|
|
|
|
local function CheckSize(self, level)
|
|
if not level.buttons then
|
|
return
|
|
end
|
|
local height = 20
|
|
for _, button in ipairs(level.buttons) do
|
|
height = height + button:GetHeight()
|
|
end
|
|
level:SetHeight(height)
|
|
local width = 160
|
|
for _, button in ipairs(level.buttons) do
|
|
local extra = 1
|
|
if button.hasArrow or button.hasColorSwatch then
|
|
extra = extra + 16
|
|
end
|
|
if not button.notCheckable then
|
|
extra = extra + 24
|
|
end
|
|
button.text:SetFont(STANDARD_TEXT_FONT, button.textHeight)
|
|
if button.text:GetStringWidth() + extra > width then
|
|
width = button.text:GetStringWidth() + extra
|
|
end
|
|
end
|
|
level:SetWidth(width + 20)
|
|
if level:GetLeft() and level:GetRight() and level:GetTop() and level:GetBottom() and (level:GetLeft() < 0 or level:GetRight() > GetScreenWidth() or level:GetTop() > GetScreenHeight() or level:GetBottom() < 0) then
|
|
level:ClearAllPoints()
|
|
local parent = level.parent or level:GetParent()
|
|
if type(parent) ~= "table" then
|
|
parent = UIParent
|
|
end
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
|
|
else
|
|
level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
end
|
|
local dirty = false
|
|
if not level:GetRight() then
|
|
self:Close()
|
|
return
|
|
end
|
|
if level:GetRight() > GetScreenWidth() and level.lastDirection == "RIGHT" then
|
|
level.lastDirection = "LEFT"
|
|
dirty = true
|
|
elseif level:GetLeft() < 0 and level.lastDirection == "LEFT" then
|
|
level.lastDirection = "RIGHT"
|
|
dirty = true
|
|
end
|
|
if level:GetTop() > GetScreenHeight() and level.lastVDirection == "UP" then
|
|
level.lastVDirection = "DOWN"
|
|
dirty = true
|
|
elseif level:GetBottom() < 0 and level.lastVDirection == "DOWN" then
|
|
level.lastVDirection = "UP"
|
|
dirty = true
|
|
end
|
|
if dirty then
|
|
level:ClearAllPoints()
|
|
local parent = level.parent or level:GetParent()
|
|
if type(parent) ~= "table" then
|
|
parent = UIParent
|
|
end
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
level:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
level:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
level:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
|
|
else
|
|
level:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
end
|
|
if level:GetTop() > GetScreenHeight() then
|
|
local top = level:GetTop()
|
|
local point, parent, relativePoint, x, y = level:GetPoint(1)
|
|
level:ClearAllPoints()
|
|
level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) + GetScreenHeight() - top)
|
|
elseif level:GetBottom() < 0 then
|
|
local bottom = level:GetBottom()
|
|
local point, parent, relativePoint, x, y = level:GetPoint(1)
|
|
level:ClearAllPoints()
|
|
level:SetPoint(point, parent, relativePoint, x or 0, (y or 0) - bottom)
|
|
end
|
|
CheckDualMonitor(self, level)
|
|
if mod(level.num, 5) == 0 then
|
|
local left, bottom = level:GetLeft(), level:GetBottom()
|
|
level:ClearAllPoints()
|
|
level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
|
|
end
|
|
end
|
|
|
|
local Open
|
|
local OpenSlider
|
|
local OpenEditBox
|
|
local Refresh
|
|
local Clear
|
|
local function ReleaseButton(self, level, index)
|
|
if not level.buttons then
|
|
return
|
|
end
|
|
if not level.buttons[index] then
|
|
return
|
|
end
|
|
local button = level.buttons[index]
|
|
button:Hide()
|
|
if button.highlight then
|
|
button.highlight:Hide()
|
|
end
|
|
-- button.arrow:SetVertexColor(1, 1, 1)
|
|
-- button.arrow:SetHeight(16)
|
|
-- button.arrow:SetWidth(16)
|
|
table.remove(level.buttons, index)
|
|
table.insert(buttons, button)
|
|
for k in pairs(button) do
|
|
if k ~= 0 and k ~= "text" and k ~= "check" and k ~= "arrow" and k ~= "colorSwatch" and k ~= "highlight" and k ~= "radioHighlight" then
|
|
button[k] = nil
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local function Scroll(self, level, down)
|
|
if down then
|
|
if level:GetBottom() < 0 then
|
|
local point, parent, relativePoint, x, y = level:GetPoint(1)
|
|
level:SetPoint(point, parent, relativePoint, x, y + 50)
|
|
if level:GetBottom() > 0 then
|
|
level:SetPoint(point, parent, relativePoint, x, y + 50 - level:GetBottom())
|
|
end
|
|
end
|
|
else
|
|
if level:GetTop() > GetScreenHeight() then
|
|
local point, parent, relativePoint, x, y = level:GetPoint(1)
|
|
level:SetPoint(point, parent, relativePoint, x, y - 50)
|
|
if level:GetTop() < GetScreenHeight() then
|
|
level:SetPoint(point, parent, relativePoint, x, y - 50 + GetScreenHeight() - level:GetTop())
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function getArgs(t, str, num, ...)
|
|
local x = t[str .. num]
|
|
if x == nil then
|
|
return ...
|
|
else
|
|
return x, getArgs(t, str, num + 1, ...)
|
|
end
|
|
end
|
|
|
|
local sliderFrame
|
|
local editBoxFrame
|
|
|
|
local normalFont
|
|
local lastSetFont
|
|
local justSetFont = false
|
|
local regionTmp = {}
|
|
local function fillRegionTmp(...)
|
|
for i = 1, select('#', ...) do
|
|
regionTmp[i] = select(i, ...)
|
|
end
|
|
end
|
|
|
|
local function showGameTooltip(this)
|
|
if this.tooltipTitle or this.tooltipText then
|
|
GameTooltip_SetDefaultAnchor(GameTooltip, this)
|
|
local disabled = not this.isTitle and this.disabled
|
|
local font
|
|
if this.tooltipTitle then
|
|
if SharedMedia and SharedMedia:IsValid("font", this.tooltipTitle) then
|
|
font = SharedMedia:Fetch("font", this.tooltipTitle)
|
|
end
|
|
if disabled then
|
|
GameTooltip:SetText(this.tooltipTitle, 0.5, 0.5, 0.5, 1)
|
|
else
|
|
GameTooltip:SetText(this.tooltipTitle, 1, 1, 1, 1)
|
|
end
|
|
if this.tooltipText then
|
|
if not font and SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
|
|
font = SharedMedia:Fetch("font", this.tooltipText)
|
|
end
|
|
if disabled then
|
|
GameTooltip:AddLine(this.tooltipText, (NORMAL_FONT_COLOR.r + 0.5) / 2, (NORMAL_FONT_COLOR.g + 0.5) / 2, (NORMAL_FONT_COLOR.b + 0.5) / 2, 1)
|
|
else
|
|
GameTooltip:AddLine(this.tooltipText, NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b, 1)
|
|
end
|
|
end
|
|
else
|
|
if SharedMedia and SharedMedia:IsValid("font", this.tooltipText) then
|
|
font = SharedMedia:Fetch("font", this.tooltipText)
|
|
end
|
|
if disabled then
|
|
GameTooltip:SetText(this.tooltipText, 0.5, 0.5, 0.5, 1)
|
|
else
|
|
GameTooltip:SetText(this.tooltipText, 1, 1, 1, 1)
|
|
end
|
|
end
|
|
if font then
|
|
fillRegionTmp(GameTooltip:GetRegions())
|
|
lastSetFont = font
|
|
justSetFont = true
|
|
for i,v in ipairs(regionTmp) do
|
|
if v.SetFont then
|
|
local norm,size,outline = v:GetFont()
|
|
v:SetFont(font, size, outline)
|
|
if not normalFont then
|
|
normalFont = norm
|
|
end
|
|
end
|
|
regionTmp[i] = nil
|
|
end
|
|
elseif not normalFont then
|
|
fillRegionTmp(GameTooltip:GetRegions())
|
|
for i,v in ipairs(regionTmp) do
|
|
if v.GetFont and not normalFont then
|
|
normalFont = v:GetFont()
|
|
end
|
|
regionTmp[i] = nil
|
|
end
|
|
end
|
|
GameTooltip:Show()
|
|
end
|
|
if this.tooltipFunc then
|
|
GameTooltip:SetOwner(this, "ANCHOR_NONE")
|
|
GameTooltip:SetPoint("TOPLEFT", this, "TOPRIGHT", 5, 0)
|
|
this.tooltipFunc(getArgs(this, 'tooltipArg', 1))
|
|
GameTooltip:Show()
|
|
end
|
|
end
|
|
|
|
local tmpt = setmetatable({}, {mode='v'})
|
|
local numButtons = 0
|
|
local function AcquireButton(self, level)
|
|
if not levels[level] then
|
|
return
|
|
end
|
|
level = levels[level]
|
|
if not level.buttons then
|
|
level.buttons = {}
|
|
end
|
|
local button
|
|
if #buttons == 0 then
|
|
numButtons = numButtons + 1
|
|
button = CreateFrame("Button", "Dewdrop20Button" .. numButtons, nil)
|
|
button:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
button:SetHeight(16)
|
|
local highlight = button:CreateTexture(nil, "BACKGROUND")
|
|
highlight:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
button.highlight = highlight
|
|
highlight:SetBlendMode("ADD")
|
|
highlight:SetAllPoints(button)
|
|
highlight:Hide()
|
|
local check = button:CreateTexture(nil, "ARTWORK")
|
|
button.check = check
|
|
check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
|
|
check:SetPoint("CENTER", button, "LEFT", 12, 0)
|
|
check:SetWidth(24)
|
|
check:SetHeight(24)
|
|
local radioHighlight = button:CreateTexture(nil, "ARTWORK")
|
|
button.radioHighlight = radioHighlight
|
|
radioHighlight:SetTexture("Interface\\Buttons\\UI-RadioButton")
|
|
radioHighlight:SetAllPoints(check)
|
|
radioHighlight:SetBlendMode("ADD")
|
|
radioHighlight:SetTexCoord(0.5, 0.75, 0, 1)
|
|
radioHighlight:Hide()
|
|
button:SetScript("OnEnter", function(this)
|
|
if (sliderFrame and sliderFrame:IsShown() and sliderFrame.mouseDown and sliderFrame.level == this.level.num + 1) or (editBoxFrame and editBoxFrame:IsShown() and editBoxFrame.mouseDown and editBoxFrame.level == this.level.num + 1) then
|
|
for i = 1, this.level.num do
|
|
Refresh(self, levels[i])
|
|
end
|
|
return
|
|
end
|
|
self:Close(this.level.num + 1)
|
|
if not this.disabled then
|
|
if this.secure then
|
|
secureFrame:Activate(this)
|
|
elseif this.hasSlider then
|
|
OpenSlider(self, this)
|
|
elseif this.hasEditBox then
|
|
OpenEditBox(self, this)
|
|
elseif this.hasArrow then
|
|
Open(self, this, nil, this.level.num + 1, this.value)
|
|
end
|
|
end
|
|
if not this.level then -- button reclaimed
|
|
return
|
|
end
|
|
StopCounting(self, this.level.num + 1)
|
|
if not this.disabled then
|
|
highlight:Show()
|
|
if this.isRadio then
|
|
button.radioHighlight:Show()
|
|
end
|
|
if this.mouseoverUnderline then
|
|
underlineFrame:SetParent(this)
|
|
underlineFrame:SetPoint("BOTTOMLEFT",this.text,0,0)
|
|
underlineFrame:SetWidth(this.text:GetWidth())
|
|
underlineFrame:Show()
|
|
end
|
|
end
|
|
showGameTooltip(this)
|
|
end)
|
|
button:SetScript("OnHide", function(this)
|
|
if this.secure and secureFrame:IsOwnedBy(this) then
|
|
secureFrame:Deactivate()
|
|
end
|
|
end)
|
|
button:SetScript("OnLeave", function(this)
|
|
if this.secure and secureFrame:IsShown() then
|
|
return; -- it's ok, we didn't actually mouse out of the button, only onto the secure frame on top of it
|
|
end
|
|
underlineFrame:Hide()
|
|
if not this.selected then
|
|
highlight:Hide()
|
|
end
|
|
button.radioHighlight:Hide()
|
|
if this.level then
|
|
StartCounting(self, this.level.num)
|
|
end
|
|
GameTooltip:Hide()
|
|
end)
|
|
local first = true
|
|
button:SetScript("OnClick", function(this)
|
|
if not this.disabled then
|
|
if this.hasColorSwatch then
|
|
local func = button.colorFunc
|
|
local hasOpacity = this.hasOpacity
|
|
local this = this
|
|
for k in pairs(tmpt) do
|
|
tmpt[k] = nil
|
|
end
|
|
for i = 1, 1000 do
|
|
local x = this['colorArg'..i]
|
|
if x == nil then
|
|
break
|
|
else
|
|
tmpt[i] = x
|
|
end
|
|
end
|
|
ColorPickerFrame.func = function()
|
|
if func then
|
|
local r,g,b = ColorPickerFrame:GetColorRGB()
|
|
local a = hasOpacity and 1 - OpacitySliderFrame:GetValue() or nil
|
|
local n = #tmpt
|
|
tmpt[n+1] = r
|
|
tmpt[n+2] = g
|
|
tmpt[n+3] = b
|
|
tmpt[n+4] = a
|
|
func(unpack(tmpt))
|
|
tmpt[n+1] = nil
|
|
tmpt[n+2] = nil
|
|
tmpt[n+3] = nil
|
|
tmpt[n+4] = nil
|
|
end
|
|
end
|
|
ColorPickerFrame.hasOpacity = this.hasOpacity
|
|
ColorPickerFrame.opacityFunc = ColorPickerFrame.func
|
|
ColorPickerFrame.opacity = 1 - this.opacity
|
|
ColorPickerFrame:SetColorRGB(this.r, this.g, this.b)
|
|
local r, g, b, a = this.r, this.g, this.b, this.opacity
|
|
ColorPickerFrame.cancelFunc = function()
|
|
if func then
|
|
local n = #tmpt
|
|
tmpt[n+1] = r
|
|
tmpt[n+2] = g
|
|
tmpt[n+3] = b
|
|
tmpt[n+4] = a
|
|
func(unpack(tmpt))
|
|
for i = 1, n+4 do
|
|
tmpt[i] = nil
|
|
end
|
|
end
|
|
end
|
|
self:Close(1)
|
|
ShowUIPanel(ColorPickerFrame)
|
|
elseif this.func then
|
|
local level = this.level
|
|
if type(this.func) == "string" then
|
|
if type(this.arg1[this.func]) ~= "function" then
|
|
self:error("Cannot call method %q", this.func)
|
|
end
|
|
this.arg1[this.func](this.arg1, getArgs(this, 'arg', 2))
|
|
else
|
|
this.func(getArgs(this, 'arg', 1))
|
|
end
|
|
if this.closeWhenClicked then
|
|
self:Close()
|
|
elseif level:IsShown() then
|
|
for i = 1, level.num do
|
|
Refresh(self, levels[i])
|
|
end
|
|
local value = levels[level.num].value
|
|
for i = level.num-1, 1, -1 do
|
|
local level = levels[i]
|
|
local good = false
|
|
for _,button in ipairs(level.buttons) do
|
|
if button.value == value then
|
|
good = true
|
|
break
|
|
end
|
|
end
|
|
if not good then
|
|
Dewdrop:Close(i+1)
|
|
end
|
|
value = levels[i].value
|
|
end
|
|
end
|
|
elseif this.closeWhenClicked then
|
|
self:Close()
|
|
end
|
|
end
|
|
end)
|
|
local text = button:CreateFontString(nil, "ARTWORK")
|
|
button.text = text
|
|
text:SetFontObject(GameFontHighlightSmall)
|
|
button.text:SetFont(STANDARD_TEXT_FONT, UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT)
|
|
button:SetScript("OnMouseDown", function(this)
|
|
if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
|
|
text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 1 or 25, -1)
|
|
end
|
|
end)
|
|
button:SetScript("OnMouseUp", function(this)
|
|
if not this.disabled and (this.func or this.colorFunc or this.closeWhenClicked) then
|
|
text:SetPoint("LEFT", button, "LEFT", this.notCheckable and 0 or 24, 0)
|
|
end
|
|
end)
|
|
local arrow = button:CreateTexture(nil, "ARTWORK")
|
|
button.arrow = arrow
|
|
arrow:SetPoint("LEFT", button, "RIGHT", -16, 0)
|
|
arrow:SetWidth(16)
|
|
arrow:SetHeight(16)
|
|
arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
|
|
local colorSwatch = button:CreateTexture(nil, "ARTWORK")
|
|
button.colorSwatch = colorSwatch
|
|
colorSwatch:SetWidth(20)
|
|
colorSwatch:SetHeight(20)
|
|
colorSwatch:SetTexture("Interface\\ChatFrame\\ChatFrameColorSwatch")
|
|
local texture = button:CreateTexture(nil, "OVERLAY")
|
|
colorSwatch.texture = texture
|
|
texture:SetTexture("Interface\\Buttons\\WHITE8X8")
|
|
texture:SetWidth(11.5)
|
|
texture:SetHeight(11.5)
|
|
texture:Show()
|
|
texture:SetPoint("CENTER", colorSwatch, "CENTER")
|
|
colorSwatch:SetPoint("RIGHT", button, "RIGHT", 0, 0)
|
|
else
|
|
button = table.remove(buttons)
|
|
end
|
|
button:ClearAllPoints()
|
|
button:SetParent(level)
|
|
button:SetFrameStrata(level:GetFrameStrata())
|
|
button:SetFrameLevel(level:GetFrameLevel() + 1)
|
|
button:SetPoint("LEFT", level, "LEFT", 10, 0)
|
|
button:SetPoint("RIGHT", level, "RIGHT", -10, 0)
|
|
if #level.buttons == 0 then
|
|
button:SetPoint("TOP", level, "TOP", 0, -10)
|
|
else
|
|
button:SetPoint("TOP", level.buttons[#level.buttons], "BOTTOM", 0, 0)
|
|
end
|
|
button.text:SetPoint("LEFT", button, "LEFT", 24, 0)
|
|
button:Show()
|
|
button.level = level
|
|
table.insert(level.buttons, button)
|
|
if not level.parented then
|
|
level.parented = true
|
|
level:ClearAllPoints()
|
|
if level.num == 1 then
|
|
if level.parent ~= UIParent and type(level.parent) == "table" then
|
|
level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT")
|
|
else
|
|
level:SetPoint("CENTER", UIParent, "CENTER")
|
|
end
|
|
else
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
level:SetPoint("TOPLEFT", level.parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
level:SetPoint("BOTTOMLEFT", level.parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
level:SetPoint("TOPRIGHT", level.parent, "TOPLEFT", -5, 10)
|
|
else
|
|
level:SetPoint("BOTTOMRIGHT", level.parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
end
|
|
level:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
end
|
|
button:SetAlpha(1)
|
|
return button
|
|
end
|
|
|
|
local numLevels = 0
|
|
local function AcquireLevel(self, level)
|
|
if not levels[level] then
|
|
for i = #levels + 1, level, -1 do
|
|
local i = i
|
|
numLevels = numLevels + 1
|
|
local frame = CreateFrame("Button", "Dewdrop20Level" .. numLevels, nil)
|
|
if i == 1 then
|
|
local old_CloseSpecialWindows = CloseSpecialWindows
|
|
function CloseSpecialWindows()
|
|
local found = old_CloseSpecialWindows()
|
|
if levels[1]:IsShown() then
|
|
self:Close()
|
|
return 1
|
|
end
|
|
return found
|
|
end
|
|
end
|
|
levels[i] = frame
|
|
frame.num = i
|
|
frame:SetParent(UIParent)
|
|
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
frame:Hide()
|
|
frame:SetWidth(180)
|
|
frame:SetHeight(10)
|
|
frame:SetFrameLevel(i * 3)
|
|
frame:SetScript("OnHide", function()
|
|
self:Close(level + 1)
|
|
end)
|
|
if frame.SetTopLevel then
|
|
frame:SetTopLevel(true)
|
|
end
|
|
frame:EnableMouse(true)
|
|
frame:EnableMouseWheel(true)
|
|
local backdrop = CreateFrame("Frame", nil, frame)
|
|
backdrop:SetAllPoints(frame)
|
|
backdrop:SetBackdrop(tmp(
|
|
'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
|
|
'tile', true,
|
|
'insets', tmp2(
|
|
'left', 5,
|
|
'right', 5,
|
|
'top', 5,
|
|
'bottom', 5
|
|
),
|
|
'tileSize', 16,
|
|
'edgeSize', 16
|
|
))
|
|
backdrop:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
|
|
backdrop:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
|
|
frame:SetScript("OnClick", function()
|
|
self:Close(i)
|
|
end)
|
|
frame:SetScript("OnEnter", function()
|
|
StopCounting(self, i)
|
|
end)
|
|
frame:SetScript("OnLeave", function()
|
|
StartCounting(self, i)
|
|
end)
|
|
frame:SetScript("OnMouseWheel", function(this, arg1)
|
|
Scroll(self, frame, arg1 < 0)
|
|
end)
|
|
if i == 1 then
|
|
frame:SetScript("OnUpdate", function(this, arg1)
|
|
OnUpdate(self, arg1)
|
|
end)
|
|
levels[1].lastDirection = "RIGHT"
|
|
levels[1].lastVDirection = "DOWN"
|
|
else
|
|
levels[i].lastDirection = levels[i - 1].lastDirection
|
|
levels[i].lastVDirection = levels[i - 1].lastVDirection
|
|
end
|
|
end
|
|
end
|
|
local fullscreenFrame = GetUIPanel("fullscreen")
|
|
local l = levels[level]
|
|
local strata, framelevel = l:GetFrameStrata(), l:GetFrameLevel()
|
|
if fullscreenFrame then
|
|
l:SetParent(fullscreenFrame)
|
|
else
|
|
l:SetParent(UIParent)
|
|
end
|
|
l:SetFrameStrata(strata)
|
|
l:SetFrameLevel(framelevel)
|
|
l:SetAlpha(1)
|
|
return l
|
|
end
|
|
|
|
local function validateOptions(options, position, baseOptions, fromPass)
|
|
if not baseOptions then
|
|
baseOptions = options
|
|
end
|
|
if type(options) ~= "table" then
|
|
return "Options must be a table.", position
|
|
end
|
|
local kind = options.type
|
|
if type(kind) ~= "string" then
|
|
return '"type" must be a string.', position
|
|
elseif kind ~= "group" and kind ~= "range" and kind ~= "text" and kind ~= "execute" and kind ~= "toggle" and kind ~= "color" and kind ~= "dragLink" and kind ~= "header" then
|
|
return '"type" must either be "range", "text", "group", "toggle", "execute", "color", "dragLink", or "header".', position
|
|
end
|
|
if options.aliases then
|
|
if type(options.aliases) ~= "table" and type(options.aliases) ~= "string" then
|
|
return '"alias" must be a table or string', position
|
|
end
|
|
end
|
|
if not fromPass then
|
|
if kind == "execute" then
|
|
if type(options.func) ~= "string" and type(options.func) ~= "function" then
|
|
return '"func" must be a string or function', position
|
|
end
|
|
elseif kind == "range" or kind == "text" or kind == "toggle" then
|
|
if type(options.set) ~= "string" and type(options.set) ~= "function" then
|
|
return '"set" must be a string or function', position
|
|
end
|
|
if kind == "text" and options.get == false then
|
|
elseif type(options.get) ~= "string" and type(options.get) ~= "function" then
|
|
return '"get" must be a string or function', position
|
|
end
|
|
elseif kind == "group" and options.pass then
|
|
if options.pass ~= true then
|
|
return '"pass" must be either nil, true, or false', position
|
|
end
|
|
if not options.func then
|
|
if type(options.set) ~= "string" and type(options.set) ~= "function" then
|
|
return '"set" must be a string or function', position
|
|
end
|
|
if type(options.get) ~= "string" and type(options.get) ~= "function" then
|
|
return '"get" must be a string or function', position
|
|
end
|
|
elseif type(options.func) ~= "string" and type(options.func) ~= "function" then
|
|
return '"func" must be a string or function', position
|
|
end
|
|
end
|
|
end
|
|
if options ~= baseOptions then
|
|
if kind == "header" then
|
|
elseif type(options.desc) ~= "string" then
|
|
return '"desc" must be a string', position
|
|
elseif options.desc:len() == 0 then
|
|
return '"desc" cannot be a 0-length string', position
|
|
end
|
|
end
|
|
if options ~= baseOptions or kind == "range" or kind == "text" or kind == "toggle" or kind == "color" then
|
|
if options.type == "header" and not options.cmdName and not options.name then
|
|
elseif options.cmdName then
|
|
if type(options.cmdName) ~= "string" then
|
|
return '"cmdName" must be a string or nil', position
|
|
elseif options.cmdName:len() == 0 then
|
|
return '"cmdName" cannot be a 0-length string', position
|
|
end
|
|
if type(options.guiName) ~= "string" then
|
|
if not options.guiNameIsMap then
|
|
return '"guiName" must be a string or nil', position
|
|
end
|
|
elseif options.guiName:len() == 0 then
|
|
return '"guiName" cannot be a 0-length string', position
|
|
end
|
|
else
|
|
if type(options.name) ~= "string" then
|
|
return '"name" must be a string', position
|
|
elseif options.name:len() == 0 then
|
|
return '"name" cannot be a 0-length string', position
|
|
end
|
|
end
|
|
end
|
|
if options.guiNameIsMap then
|
|
if type(options.guiNameIsMap) ~= "boolean" then
|
|
return '"guiNameIsMap" must be a boolean or nil', position
|
|
elseif options.type ~= "toggle" then
|
|
return 'if "guiNameIsMap" is true, then "type" must be set to \'toggle\'', position
|
|
elseif type(options.map) ~= "table" then
|
|
return '"map" must be a table', position
|
|
end
|
|
end
|
|
if options.message and type(options.message) ~= "string" then
|
|
return '"message" must be a string or nil', position
|
|
end
|
|
if options.error and type(options.error) ~= "string" then
|
|
return '"error" must be a string or nil', position
|
|
end
|
|
if options.current and type(options.current) ~= "string" then
|
|
return '"current" must be a string or nil', position
|
|
end
|
|
if options.order then
|
|
if type(options.order) ~= "number" or (-1 < options.order and options.order < 0.999) then
|
|
return '"order" must be a non-zero number or nil', position
|
|
end
|
|
end
|
|
if options.disabled then
|
|
if type(options.disabled) ~= "function" and type(options.disabled) ~= "string" and options.disabled ~= true then
|
|
return '"disabled" must be a function, string, or boolean', position
|
|
end
|
|
end
|
|
if options.cmdHidden then
|
|
if type(options.cmdHidden) ~= "function" and type(options.cmdHidden) ~= "string" and options.cmdHidden ~= true then
|
|
return '"cmdHidden" must be a function, string, or boolean', position
|
|
end
|
|
end
|
|
if options.guiHidden then
|
|
if type(options.guiHidden) ~= "function" and type(options.guiHidden) ~= "string" and options.guiHidden ~= true then
|
|
return '"guiHidden" must be a function, string, or boolean', position
|
|
end
|
|
end
|
|
if options.hidden then
|
|
if type(options.hidden) ~= "function" and type(options.hidden) ~= "string" and options.hidden ~= true then
|
|
return '"hidden" must be a function, string, or boolean', position
|
|
end
|
|
end
|
|
if kind == "text" then
|
|
if type(options.validate) == "table" then
|
|
local t = options.validate
|
|
local iTable = nil
|
|
for k,v in pairs(t) do
|
|
if type(k) == "number" then
|
|
if iTable == nil then
|
|
iTable = true
|
|
elseif not iTable then
|
|
return '"validate" must either have all keys be indexed numbers or strings', position
|
|
elseif k < 1 or k > #t then
|
|
return '"validate" numeric keys must be indexed properly. >= 1 and <= #t', position
|
|
end
|
|
else
|
|
if iTable == nil then
|
|
iTable = false
|
|
elseif iTable then
|
|
return '"validate" must either have all keys be indexed numbers or strings', position
|
|
end
|
|
end
|
|
if type(v) ~= "string" then
|
|
return '"validate" values must all be strings', position
|
|
end
|
|
end
|
|
if options.multiToggle and options.multiToggle ~= true then
|
|
return '"multiToggle" must be a boolean or nil if "validate" is a table', position
|
|
end
|
|
elseif options.validate == "keybinding" then
|
|
-- no other checks
|
|
else
|
|
if type(options.usage) ~= "string" then
|
|
return '"usage" must be a string', position
|
|
elseif options.validate and type(options.validate) ~= "string" and type(options.validate) ~= "function" then
|
|
return '"validate" must be a string, function, or table', position
|
|
end
|
|
end
|
|
if options.multiToggle and type(options.validate) ~= "table" then
|
|
return '"validate" must be a table if "multiToggle" is true', position
|
|
end
|
|
elseif kind == "range" then
|
|
if options.min or options.max then
|
|
if type(options.min) ~= "number" then
|
|
return '"min" must be a number', position
|
|
elseif type(options.max) ~= "number" then
|
|
return '"max" must be a number', position
|
|
elseif options.min >= options.max then
|
|
return '"min" must be less than "max"', position
|
|
end
|
|
end
|
|
if options.step then
|
|
if type(options.step) ~= "number" then
|
|
return '"step" must be a number', position
|
|
elseif options.step < 0 then
|
|
return '"step" must be nonnegative', position
|
|
end
|
|
end
|
|
if options.bigStep then
|
|
if type(options.bigStep) ~= "number" then
|
|
return '"bigStep" must be a number', position
|
|
elseif options.bigStep < 0 then
|
|
return '"bigStep" must be nonnegative', position
|
|
end
|
|
end
|
|
if options.isPercent and options.isPercent ~= true then
|
|
return '"isPercent" must either be nil, true, or false', position
|
|
end
|
|
elseif kind == "toggle" then
|
|
if options.map then
|
|
if type(options.map) ~= "table" then
|
|
return '"map" must be a table', position
|
|
elseif type(options.map[true]) ~= "string" then
|
|
return '"map[true]" must be a string', position
|
|
elseif type(options.map[false]) ~= "string" then
|
|
return '"map[false]" must be a string', position
|
|
end
|
|
end
|
|
elseif kind == "color" then
|
|
if options.hasAlpha and options.hasAlpha ~= true then
|
|
return '"hasAlpha" must be nil, true, or false', position
|
|
end
|
|
elseif kind == "group" then
|
|
if options.pass and options.pass ~= true then
|
|
return '"pass" must be nil, true, or false', position
|
|
end
|
|
if type(options.args) ~= "table" then
|
|
return '"args" must be a table', position
|
|
end
|
|
for k,v in pairs(options.args) do
|
|
if type(k) ~= "number" then
|
|
if type(k) ~= "string" then
|
|
return '"args" keys must be strings or numbers', position
|
|
elseif k:len() == 0 then
|
|
return '"args" keys must not be 0-length strings.', position
|
|
end
|
|
end
|
|
if type(v) ~= "table" then
|
|
return '"args" values must be tables', position and position .. "." .. k or k
|
|
end
|
|
local newposition
|
|
if position then
|
|
newposition = position .. ".args." .. k
|
|
else
|
|
newposition = "args." .. k
|
|
end
|
|
local err, pos = validateOptions(v, newposition, baseOptions, options.pass)
|
|
if err then
|
|
return err, pos
|
|
end
|
|
end
|
|
elseif kind == "execute" then
|
|
if type(options.confirm) ~= "string" and type(options.confirm) ~= "boolean" and type(options.confirm) ~= "nil" then
|
|
return '"confirm" must be a string, boolean, or nil', position
|
|
end
|
|
end
|
|
if options.icon and type(options.icon) ~= "string" then
|
|
return'"icon" must be a string', position
|
|
end
|
|
if options.iconWidth or options.iconHeight then
|
|
if type(options.iconWidth) ~= "number" or type(options.iconHeight) ~= "number" then
|
|
return '"iconHeight" and "iconWidth" must be numbers', position
|
|
end
|
|
end
|
|
if options.iconCoordLeft or options.iconCoordRight or options.iconCoordTop or options.iconCoordBottom then
|
|
if type(options.iconCoordLeft) ~= "number" or type(options.iconCoordRight) ~= "number" or type(options.iconCoordTop) ~= "number" or type(options.iconCoordBottom) ~= "number" then
|
|
return '"iconCoordLeft", "iconCoordRight", "iconCoordTop", and "iconCoordBottom" must be numbers', position
|
|
end
|
|
end
|
|
end
|
|
|
|
local validatedOptions
|
|
|
|
local values
|
|
local mysort_args
|
|
local mysort
|
|
local othersort
|
|
local othersort_validate
|
|
|
|
local baseFunc, currentLevel
|
|
|
|
local function confirmPopup(message, func, ...)
|
|
if not StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] then
|
|
StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"] = {}
|
|
end
|
|
local t = StaticPopupDialogs["DEWDROP20_CONFIRM_DIALOG"]
|
|
for k in pairs(t) do
|
|
t[k] = nil
|
|
end
|
|
t.text = message
|
|
t.button1 = ACCEPT or "Accept"
|
|
t.button2 = CANCEL or "Cancel"
|
|
t.OnAccept = function()
|
|
func(unpack(t))
|
|
end
|
|
for i = 1, select('#', ...) do
|
|
t[i] = select(i, ...)
|
|
end
|
|
t.timeout = 0
|
|
t.whileDead = 1
|
|
t.hideOnEscape = 1
|
|
|
|
Dewdrop:Close()
|
|
StaticPopup_Show("DEWDROP20_CONFIRM_DIALOG")
|
|
end
|
|
|
|
|
|
local function getMethod(settingname, handler, v, methodName, ...) -- "..." is simply returned straight out cause you can't do "a,b,c = 111,f(),222"
|
|
assert(v and type(v)=="table")
|
|
assert(methodName and type(methodName)=="string")
|
|
|
|
local method = v[methodName]
|
|
if type(method)=="function" then
|
|
return method, ...
|
|
elseif type(method)=="string" then
|
|
if not handler then
|
|
Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
|
|
elseif not handler[method] then
|
|
Dewdrop:error("[%s] 'handler' method %q not defined", tostring(settingname), method)
|
|
end
|
|
return handler[method], handler, ...
|
|
end
|
|
|
|
Dewdrop:error("[%s] Missing %q directive", tostring(settingname), methodName)
|
|
end
|
|
|
|
local function callMethod(settingname, handler, v, methodName, ...)
|
|
assert(v and type(v)=="table")
|
|
assert(methodName and type(methodName)=="string")
|
|
|
|
local method = v[methodName]
|
|
if type(method)=="function" then
|
|
local success, ret,ret2,ret3,ret4 = pcall(v[methodName], ...)
|
|
if not success then
|
|
geterrorhandler()(ret)
|
|
return nil
|
|
end
|
|
return ret,ret2,ret3,ret4
|
|
|
|
elseif type(method)=="string" then
|
|
|
|
local neg = method:match("^~(.-)$")
|
|
if neg then
|
|
method = neg
|
|
end
|
|
if not handler then
|
|
Dewdrop:error("[%s] 'handler' is required if providing a method name: %q", tostring(settingname), method)
|
|
elseif not handler[method] then
|
|
Dewdrop:error("[%s] 'handler' (%q) method %q not defined", tostring(settingname), handler.name or "(unnamed)", method)
|
|
end
|
|
local success, ret,ret2,ret3,ret4 = pcall(handler[method], handler, ...)
|
|
if not success then
|
|
geterrorhandler()(ret)
|
|
return nil
|
|
end
|
|
if neg then
|
|
return not ret
|
|
end
|
|
return ret,ret2,ret3,ret4
|
|
elseif method == false then
|
|
return nil
|
|
end
|
|
|
|
Dewdrop:error("[%s] Missing %q directive in %q", tostring(settingname), methodName, v.name or "(unnamed)")
|
|
end
|
|
|
|
local function skip1Nil(...)
|
|
if select(1,...)==nil then
|
|
return select(2,...)
|
|
end
|
|
return ...
|
|
end
|
|
|
|
function Dewdrop:FeedAceOptionsTable(options, difference)
|
|
self:argCheck(options, 2, "table")
|
|
self:argCheck(difference, 3, "nil", "number")
|
|
if not currentLevel then
|
|
self:error("Cannot call `FeedAceOptionsTable' outside of a Dewdrop declaration")
|
|
end
|
|
if not difference then
|
|
difference = 0
|
|
end
|
|
if not validatedOptions then
|
|
validatedOptions = {}
|
|
end
|
|
if not validatedOptions[options] then
|
|
local err, position = validateOptions(options)
|
|
|
|
if err then
|
|
if position then
|
|
Dewdrop:error(position .. ": " .. err)
|
|
else
|
|
Dewdrop:error(err)
|
|
end
|
|
end
|
|
|
|
validatedOptions[options] = true
|
|
end
|
|
local level = levels[currentLevel]
|
|
if not level then
|
|
self:error("Improper level given")
|
|
end
|
|
if not values then
|
|
values = {}
|
|
else
|
|
for k,v in pairs(values) do
|
|
values[k] = nil
|
|
end
|
|
end
|
|
|
|
local current = level
|
|
while current do -- this traverses from higher level numbers to lower, building "values" with leaf nodes first and trunk nodes later
|
|
if current.num == difference + 1 then
|
|
break
|
|
end
|
|
table.insert(values, current.value)
|
|
current = levels[current.num - 1]
|
|
end
|
|
|
|
local realOptions = options
|
|
local handler = options.handler
|
|
local passTable
|
|
local passValue
|
|
while #values > 0 do -- This loop traverses values from the END (trunk nodes first, then onto leaf nodes)
|
|
if options.pass then
|
|
if options.get and options.set then
|
|
passTable = options
|
|
elseif not passTable then
|
|
passTable = options
|
|
end
|
|
else
|
|
passTable = nil
|
|
end
|
|
local value = table.remove(values)
|
|
options = options.args and options.args[value]
|
|
if not options then
|
|
return
|
|
end
|
|
handler = options.handler or handler
|
|
passValue = passTable and value or nil
|
|
end
|
|
|
|
if options.type == "group" then
|
|
local hidden = options.hidden
|
|
if type(hidden) == "function" or type(hidden) == "string" then
|
|
hidden = callMethod(options.name or "(options root)", handler, options, "hidden", options.passValue) or false
|
|
end
|
|
if hidden then
|
|
return
|
|
end
|
|
local disabled = options.disabled
|
|
if type(disabled) == "function" or type(disabled) == "string" then
|
|
disabled = callMethod(options.name or "(options root)", handler, options, "disabled", options.passValue) or false
|
|
end
|
|
if disabled then
|
|
self:AddLine(
|
|
'text', DISABLED,
|
|
'disabled', true
|
|
)
|
|
return
|
|
end
|
|
for k in pairs(options.args) do
|
|
table.insert(values, k)
|
|
end
|
|
if options.pass then
|
|
if options.get and options.set then
|
|
passTable = options
|
|
elseif not passTable then
|
|
passTable = options
|
|
end
|
|
else
|
|
passTable = nil
|
|
end
|
|
if not mysort then
|
|
mysort = function(a, b)
|
|
local alpha, bravo = mysort_args[a], mysort_args[b]
|
|
local alpha_order = alpha.order or 100
|
|
local bravo_order = bravo.order or 100
|
|
local alpha_name = alpha.guiName or alpha.name
|
|
local bravo_name = bravo.guiName or bravo.name
|
|
if alpha_order == bravo_order then
|
|
if not alpha_name then
|
|
return bravo_name
|
|
elseif not bravo_name then
|
|
return false
|
|
else
|
|
return alpha_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < bravo_name:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
|
|
end
|
|
else
|
|
if alpha_order < 0 then
|
|
if bravo_order > 0 then
|
|
return false
|
|
end
|
|
else
|
|
if bravo_order < 0 then
|
|
return true
|
|
end
|
|
end
|
|
return alpha_order < bravo_order
|
|
end
|
|
end
|
|
end
|
|
mysort_args = options.args
|
|
table.sort(values, mysort)
|
|
mysort_args = nil
|
|
local hasBoth = #values >= 1 and (options.args[values[1]].order or 100) > 0 and (options.args[values[#values]].order or 100) < 0
|
|
local last_order = 1
|
|
for _,k in ipairs(values) do
|
|
local v = options.args[k]
|
|
local handler = v.handler or handler
|
|
if hasBoth and last_order > 0 and (v.order or 100) < 0 then
|
|
hasBoth = false
|
|
self:AddLine()
|
|
end
|
|
local hidden, disabled = v.guiHidden or v.hidden, v.disabled
|
|
|
|
if type(hidden) == "function" or type(hidden) == "string" then
|
|
hidden = callMethod(k, handler, v, "hidden", v.passValue) or false
|
|
end
|
|
if not hidden then
|
|
if type(disabled) == "function" or type(disabled) == "string" then
|
|
disabled = callMethod(k, handler, v, "disabled", v.passValue) or false
|
|
end
|
|
local name = (v.guiIconOnly and v.icon) and "" or (v.guiName or v.name)
|
|
local desc = v.guiDesc or v.desc
|
|
local iconHeight = v.iconHeight or 16
|
|
local iconWidth = v.iconWidth or 16
|
|
local iconCoordLeft = v.iconCoordLeft
|
|
local iconCoordRight = v.iconCoordRight
|
|
local iconCoordBottom = v.iconCoordBottom
|
|
local iconCoordTop = v.iconCoordTop
|
|
local tooltipTitle, tooltipText
|
|
tooltipTitle = name
|
|
if name ~= desc then
|
|
tooltipText = desc
|
|
end
|
|
if type(v.usage) == "string" and v.usage:trim():len() > 0 then
|
|
if tooltipText then
|
|
tooltipText = tooltipText .. "\n\n" .. USAGE_TOOLTIP:format(v.usage)
|
|
else
|
|
tooltipText = USAGE_TOOLTIP:format(v.usage)
|
|
end
|
|
end
|
|
local v_p = passTable
|
|
if not v_p or (v.type ~= "execute" and v.get and v.set) or (v.type == "execute" and v.func) then
|
|
v_p = v
|
|
end
|
|
local passValue = v.passValue or (v_p~=v and k) or nil
|
|
if v.type == "toggle" then
|
|
local checked = callMethod(name, handler, v_p, "get", passValue) or false
|
|
local checked_arg = checked
|
|
if type(v_p.get)=="string" and v_p.get:match("^~") then
|
|
checked_arg = not checked
|
|
end
|
|
local func, arg1, arg2, arg3 = getMethod(name, handler, v_p, "set", skip1Nil(passValue, not checked_arg))
|
|
if v.guiNameIsMap then
|
|
checked = checked and true or false
|
|
name = tostring(v.map and v.map[checked]):gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
|
|
tooltipTitle = name
|
|
checked = true--nil
|
|
end
|
|
self:AddLine(
|
|
'text', name,
|
|
'checked', checked,
|
|
'isRadio', v.isRadio,
|
|
'func', func,
|
|
'arg1', arg1,
|
|
'arg2', arg2,
|
|
'arg3', arg3,
|
|
'disabled', disabled,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText
|
|
)
|
|
elseif v.type == "execute" then
|
|
local func, arg1, arg2, arg3, arg4
|
|
local confirm = v.confirm
|
|
if confirm == true then
|
|
confirm = DEFAULT_CONFIRM_MESSAGE:format(tooltipText or tooltipTitle)
|
|
func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
|
|
elseif type(confirm) == "string" then
|
|
func,arg1,arg2,arg3,arg4 = confirmPopup, confirm, getMethod(name, handler, v_p, "func", passValue)
|
|
else
|
|
func,arg1,arg2 = getMethod(name, handler, v_p, "func", passValue)
|
|
end
|
|
self:AddLine(
|
|
'text', name,
|
|
'checked', checked,
|
|
'func', func,
|
|
'arg1', arg1,
|
|
'arg2', arg2,
|
|
'arg3', arg3,
|
|
'arg4', arg4,
|
|
'disabled', disabled,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom
|
|
)
|
|
elseif v.type == "range" then
|
|
local sliderValue
|
|
sliderValue = callMethod(name, handler, v_p, "get", passValue) or 0
|
|
local sliderFunc, sliderArg1, sliderArg2 = getMethod(name, handler, v_p, "set", passValue)
|
|
if tooltipText then
|
|
tooltipText = format("%s\n\n%s", tooltipText, RANGE_TOOLTIP)
|
|
else
|
|
tooltipText = RANGE_TOOLTIP
|
|
end
|
|
self:AddLine(
|
|
'text', name,
|
|
'hasArrow', true,
|
|
'hasSlider', true,
|
|
'sliderMin', v.min or 0,
|
|
'sliderMax', v.max or 1,
|
|
'sliderStep', v.step or 0,
|
|
'sliderBigStep', v.bigStep or nil,
|
|
'sliderIsPercent', v.isPercent or false,
|
|
'sliderValue', sliderValue,
|
|
'sliderFunc', sliderFunc,
|
|
'sliderArg1', sliderArg1,
|
|
'sliderArg2', sliderArg2,
|
|
'fromAceOptions', true,
|
|
'disabled', disabled,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom
|
|
)
|
|
elseif v.type == "color" then
|
|
local r,g,b,a = callMethod(name, handler, v_p, "get", passValue)
|
|
if not r then
|
|
r,g,b,a = 0,0,0,0
|
|
end
|
|
local colorFunc, colorArg1, colorArg2 = getMethod(name, handler, v_p, "set", passValue)
|
|
self:AddLine(
|
|
'text', name,
|
|
'hasArrow', true,
|
|
'hasColorSwatch', true,
|
|
'r', r,
|
|
'g', g,
|
|
'b', b,
|
|
'opacity', v.hasAlpha and a or nil,
|
|
'hasOpacity', v.hasAlpha,
|
|
'colorFunc', colorFunc,
|
|
'colorArg1', colorArg1,
|
|
'colorArg2', colorArg2,
|
|
'disabled', disabled,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText
|
|
)
|
|
elseif v.type == "text" then
|
|
if type(v.validate) == "table" then
|
|
local func,arg1,arg2
|
|
if v.onClick then
|
|
func,arg1,arg2 = getMethod(name, handler, v, "onClick", passValue)
|
|
end
|
|
local checked
|
|
if v.isChecked then
|
|
checked = callMethod(name, handler, v, "isChecked", passValue) or false
|
|
end
|
|
self:AddLine(
|
|
'text', name,
|
|
'hasArrow', true,
|
|
'value', k,
|
|
'func', func,
|
|
'arg1', arg1,
|
|
'arg2', arg2,
|
|
'mouseoverUnderline', func and true or nil,
|
|
'disabled', disabled,
|
|
'checked', checked,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom
|
|
)
|
|
else
|
|
local editBoxText
|
|
editBoxText = callMethod(name, handler, v_p, "get", passValue) or ""
|
|
local editBoxFunc, editBoxArg1, editBoxArg2 = getMethod(name, handler, v_p, "set", passValue)
|
|
|
|
local editBoxValidateFunc, editBoxValidateArg1
|
|
|
|
if v.validate and v.validate ~= "keybinding" then
|
|
if v.validate == "keybinding" then
|
|
if tooltipText then
|
|
tooltipText = format("%s\n\n%s", tooltipText, RESET_KEYBINDING_DESC)
|
|
else
|
|
tooltipText = RESET_KEYBINDING_DESC
|
|
end
|
|
else
|
|
editBoxValidateFunc, editBoxValidateArg1 = getMethod(name, handler, v, "validate") -- no passvalue!
|
|
end
|
|
end
|
|
|
|
self:AddLine(
|
|
'text', name,
|
|
'hasArrow', true,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom,
|
|
'hasEditBox', true,
|
|
'editBoxText', editBoxText,
|
|
'editBoxFunc', editBoxFunc,
|
|
'editBoxArg1', editBoxArg1,
|
|
'editBoxArg2', editBoxArg2,
|
|
'editBoxValidateFunc', editBoxValidateFunc,
|
|
'editBoxValidateArg1', editBoxValidateArg1,
|
|
'editBoxIsKeybinding', v.validate == "keybinding",
|
|
'editBoxKeybindingOnly', v.keybindingOnly,
|
|
'editBoxKeybindingExcept', v.keybindingExcept,
|
|
'disabled', disabled,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText
|
|
)
|
|
end
|
|
elseif v.type == "group" then
|
|
local func,arg1,arg2
|
|
if v.onClick then
|
|
func,arg1,arg2 = getMethod(name, handler, v, "onClick", passValue)
|
|
end
|
|
local checked
|
|
if v.isChecked then
|
|
checked = callMethod(name, handler, v, "isChecked", passValue) or false
|
|
end
|
|
self:AddLine(
|
|
'text', name,
|
|
'hasArrow', true,
|
|
'value', k,
|
|
'func', func,
|
|
'arg1', arg1,
|
|
'arg2', arg2,
|
|
'mouseoverUnderline', func and true or nil,
|
|
'disabled', disabled,
|
|
'checked', checked,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom
|
|
)
|
|
elseif v.type == "header" then
|
|
if name == "" or not name then
|
|
self:AddLine(
|
|
'isTitle', true,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom
|
|
)
|
|
else
|
|
self:AddLine(
|
|
'text', name,
|
|
'isTitle', true,
|
|
'icon', v.icon,
|
|
'iconHeight', iconHeight,
|
|
'iconWidth', iconWidth,
|
|
'iconCoordLeft', iconCoordLeft,
|
|
'iconCoordRight', iconCoordRight,
|
|
'iconCoordTop', iconCoordTop,
|
|
'iconCoordBottom', iconCoordBottom
|
|
)
|
|
end
|
|
end
|
|
end
|
|
last_order = v.order or 100
|
|
end
|
|
elseif options.type == "text" and type(options.validate) == "table" then
|
|
local current
|
|
local options_p = passTable
|
|
if not options_p or (options.get and options.set) then
|
|
options_p = options
|
|
passTable = nil
|
|
passValue = nil
|
|
end
|
|
local multiToggle = options.multiToggle
|
|
local passValue = options.passValue or passValue
|
|
if not multiToggle then
|
|
current = callMethod(k, handler, options_p, "get", passValue)
|
|
end
|
|
local indexed = true
|
|
for k,v in pairs(options.validate) do
|
|
if type(k) ~= "number" then
|
|
indexed = false
|
|
end
|
|
table.insert(values, k)
|
|
end
|
|
if not indexed then
|
|
if not othersort then
|
|
othersort = function(alpha, bravo)
|
|
return othersort_validate[alpha]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper() < othersort_validate[bravo]:gsub("|c%x%x%x%x%x%x%x%x", ""):gsub("|r", ""):upper()
|
|
end
|
|
end
|
|
othersort_validate = options.validate
|
|
table.sort(values, othersort)
|
|
othersort_validate = nil
|
|
end
|
|
for _,k in ipairs(values) do
|
|
local v = options.validate[k]
|
|
if type(k) == "number" then
|
|
k = v
|
|
end
|
|
local func, arg1, arg2, arg3, arg4 = getMethod(k, handler, options_p, "set", skip1Nil(passValue, k))
|
|
local checked
|
|
if multiToggle then
|
|
checked = callMethod(k, handler, options_p, "get", skip1Nil(passValue, k)) or false
|
|
if arg2 == nil then
|
|
arg2 = not checked
|
|
elseif arg3 == nil then
|
|
arg3 = not checked
|
|
else
|
|
arg4 = not checked
|
|
end
|
|
else
|
|
checked = (k == current or (type(k) == "string" and type(current) == "string" and k:lower() == current:lower()))
|
|
if checked then
|
|
func, arg1, arg2, arg3, arg4 = nil, nil, nil, nil, nil
|
|
end
|
|
end
|
|
local tooltipTitle
|
|
local tooltipText
|
|
if options.validateDesc then
|
|
tooltipTitle = v
|
|
tooltipText = options.validateDesc[k]
|
|
else
|
|
tooltipTitle = options.guiName or options.name
|
|
tooltipText = v
|
|
end
|
|
self:AddLine(
|
|
'text', v,
|
|
'func', func,
|
|
'arg1', arg1,
|
|
'arg2', arg2,
|
|
'arg3', arg3,
|
|
'arg4', arg4,
|
|
'isRadio', not multiToggle,
|
|
'checked', checked,
|
|
'tooltipTitle', tooltipTitle,
|
|
'tooltipText', tooltipText
|
|
)
|
|
end
|
|
for k in pairs(values) do
|
|
values[k] = nil
|
|
end
|
|
else
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
function Dewdrop:FeedTable(s, difference)
|
|
self:argCheck(s, 2, "table")
|
|
self:argCheck(difference, 3, "nil", "number")
|
|
if not currentLevel then
|
|
self:error("Cannot call `FeedTable' outside of a Dewdrop declaration")
|
|
end
|
|
if not difference then
|
|
difference = 0
|
|
end
|
|
local level = levels[currentLevel]
|
|
if not level then
|
|
self:error("Improper level given")
|
|
end
|
|
if not values then
|
|
values = {}
|
|
else
|
|
for k,v in pairs(values) do
|
|
values[k] = nil
|
|
end
|
|
end
|
|
local t = s.subMenu and s or {subMenu = s}
|
|
local current = level
|
|
while current do
|
|
if current.num == difference + 1 then
|
|
break
|
|
end
|
|
table.insert(values, current.value)
|
|
current = levels[current.num - 1]
|
|
end
|
|
|
|
while #values > 0 do
|
|
local value = table.remove(values)
|
|
t = t.subMenu and t.subMenu[value]
|
|
if not t then
|
|
return
|
|
end
|
|
end
|
|
|
|
if t.subMenu or current.num == 1 then
|
|
for k in pairs(t.subMenu) do
|
|
table.insert(values, k)
|
|
end
|
|
table.sort(values)
|
|
for _,k in ipairs(values) do
|
|
local argTable = {"value", k}
|
|
for key, val in pairs(t.subMenu[k]) do
|
|
table.insert(argTable, key)
|
|
table.insert(argTable, val)
|
|
end
|
|
self:AddLine(unpack(argTable))
|
|
end
|
|
for k in pairs(values) do
|
|
values[k] = nil
|
|
end
|
|
return false
|
|
end
|
|
return true
|
|
end
|
|
|
|
function Refresh(self, level)
|
|
if type(level) == "number" then
|
|
level = levels[level]
|
|
end
|
|
if not level then
|
|
return
|
|
end
|
|
if baseFunc then
|
|
Clear(self, level)
|
|
currentLevel = level.num
|
|
if type(baseFunc) == "table" then
|
|
if currentLevel == 1 then
|
|
local handler = baseFunc.handler
|
|
if handler then
|
|
local name = tostring(handler)
|
|
if not name:find('^table:') and not handler.hideMenuTitle then
|
|
name = name:gsub("|c%x%x%x%x%x%x%x%x(.-)|r", "%1")
|
|
self:AddLine(
|
|
'text', name,
|
|
'isTitle', true
|
|
)
|
|
end
|
|
end
|
|
-- elseif level.parentText then
|
|
-- self:AddLine(
|
|
-- 'text', level.parentText,
|
|
-- 'tooltipTitle', level.parentTooltipTitle,
|
|
-- 'tooltipText', level.parentTooltipText,
|
|
-- 'tooltipFunc', level.parentTooltipFunc,
|
|
-- 'isTitle', true
|
|
-- )
|
|
end
|
|
self:FeedAceOptionsTable(baseFunc)
|
|
if currentLevel == 1 then
|
|
self:AddLine(
|
|
'text', CLOSE,
|
|
'tooltipTitle', CLOSE,
|
|
'tooltipText', CLOSE_DESC,
|
|
'closeWhenClicked', true
|
|
)
|
|
end
|
|
else
|
|
-- if level.parentText then
|
|
-- self:AddLine(
|
|
-- 'text', level.parentText,
|
|
-- 'tooltipTitle', level.parentTooltipTitle,
|
|
-- 'tooltipText', level.parentTooltipText,
|
|
-- 'tooltipFunc', level.parentTooltipFunc,
|
|
-- 'isTitle', true
|
|
-- )
|
|
-- end
|
|
baseFunc(currentLevel, level.value, levels[level.num - 1] and levels[level.num - 1].value, levels[level.num - 2] and levels[level.num - 2].value, levels[level.num - 3] and levels[level.num - 3].value, levels[level.num - 4] and levels[level.num - 4].value)
|
|
end
|
|
currentLevel = nil
|
|
CheckSize(self, level)
|
|
end
|
|
end
|
|
|
|
function Dewdrop:Refresh(level)
|
|
self:argCheck(level, 2, "number", "nil")
|
|
if not level then
|
|
for k,v in pairs(levels) do
|
|
Refresh(self, v)
|
|
end
|
|
else
|
|
Refresh(self, levels[level])
|
|
end
|
|
end
|
|
|
|
function OpenSlider(self, parent)
|
|
if not sliderFrame then
|
|
sliderFrame = CreateFrame("Frame", nil, nil)
|
|
sliderFrame:SetWidth(100)
|
|
sliderFrame:SetHeight(170)
|
|
sliderFrame:SetScale(UIParent:GetScale())
|
|
sliderFrame:SetBackdrop(tmp(
|
|
'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
|
|
'tile', true,
|
|
'insets', tmp2(
|
|
'left', 5,
|
|
'right', 5,
|
|
'top', 5,
|
|
'bottom', 5
|
|
),
|
|
'tileSize', 16,
|
|
'edgeSize', 16
|
|
))
|
|
sliderFrame:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
if sliderFrame.SetTopLevel then
|
|
sliderFrame:SetTopLevel(true)
|
|
end
|
|
sliderFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
|
|
sliderFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
|
|
sliderFrame:EnableMouse(true)
|
|
sliderFrame:EnableMouseWheel(true)
|
|
sliderFrame:Hide()
|
|
sliderFrame:SetPoint("CENTER", UIParent, "CENTER")
|
|
local slider = CreateFrame("Slider", nil, sliderFrame)
|
|
sliderFrame.slider = slider
|
|
slider:SetOrientation("VERTICAL")
|
|
slider:SetMinMaxValues(0, 1)
|
|
slider:SetValueStep(0.000000001)
|
|
slider:SetValue(0.5)
|
|
slider:SetWidth(16)
|
|
slider:SetHeight(128)
|
|
slider:SetPoint("LEFT", sliderFrame, "LEFT", 15, 0)
|
|
slider:SetBackdrop(tmp(
|
|
'bgFile', "Interface\\Buttons\\UI-SliderBar-Background",
|
|
'edgeFile', "Interface\\Buttons\\UI-SliderBar-Border",
|
|
'tile', true,
|
|
'edgeSize', 8,
|
|
'tileSize', 8,
|
|
'insets', tmp2(
|
|
'left', 3,
|
|
'right', 3,
|
|
'top', 3,
|
|
'bottom', 3
|
|
)
|
|
))
|
|
local texture = slider:CreateTexture()
|
|
slider:SetThumbTexture("Interface\\Buttons\\UI-SliderBar-Button-Vertical")
|
|
local text = slider:CreateFontString(nil, "ARTWORK")
|
|
sliderFrame.topText = text
|
|
text:SetFontObject(GameFontGreenSmall)
|
|
text:SetText("100%")
|
|
text:SetPoint("BOTTOM", slider, "TOP")
|
|
local text = slider:CreateFontString(nil, "ARTWORK")
|
|
sliderFrame.bottomText = text
|
|
text:SetFontObject(GameFontGreenSmall)
|
|
text:SetText("0%")
|
|
text:SetPoint("TOP", slider, "BOTTOM")
|
|
local editBox = CreateFrame("EditBox", nil, sliderFrame)
|
|
sliderFrame.currentText = editBox
|
|
editBox:SetFontObject(ChatFontNormal)
|
|
editBox:SetHeight(13)
|
|
editBox:SetPoint("RIGHT", sliderFrame, "RIGHT", -16, 0)
|
|
editBox:SetPoint("LEFT", slider, "RIGHT", 12, 0)
|
|
editBox:SetText("50%")
|
|
editBox:SetJustifyH("CENTER")
|
|
|
|
local width = editBox:GetWidth()/2 + 10
|
|
local left = editBox:CreateTexture(nil, "BACKGROUND")
|
|
left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
|
|
left:SetTexCoord(0, width / 256, 0, 1)
|
|
left:SetWidth(width)
|
|
left:SetHeight(32)
|
|
left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
|
|
local right = editBox:CreateTexture(nil, "BACKGROUND")
|
|
right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
|
|
right:SetTexCoord(1 - width / 256, 1, 0, 1)
|
|
right:SetWidth(width)
|
|
right:SetHeight(32)
|
|
right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
|
|
|
|
local changed = false
|
|
local inside = false
|
|
slider:SetScript("OnValueChanged", function()
|
|
if sliderFrame.changing then
|
|
return
|
|
end
|
|
changed = true
|
|
local done = false
|
|
if sliderFrame.parent and sliderFrame.parent.sliderFunc then
|
|
local min = sliderFrame.parent.sliderMin or 0
|
|
local max = sliderFrame.parent.sliderMax or 1
|
|
local step
|
|
if sliderFrame.fineStep then
|
|
step = sliderFrame.parent.sliderStep or (max - min) / 100
|
|
else
|
|
step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
|
|
end
|
|
local value = (1 - slider:GetValue()) * (max - min) + min
|
|
if step > 0 then
|
|
value = math.floor((value - min) / step + 0.5) * step + min
|
|
if value > max then
|
|
value = max
|
|
elseif value < min then
|
|
value = min
|
|
end
|
|
end
|
|
if value == sliderFrame.lastValue then
|
|
return
|
|
end
|
|
sliderFrame.lastValue = value
|
|
local text = sliderFrame.parent.sliderFunc(getArgs(sliderFrame.parent, 'sliderArg', 1, value))
|
|
if sliderFrame.parent.fromAceOptions then
|
|
text = nil
|
|
elseif type(text) == "string" or type(text) == "number" then
|
|
sliderFrame.currentText:SetText(text)
|
|
done = true
|
|
end
|
|
end
|
|
if not done then
|
|
local min = sliderFrame.parent.sliderMin or 0
|
|
local max = sliderFrame.parent.sliderMax or 1
|
|
local step
|
|
if sliderFrame.fineStep then
|
|
step = sliderFrame.parent.sliderStep or (max - min) / 100
|
|
else
|
|
step = sliderFrame.parent.sliderBigStep or sliderFrame.parent.sliderStep or (max - min) / 100
|
|
end
|
|
local value = (1 - slider:GetValue()) * (max - min) + min
|
|
if step > 0 then
|
|
value = math.floor((value - min) / step + 0.5) * step + min
|
|
if value > max then
|
|
value = max
|
|
elseif value < min then
|
|
value = min
|
|
end
|
|
end
|
|
if sliderFrame.parent.sliderIsPercent then
|
|
sliderFrame.currentText:SetText(string.format("%.0f%%", value * 100))
|
|
else
|
|
if step < 0.1 then
|
|
sliderFrame.currentText:SetText(string.format("%.2f", value))
|
|
elseif step < 1 then
|
|
sliderFrame.currentText:SetText(string.format("%.1f", value))
|
|
else
|
|
sliderFrame.currentText:SetText(string.format("%.0f", value))
|
|
end
|
|
end
|
|
end
|
|
end)
|
|
local function onEnter()
|
|
StopCounting(self, sliderFrame.level)
|
|
showGameTooltip(sliderFrame.parent)
|
|
end
|
|
local function onLeave()
|
|
GameTooltip:Hide()
|
|
end
|
|
sliderFrame:SetScript("OnEnter", onEnter)
|
|
sliderFrame:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
if changed then
|
|
local parent = sliderFrame.parent
|
|
local sliderFunc = parent.sliderFunc
|
|
for i = 1, sliderFrame.level - 1 do
|
|
Refresh(self, levels[i])
|
|
end
|
|
local newParent
|
|
for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
|
|
if button.sliderFunc == sliderFunc then
|
|
newParent = button
|
|
break
|
|
end
|
|
end
|
|
if newParent then
|
|
OpenSlider(self, newParent)
|
|
else
|
|
sliderFrame:Hide()
|
|
end
|
|
end
|
|
end)
|
|
editBox:SetScript("OnEnter", onEnter)
|
|
editBox:SetScript("OnLeave", onLeave)
|
|
slider:SetScript("OnMouseDown", function()
|
|
sliderFrame.mouseDown = true
|
|
GameTooltip:Hide()
|
|
end)
|
|
slider:SetScript("OnMouseUp", function()
|
|
sliderFrame.mouseDown = false
|
|
if changed--[[ and not inside]] then
|
|
local parent = sliderFrame.parent
|
|
local sliderFunc = parent.sliderFunc
|
|
for i = 1, sliderFrame.level - 1 do
|
|
Refresh(self, levels[i])
|
|
end
|
|
local newParent
|
|
for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
|
|
if button.sliderFunc == sliderFunc then
|
|
newParent = button
|
|
break
|
|
end
|
|
end
|
|
if newParent then
|
|
OpenSlider(self, newParent)
|
|
else
|
|
sliderFrame:Hide()
|
|
end
|
|
end
|
|
if inside then
|
|
showGameTooltip(sliderFrame.parent)
|
|
end
|
|
end)
|
|
slider:SetScript("OnEnter", function()
|
|
inside = true
|
|
StopCounting(self, sliderFrame.level)
|
|
showGameTooltip(sliderFrame.parent)
|
|
end)
|
|
slider:SetScript("OnLeave", function()
|
|
inside = false
|
|
GameTooltip:Hide()
|
|
if changed and not sliderFrame.mouseDown then
|
|
local parent = sliderFrame.parent
|
|
local sliderFunc = parent.sliderFunc
|
|
for i = 1, sliderFrame.level - 1 do
|
|
Refresh(self, levels[i])
|
|
end
|
|
local newParent
|
|
for _,button in ipairs(levels[sliderFrame.level-1].buttons) do
|
|
if button.sliderFunc == sliderFunc then
|
|
newParent = button
|
|
break
|
|
end
|
|
end
|
|
if newParent then
|
|
OpenSlider(self, newParent)
|
|
else
|
|
sliderFrame:Hide()
|
|
end
|
|
|
|
changed = false
|
|
end
|
|
end)
|
|
sliderFrame:SetScript("OnMouseWheel", function(t, a1)
|
|
local arg1 = a1 or arg1
|
|
local up = arg1 > 0
|
|
|
|
local min = sliderFrame.parent.sliderMin or 0
|
|
local max = sliderFrame.parent.sliderMax or 1
|
|
local step = sliderFrame.parent.sliderStep or (max - min) / 100
|
|
if step <= 0 then
|
|
step = (max - min) / 100
|
|
end
|
|
|
|
local value = (1 - slider:GetValue()) * (max - min) + min
|
|
if up then
|
|
value = value + step
|
|
else
|
|
value = value - step
|
|
end
|
|
if value > max then
|
|
value = max
|
|
elseif value < min then
|
|
value = min
|
|
end
|
|
sliderFrame.fineStep = true
|
|
if max<=min then
|
|
slider:SetValue(0)
|
|
else
|
|
slider:SetValue(1 - (value - min) / (max - min))
|
|
end
|
|
sliderFrame.fineStep = nil
|
|
end)
|
|
slider:SetScript("OnMouseWheel", sliderFrame:GetScript("OnMouseWheel"))
|
|
editBox:SetScript("OnEnterPressed", function(t, a1)
|
|
local value = editBox:GetNumber()
|
|
|
|
if sliderFrame.parent.sliderIsPercent then
|
|
value = value / 100
|
|
end
|
|
|
|
local min = sliderFrame.parent.sliderMin or 0
|
|
local max = sliderFrame.parent.sliderMax or 1
|
|
|
|
if value > max then
|
|
value = max
|
|
elseif value < min then
|
|
value = min
|
|
end
|
|
sliderFrame.fineStep = true
|
|
if max <= min then
|
|
slider:SetValue(0)
|
|
else
|
|
slider:SetValue(1 - (value - min) / (max - min))
|
|
end
|
|
sliderFrame.fineStep = nil
|
|
|
|
StartCounting(self, sliderFrame.level)
|
|
end)
|
|
editBox:SetScript("OnEscapePressed", function()
|
|
self:Close(sliderFrame.level)
|
|
StartCounting(self, sliderFrame.level)
|
|
end)
|
|
editBox:SetAutoFocus(false)
|
|
end
|
|
sliderFrame.parent = parent
|
|
sliderFrame.level = parent.level.num + 1
|
|
sliderFrame.parentValue = parent.level.value
|
|
sliderFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
|
|
sliderFrame.slider:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
|
|
sliderFrame.currentText:SetFrameLevel(sliderFrame:GetFrameLevel() + 1)
|
|
sliderFrame.currentText:ClearFocus()
|
|
sliderFrame.changing = true
|
|
if not parent.sliderMin or not parent.sliderMax then
|
|
return
|
|
end
|
|
|
|
if parent.arrow then
|
|
-- parent.arrow:SetVertexColor(0.2, 0.6, 0)
|
|
-- parent.arrow:SetHeight(24)
|
|
-- parent.arrow:SetWidth(24)
|
|
parent.selected = true
|
|
parent.highlight:Show()
|
|
end
|
|
|
|
sliderFrame:SetClampedToScreen(false)
|
|
if not parent.sliderValue then
|
|
parent.sliderValue = (parent.sliderMin + parent.sliderMax) / 2
|
|
end
|
|
if parent.sliderMax <= parent.sliderMin then
|
|
sliderFrame.slider:SetValue(0)
|
|
else
|
|
sliderFrame.slider:SetValue(1 - (parent.sliderValue - parent.sliderMin) / (parent.sliderMax - parent.sliderMin))
|
|
end
|
|
sliderFrame.changing = false
|
|
sliderFrame.bottomText:SetText(parent.sliderMinText or "0")
|
|
sliderFrame.topText:SetText(parent.sliderMaxText or "1")
|
|
local text
|
|
if parent.sliderFunc and not parent.fromAceOptions then
|
|
text = parent.sliderFunc(getArgs(parent, 'sliderArg', 1, parent.sliderValue))
|
|
end
|
|
if type(text) == "number" or type(text) == "string" then
|
|
sliderFrame.currentText:SetText(text)
|
|
elseif parent.sliderIsPercent then
|
|
sliderFrame.currentText:SetText(string.format("%.0f%%", parent.sliderValue * 100))
|
|
else
|
|
if parent.sliderStep < 0.1 then
|
|
sliderFrame.currentText:SetText(string.format("%.2f", parent.sliderValue))
|
|
elseif parent.sliderStep < 1 then
|
|
sliderFrame.currentText:SetText(string.format("%.1f", parent.sliderValue))
|
|
else
|
|
sliderFrame.currentText:SetText(string.format("%.0f", parent.sliderValue))
|
|
end
|
|
end
|
|
|
|
|
|
sliderFrame.lastValue = parent.sliderValue
|
|
|
|
local level = parent.level
|
|
sliderFrame:Show()
|
|
sliderFrame:ClearAllPoints()
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
|
|
else
|
|
sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
local dirty
|
|
if level.lastDirection == "RIGHT" then
|
|
if sliderFrame:GetRight() > GetScreenWidth() then
|
|
level.lastDirection = "LEFT"
|
|
dirty = true
|
|
end
|
|
elseif sliderFrame:GetLeft() < 0 then
|
|
level.lastDirection = "RIGHT"
|
|
dirty = true
|
|
end
|
|
if level.lastVDirection == "DOWN" then
|
|
if sliderFrame:GetBottom() < 0 then
|
|
level.lastVDirection = "UP"
|
|
dirty = true
|
|
end
|
|
elseif sliderFrame:GetTop() > GetScreenWidth() then
|
|
level.lastVDirection = "DOWN"
|
|
dirty = true
|
|
end
|
|
if dirty then
|
|
sliderFrame:ClearAllPoints()
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
sliderFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
sliderFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
sliderFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
|
|
else
|
|
sliderFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
end
|
|
local left, bottom = sliderFrame:GetLeft(), sliderFrame:GetBottom()
|
|
sliderFrame:ClearAllPoints()
|
|
sliderFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
|
|
if mod(level.num, 5) == 0 then
|
|
local left, bottom = level:GetLeft(), level:GetBottom()
|
|
level:ClearAllPoints()
|
|
level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
|
|
end
|
|
sliderFrame:SetClampedToScreen(true)
|
|
end
|
|
|
|
function OpenEditBox(self, parent)
|
|
if not editBoxFrame then
|
|
editBoxFrame = CreateFrame("Frame", nil, nil)
|
|
editBoxFrame:SetWidth(200)
|
|
editBoxFrame:SetHeight(40)
|
|
editBoxFrame:SetScale(UIParent:GetScale())
|
|
editBoxFrame:SetBackdrop(tmp(
|
|
'bgFile', "Interface\\Tooltips\\UI-Tooltip-Background",
|
|
'edgeFile', "Interface\\Tooltips\\UI-Tooltip-Border",
|
|
'tile', true,
|
|
'insets', tmp2(
|
|
'left', 5,
|
|
'right', 5,
|
|
'top', 5,
|
|
'bottom', 5
|
|
),
|
|
'tileSize', 16,
|
|
'edgeSize', 16
|
|
))
|
|
editBoxFrame:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
if editBoxFrame.SetTopLevel then
|
|
editBoxFrame:SetTopLevel(true)
|
|
end
|
|
editBoxFrame:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
|
|
editBoxFrame:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
|
|
editBoxFrame:EnableMouse(true)
|
|
editBoxFrame:EnableMouseWheel(true)
|
|
editBoxFrame:Hide()
|
|
editBoxFrame:SetPoint("CENTER", UIParent, "CENTER")
|
|
|
|
local editBox = CreateFrame("EditBox", nil, editBoxFrame)
|
|
editBoxFrame.editBox = editBox
|
|
editBox:SetFontObject(ChatFontNormal)
|
|
editBox:SetWidth(160)
|
|
editBox:SetHeight(13)
|
|
editBox:SetPoint("CENTER", editBoxFrame, "CENTER", 0, 0)
|
|
|
|
local left = editBox:CreateTexture(nil, "BACKGROUND")
|
|
left:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Left")
|
|
left:SetTexCoord(0, 100 / 256, 0, 1)
|
|
left:SetWidth(100)
|
|
left:SetHeight(32)
|
|
left:SetPoint("LEFT", editBox, "LEFT", -10, 0)
|
|
local right = editBox:CreateTexture(nil, "BACKGROUND")
|
|
right:SetTexture("Interface\\ChatFrame\\UI-ChatInputBorder-Right")
|
|
right:SetTexCoord(156/256, 1, 0, 1)
|
|
right:SetWidth(100)
|
|
right:SetHeight(32)
|
|
right:SetPoint("RIGHT", editBox, "RIGHT", 10, 0)
|
|
|
|
editBox:SetScript("OnEnterPressed", function()
|
|
if editBoxFrame.parent and editBoxFrame.parent.editBoxValidateFunc then
|
|
local t = editBox.realText or editBox:GetText() or ""
|
|
local result = editBoxFrame.parent.editBoxValidateFunc(getArgs(editBoxFrame.parent, 'editBoxValidateArg', 1, t))
|
|
if not result then
|
|
UIErrorsFrame:AddMessage(VALIDATION_ERROR, 1, 0, 0)
|
|
return
|
|
end
|
|
end
|
|
if editBoxFrame.parent and editBoxFrame.parent.editBoxFunc then
|
|
local t
|
|
if editBox.realText ~= "NONE" then
|
|
t = editBox.realText or editBox:GetText() or ""
|
|
end
|
|
editBoxFrame.parent.editBoxFunc(getArgs(editBoxFrame.parent, 'editBoxArg', 1, t))
|
|
end
|
|
self:Close(editBoxFrame.level)
|
|
for i = 1, editBoxFrame.level - 1 do
|
|
Refresh(self, levels[i])
|
|
end
|
|
StartCounting(self, editBoxFrame.level-1)
|
|
end)
|
|
editBox:SetScript("OnEscapePressed", function()
|
|
self:Close(editBoxFrame.level)
|
|
StartCounting(self, editBoxFrame.level-1)
|
|
end)
|
|
editBox:SetScript("OnReceiveDrag", function(this)
|
|
if GetCursorInfo then
|
|
local type, alpha, bravo = GetCursorInfo()
|
|
local text
|
|
if type == "spell" then
|
|
text = GetSpellName(alpha, bravo)
|
|
elseif type == "item" then
|
|
text = bravo
|
|
end
|
|
if not text then
|
|
return
|
|
end
|
|
ClearCursor()
|
|
editBox:SetText(text)
|
|
end
|
|
end)
|
|
local changing = false
|
|
local skipNext = false
|
|
|
|
function editBox:SpecialSetText(text)
|
|
local oldText = editBox:GetText() or ""
|
|
if not text then
|
|
text = ""
|
|
end
|
|
if text ~= oldText then
|
|
changing = true
|
|
self:SetText(tostring(text))
|
|
changing = false
|
|
skipNext = true
|
|
end
|
|
end
|
|
|
|
editBox:SetScript("OnTextChanged", function()
|
|
if skipNext then
|
|
skipNext = false
|
|
elseif not changing and editBoxFrame.parent and editBoxFrame.parent.editBoxChangeFunc then
|
|
local t
|
|
if editBox.realText ~= "NONE" then
|
|
t = editBox.realText or editBox:GetText() or ""
|
|
end
|
|
local text = editBoxFrame.parent.editBoxChangeFunc(getArgs(editBoxFrame.parent, 'editBoxChangeArg', 1, t))
|
|
if text then
|
|
editBox:SpecialSetText(text)
|
|
end
|
|
end
|
|
end)
|
|
editBoxFrame:SetScript("OnEnter", function()
|
|
StopCounting(self, editBoxFrame.level)
|
|
showGameTooltip(editBoxFrame.parent)
|
|
end)
|
|
editBoxFrame:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
end)
|
|
editBox:SetScript("OnEnter", function()
|
|
StopCounting(self, editBoxFrame.level)
|
|
showGameTooltip(editBoxFrame.parent)
|
|
end)
|
|
editBox:SetScript("OnLeave", function()
|
|
GameTooltip:Hide()
|
|
end)
|
|
editBoxFrame:SetScript("OnKeyDown", function(this, a1)
|
|
if not editBox.keybinding then
|
|
return
|
|
end
|
|
local arg1 = a1 or arg1
|
|
local screenshotKey = GetBindingKey("SCREENSHOT")
|
|
if screenshotKey and arg1 == screenshotKey then
|
|
Screenshot()
|
|
return
|
|
end
|
|
|
|
if arg1 == "LeftButton" then
|
|
arg1 = "BUTTON1"
|
|
elseif arg1 == "RightButton" then
|
|
arg1 = "BUTTON2"
|
|
elseif arg1 == "MiddleButton" then
|
|
arg1 = "BUTTON3"
|
|
elseif arg1 == "Button4" then
|
|
arg1 = "BUTTON4"
|
|
elseif arg1 == "Button5" then
|
|
arg1 = "BUTTON5"
|
|
end
|
|
if arg1 == "UNKNOWN" then
|
|
return
|
|
elseif arg1 == "SHIFT" or arg1 == "CTRL" or arg1 == "ALT" then
|
|
return
|
|
elseif arg1 == "ENTER" then
|
|
if editBox.keybindingOnly and not editBox.keybindingOnly[editBox.realText] then
|
|
return editBox:GetScript("OnEscapePressed")()
|
|
elseif editBox.keybindingExcept and editBox.keybindingExcept[editBox.realText] then
|
|
return editBox:GetScript("OnEscapePressed")()
|
|
else
|
|
return editBox:GetScript("OnEnterPressed")()
|
|
end
|
|
elseif arg1 == "ESCAPE" then
|
|
if editBox.realText == "NONE" then
|
|
return editBox:GetScript("OnEscapePressed")()
|
|
else
|
|
editBox:SpecialSetText(NONE or "NONE")
|
|
editBox.realText = "NONE"
|
|
return
|
|
end
|
|
elseif editBox.keybindingOnly and not editBox.keybindingOnly[arg1] then
|
|
return
|
|
elseif editBox.keybindingExcept and editBox.keybindingExcept[arg1] then
|
|
return
|
|
end
|
|
local s = GetBindingText(arg1, "KEY_")
|
|
if s == "BUTTON1" then
|
|
s = KEY_BUTTON1
|
|
elseif s == "BUTTON2" then
|
|
s = KEY_BUTTON2
|
|
end
|
|
local real = arg1
|
|
if IsShiftKeyDown() then
|
|
s = "Shift-" .. s
|
|
real = "SHIFT-" .. real
|
|
end
|
|
if IsControlKeyDown() then
|
|
s = "Ctrl-" .. s
|
|
real = "CTRL-" .. real
|
|
end
|
|
if IsAltKeyDown() then
|
|
s = "Alt-" .. s
|
|
real = "ALT-" .. real
|
|
end
|
|
if editBox:GetText() ~= s then
|
|
editBox:SpecialSetText("-")
|
|
editBox:SpecialSetText(s)
|
|
editBox.realText = real
|
|
return editBox:GetScript("OnTextChanged")()
|
|
end
|
|
end)
|
|
editBoxFrame:SetScript("OnMouseDown", editBoxFrame:GetScript("OnKeyDown"))
|
|
editBox:SetScript("OnMouseDown", function(this, ...)
|
|
if GetCursorInfo and (CursorHasItem() or CursorHasSpell()) then
|
|
return editBox:GetScript("OnReceiveDrag")(this, ...)
|
|
end
|
|
return editBoxFrame:GetScript("OnKeyDown")(this, ...)
|
|
end)
|
|
editBoxFrame:SetScript("OnMouseWheel", function(t, a1)
|
|
local arg1 = a1 or arg1
|
|
local up = arg1 > 0
|
|
arg1 = up and "MOUSEWHEELUP" or "MOUSEWHEELDOWN"
|
|
return editBoxFrame:GetScript("OnKeyDown")(t or this, arg1)
|
|
end)
|
|
editBox:SetScript("OnMouseWheel", editBoxFrame:GetScript("OnMouseWheel"))
|
|
end
|
|
editBoxFrame.parent = parent
|
|
editBoxFrame.level = parent.level.num + 1
|
|
editBoxFrame.parentValue = parent.level.value
|
|
editBoxFrame:SetFrameLevel(parent.level:GetFrameLevel() + 3)
|
|
editBoxFrame.editBox:SetFrameLevel(editBoxFrame:GetFrameLevel() + 1)
|
|
editBoxFrame.editBox.realText = nil
|
|
editBoxFrame:SetClampedToScreen(false)
|
|
|
|
editBoxFrame.editBox:SpecialSetText("")
|
|
if parent.editBoxIsKeybinding then
|
|
local s = parent.editBoxText
|
|
if s == "" then
|
|
s = "NONE"
|
|
end
|
|
editBoxFrame.editBox.realText = s
|
|
if s and s ~= "NONE" then
|
|
local alpha,bravo = s:match("^(.+)%-(.+)$")
|
|
if not bravo then
|
|
alpha = nil
|
|
bravo = s
|
|
end
|
|
bravo = GetBindingText(bravo, "KEY_")
|
|
if alpha then
|
|
editBoxFrame.editBox:SpecialSetText(alpha:upper() .. "-" .. bravo)
|
|
else
|
|
editBoxFrame.editBox:SpecialSetText(bravo)
|
|
end
|
|
else
|
|
editBoxFrame.editBox:SpecialSetText(NONE or "NONE")
|
|
end
|
|
else
|
|
editBoxFrame.editBox:SpecialSetText(parent.editBoxText)
|
|
end
|
|
|
|
editBoxFrame.editBox.keybinding = parent.editBoxIsKeybinding
|
|
editBoxFrame.editBox.keybindingOnly = parent.editBoxKeybindingOnly
|
|
editBoxFrame.editBox.keybindingExcept = parent.editBoxKeybindingExcept
|
|
editBoxFrame.editBox:EnableKeyboard(not parent.editBoxIsKeybinding)
|
|
editBoxFrame:EnableKeyboard(parent.editBoxIsKeybinding)
|
|
|
|
if parent.arrow then
|
|
-- parent.arrow:SetVertexColor(0.2, 0.6, 0)
|
|
-- parent.arrow:SetHeight(24)
|
|
-- parent.arrow:SetWidth(24)
|
|
parent.selected = true
|
|
parent.highlight:Show()
|
|
end
|
|
|
|
local level = parent.level
|
|
editBoxFrame:Show()
|
|
editBoxFrame:ClearAllPoints()
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
|
|
else
|
|
editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
local dirty
|
|
if level.lastDirection == "RIGHT" then
|
|
if editBoxFrame:GetRight() > GetScreenWidth() then
|
|
level.lastDirection = "LEFT"
|
|
dirty = true
|
|
end
|
|
elseif editBoxFrame:GetLeft() < 0 then
|
|
level.lastDirection = "RIGHT"
|
|
dirty = true
|
|
end
|
|
if level.lastVDirection == "DOWN" then
|
|
if editBoxFrame:GetBottom() < 0 then
|
|
level.lastVDirection = "UP"
|
|
dirty = true
|
|
end
|
|
elseif editBoxFrame:GetTop() > GetScreenWidth() then
|
|
level.lastVDirection = "DOWN"
|
|
dirty = true
|
|
end
|
|
if dirty then
|
|
editBoxFrame:ClearAllPoints()
|
|
if level.lastDirection == "RIGHT" then
|
|
if level.lastVDirection == "DOWN" then
|
|
editBoxFrame:SetPoint("TOPLEFT", parent, "TOPRIGHT", 5, 10)
|
|
else
|
|
editBoxFrame:SetPoint("BOTTOMLEFT", parent, "BOTTOMRIGHT", 5, -10)
|
|
end
|
|
else
|
|
if level.lastVDirection == "DOWN" then
|
|
editBoxFrame:SetPoint("TOPRIGHT", parent, "TOPLEFT", -5, 10)
|
|
else
|
|
editBoxFrame:SetPoint("BOTTOMRIGHT", parent, "BOTTOMLEFT", -5, -10)
|
|
end
|
|
end
|
|
end
|
|
local left, bottom = editBoxFrame:GetLeft(), editBoxFrame:GetBottom()
|
|
editBoxFrame:ClearAllPoints()
|
|
editBoxFrame:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
|
|
if mod(level.num, 5) == 0 then
|
|
local left, bottom = level:GetLeft(), level:GetBottom()
|
|
level:ClearAllPoints()
|
|
level:SetPoint("BOTTOMLEFT", UIParent, "BOTTOMLEFT", left, bottom)
|
|
end
|
|
editBoxFrame:SetClampedToScreen(true)
|
|
end
|
|
|
|
function Dewdrop:EncodeKeybinding(text)
|
|
if text == nil or text == "NONE" then
|
|
return nil
|
|
end
|
|
text = tostring(text):upper()
|
|
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 not text:find("^F%d+$") and text ~= "CAPSLOCK" and text:len() ~= 1 and (text:len() == 0 or text:byte() < 128 or text:len() > 4) and not _G["KEY_" .. text] and text ~= "BUTTON1" and text ~= "BUTTON2" then
|
|
return false
|
|
end
|
|
local s = GetBindingText(text, "KEY_")
|
|
if s == "BUTTON1" then
|
|
s = KEY_BUTTON1
|
|
elseif s == "BUTTON2" then
|
|
s = KEY_BUTTON2
|
|
end
|
|
if shift then
|
|
s = "Shift-" .. s
|
|
end
|
|
if ctrl then
|
|
s = "Ctrl-" .. s
|
|
end
|
|
if alt then
|
|
s = "Alt-" .. s
|
|
end
|
|
return s
|
|
end
|
|
|
|
function Dewdrop:IsOpen(parent)
|
|
self:argCheck(parent, 2, "table", "string", "nil")
|
|
return levels[1] and levels[1]:IsShown() and (not parent or parent == levels[1].parent or parent == levels[1]:GetParent())
|
|
end
|
|
|
|
function Dewdrop:GetOpenedParent()
|
|
return (levels[1] and levels[1]:IsShown()) and (levels[1].parent or levels[1]:GetParent())
|
|
end
|
|
|
|
function Open(self, parent, func, level, value, point, relativePoint, cursorX, cursorY)
|
|
self:Close(level)
|
|
if DewdropLib then
|
|
local d = DewdropLib:GetInstance('1.0')
|
|
local ret, val = pcall(d, IsOpen, d)
|
|
if ret and val then
|
|
DewdropLib:GetInstance('1.0'):Close()
|
|
end
|
|
end
|
|
if type(parent) == "table" then
|
|
parent:GetCenter()
|
|
end
|
|
local frame = AcquireLevel(self, level)
|
|
if level == 1 then
|
|
frame.lastDirection = "RIGHT"
|
|
frame.lastVDirection = "DOWN"
|
|
else
|
|
frame.lastDirection = levels[level - 1].lastDirection
|
|
frame.lastVDirection = levels[level - 1].lastVDirection
|
|
end
|
|
frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
frame:ClearAllPoints()
|
|
frame.parent = parent
|
|
frame:SetPoint("LEFT", UIParent, "RIGHT", 10000, 0)
|
|
frame:Show()
|
|
if level == 1 then
|
|
baseFunc = func
|
|
end
|
|
levels[level].value = value
|
|
-- levels[level].parentText = parent.text and parent.text:GetText() or nil
|
|
-- levels[level].parentTooltipTitle = parent.tooltipTitle
|
|
-- levels[level].parentTooltipText = parent.tooltipText
|
|
-- levels[level].parentTooltipFunc = parent.tooltipFunc
|
|
if type(parent) == "table" and parent.arrow then
|
|
-- parent.arrow:SetVertexColor(0.2, 0.6, 0)
|
|
-- parent.arrow:SetHeight(24)
|
|
-- parent.arrow:SetWidth(24)
|
|
parent.selected = true
|
|
parent.highlight:Show()
|
|
end
|
|
relativePoint = relativePoint or point
|
|
Refresh(self, levels[level])
|
|
if point or (cursorX and cursorY) then
|
|
frame:ClearAllPoints()
|
|
if cursorX and cursorY then
|
|
local curX, curY = GetScaledCursorPosition()
|
|
if curY < GetScreenHeight() / 2 then
|
|
point, relativePoint = "BOTTOM", "BOTTOM"
|
|
else
|
|
point, relativePoint = "TOP", "TOP"
|
|
end
|
|
if curX < GetScreenWidth() / 2 then
|
|
point, relativePoint = point .. "LEFT", relativePoint .. "RIGHT"
|
|
else
|
|
point, relativePoint = point .. "RIGHT", relativePoint .. "LEFT"
|
|
end
|
|
end
|
|
frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint)
|
|
if cursorX and cursorY then
|
|
local left = frame:GetLeft()
|
|
local width = frame:GetWidth()
|
|
local bottom = frame:GetBottom()
|
|
local height = frame:GetHeight()
|
|
local curX, curY = GetScaledCursorPosition()
|
|
frame:ClearAllPoints()
|
|
relativePoint = relativePoint or point
|
|
if point == "BOTTOM" or point == "TOP" then
|
|
if curX < GetScreenWidth() / 2 then
|
|
point = point .. "LEFT"
|
|
else
|
|
point = point .. "RIGHT"
|
|
end
|
|
elseif point == "CENTER" then
|
|
if curX < GetScreenWidth() / 2 then
|
|
point = "LEFT"
|
|
else
|
|
point = "RIGHT"
|
|
end
|
|
end
|
|
local xOffset, yOffset = 0, 0
|
|
if curY > GetScreenHeight() / 2 then
|
|
yOffset = -height
|
|
end
|
|
if curX > GetScreenWidth() / 2 then
|
|
xOffset = -width
|
|
end
|
|
frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left + xOffset, curY - bottom + yOffset)
|
|
if level == 1 then
|
|
frame.lastDirection = "RIGHT"
|
|
end
|
|
elseif cursorX then
|
|
local left = frame:GetLeft()
|
|
local width = frame:GetWidth()
|
|
local curX, curY = GetScaledCursorPosition()
|
|
frame:ClearAllPoints()
|
|
relativePoint = relativePoint or point
|
|
if point == "BOTTOM" or point == "TOP" then
|
|
if curX < GetScreenWidth() / 2 then
|
|
point = point .. "LEFT"
|
|
else
|
|
point = point .. "RIGHT"
|
|
end
|
|
elseif point == "CENTER" then
|
|
if curX < GetScreenWidth() / 2 then
|
|
point = "LEFT"
|
|
else
|
|
point = "RIGHT"
|
|
end
|
|
end
|
|
frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, curX - left - width / 2, 0)
|
|
if level == 1 then
|
|
frame.lastDirection = "RIGHT"
|
|
end
|
|
elseif cursorY then
|
|
local bottom = frame:GetBottom()
|
|
local height = frame:GetHeight()
|
|
local curX, curY = GetScaledCursorPosition()
|
|
frame:ClearAllPoints()
|
|
relativePoint = relativePoint or point
|
|
if point == "LEFT" or point == "RIGHT" then
|
|
if curX < GetScreenHeight() / 2 then
|
|
point = point .. "BOTTOM"
|
|
else
|
|
point = point .. "TOP"
|
|
end
|
|
elseif point == "CENTER" then
|
|
if curX < GetScreenHeight() / 2 then
|
|
point = "BOTTOM"
|
|
else
|
|
point = "TOP"
|
|
end
|
|
end
|
|
frame:SetPoint(point, type(parent) == "table" and parent or UIParent, relativePoint, 0, curY - bottom - height / 2)
|
|
if level == 1 then
|
|
frame.lastDirection = "DOWN"
|
|
end
|
|
end
|
|
if (strsub(point, 1, 3) ~= strsub(relativePoint, 1, 3)) then
|
|
if frame:GetBottom() < 0 then
|
|
local point, parent, relativePoint, x, y = frame:GetPoint(1)
|
|
local change = GetScreenHeight() - frame:GetTop()
|
|
local otherChange = -frame:GetBottom()
|
|
if otherChange < change then
|
|
change = otherChange
|
|
end
|
|
frame:SetPoint(point, parent, relativePoint, x, y + change)
|
|
elseif frame:GetTop() > GetScreenHeight() then
|
|
local point, parent, relativePoint, x, y = frame:GetPoint(1)
|
|
local change = GetScreenHeight() - frame:GetTop()
|
|
local otherChange = -frame:GetBottom()
|
|
if otherChange < change then
|
|
change = otherChange
|
|
end
|
|
frame:SetPoint(point, parent, relativePoint, x, y + change)
|
|
end
|
|
end
|
|
end
|
|
CheckDualMonitor(self, frame)
|
|
frame:SetClampedToScreen(true)
|
|
frame:SetClampedToScreen(false)
|
|
StartCounting(self, level)
|
|
end
|
|
|
|
function Dewdrop:IsRegistered(parent)
|
|
self:argCheck(parent, 2, "table", "string")
|
|
return not not self.registry[parent]
|
|
end
|
|
|
|
function Dewdrop:Register(parent, ...)
|
|
self:argCheck(parent, 2, "table", "string")
|
|
if self.registry[parent] then
|
|
self:Unregister(parent)
|
|
end
|
|
local info = new(...)
|
|
if type(info.children) == "table" then
|
|
local err, position = validateOptions(info.children)
|
|
|
|
if err then
|
|
if position then
|
|
Dewdrop:error(position .. ": " .. err)
|
|
else
|
|
Dewdrop:error(err)
|
|
end
|
|
end
|
|
end
|
|
self.registry[parent] = info
|
|
if not info.dontHook and not self.onceRegistered[parent] and type(parent) == "table" then
|
|
if parent:HasScript("OnMouseUp") then
|
|
local script = parent:GetScript("OnMouseUp")
|
|
parent:SetScript("OnMouseUp", function(this, ...)
|
|
local arg1 = ...
|
|
if script then
|
|
script(this, ...)
|
|
end
|
|
if arg1 == "RightButton" and self.registry[parent] then
|
|
if self:IsOpen(parent) then
|
|
self:Close()
|
|
else
|
|
self:Open(parent)
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
if parent:HasScript("OnMouseDown") then
|
|
local script = parent:GetScript("OnMouseDown")
|
|
parent:SetScript("OnMouseDown", function(this, ...)
|
|
if script then
|
|
script(this, ...)
|
|
end
|
|
if self.registry[parent] then
|
|
self:Close()
|
|
end
|
|
end)
|
|
end
|
|
end
|
|
self.onceRegistered[parent] = true
|
|
end
|
|
|
|
function Dewdrop:Unregister(parent)
|
|
self:argCheck(parent, 2, "table", "string")
|
|
self.registry[parent] = nil
|
|
end
|
|
|
|
function Dewdrop:Open(parent, ...)
|
|
self:argCheck(parent, 2, "table", "string")
|
|
local info
|
|
local k1 = ...
|
|
if type(k1) == "table" and k1[0] and k1.IsObjectType and self.registry[k1] then
|
|
info = tmp(select(2, ...))
|
|
for k,v in pairs(self.registry[k1]) do
|
|
if info[k] == nil then
|
|
info[k] = v
|
|
end
|
|
end
|
|
else
|
|
info = tmp(...)
|
|
if self.registry[parent] then
|
|
for k,v in pairs(self.registry[parent]) do
|
|
if info[k] == nil then
|
|
info[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local point = info.point
|
|
local relativePoint = info.relativePoint
|
|
local cursorX = info.cursorX
|
|
local cursorY = info.cursorY
|
|
if type(point) == "function" then
|
|
local b
|
|
point, b = point(parent)
|
|
if b then
|
|
relativePoint = b
|
|
end
|
|
end
|
|
if type(relativePoint) == "function" then
|
|
relativePoint = relativePoint(parent)
|
|
end
|
|
Open(self, parent, info.children, 1, nil, point, relativePoint, cursorX, cursorY)
|
|
end
|
|
|
|
function Clear(self, level)
|
|
if level then
|
|
if level.buttons then
|
|
for i = #level.buttons, 1, -1 do
|
|
ReleaseButton(self, level, i)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function Dewdrop:Close(level)
|
|
if DropDownList1:IsShown() then
|
|
DropDownList1:Hide()
|
|
end
|
|
if DewdropLib then
|
|
local d = DewdropLib:GetInstance('1.0')
|
|
local ret, val = pcall(d, IsOpen, d)
|
|
if ret and val then
|
|
DewdropLib:GetInstance('1.0'):Close()
|
|
end
|
|
end
|
|
self:argCheck(level, 2, "number", "nil")
|
|
if not level then
|
|
level = 1
|
|
end
|
|
if level == 1 and levels[level] then
|
|
levels[level].parented = false
|
|
end
|
|
if level > 1 and levels[level-1].buttons then
|
|
local buttons = levels[level-1].buttons
|
|
for _,button in ipairs(buttons) do
|
|
-- button.arrow:SetWidth(16)
|
|
-- button.arrow:SetHeight(16)
|
|
button.selected = nil
|
|
button.highlight:Hide()
|
|
-- button.arrow:SetVertexColor(1, 1, 1)
|
|
end
|
|
end
|
|
if sliderFrame and sliderFrame.level >= level then
|
|
sliderFrame:Hide()
|
|
end
|
|
if editBoxFrame and editBoxFrame.level >= level then
|
|
editBoxFrame:Hide()
|
|
end
|
|
for i = level, #levels do
|
|
Clear(self, levels[level])
|
|
levels[i]:Hide()
|
|
levels[i]:ClearAllPoints()
|
|
levels[i]:SetPoint("CENTER", UIParent, "CENTER")
|
|
levels[i].value = nil
|
|
end
|
|
end
|
|
|
|
function Dewdrop:AddSeparator(level)
|
|
level = levels[level or currentLevel]
|
|
if not level or not level.buttons then return; end
|
|
|
|
local prevbutton = level.buttons[#level.buttons]
|
|
if not prevbutton then return; end
|
|
|
|
if prevbutton.disabled and prevbutton.text:GetText() == "" then
|
|
return
|
|
end
|
|
self:AddLine("text", "", "disabled", true)
|
|
end
|
|
|
|
function Dewdrop:AddLine(...)
|
|
local info = tmp(...)
|
|
local level = info.level or currentLevel
|
|
info.level = nil
|
|
local button = AcquireButton(self, level)
|
|
if not next(info) then
|
|
info.disabled = true
|
|
end
|
|
button.disabled = info.isTitle or info.notClickable or info.disabled or (self.combat and info.secure)
|
|
button.isTitle = info.isTitle
|
|
button.notClickable = info.notClickable
|
|
if button.isTitle then
|
|
button.text:SetFontObject(GameFontNormalSmall)
|
|
elseif button.notClickable then
|
|
button.text:SetFontObject(GameFontHighlightSmall)
|
|
elseif button.disabled then
|
|
button.text:SetFontObject(GameFontDisableSmall)
|
|
else
|
|
button.text:SetFontObject(GameFontHighlightSmall)
|
|
end
|
|
if info.disabled then
|
|
button.arrow:SetDesaturated(true)
|
|
button.check:SetDesaturated(true)
|
|
else
|
|
button.arrow:SetDesaturated(false)
|
|
button.check:SetDesaturated(false)
|
|
end
|
|
if info.textR and info.textG and info.textB then
|
|
button.textR = info.textR
|
|
button.textG = info.textG
|
|
button.textB = info.textB
|
|
button.text:SetTextColor(button.textR, button.textG, button.textB)
|
|
else
|
|
button.text:SetTextColor(button.text:GetFontObject():GetTextColor())
|
|
end
|
|
button.notCheckable = info.notCheckable
|
|
button.text:SetPoint("LEFT", button, "LEFT", button.notCheckable and 0 or 24, 0)
|
|
button.checked = not info.notCheckable and info.checked
|
|
button.mouseoverUnderline = info.mouseoverUnderline
|
|
button.isRadio = not info.notCheckable and info.isRadio
|
|
if info.isRadio then
|
|
button.check:Show()
|
|
button.check:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
|
|
if button.checked then
|
|
button.check:SetTexCoord(0.25, 0.5, 0, 1)
|
|
button.check:SetVertexColor(1, 1, 1, 1)
|
|
else
|
|
button.check:SetTexCoord(0, 0.25, 0, 1)
|
|
button.check:SetVertexColor(1, 1, 1, 0.5)
|
|
end
|
|
button.radioHighlight:SetTexture(info.checkIcon or "Interface\\Buttons\\UI-RadioButton")
|
|
button.check:SetWidth(16)
|
|
button.check:SetHeight(16)
|
|
elseif info.icon then
|
|
button.check:Show()
|
|
button.check:SetTexture(info.icon)
|
|
if info.iconWidth and info.iconHeight then
|
|
button.check:SetWidth(info.iconWidth)
|
|
button.check:SetHeight(info.iconHeight)
|
|
else
|
|
button.check:SetWidth(16)
|
|
button.check:SetHeight(16)
|
|
end
|
|
if info.iconCoordLeft and info.iconCoordRight and info.iconCoordTop and info.iconCoordBottom then
|
|
button.check:SetTexCoord(info.iconCoordLeft, info.iconCoordRight, info.iconCoordTop, info.iconCoordBottom)
|
|
elseif info.icon:find("^Interface\\Icons\\") then
|
|
button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
|
|
else
|
|
button.check:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
button.check:SetVertexColor(1, 1, 1, 1)
|
|
else
|
|
if button.checked then
|
|
if info.checkIcon then
|
|
button.check:SetWidth(16)
|
|
button.check:SetHeight(16)
|
|
button.check:SetTexture(info.checkIcon)
|
|
if info.checkIcon:find("^Interface\\Icons\\") then
|
|
button.check:SetTexCoord(0.05, 0.95, 0.05, 0.95)
|
|
else
|
|
button.check:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
else
|
|
button.check:SetWidth(24)
|
|
button.check:SetHeight(24)
|
|
button.check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
|
|
button.check:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
button.check:SetVertexColor(1, 1, 1, 1)
|
|
else
|
|
button.check:SetVertexColor(1, 1, 1, 0)
|
|
end
|
|
end
|
|
if not button.disabled then
|
|
button.func = info.func
|
|
button.secure = info.secure
|
|
end
|
|
button.hasColorSwatch = info.hasColorSwatch
|
|
if button.hasColorSwatch then
|
|
button.colorSwatch:Show()
|
|
button.colorSwatch.texture:Show()
|
|
button.r = info.r or 1
|
|
button.g = info.g or 1
|
|
button.b = info.b or 1
|
|
button.colorSwatch.texture:SetVertexColor(button.r, button.g, button.b)
|
|
button.checked = false
|
|
button.func = nil
|
|
button.colorFunc = info.colorFunc
|
|
local i = 1
|
|
while true do
|
|
local k = "colorArg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
button.hasOpacity = info.hasOpacity
|
|
button.opacity = info.opacity or 1
|
|
else
|
|
button.colorSwatch:Hide()
|
|
button.colorSwatch.texture:Hide()
|
|
end
|
|
button.hasArrow = not button.hasColorSwatch and (info.value or info.hasSlider or info.hasEditBox) and info.hasArrow
|
|
if button.hasArrow then
|
|
button.arrow:SetAlpha(1)
|
|
if info.hasSlider then
|
|
button.hasSlider = true
|
|
button.sliderMin = info.sliderMin or 0
|
|
button.sliderMax = info.sliderMax or 1
|
|
button.sliderStep = info.sliderStep or 0
|
|
button.sliderBigStep = info.sliderBigStep or button.sliderStep
|
|
if button.sliderBigStep < button.sliderStep then
|
|
button.sliderBigStep = button.sliderStep
|
|
end
|
|
button.sliderIsPercent = info.sliderIsPercent and true or false
|
|
button.sliderMinText = info.sliderMinText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMin * 100) or button.sliderMin
|
|
button.sliderMaxText = info.sliderMaxText or button.sliderIsPercent and string.format("%.0f%%", button.sliderMax * 100) or button.sliderMax
|
|
button.sliderFunc = info.sliderFunc
|
|
button.sliderValue = info.sliderValue
|
|
button.fromAceOptions = info.fromAceOptions
|
|
local i = 1
|
|
while true do
|
|
local k = "sliderArg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
elseif info.hasEditBox then
|
|
button.hasEditBox = true
|
|
button.editBoxText = info.editBoxText or ""
|
|
button.editBoxFunc = info.editBoxFunc
|
|
local i = 1
|
|
while true do
|
|
local k = "editBoxArg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
button.editBoxChangeFunc = info.editBoxChangeFunc
|
|
local i = 1
|
|
while true do
|
|
local k = "editBoxChangeArg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
button.editBoxValidateFunc = info.editBoxValidateFunc
|
|
local i = 1
|
|
while true do
|
|
local k = "editBoxValidateArg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
button.editBoxIsKeybinding = info.editBoxIsKeybinding
|
|
button.editBoxKeybindingOnly = info.editBoxKeybindingOnly
|
|
button.editBoxKeybindingExcept = info.editBoxKeybindingExcept
|
|
else
|
|
button.value = info.value
|
|
local l = levels[level+1]
|
|
if l and info.value == l.value then
|
|
-- button.arrow:SetWidth(24)
|
|
-- button.arrow:SetHeight(24)
|
|
button.selected = true
|
|
button.highlight:Show()
|
|
end
|
|
end
|
|
else
|
|
button.arrow:SetAlpha(0)
|
|
end
|
|
local i = 1
|
|
while true do
|
|
local k = "arg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
button.closeWhenClicked = info.closeWhenClicked
|
|
button.textHeight = info.textHeight or UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT or 10
|
|
local font,_ = button.text:GetFont()
|
|
button.text:SetFont(STANDARD_TEXT_FONT or "Fonts\\FRIZQT__.TTF", button.textHeight)
|
|
button:SetHeight(button.textHeight + 6)
|
|
button.text:SetPoint("RIGHT", button.arrow, (button.hasColorSwatch or button.hasArrow) and "LEFT" or "RIGHT")
|
|
button.text:SetJustifyH(info.justifyH or "LEFT")
|
|
button.text:SetText(info.text)
|
|
button.tooltipTitle = info.tooltipTitle
|
|
button.tooltipText = info.tooltipText
|
|
button.tooltipFunc = info.tooltipFunc
|
|
local i = 1
|
|
while true do
|
|
local k = "tooltipArg" .. i
|
|
local x = info[k]
|
|
if x == nil then
|
|
break
|
|
end
|
|
button[k] = x
|
|
i = i + 1
|
|
end
|
|
if not button.tooltipTitle and not button.tooltipText and not button.tooltipFunc and not info.isTitle then
|
|
button.tooltipTitle = info.text
|
|
end
|
|
if type(button.func) == "string" then
|
|
if type(button.arg1) ~= "table" then
|
|
self:error("Cannot call method %q on a non-table", button.func)
|
|
end
|
|
if type(button.arg1[button.func]) ~= "function" then
|
|
self:error("Method %q nonexistant.", button.func)
|
|
end
|
|
end
|
|
end
|
|
|
|
function Dewdrop:InjectAceOptionsTable(handler, options)
|
|
self:argCheck(handler, 2, "table")
|
|
self:argCheck(options, 3, "table")
|
|
if tostring(options.type):lower() ~= "group" then
|
|
self:error('Cannot inject into options table argument #3 if its type is not "group"')
|
|
end
|
|
if options.handler ~= nil and options.handler ~= handler then
|
|
self:error("Cannot inject into options table argument #3 if it has a different handler than argument #2")
|
|
end
|
|
options.handler = handler
|
|
local class = handler.class
|
|
if not AceLibrary:HasInstance("AceOO-2.0") or not class then
|
|
if Rock then
|
|
-- possible Rock object
|
|
for mixin in Rock:IterateObjectMixins(handler) do
|
|
if type(mixin.GetAceOptionsDataTable) == "function" then
|
|
local t = mixin:GetAceOptionsDataTable(handler)
|
|
for k,v in pairs(t) do
|
|
if type(options.args) ~= "table" then
|
|
options.args = {}
|
|
end
|
|
if options.args[k] == nil then
|
|
options.args[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
else
|
|
-- Ace2 object
|
|
while class and class ~= AceLibrary("AceOO-2.0").Class do
|
|
if type(class.GetAceOptionsDataTable) == "function" then
|
|
local t = class:GetAceOptionsDataTable(handler)
|
|
for k,v in pairs(t) do
|
|
if type(options.args) ~= "table" then
|
|
options.args = {}
|
|
end
|
|
if options.args[k] == nil then
|
|
options.args[k] = v
|
|
end
|
|
end
|
|
end
|
|
local mixins = class.mixins
|
|
if mixins then
|
|
for mixin in pairs(mixins) do
|
|
if type(mixin.GetAceOptionsDataTable) == "function" then
|
|
local t = mixin:GetAceOptionsDataTable(handler)
|
|
for k,v in pairs(t) do
|
|
if type(options.args) ~= "table" then
|
|
options.args = {}
|
|
end
|
|
if options.args[k] == nil then
|
|
options.args[k] = v
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
class = class.super
|
|
end
|
|
end
|
|
return options
|
|
end
|
|
|
|
function Dewdrop:OnTooltipHide()
|
|
if lastSetFont then
|
|
if lastSetFont == normalFont then
|
|
lastSetFont = nil
|
|
return
|
|
end
|
|
fillRegionTmp(GameTooltip:GetRegions())
|
|
for i,v in ipairs(regionTmp) do
|
|
if v.GetFont then
|
|
local font,size,outline = v:GetFont()
|
|
if font == lastSetFont then
|
|
v:SetFont(normalFont, size, outline)
|
|
end
|
|
end
|
|
regionTmp[i] = nil
|
|
end
|
|
lastSetFont = nil
|
|
end
|
|
end
|
|
|
|
local function activate(self, oldLib, oldDeactivate)
|
|
Dewdrop = self
|
|
if oldLib and oldLib.registry then
|
|
self.registry = oldLib.registry
|
|
self.onceRegistered = oldLib.onceRegistered
|
|
else
|
|
self.registry = {}
|
|
self.onceRegistered = {}
|
|
|
|
local WorldFrame_OnMouseDown = WorldFrame:GetScript("OnMouseDown")
|
|
local WorldFrame_OnMouseUp = WorldFrame:GetScript("OnMouseUp")
|
|
local oldX, oldY, clickTime
|
|
WorldFrame:SetScript("OnMouseDown", function(this, ...)
|
|
oldX,oldY = GetCursorPosition()
|
|
clickTime = GetTime()
|
|
if WorldFrame_OnMouseDown then
|
|
WorldFrame_OnMouseDown(this, ...)
|
|
end
|
|
end)
|
|
|
|
WorldFrame:SetScript("OnMouseUp", function(this, ...)
|
|
local x,y = GetCursorPosition()
|
|
if not oldX or not oldY or not x or not y or not clickTime then
|
|
self:Close()
|
|
if WorldFrame_OnMouseUp then
|
|
WorldFrame_OnMouseUp(this, ...)
|
|
end
|
|
return
|
|
end
|
|
local d = math.abs(x - oldX) + math.abs(y - oldY)
|
|
if d <= 5 and GetTime() - clickTime < 0.5 then
|
|
self:Close()
|
|
end
|
|
if WorldFrame_OnMouseUp then
|
|
WorldFrame_OnMouseUp(this, ...)
|
|
end
|
|
end)
|
|
|
|
hooksecurefunc(DropDownList1, "Show", function()
|
|
if levels[1] and levels[1]:IsVisible() then
|
|
self:Close()
|
|
end
|
|
end)
|
|
|
|
hooksecurefunc("HideDropDownMenu", function()
|
|
if levels[1] and levels[1]:IsVisible() then
|
|
self:Close()
|
|
end
|
|
end)
|
|
|
|
hooksecurefunc("CloseDropDownMenus", function()
|
|
if levels[1] and levels[1]:IsVisible() then
|
|
local stack = debugstack()
|
|
if not stack:find("`TargetFrame_OnHide'") then
|
|
self:Close()
|
|
end
|
|
end
|
|
end)
|
|
end
|
|
self.frame = oldLib and oldLib.frame or CreateFrame("Frame")
|
|
self.frame:UnregisterAllEvents()
|
|
self.frame:RegisterEvent("PLAYER_REGEN_ENABLED")
|
|
self.frame:RegisterEvent("PLAYER_REGEN_DISABLED")
|
|
self.frame:Hide()
|
|
self.frame:SetScript("OnEvent", function(this, event)
|
|
this:Show()
|
|
if event=="PLAYER_REGEN_ENABLED" then -- track combat state for secure frame operations
|
|
self.combat = false
|
|
elseif event=="PLAYER_REGEN_DISABLED" then
|
|
self.combat = true
|
|
end
|
|
end)
|
|
self.frame:SetScript("OnUpdate", function(this)
|
|
this:Hide()
|
|
self:Refresh(1)
|
|
end)
|
|
self.hookedTooltip = true
|
|
if not oldLib or not oldLib.hookedTooltip then
|
|
local OnTooltipHide = GameTooltip:GetScript("OnHide")
|
|
GameTooltip:SetScript("OnHide", function(this, ...)
|
|
if OnTooltipHide then
|
|
OnTooltipHide(this, ...)
|
|
end
|
|
if type(self.OnTooltipHide) == "function" then
|
|
self:OnTooltipHide()
|
|
end
|
|
end)
|
|
end
|
|
levels = {}
|
|
buttons = {}
|
|
|
|
if oldDeactivate then
|
|
oldDeactivate(oldLib)
|
|
end
|
|
end
|
|
|
|
local function external(lib, major, instance)
|
|
if major == "LibSharedMedia-3.0" then
|
|
SharedMedia = instance
|
|
end
|
|
end
|
|
|
|
AceLibrary:Register(Dewdrop, MAJOR_VERSION, MINOR_VERSION, activate, nil, external)
|