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.
346 lines
10 KiB
346 lines
10 KiB
|
2 years ago
|
--[[
|
||
|
|
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
|