342 lines
12 KiB
Lua
342 lines
12 KiB
Lua
-- This can either be used as a normal dropdown or a context menu. If the parent is UIParent, it will be a context menu.
|
|
-- Otherwise, it is a dropdown. This can be changed during runtime simply by calling SetParent through the GUI component.
|
|
-- The positioning of the dropdown's components are modified to behave like you'd expect. SetWidth actually sets the width.
|
|
-- SetHeight does nothing, and will always be 25 px.
|
|
|
|
-- Initialization is handled by this component. Use SetOptions to set the options table.
|
|
-- === New Fields ===
|
|
-- initFunc(self, gui) - Runs on level initialization before the button is added
|
|
-- dropdownText - The text the dropdown will be set to when this option is clicked, runs before "func"
|
|
-- closeOnClick - If true, closes all menus regardless of level, runs after "func"
|
|
-- children - Array for a next level menu; If present, "hasArrow" is automatically set to true
|
|
-- parent [Automatically Assigned] - Reference to the previous level element, or nil if root
|
|
-- siblings [Automatically Assigned] - Reference to the array of elements in the level
|
|
-- gui [Automatically Assigned] - Reference to the GUI instance
|
|
-- === Adjusted Existing Fields ===
|
|
-- func(self, gui) - Always passes self as the first argument and the GUI instance as the second argument
|
|
-- arg1, arg2 - Do not use, now internally used to pass self and gui to "func"
|
|
-- value - Do not use, now internally used to reference self
|
|
|
|
PTGuiDropdown = PTGuiComponent:Extend("dropdown")
|
|
local _G = getfenv(0)
|
|
local compost = PTPool
|
|
|
|
PTGuiDropdown.Options = nil
|
|
|
|
function PTGuiDropdown:New()
|
|
local obj = setmetatable({}, self)
|
|
local dropdown = CreateFrame("Frame", self:GenerateName(), nil, "UIDropDownMenuTemplate")
|
|
obj:SetHandle(dropdown)
|
|
-- Make SetWidth actually set the width, like one would hope
|
|
local dropdownSetWidth = dropdown.SetWidth
|
|
dropdownSetWidth(dropdown, 0) -- Allows points to size the dropdown
|
|
dropdown.SetWidth = function(self, width)
|
|
_G[dropdown:GetName().."Middle"]:SetWidth(width - 15)
|
|
dropdownSetWidth(self, width)
|
|
_G[dropdown:GetName().."Text"]:SetWidth(width - 25)
|
|
dropdown.noResize = 1
|
|
end
|
|
-- The dropdown doesn't stretch vertically, so the height is always 25
|
|
local dropdownSetHeight = dropdown.SetHeight
|
|
dropdown.SetHeight = function(self, height)
|
|
dropdownSetHeight(self, 25)
|
|
end
|
|
obj:SetShowShadow(true) -- Need to do this on creation to fix the visual position
|
|
-- Make clicking anywhere on the dropdown open the menu
|
|
dropdown:EnableMouse(true)
|
|
dropdown:SetScript("OnMouseUp", function()
|
|
if MouseIsOver(this) then
|
|
PTUtil.CallWithThis(_G[this:GetName().."Button"], _G[this:GetName().."Button"]:GetScript("OnClick"))
|
|
end
|
|
end)
|
|
dropdown:SetScript("OnSizeChanged", function()
|
|
local width = this:GetWidth()
|
|
_G[this:GetName().."Middle"]:SetWidth(width - 15)
|
|
dropdownSetWidth(this, width)
|
|
_G[this:GetName().."Text"]:SetWidth(width - 25)
|
|
end)
|
|
return obj
|
|
end
|
|
|
|
local partProps = {
|
|
Default = {
|
|
Left = {
|
|
Width = 25, Height = 64,
|
|
Coords = {0, 0.1953125, 0, 1},
|
|
Point = {"TOPLEFT", "$parent", "TOPLEFT", -18, 18}
|
|
},
|
|
Middle = {
|
|
Width = 115, Height = 64,
|
|
Coords = {0.1953125, 0.8046875, 0, 1},
|
|
Point = {"LEFT", "$parentLeft", "RIGHT"}
|
|
},
|
|
Right = {
|
|
Width = 25, Height = 64,
|
|
Coords = {0.8046875, 1, 0, 1},
|
|
Point = {"LEFT", "$parentMiddle", "RIGHT"}
|
|
},
|
|
Text = {
|
|
Point = {"RIGHT", "$parentRight", -43, 2}
|
|
},
|
|
Button = {
|
|
Point = {"TOPRIGHT", "$parentRight", -16, -18}
|
|
}
|
|
},
|
|
Shadowless = {
|
|
Left = {
|
|
Width = 6, Height = 24,
|
|
Coords = {18 / 128, (18 + 6) / 128, 19 / 64, (19 + 24) / 64},
|
|
Point = {"TOPLEFT", 0, -1}
|
|
},
|
|
Middle = {
|
|
Width = 115, Height = 24,
|
|
Coords = {0.1953125, 0.8046875, 19 / 64, (19 + 24) / 64},
|
|
Point = {"LEFT", "$parentLeft", "RIGHT", 0, 0}
|
|
},
|
|
Right = {
|
|
Width = 9, Height = 24,
|
|
Coords = {102 / 128, (102 + 9) / 128, 19 / 64, (19 + 24) / 64},
|
|
Point = {"LEFT", "$parentMiddle", "RIGHT", 0, 0}
|
|
},
|
|
Text = {
|
|
Point = {"RIGHT", "$parentRight", -26, 1}
|
|
},
|
|
Button = {
|
|
Point = {"TOPRIGHT", "$parentRight", 1, 1}
|
|
}
|
|
}
|
|
}
|
|
function PTGuiDropdown:SetShowShadow(showShadow)
|
|
local frameName = self:GetHandle():GetName()
|
|
for partName, props in pairs(showShadow and partProps.Default or partProps.Shadowless) do
|
|
local part = _G[frameName..partName]
|
|
if props.Coords then
|
|
part:SetTexCoord(unpack(props.Coords))
|
|
end
|
|
if props.Width then
|
|
part:SetWidth(props.Width)
|
|
part:SetHeight(props.Height)
|
|
end
|
|
part:ClearAllPoints()
|
|
part:SetPoint(unpack(props.Point))
|
|
end
|
|
self:SetWidth(self:GetWidth())
|
|
UIDropDownMenu_SetAnchor(showShadow and 18 or 0, showShadow and 17 or 0, self:GetHandle(), "TOPLEFT",
|
|
_G[frameName.."Left"], "BOTTOMLEFT")
|
|
return self
|
|
end
|
|
|
|
local defaultTextures = {
|
|
ButtonNormalTexture = "Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Up",
|
|
ButtonDisabledTexture = "Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Disabled",
|
|
ButtonPushedTexture = "Interface\\ChatFrame\\UI-ChatIcon-ScrollDown-Down",
|
|
ButtonHighlightTexture = "Interface\\Buttons\\UI-Common-MouseHilight"
|
|
}
|
|
function PTGuiDropdown:SetParent(obj)
|
|
local menuType = obj == UIParent and "MENU" or nil
|
|
if menuType ~= "MENU" and self:GetHandle().displayMode == "MENU" then
|
|
-- Show the dropdown if we're switching from a context menu to a dropdown
|
|
-- BROKEN: When put back as a context menu, the button shows forever???
|
|
local frameName = self:GetHandle():GetName()
|
|
_G[frameName.."Left"]:Show()
|
|
_G[frameName.."Middle"]:Show()
|
|
_G[frameName.."Right"]:Show()
|
|
for texName, texPath in pairs(defaultTextures) do
|
|
_G[frameName..texName]:SetTexture(texPath)
|
|
end
|
|
self:SetShowShadow(true)
|
|
self:GetHandle().displayMode = nil
|
|
end
|
|
UIDropDownMenu_Initialize(self:GetHandle(), function(level)
|
|
self:Initialize(level)
|
|
end, menuType)
|
|
self.super.SetParent(self, obj)
|
|
end
|
|
|
|
function PTGuiDropdown:OnDispose()
|
|
self:SetToggleState(false)
|
|
self:SetShowShadow(true)
|
|
self.super.OnDispose(self)
|
|
|
|
self.Options = nil
|
|
if self.DynamicOptions then
|
|
compost:Reclaim(self.DynamicOptions, 1)
|
|
self.DynamicOptionsArgs = nil
|
|
end
|
|
self.TextUpdater = nil
|
|
self:SetText("")
|
|
end
|
|
|
|
local dynamicOptions
|
|
local function addDynamicOption(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
|
|
local option = compost:AcquireHash(k1,v1,k2,v2,k3,v3,k4,v4,k5,v5,k6,v6,k7,v7,k8,v8,k9,v9,k10,v10)
|
|
table.insert(dynamicOptions, option)
|
|
end
|
|
|
|
function PTGuiDropdown:Initialize(level)
|
|
if not self.Options then
|
|
return
|
|
end
|
|
level = level or 1
|
|
local options = self.Options
|
|
if type(options) == "function" then
|
|
if self.DynamicOptions then
|
|
compost:Reclaim(self.DynamicOptions, 1)
|
|
end
|
|
self.DynamicOptions = compost:GetTable()
|
|
dynamicOptions = self.DynamicOptions
|
|
options(addDynamicOption, level, self.DynamicOptionsArgs)
|
|
options = dynamicOptions
|
|
self:BakeOptions(options)
|
|
end
|
|
local value = UIDROPDOWNMENU_MENU_VALUE
|
|
options = level == 1 and options or value.children
|
|
for _, option in ipairs(options) do
|
|
if option.initFunc then
|
|
option:initFunc(self)
|
|
end
|
|
UIDropDownMenu_AddButton(option, level)
|
|
end
|
|
end
|
|
|
|
-- Similar to normal dropdown initialization. Really only should be used for single level options.
|
|
-- The initFunc receives a function to add options, use in the style of Compost:AcquireHash
|
|
-- args are passed into the initFunc
|
|
function PTGuiDropdown:SetDynamicOptions(initFunc, args)
|
|
self.Options = initFunc
|
|
self.DynamicOptionsArgs = args
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:SetSimpleOptions(options, createFunc, dropdownText)
|
|
local optTable = {}
|
|
for _, option in ipairs(options) do
|
|
table.insert(optTable, createFunc(option))
|
|
end
|
|
self:SetOptions(optTable)
|
|
if dropdownText then
|
|
self:SetText(dropdownText)
|
|
end
|
|
return self
|
|
end
|
|
|
|
-- Sets the options for this dropdown. If elements are added afterwards, it is mandatory that this function is called again.
|
|
function PTGuiDropdown:SetOptions(options)
|
|
self.Options = options
|
|
self:BakeOptions(options)
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:BakeOptions(options, parent)
|
|
for _, opt in ipairs(options) do
|
|
opt.parent = parent
|
|
opt.siblings = options
|
|
opt.value = opt
|
|
opt.arg1 = opt
|
|
opt.arg2 = self
|
|
opt.gui = self
|
|
if opt.dropdownText or opt.closeOnClick then
|
|
local func = opt.func
|
|
opt.func = function(self, gui)
|
|
if self.dropdownText then
|
|
gui:SetText(self.dropdownText)
|
|
end
|
|
if func then
|
|
func(self, gui)
|
|
end
|
|
if self.closeOnClick then
|
|
gui:SetToggleState(false)
|
|
end
|
|
end
|
|
end
|
|
if opt.children then
|
|
opt.hasArrow = true
|
|
self:BakeOptions(opt.children, opt)
|
|
end
|
|
end
|
|
end
|
|
|
|
function PTGuiDropdown:SetTextBy(field, value, textField)
|
|
for _, option in ipairs(self.Options) do
|
|
if option[field] == value then
|
|
self:SetText(option[textField or "text"])
|
|
return self
|
|
end
|
|
end
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:SetTextUpdater(updater, noImmediateUpdate)
|
|
self.TextUpdater = updater
|
|
if not noImmediateUpdate then
|
|
self:TextUpdater()
|
|
end
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:UpdateText()
|
|
if self.TextUpdater then
|
|
self:TextUpdater()
|
|
end
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:SetText(text)
|
|
UIDropDownMenu_SetText(text, self:GetHandle())
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:GetText()
|
|
return UIDropDownMenu_GetText(self:GetHandle())
|
|
end
|
|
|
|
local dropdownList1 = _G["DropDownList1"]
|
|
function PTGuiDropdown:SetTimer(time)
|
|
dropdownList1.showTimer = time
|
|
end
|
|
|
|
function PTGuiDropdown:StopCounting()
|
|
dropdownList1.isCounting = nil
|
|
end
|
|
|
|
local keepOpenFrame = CreateFrame("Frame", "PTGuiDropdownKeepOpen")
|
|
local keepOpenDropdown
|
|
local checkNext
|
|
local keepOpenScript = function()
|
|
if GetTime() > checkNext then
|
|
if not keepOpenDropdown:IsToggledOn() then
|
|
keepOpenFrame:SetScript("OnUpdate", nil)
|
|
return
|
|
end
|
|
checkNext = GetTime() + 1
|
|
dropdownList1.showTimer = 10
|
|
dropdownList1.isCounting = nil
|
|
end
|
|
end
|
|
-- Keeps the dropdown open until it is forcibly hidden
|
|
function PTGuiDropdown:SetKeepOpen(keepOpen)
|
|
if not self:IsToggledOn() then
|
|
return self
|
|
end
|
|
if keepOpen then
|
|
checkNext = 0
|
|
keepOpenDropdown = self
|
|
keepOpenFrame:SetScript("OnUpdate", keepOpenScript)
|
|
else
|
|
keepOpenFrame:SetScript("OnUpdate", nil)
|
|
end
|
|
return self
|
|
end
|
|
|
|
function PTGuiDropdown:IsToggledOn()
|
|
return (dropdownList1:IsVisible() ~= nil) and (UIDROPDOWNMENU_OPEN_MENU == self:GetName())
|
|
end
|
|
|
|
function PTGuiDropdown:SetToggleState(shown, anchor, xOffset, yOffset)
|
|
if self:IsToggledOn() ~= shown then
|
|
ToggleDropDownMenu(1, nil, self:GetHandle(), anchor and anchor:GetName() or nil, xOffset, yOffset)
|
|
end
|
|
return self
|
|
end
|
|
|
|
PTGuiLib.RegisterComponent(PTGuiDropdown) |