You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

345 lines
10 KiB

--[[
DropMenu.lua
@Author : DengSir (tdaddon@163.com)
@Link : https://dengsir.github.io
]]
local MAJOR, MINOR = 'DropMenu', 2
local GUI = LibStub('tdGUI-1.0')
local DropMenu, oldminor, ns = GUI:NewClass(MAJOR, MINOR, GUI:GetClass('GridView'))
if not DropMenu then return end
ns._Objects = ns._Objects or {}
local function check(value, data, owner)
if type(value) == 'function' then
return value(data, owner)
end
return value
end
function DropMenu:Constructor(_, style, withOtherMenu, menuList, level)
self.menuList = menuList or setmetatable({}, {__index = function(t, i)
local menu = DropMenu:New(UIParent, style, withOtherMenu, self.menuList, i)
t[i] = menu
return menu
end})
level = level or 1
if style == 'MENU' then
self:SetPadding(15)
self:SetBackdrop{
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]],
edgeSize = 16, tileSize = 16, tile = true,
insets = {left = 4, right = 4, top = 4, bottom = 4},
}
self:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b)
self:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g, TOOLTIP_DEFAULT_BACKGROUND_COLOR.b)
else
self:SetPadding(18, 18, 15, 15)
self:SetBackdrop{
bgFile = [[Interface\DialogFrame\UI-DialogBox-Background-Dark]],
edgeFile = [[Interface\DialogFrame\UI-DialogBox-Border]],
insets = { left = 11, right = 12, top = 12, bottom = 9 },
tileSize = 32, edgeSize = 32, tile = true,
}
self.scrollBar:ClearAllPoints()
self.scrollBar:SetPoint('TOPRIGHT', -10, -26)
self.scrollBar:SetPoint('BOTTOMRIGHT', -10, 26)
end
self:Hide()
self:SetSize(100, 100)
self:SetItemClass(GUI:GetClass('DropMenuItem'))
self:SetItemHeight(16)
self:SetAutoSize(true)
self:SetClampedToScreen(true)
self:SetFrameStrata('FULLSCREEN_DIALOG')
self:EnableMouse(true)
self:SetLevel(level)
self:SetCallback('OnItemFormatting', self.OnItemFormatting)
self:SetCallback('OnItemClick', self.OnItemClick)
self:SetCallback('OnItemEnter', self.OnItemEnter)
self:SetCallback('OnItemLeave', self.OnItemLeave)
self:SetScript('OnHide', self.OnHide)
self.withOtherMenu = withOtherMenu
if level == 1 then
tinsert(ns._Objects, self)
self.menuList[1] = self
local AutoHide = GUI:GetClass('AutoHideController'):New(self) do
AutoHide.IsMenuOver = function()
for _, menu in ipairs(self.menuList) do
if menu:IsMouseOver() then
return true
end
end
end
end
if not withOtherMenu then
AutoHide:SetCallback('OnUpdateCheck', function()
return DropDownList1:IsVisible()
end)
self:SetScript('OnShow', self.CloseOtherMenu)
end
self.AutoHide = AutoHide
else
self:SetCallback('OnRefresh', self.OnRefresh)
self:SetFrameLevel(self.menuList[level-1]:GetFrameLevel()+5)
end
end
function DropMenu:Open(level, menuTable, owner, ...)
if type(menuTable) == 'function' then
local list = {}
menuTable = menuTable(list) or list
end
if type(menuTable) ~= 'table' then
return self:Close()
end
for i = #menuTable, 1, -1 do
local v = menuTable[i]
if check(v.hidden, v, owner) then
tremove(menuTable, i)
end
end
level = level or 1
for i = level + 1, #self.menuList do
self.menuList[i]:Hide()
end
if not self.withOtherMenu then
CloseDropDownMenus(1)
end
local maxItem, hasMaxItem
if select('#', ...) > 0 then
local arg1 = ...
if type(arg1) == 'number' or type(arg1) == 'nil' then
maxItem = tonumber(arg1)
hasMaxItem = true
end
end
local menu = self.menuList[level]
menu.maxItem = maxItem
menu:SetRowCount(maxItem)
menu:SetScrollStep(max((maxItem or 0)-1, 1))
if menu:GetItemList() ~= menuTable then
menu:SetOffset(1)
end
menu:SetItemList(menuTable)
menu:SetOwner(owner)
menu:ClearAllPoints()
menu:SetPoint(self:GetOpenPosition(owner, select(hasMaxItem and 2 or 1, ...)))
menu:Show()
menu:Refresh()
menu:SetScale(menu:GetParent() and 1
or GetCVarBool('useUIScale') and min(UIParent:GetScale(), tonumber(GetCVar('uiscale')))
or UIParent:GetScale())
end
function DropMenu:Toggle(level, menuTable, owner, ...)
level = level or 1
local menu = self.menuList[level]
if menu:IsShown() and menu:GetOwner() == owner then
menu:Hide()
return
end
menu:Hide()
self:Open(level, menuTable, owner, ...)
end
function DropMenu:RefreshMenu(level)
local menu = self.menuList[level or 1]
if menu then
menu:Refresh()
end
end
function DropMenu:Close(level)
level = level or 1
self.menuList[level]:Hide()
end
function DropMenu:CloseOtherMenu()
for _, object in ipairs(ns._Objects) do
if object ~= self then
object:Close(1)
end
end
end
function DropMenu:OnRefresh()
local prevMenu = self.menuList[self:GetLevel() - 1]
local anchor = select(2, self:GetPoint())
self:ClearAllPoints()
if self:GetWidth() > GetScreenWidth() - prevMenu:GetRight() then
self:SetPoint('TOPRIGHT', anchor, 'TOPLEFT', 0, 10)
else
self:SetPoint('TOPLEFT', anchor, 'TOPRIGHT', 0, 10)
end
end
function DropMenu:OnItemClick(button, data)
if data.notClickable then
return
end
if not data.keepShownOnClick then
self:Hide()
end
local owner = self:GetOwner()
if type(data.func) == 'function' then
-- if data.confirm then
-- if data.confirmInput then
-- GUI:CallInputDialog(data.confirm, data.func, data.confirmKey, data.confirmDefault, data.confirmMaxBytes, data.confirmInput, owner, data)
-- else
-- GUI:CallMessageDialog(data.confirm, data.func, data.confirmKey, owner, data)
-- end
-- else
data.func(owner, data, self, button:GetChecked())
-- end
end
if owner then
if type(owner.SetItem) == 'function' then
owner:SetItem(data)
end
end
if data.keepShownOnClick then
self:Refresh()
end
if data.refreshParentOnClick and self:GetLevel() > 1 then
self:RefreshMenu(self:GetLevel() - 1)
end
end
function DropMenu:OnItemFormatting(button, data)
if data.isSeparator then
button:Disable()
button:SetText()
button:SetCheckState()
button:SetHasArrow()
button:SetSeparator(true)
else
button:SetText(check(data.text, self:GetOwner()))
button:SetCheckState(data.checkable, data.isNotRadio, check(data.checked, data, self:GetOwner()))
button:SetHasArrow(data.hasArrow)
button:SetSeparator()
button:SetFontObject(check(data.fontObject, data, self:GetOwner()))
if check(data.disabled, data, self:GetOwner()) then
button:Disable()
button:SetDisabledFontObject('GameFontDisableSmallLeft')
elseif data.isTitle then
button:Disable()
button:SetDisabledFontObject('GameFontNormalSmallLeft')
else
button:Enable()
end
end
button:SetWidth(button:GetAutoWidth())
end
function DropMenu:OnItemEnter(button, data)
if not data.hasArrow then
self:Close(self:GetLevel() + 1)
else
self:Open(self:GetLevel() + 1,
data.menuTable,
self:GetOwner(), self.maxItem, 'TOPLEFT', button, 'TOPRIGHT', 0, 10)
end
local tip = GameTooltip
if (data.tooltipTitle or data.tooltipText or data.tooltipMore) and (data.tooltipWhileDisabled or button:IsEnabled()) then
if data.tooltipOnButton then
tip:SetOwner(button, button:GetLeft() < GetScreenWidth() / 2 and 'ANCHOR_RIGHT' or 'ANCHOR_LEFT')
else
GameTooltip_SetDefaultAnchor(tip, button)
end
if data.tooltipTitle then
tip:SetText(data.tooltipTitle, 1, 1, 1)
end
if data.tooltipText then
tip:AddLine(data.tooltipText, nil, nil, nil, true)
end
if type(data.tooltipMore) == 'function' then
data.tooltipMore(tip, data)
end
tip:Show()
end
end
function DropMenu:OnItemLeave()
GameTooltip:Hide()
end
function DropMenu:OnHide()
self:Close(self:GetLevel() + 1)
end
function DropMenu:GetOpenPosition(owner, point, ...)
if point == 'cursor' then
local x, y = GetCursorPosition()
local uiScale = owner:GetEffectiveScale()
x = x / uiScale - owner:GetLeft() + 20
y = y / uiScale - owner:GetTop() - 2
return 'TOPLEFT', owner, 'TOPLEFT', x, y
elseif point then
return point, ...
elseif owner:GetBottom() >= GetScreenHeight() - owner:GetTop() then
return 'TOPLEFT', owner, 'BOTTOMLEFT'
else
return 'BOTTOMLEFT', owner, 'TOPLEFT'
end
end
function DropMenu:SetLevel(level)
self.level = level
end
function DropMenu:GetLevel()
return self.level
end
---- Public
if ns.Public then
ns.Public:Hide()
ns.Public:SetParent(nil)
tDeleteItem(ns._Objects, ns.Public)
end
ns.Public = GUI:GetClass('DropMenu'):New(nil, 'MENU')
function GUI:OpenMenu(owner, menuTable, ...)
return ns.Public:Open(1, menuTable, owner, ...)
end
function GUI:ToggleMenu(owner, menuTable, ...)
return ns.Public:Toggle(1, menuTable, owner, ...)
end
function GUI:CloseMenu()
return ns.Public:Close(1)
end
function GUI:RefreshMenu(level)
return ns.Public:RefreshMenu(level)
end