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.
867 lines
29 KiB
867 lines
29 KiB
|
5 years ago
|
--========================================================--
|
||
|
|
-- Scorpio UIDropDownMenu Widget --
|
||
|
|
-- --
|
||
|
|
-- Author : kurapica125@outlook.com --
|
||
|
|
-- Create Date : 2020/03/05 --
|
||
|
|
--========================================================--
|
||
|
|
|
||
|
|
--========================================================--
|
||
|
|
Scorpio "Scorpio.Widget.UIDropDownMenu" "1.0.0"
|
||
|
|
--========================================================--
|
||
|
|
|
||
|
|
local DELAY_TO_HIDE = 1.5
|
||
|
|
|
||
|
|
local _UIDropDownListLevel = {}
|
||
|
|
local _UIDropDownMenuButton = {}
|
||
|
|
local _UIDropDownCountDown = {}
|
||
|
|
|
||
|
|
local function autoHideMenuList()
|
||
|
|
while #_UIDropDownListLevel > 0 do
|
||
|
|
local mouseover = #_UIDropDownListLevel
|
||
|
|
|
||
|
|
while mouseover > 0 and not (_UIDropDownListLevel[mouseover]:IsMouseOver() or mouseover > 1 and _UIDropDownMenuButton[mouseover]:IsMouseOver()) do
|
||
|
|
mouseover = mouseover - 1
|
||
|
|
end
|
||
|
|
|
||
|
|
local now = GetTime()
|
||
|
|
|
||
|
|
-- Refreshing the hide delay
|
||
|
|
for i = 1, mouseover do
|
||
|
|
_UIDropDownCountDown[i] = now + DELAY_TO_HIDE
|
||
|
|
end
|
||
|
|
|
||
|
|
for i = mouseover + 1, #_UIDropDownListLevel do
|
||
|
|
if _UIDropDownCountDown[i] then
|
||
|
|
if _UIDropDownCountDown[i] <= now then
|
||
|
|
for j = #_UIDropDownListLevel, i, -1 do
|
||
|
|
_UIDropDownListLevel[j]:Hide()
|
||
|
|
_UIDropDownListLevel[j] = nil
|
||
|
|
_UIDropDownMenuButton[j]= nil
|
||
|
|
_UIDropDownCountDown[j] = nil
|
||
|
|
end
|
||
|
|
break
|
||
|
|
end
|
||
|
|
else
|
||
|
|
_UIDropDownCountDown[i] = _UIDropDownCountDown[i - 1] or (now + DELAY_TO_HIDE)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
Next()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function adjustLocation(self, owner, anchor)
|
||
|
|
anchor = anchor or "ANCHOR_TOPRIGHT"
|
||
|
|
|
||
|
|
local scale = self:GetEffectiveScale()
|
||
|
|
|
||
|
|
local width = self:GetWidth() * scale
|
||
|
|
local height = self:GetHeight() * scale
|
||
|
|
|
||
|
|
local x, y
|
||
|
|
if owner then
|
||
|
|
local ownerScale = owner:GetEffectiveScale()
|
||
|
|
|
||
|
|
if anchor == "ANCHOR_TOPRIGHT" then
|
||
|
|
x, y = owner:GetRight() * ownerScale, owner:GetTop() * ownerScale
|
||
|
|
elseif anchor == "ANCHOR_RIGHT" then
|
||
|
|
x, y = owner:GetRight() * ownerScale, select(2, owner:GetCenter()) * ownerScale
|
||
|
|
elseif anchor == "ANCHOR_BOTTOMRIGHT" then
|
||
|
|
x, y = owner:GetRight() * ownerScale - width, owner:GetBottom() * ownerScale
|
||
|
|
elseif anchor == "ANCHOR_TOPLEFT" then
|
||
|
|
x, y = owner:GetLeft() * ownerScale - width, owner:GetTop() * ownerScale
|
||
|
|
elseif anchor == "ANCHOR_LEFT" then
|
||
|
|
x, y = owner:GetLeft() * ownerScale - width, select(2, owner:GetCenter()) * ownerScale
|
||
|
|
elseif anchor == "ANCHOR_BOTTOMLEFT" then
|
||
|
|
x, y = owner:GetLeft() * ownerScale, owner:GetBottom() * ownerScale
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
if not (x and y) then x, y = GetCursorPosition() end
|
||
|
|
|
||
|
|
if self.OwnerOffset then
|
||
|
|
x = x + self.OwnerOffset.x * scale
|
||
|
|
y = y + self.OwnerOffset.y * scale
|
||
|
|
end
|
||
|
|
|
||
|
|
x = math.min(math.max(x, 0), GetScreenWidth() * scale - width)
|
||
|
|
y = math.max(y, height)
|
||
|
|
|
||
|
|
self:ClearAllPoints()
|
||
|
|
self:SetPoint("TOPLEFT", UIParent, "BOTTOMLEFT", x / scale, y / scale)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function showMenuList(self)
|
||
|
|
if #_UIDropDownListLevel == 0 then
|
||
|
|
-- Start the scan, don't use OnEnter/OnLeave since it's too hard to keeping tracking
|
||
|
|
Next(autoHideMenuList)
|
||
|
|
end
|
||
|
|
|
||
|
|
-- Reset the count down
|
||
|
|
wipe(_UIDropDownCountDown)
|
||
|
|
if _UIDropDownListLevel[1] == self then return end
|
||
|
|
|
||
|
|
for i = #_UIDropDownListLevel, 1, -1 do
|
||
|
|
_UIDropDownListLevel[i]:Hide()
|
||
|
|
_UIDropDownListLevel[i] = nil
|
||
|
|
_UIDropDownMenuButton[i]= nil
|
||
|
|
end
|
||
|
|
|
||
|
|
_UIDropDownListLevel[1] = self
|
||
|
|
|
||
|
|
for _, child in UIObject.GetChilds(self) do
|
||
|
|
if Class.IsObjectType(child, UIDropDownMenuButton) then
|
||
|
|
child.MenuLevel = 2
|
||
|
|
child:SetFrameLevel(self:GetFrameLevel() + 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
adjustLocation(self, self.Owner, self.Anchor)
|
||
|
|
self:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
local function showSubList(self)
|
||
|
|
local level = self.MenuLevel
|
||
|
|
local submenu = self.SubMenu
|
||
|
|
|
||
|
|
if not (level and submenu and self:IsShown()) then return end
|
||
|
|
if _UIDropDownListLevel[level] == submenu then return end
|
||
|
|
|
||
|
|
submenu:SetFrameLevel(self:GetFrameLevel() + 2)
|
||
|
|
|
||
|
|
for i = #_UIDropDownListLevel, level, -1 do
|
||
|
|
_UIDropDownListLevel[i]:Hide()
|
||
|
|
_UIDropDownListLevel[i] = nil
|
||
|
|
_UIDropDownCountDown[i] = nil
|
||
|
|
_UIDropDownMenuButton[i]= nil
|
||
|
|
end
|
||
|
|
|
||
|
|
_UIDropDownListLevel[level] = submenu
|
||
|
|
_UIDropDownMenuButton[level]= self
|
||
|
|
|
||
|
|
for _, child in UIObject.GetChilds(submenu) do
|
||
|
|
if Class.IsObjectType(child, UIDropDownMenuButton) then
|
||
|
|
child.MenuLevel = level + 1
|
||
|
|
child:SetFrameLevel(submenu:GetFrameLevel() + 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
adjustLocation(submenu, self)
|
||
|
|
submenu:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
local function closeMenuList(self)
|
||
|
|
if not self or _UIDropDownListLevel[1] == self then
|
||
|
|
for i = #_UIDropDownListLevel, 1, -1 do
|
||
|
|
_UIDropDownListLevel[i]:Hide()
|
||
|
|
_UIDropDownListLevel[i] = nil
|
||
|
|
_UIDropDownCountDown[i] = nil
|
||
|
|
_UIDropDownMenuButton[i]= nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function closeSubList(self)
|
||
|
|
local level = self.MenuLevel
|
||
|
|
local submenu = self.SubMenu
|
||
|
|
if not (level and submenu and _UIDropDownListLevel[level] == submenu) then return end
|
||
|
|
|
||
|
|
for i = #_UIDropDownListLevel, level, -1 do
|
||
|
|
_UIDropDownListLevel[i]:Hide()
|
||
|
|
_UIDropDownListLevel[i] = nil
|
||
|
|
_UIDropDownCountDown[i] = nil
|
||
|
|
_UIDropDownMenuButton[i]= nil
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function tryCloseMenuList()
|
||
|
|
return Next(closeMenuList)
|
||
|
|
end
|
||
|
|
|
||
|
|
--- Secure hook the gobal mouse event to auto close the drop down list
|
||
|
|
if Scorpio.IsRetail then
|
||
|
|
__SecureHook__()
|
||
|
|
function UIDropDownMenu_HandleGlobalMouseEvent(button, event)
|
||
|
|
-- must use the mouse up here
|
||
|
|
if event == "GLOBAL_MOUSE_UP" and (button == "LeftButton" or button == "RightButton") then
|
||
|
|
return Next(closeMenuList)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
tryCloseMenuList = function() end
|
||
|
|
end
|
||
|
|
|
||
|
|
-----------------------------------------------------------
|
||
|
|
-- UIDropDownMenu Widget --
|
||
|
|
-----------------------------------------------------------
|
||
|
|
--- The UI drop down menu button template
|
||
|
|
__Sealed__()
|
||
|
|
class "UIDropDownMenuButton" (function(_ENV)
|
||
|
|
inherit "Button"
|
||
|
|
|
||
|
|
export { IsObjectType = Class.IsObjectType, GetChilds = UIObject.GetChilds }
|
||
|
|
|
||
|
|
local function refreshCheckState(self)
|
||
|
|
self:GetChild("Check"):Hide()
|
||
|
|
self:GetChild("UnCheck"):Hide()
|
||
|
|
self:GetChild("RadioCheck"):Hide()
|
||
|
|
self:GetChild("RadioUnCheck"):Hide()
|
||
|
|
|
||
|
|
if self.IsCheckButton then
|
||
|
|
if self:GetParent() and self:GetParent().IsMultiCheck then
|
||
|
|
if self.Checked then
|
||
|
|
self:GetChild("Check"):Show()
|
||
|
|
else
|
||
|
|
self:GetChild("UnCheck"):Show()
|
||
|
|
end
|
||
|
|
else
|
||
|
|
if self.Checked then
|
||
|
|
self:GetChild("RadioCheck"):Show()
|
||
|
|
else
|
||
|
|
self:GetChild("RadioUnCheck"):Show()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function refreshIcon(self)
|
||
|
|
local icon = self:GetChild("DisplayIcon")
|
||
|
|
|
||
|
|
if self.MouseOverIcon and self:IsMouseOver() then
|
||
|
|
icon:SetTexture(self.MouseOverIcon)
|
||
|
|
icon:Show()
|
||
|
|
elseif self.Icon then
|
||
|
|
icon:SetTexture(self.Icon)
|
||
|
|
icon:Show()
|
||
|
|
else
|
||
|
|
icon:Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
-- The event handlers
|
||
|
|
local function OnEnter(self)
|
||
|
|
local color = Color.NORMAL
|
||
|
|
self:GetChild("ColorSwatch"):SetVertexColor(color.r, color.g, color.b)
|
||
|
|
self:GetChild("Highlight"):Show()
|
||
|
|
refreshIcon(self)
|
||
|
|
|
||
|
|
if self.TooltipTitle then
|
||
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
||
|
|
GameTooltip_SetTitle(GameTooltip, self.TooltipTitle)
|
||
|
|
if self.TooltipText then
|
||
|
|
GameTooltip_AddNormalLine(GameTooltip, self.TooltipText, true)
|
||
|
|
end
|
||
|
|
GameTooltip:Show()
|
||
|
|
end
|
||
|
|
|
||
|
|
if not self.Disabled and self.SubMenu then
|
||
|
|
showSubList(self)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function OnLeave(self)
|
||
|
|
local color = Color.HIGHLIGHT
|
||
|
|
self:GetChild("ColorSwatch"):SetVertexColor(color.r, color.g, color.b)
|
||
|
|
self:GetChild("Highlight"):Hide()
|
||
|
|
refreshIcon(self)
|
||
|
|
|
||
|
|
GameTooltip:Hide()
|
||
|
|
end
|
||
|
|
|
||
|
|
local function OnClick(self)
|
||
|
|
if self.Disabled then return true end
|
||
|
|
|
||
|
|
if self.IsColorButton then
|
||
|
|
local color = self.Color
|
||
|
|
|
||
|
|
Scorpio.Continue(function()
|
||
|
|
OnColorChoosed(self, Scorpio.PickColor(color))
|
||
|
|
end)
|
||
|
|
|
||
|
|
tryCloseMenuList()
|
||
|
|
|
||
|
|
-- Block the custom onclick
|
||
|
|
return true
|
||
|
|
elseif self.IsCheckButton then
|
||
|
|
if self:GetParent().IsMultiCheck then
|
||
|
|
self.Checked = not self.Checked
|
||
|
|
OnCheckStateChanged(self, self.Checked)
|
||
|
|
elseif not self.Checked then
|
||
|
|
self.Checked = true
|
||
|
|
self:GetParent():OnCheckStateChanged(self.CheckValue)
|
||
|
|
end
|
||
|
|
|
||
|
|
tryCloseMenuList()
|
||
|
|
|
||
|
|
return true
|
||
|
|
elseif self.SubMenu then
|
||
|
|
if self.SubMenu:IsShown() then
|
||
|
|
closeSubList(self)
|
||
|
|
else
|
||
|
|
showSubList(self)
|
||
|
|
end
|
||
|
|
|
||
|
|
return true
|
||
|
|
end
|
||
|
|
|
||
|
|
tryCloseMenuList()
|
||
|
|
end
|
||
|
|
|
||
|
|
local function InvisibleButton_OnEnter(self)
|
||
|
|
return OnEnter(self:GetParent())
|
||
|
|
end
|
||
|
|
|
||
|
|
local function InvisibleButton_OnLeave(self)
|
||
|
|
return OnLeave(self:GetParent())
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------
|
||
|
|
-- Event --
|
||
|
|
-------------------------------------------------------
|
||
|
|
--- Fired when the color is choosed
|
||
|
|
event "OnColorChoosed"
|
||
|
|
|
||
|
|
--- Fired when the check state changes
|
||
|
|
event "OnCheckStateChanged"
|
||
|
|
|
||
|
|
-------------------------------------------------------
|
||
|
|
-- Property --
|
||
|
|
-------------------------------------------------------
|
||
|
|
--- Whether the button is a check button
|
||
|
|
property "IsCheckButton" { type = Boolean, handler = refreshCheckState }
|
||
|
|
|
||
|
|
--- Whether the button is used for choosing color
|
||
|
|
property "IsColorButton" {
|
||
|
|
type = Boolean,
|
||
|
|
handler = function(self, val)
|
||
|
|
if val then
|
||
|
|
self:GetChild("ColorSwatch"):Show()
|
||
|
|
self:GetChild("ColorSwatchBG"):Show()
|
||
|
|
else
|
||
|
|
self:GetChild("ColorSwatch"):Hide()
|
||
|
|
self:GetChild("ColorSwatchBG"):Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
}
|
||
|
|
|
||
|
|
--- Whether the button is checked
|
||
|
|
property "Checked" { type = Boolean, handler = refreshCheckState }
|
||
|
|
|
||
|
|
--- The check value of the menu button
|
||
|
|
property "CheckValue" { type = Any }
|
||
|
|
|
||
|
|
--- The color property used to hold the color value
|
||
|
|
__Set__(PropertySet.Clone)
|
||
|
|
property "Color" {
|
||
|
|
type = ColorType,
|
||
|
|
handler = function(self, color)
|
||
|
|
if color then
|
||
|
|
self:GetChild("ColorSwatch"):SetColorTexture(color.r, color.g, color.b)
|
||
|
|
else
|
||
|
|
self:GetChild("ColorSwatch"):SetColorTexture(1, 1, 1)
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
}
|
||
|
|
|
||
|
|
--- The sub menu list
|
||
|
|
property "SubMenu" {
|
||
|
|
type = UIObject,
|
||
|
|
handler = function(self, val)
|
||
|
|
if val then
|
||
|
|
self:GetChild("ExpandArrow"):Show()
|
||
|
|
else
|
||
|
|
self:GetChild("ExpandArrow"):Hide()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
}
|
||
|
|
|
||
|
|
--- The icon
|
||
|
|
property "Icon" { type = String + Number, handler = refreshIcon }
|
||
|
|
|
||
|
|
--- The mouse over icon
|
||
|
|
property "MouseOverIcon" { type = String + Number, handler = refreshIcon }
|
||
|
|
|
||
|
|
--- The tooltip title
|
||
|
|
property "TooltipTitle" { type = String }
|
||
|
|
|
||
|
|
--- The tooltip text
|
||
|
|
property "TooltipText" { type = String }
|
||
|
|
|
||
|
|
--- Whether disable the menu button
|
||
|
|
property "Disabled" {
|
||
|
|
type = Boolean,
|
||
|
|
handler = function(self, value)
|
||
|
|
if value then
|
||
|
|
self:Disable()
|
||
|
|
self:GetChild("InvisibleButton"):Show()
|
||
|
|
else
|
||
|
|
self:Enable()
|
||
|
|
self:GetChild("InvisibleButton"):Hide()
|
||
|
|
end
|
||
|
|
end,
|
||
|
|
}
|
||
|
|
|
||
|
|
--- The menu level
|
||
|
|
property "MenuLevel" { type = Number }
|
||
|
|
|
||
|
|
--- AsSeparator
|
||
|
|
property "AsSeparator" { type = Boolean, handler = function(self, val) self:GetChild("Separator"):SetShown(val) end }
|
||
|
|
|
||
|
|
-------------------------------------------------------
|
||
|
|
-- Method --
|
||
|
|
-------------------------------------------------------
|
||
|
|
--- To prevent the auto drop down hidden for sub menu button
|
||
|
|
function HandlesGlobalMouseEvent(self, buttonID, event)
|
||
|
|
return self.SubMenu and buttonID == "LeftButton"
|
||
|
|
end
|
||
|
|
|
||
|
|
-------------------------------------------------------
|
||
|
|
-- Constructor --
|
||
|
|
-------------------------------------------------------
|
||
|
|
__Template__{
|
||
|
|
Highlight = Texture,
|
||
|
|
Check = Texture,
|
||
|
|
UnCheck = Texture,
|
||
|
|
RadioCheck = Texture,
|
||
|
|
RadioUnCheck = Texture,
|
||
|
|
DisplayIcon = Texture,
|
||
|
|
ExpandArrow = Texture,
|
||
|
|
ColorSwatchBG = Texture,
|
||
|
|
ColorSwatch = Texture,
|
||
|
|
InvisibleButton = Button,
|
||
|
|
Separator = Line,
|
||
|
|
}
|
||
|
|
function __ctor(self)
|
||
|
|
self:GetChild("Highlight"):Hide()
|
||
|
|
self:GetChild("Check"):Hide()
|
||
|
|
self:GetChild("UnCheck"):Hide()
|
||
|
|
self:GetChild("RadioCheck"):Hide()
|
||
|
|
self:GetChild("RadioUnCheck"):Hide()
|
||
|
|
self:GetChild("DisplayIcon"):Hide()
|
||
|
|
self:GetChild("ColorSwatch"):Hide()
|
||
|
|
self:GetChild("ColorSwatchBG"):Hide()
|
||
|
|
self:GetChild("ExpandArrow"):Hide()
|
||
|
|
self:GetChild("InvisibleButton"):Hide()
|
||
|
|
self:GetChild("Separator"):Hide()
|
||
|
|
|
||
|
|
self.OnEnter = self.OnEnter + OnEnter
|
||
|
|
self.OnLeave = self.OnLeave + OnLeave
|
||
|
|
self.OnClick = self.OnClick + OnClick
|
||
|
|
|
||
|
|
self:GetChild("InvisibleButton"):SetScript("OnEnter", InvisibleButton_OnEnter)
|
||
|
|
self:GetChild("InvisibleButton"):SetScript("OnLeave", InvisibleButton_OnLeave)
|
||
|
|
end
|
||
|
|
end)
|
||
|
|
|
||
|
|
--- The drop down list template
|
||
|
|
__Sealed__() class "UIDropDownMenuList" (function(_ENV)
|
||
|
|
inherit "Button"
|
||
|
|
|
||
|
|
--- The check state change event, only triggered when IsMultiCheck is false
|
||
|
|
event "OnCheckStateChanged"
|
||
|
|
|
||
|
|
--- The owner of the drop down list
|
||
|
|
property "Owner" { type = UI }
|
||
|
|
|
||
|
|
--- The Anchor to the owner
|
||
|
|
property "Anchor" { type = AnchorType, default = "ANCHOR_TOPRIGHT" }
|
||
|
|
|
||
|
|
--- Whether the check menu buttons on the list is multi choosable
|
||
|
|
property "IsMultiCheck" { type = Boolean, default = true }
|
||
|
|
|
||
|
|
--- The offsets from the frame's edges used to limit the menu buttons
|
||
|
|
property "ContainerInsets" { type = Inset }
|
||
|
|
|
||
|
|
--- The x & y offsets to the owner
|
||
|
|
property "OwnerOffset" { type = Dimension }
|
||
|
|
end)
|
||
|
|
|
||
|
|
--- The drop down list menu template
|
||
|
|
__Sealed__() class "UIDropDownList" { UIDropDownMenuList }
|
||
|
|
|
||
|
|
-----------------------------------------------------------
|
||
|
|
-- UIDropDownMenu Style --
|
||
|
|
-----------------------------------------------------------
|
||
|
|
Style.UpdateSkin("Default", {
|
||
|
|
[UIDropDownMenuButton] = {
|
||
|
|
size = Size(100, 16),
|
||
|
|
normalFont = GameFontHighlightSmallLeft,
|
||
|
|
highlightFont = GameFontHighlightSmallLeft,
|
||
|
|
disabledFont = GameFontDisableSmallLeft,
|
||
|
|
|
||
|
|
ButtonText = {
|
||
|
|
location = { Anchor("LEFT", 18, 0), Anchor("RIGHT", -24, 0) },
|
||
|
|
},
|
||
|
|
|
||
|
|
-- Layer
|
||
|
|
Highlight = {
|
||
|
|
drawLayer = "BACKGROUND",
|
||
|
|
file = [[Interface\QuestFrame\UI-QuestTitleHighlight]],
|
||
|
|
alphaMode = "ADD",
|
||
|
|
location = { Anchor("TOPLEFT"), Anchor("BOTTOMRIGHT") },
|
||
|
|
},
|
||
|
|
Check = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
file = [[Interface\Buttons\UI-CheckBox-Check]],
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("LEFT") },
|
||
|
|
},
|
||
|
|
UnCheck = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
file = [[Interface\Buttons\UI-CheckBox-Up]],
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("LEFT") },
|
||
|
|
},
|
||
|
|
RadioCheck = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
file = [[Interface\Common\UI-DropDownRadioChecks]],
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("LEFT") },
|
||
|
|
texCoords = RectType(0, 0.5, 0.5, 1.0),
|
||
|
|
},
|
||
|
|
RadioUnCheck = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
file = [[Interface\Common\UI-DropDownRadioChecks]],
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("LEFT") },
|
||
|
|
texCoords = RectType(0.5, 1.0, 0.5, 1.0),
|
||
|
|
},
|
||
|
|
DisplayIcon = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("RIGHT") },
|
||
|
|
},
|
||
|
|
ColorSwatchBG = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("RIGHT", -6, 0) },
|
||
|
|
file = [[Interface\ChatFrame\ChatFrameColorSwatch]],
|
||
|
|
},
|
||
|
|
ColorSwatch = {
|
||
|
|
drawLayer = "OVERLAY",
|
||
|
|
size = Size(14, 14),
|
||
|
|
location = { Anchor("CENTER", 0, 0, "ColorSwatchBG") },
|
||
|
|
},
|
||
|
|
ExpandArrow = {
|
||
|
|
drawLayer = "ARTWORK",
|
||
|
|
file = [[Interface\ChatFrame\ChatFrameExpandArrow]],
|
||
|
|
size = Size(16, 16),
|
||
|
|
location = { Anchor("RIGHT", 0, 0) },
|
||
|
|
},
|
||
|
|
InvisibleButton = {
|
||
|
|
location = { Anchor("TOPLEFT"), Anchor("BOTTOMLEFT"), Anchor("RIGHT", 0, 0, "ColorSwatch", "LEFT") },
|
||
|
|
},
|
||
|
|
Separator = {
|
||
|
|
startPoint = Anchor("LEFT", 0, 0),
|
||
|
|
endPoint = Anchor("RIGHT", 0, 0),
|
||
|
|
thickness = 2,
|
||
|
|
color = Color.DISABLED,
|
||
|
|
},
|
||
|
|
},
|
||
|
|
[UIDropDownMenuList] = {
|
||
|
|
frameStrata = "FULLSCREEN_DIALOG",
|
||
|
|
enableMouse = true,
|
||
|
|
Toplevel = true,
|
||
|
|
backdrop = {
|
||
|
|
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
|
||
|
|
edgeFile = [[Interface\Tooltips\UI-Tooltip-Border]],
|
||
|
|
tile = true, tileSize = 16, edgeSize = 16,
|
||
|
|
insets = { left = 5, right = 4, top = 4, bottom = 4 }
|
||
|
|
},
|
||
|
|
backdropBorderColor = Color(1, 1, 1),
|
||
|
|
backdropColor = Color(0.09, 0.09, 0.19),
|
||
|
|
containerInsets = Inset(8, 8, 8, 8),
|
||
|
|
},
|
||
|
|
[UIDropDownList] = {
|
||
|
|
backdrop = {
|
||
|
|
bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]],
|
||
|
|
edgeFile = [[Interface\DialogFrame\UI-DialogBox-Border]],
|
||
|
|
tile = true, tileSize = 32, edgeSize = 32,
|
||
|
|
insets = { left = 11, right = 12, top = 12, bottom = 11 }
|
||
|
|
},
|
||
|
|
backdropColor = NIL,
|
||
|
|
containerInsets = Inset(16, 16, 16, 16),
|
||
|
|
ownerOffset = Dimension(0, 0),
|
||
|
|
},
|
||
|
|
})
|
||
|
|
|
||
|
|
-----------------------------------------------------------
|
||
|
|
-- UIDropDownMenu API --
|
||
|
|
-----------------------------------------------------------
|
||
|
|
local _ButtonHolder = CreateFrame("Frame")
|
||
|
|
_ButtonHolder:Hide()
|
||
|
|
|
||
|
|
local rycDropDownMenuButtons = Recycle(UIDropDownMenuButton, "Scorpio_UIDropDownMenuButton%d", _ButtonHolder)
|
||
|
|
local rycDropDownMenuLists = Recycle(UIDropDownMenuList, "Scorpio_UIDropDownMenuList%d")
|
||
|
|
local rycDropDownLists = Recycle(UIDropDownList, "Scorpio_UIDropDownList%d")
|
||
|
|
|
||
|
|
function rycDropDownMenuButtons:OnInit(obj)
|
||
|
|
return obj:InstantApplyStyle()
|
||
|
|
end
|
||
|
|
|
||
|
|
function rycDropDownMenuLists:OnInit(obj)
|
||
|
|
return obj:InstantApplyStyle()
|
||
|
|
end
|
||
|
|
|
||
|
|
function rycDropDownLists:OnInit(obj)
|
||
|
|
return obj:InstantApplyStyle()
|
||
|
|
end
|
||
|
|
|
||
|
|
local function refreshMenuSize(self)
|
||
|
|
local insets = self.ContainerInsets
|
||
|
|
local offset = -(insets and insets.top or 0)
|
||
|
|
local leftoff = insets and insets.left or 0
|
||
|
|
local rightoff = -(insets and insets.right or 0)
|
||
|
|
|
||
|
|
local maxw = 0
|
||
|
|
local child
|
||
|
|
|
||
|
|
for i = 1, #self do
|
||
|
|
child = self[i]
|
||
|
|
child:ClearAllPoints()
|
||
|
|
child:SetPoint("LEFT", leftoff, 0)
|
||
|
|
child:SetPoint("RIGHT", rightoff, 0)
|
||
|
|
child:SetPoint("TOP", 0, offset)
|
||
|
|
|
||
|
|
offset = offset - child:GetHeight()
|
||
|
|
|
||
|
|
local ft = child:GetFontString()
|
||
|
|
if ft then
|
||
|
|
local leftw = 0
|
||
|
|
local rightw = 0
|
||
|
|
for i = 1, ft:GetNumPoints() do
|
||
|
|
local p, f, r, x, y = ft:GetPoint(i)
|
||
|
|
|
||
|
|
if f and IsSameUI(f, child) and p and r then
|
||
|
|
if p:match("LEFT") and r:match("LEFT") then
|
||
|
|
leftw = x
|
||
|
|
elseif p:match("RIGHT") and r:match("RIGHT") then
|
||
|
|
rightw = - x
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
maxw = math.max(maxw, (ft:GetStringWidth() or 0) + leftw + rightw)
|
||
|
|
end
|
||
|
|
|
||
|
|
if child.SubMenu then
|
||
|
|
refreshMenuSize(child.SubMenu)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
maxw = maxw + (insets and (insets.left + insets.right) or 0)
|
||
|
|
offset = math.abs(offset) + (insets and insets.bottom or 0)
|
||
|
|
|
||
|
|
local minwidth, minheight = self:GetMinResize()
|
||
|
|
local maxwidth, maxheight = self:GetMaxResize()
|
||
|
|
|
||
|
|
if maxwidth == 0 then maxwidth = nil end
|
||
|
|
if maxheight == 0 then maxheight = nil end
|
||
|
|
|
||
|
|
maxw = math.min(math.max(maxw, minwidth or 0), maxwidth or math.huge)
|
||
|
|
offset = math.min(math.max(offset, minheight or 0), maxheight or math.huge)
|
||
|
|
|
||
|
|
self:SetWidth(maxw)
|
||
|
|
self:SetHeight(offset)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function recycleMenu(self)
|
||
|
|
if self.CallbackOnClose then
|
||
|
|
Continue(self.CallbackOnClose)
|
||
|
|
end
|
||
|
|
|
||
|
|
for i = #self, 1, -1 do
|
||
|
|
local button = self[i]
|
||
|
|
|
||
|
|
if button.SubMenu then
|
||
|
|
recycleMenu(button.SubMenu)
|
||
|
|
button.SubMenu = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
button:SetParent(_ButtonHolder)
|
||
|
|
button:ClearAllPoints()
|
||
|
|
|
||
|
|
rycDropDownMenuButtons(button)
|
||
|
|
|
||
|
|
self[i] = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
self.OnHide = nil
|
||
|
|
self.CallbackOnClose = nil
|
||
|
|
|
||
|
|
if Class.IsObjectType(self, UIDropDownList) then
|
||
|
|
rycDropDownLists(self)
|
||
|
|
else
|
||
|
|
rycDropDownMenuLists(self)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function buildDropDownMenuList(info, dropdown, root)
|
||
|
|
local menu = dropdown and rycDropDownLists() or rycDropDownMenuLists()
|
||
|
|
|
||
|
|
if root then
|
||
|
|
menu.Owner = info.owner
|
||
|
|
menu.Anchor = info.anchor
|
||
|
|
menu.OnHide = recycleMenu
|
||
|
|
|
||
|
|
if info.close then
|
||
|
|
menu.CallbackOnClose= info.close
|
||
|
|
end
|
||
|
|
else
|
||
|
|
menu.OnHide = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
local checkvalue
|
||
|
|
|
||
|
|
if info.check then
|
||
|
|
menu.IsMultiCheck = false
|
||
|
|
|
||
|
|
menu.OnCheckStateChanged= function(self, val)
|
||
|
|
return info.check.set(val)
|
||
|
|
end
|
||
|
|
|
||
|
|
if type(info.check.get) == "function" then
|
||
|
|
checkvalue = info.check.get()
|
||
|
|
else
|
||
|
|
checkvalue = info.check.get
|
||
|
|
end
|
||
|
|
else
|
||
|
|
menu.IsMultiCheck = true
|
||
|
|
|
||
|
|
menu.OnCheckStateChanged= nil
|
||
|
|
end
|
||
|
|
|
||
|
|
for i, binfo in ipairs(info) do
|
||
|
|
local button = rycDropDownMenuButtons()
|
||
|
|
button:SetParent(menu)
|
||
|
|
button:SetID(i)
|
||
|
|
|
||
|
|
menu[i] = button
|
||
|
|
|
||
|
|
button:SetText(binfo.text)
|
||
|
|
|
||
|
|
if binfo.color then
|
||
|
|
button.IsColorButton= true
|
||
|
|
local value
|
||
|
|
if type(binfo.color.get) == "function" then
|
||
|
|
value = binfo.color.get()
|
||
|
|
else
|
||
|
|
value = binfo.color.get
|
||
|
|
end
|
||
|
|
value = value and Struct.ValidateValue(ColorType, value)
|
||
|
|
if value then
|
||
|
|
button.Color = value
|
||
|
|
end
|
||
|
|
|
||
|
|
button.OnColorChoosed = function(self, color)
|
||
|
|
return binfo.color.set(color)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
button.IsColorButton= nil
|
||
|
|
button.OnColorChoosed = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if not menu.IsMultiCheck and binfo.checkvalue ~= nil then
|
||
|
|
button.IsCheckButton= true
|
||
|
|
button.CheckValue = binfo.checkvalue
|
||
|
|
button.Checked = checkvalue == binfo.checkvalue
|
||
|
|
button.OnCheckStateChanged = nil
|
||
|
|
elseif binfo.check then
|
||
|
|
button.IsCheckButton= true
|
||
|
|
local value
|
||
|
|
if type(binfo.check.get) == "function" then
|
||
|
|
value = binfo.check.get()
|
||
|
|
else
|
||
|
|
value = binfo.check.get
|
||
|
|
end
|
||
|
|
button.Checked = value and true or false
|
||
|
|
|
||
|
|
button.OnCheckStateChanged = function(self, checked)
|
||
|
|
return binfo.check.set(checked)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
button.IsCheckButton= false
|
||
|
|
button.OnCheckStateChanged = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if binfo.click then
|
||
|
|
button.OnClick = function(self)
|
||
|
|
return Continue(binfo.click)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
button.OnClick = nil
|
||
|
|
end
|
||
|
|
|
||
|
|
if binfo.disabled then
|
||
|
|
button.Disabled = true
|
||
|
|
else
|
||
|
|
button.Disabled = false
|
||
|
|
end
|
||
|
|
|
||
|
|
button.Icon = binfo.icon
|
||
|
|
button.MouseOverIcon = binfo.mouseOverIcon
|
||
|
|
button.TooltipTitle = binfo.tiptitle
|
||
|
|
button.TooltipText = binfo.tiptext
|
||
|
|
button.AsSeparator = binfo.separator
|
||
|
|
|
||
|
|
if binfo.submenu then
|
||
|
|
button.SubMenu = buildDropDownMenuList(binfo.submenu, dropdown)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
return menu
|
||
|
|
end
|
||
|
|
|
||
|
|
struct "UIDropDownMenuInfo" {}
|
||
|
|
|
||
|
|
__Sealed__() struct "UIDropDownMenuButtonInfo" {
|
||
|
|
{ name = "text", type = String },
|
||
|
|
{ name = "color", type = PropertyAccessor },
|
||
|
|
{ name = "check", type = PropertyAccessor },
|
||
|
|
{ name = "checkvalue", type = Any },
|
||
|
|
{ name = "click", type = Function },
|
||
|
|
{ name = "disabled", type = Boolean },
|
||
|
|
{ name = "submenu", type = UIDropDownMenuInfo },
|
||
|
|
{ name = "separator", type = Boolean },
|
||
|
|
|
||
|
|
{ name = "icon", type = String + Number },
|
||
|
|
{ name = "mouseovericon", type = String + Number },
|
||
|
|
{ name = "tiptitle", type = String },
|
||
|
|
{ name = "tiptext", type = String },
|
||
|
|
}
|
||
|
|
|
||
|
|
__Sealed__() struct "UIDropDownMenuInfo" {
|
||
|
|
{ name = "dropdown",type = Boolean },
|
||
|
|
{ name = "owner", type = UIObject },
|
||
|
|
{ name = "anchor", type = AnchorType },
|
||
|
|
{ name = "check", type = PropertyAccessor },
|
||
|
|
{ name = "close", type = Function },
|
||
|
|
|
||
|
|
function (self)
|
||
|
|
if #self == 0 then
|
||
|
|
return "%s must contain menu button settings"
|
||
|
|
end
|
||
|
|
|
||
|
|
for i = 1, #self do
|
||
|
|
local val, message = Struct.ValidateValue(UIDropDownMenuButtonInfo, self[i])
|
||
|
|
if message then
|
||
|
|
return message:gsub("%%s", "%%s[" .. i .. "]")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
}
|
||
|
|
|
||
|
|
--- The API to show the drop down menu
|
||
|
|
__Static__() __Async__()
|
||
|
|
__Arguments__{ UIDropDownMenuInfo }
|
||
|
|
function Scorpio.ShowDropDownMenu(info)
|
||
|
|
local menu = buildDropDownMenuList(info, info.dropdown and info.owner and true or false, true)
|
||
|
|
Next() Next()
|
||
|
|
|
||
|
|
refreshMenuSize(menu)
|
||
|
|
|
||
|
|
return showMenuList(menu)
|
||
|
|
end
|
||
|
|
|
||
|
|
__Static__()
|
||
|
|
Scorpio.CloseDropDownMenu = closeMenuList
|