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.
912 lines
32 KiB
912 lines
32 KiB
--lua
|
|
local unpack, select, pairs, strjoin, type, wipe, tostring, tonumber, error, setmetatable, tinsert, tsort, tremove, max, min
|
|
= unpack, select, pairs, strjoin, type, wipe, tostring, tonumber, error, setmetatable, table.insert, table.sort, table.remove, math.max, math.min
|
|
-- WoW API
|
|
local GetSpecializationInfoByID, GetItemStatDelta, IsControlKeyDown, IsShiftKeyDown, SetCursor, ResetCursor, DressUpItemLink, GetItemIcon, GetAddOnMetadata, GetSpecializationInfo, PlaySound, GetNumSpecializations, UnitAffectingCombat
|
|
= GetSpecializationInfoByID, GetItemStatDelta, IsControlKeyDown, IsShiftKeyDown, SetCursor, ResetCursor, DressUpItemLink, GetItemIcon, GetAddOnMetadata, GetSpecializationInfo, PlaySound, GetNumSpecializations, UnitAffectingCombat
|
|
local BestInSlot, L, AceGUI = unpack(select(2, ...))
|
|
local frameName = "BestInSlotFrame"
|
|
local frame
|
|
local menuItems = {}
|
|
local menuLabels
|
|
local container, menuContainer
|
|
local selectedMenuId = 1
|
|
local MINIMUMWIDTH, MINIMUMHEIGHT = 250, 300
|
|
local animating = false
|
|
local biggestWidth, biggestHeight
|
|
|
|
local function getWidthForModule(module)
|
|
local type = type(module.Width)
|
|
if type == "number" then return module.Width
|
|
elseif type == "function" then return module.Width() end
|
|
end
|
|
|
|
local function getHeightForModule(module)
|
|
local type = type(module.Height)
|
|
if type == "number" then return module.Height
|
|
elseif type == "function" then return module.Height() end
|
|
end
|
|
|
|
local function getBiggestWidth()
|
|
if not biggestWidth then
|
|
biggestWidth = getWidthForModule(menuItems[1])
|
|
for i=2,#menuItems do
|
|
local modWidth = getWidthForModule(menuItems[i])
|
|
if modWidth > biggestWidth then
|
|
biggestWidth = modWidth
|
|
end
|
|
end
|
|
end
|
|
return biggestWidth
|
|
end
|
|
|
|
local function getBiggestHeight()
|
|
if not biggestHeight then
|
|
biggestHeight = getHeightForModule(menuItems[1])
|
|
for i=2,#menuItems do
|
|
local height = getHeightForModule(menuItems[i])
|
|
if height > biggestHeight then
|
|
biggestHeight = height
|
|
end
|
|
end
|
|
end
|
|
return biggestHeight
|
|
end
|
|
|
|
local function addGuildInfoToTooltip(tooltip, itemId, difficultyId)
|
|
if BestInSlot.db.char.options.guildtooltip then
|
|
local players = BestInSlot:GetGuildMembersByItemID(itemId, difficultyId)
|
|
local first = true
|
|
local obtainedPlayers = {}
|
|
for player, playerSpecs in pairs(players) do
|
|
local guildRank = BestInSlot:GetGuildRank(player)
|
|
if guildRank and BestInSlot.db.char.options.showGuildRankInTooltip[guildRank] then
|
|
local specNames = {}
|
|
local specNamesObtained = {}
|
|
for specId, obtained in pairs(playerSpecs) do
|
|
local tbl = specNames
|
|
if obtained then tbl = specNamesObtained end
|
|
local specName = select(2, GetSpecializationInfoByID(specId))
|
|
tinsert(tbl, specName)
|
|
end
|
|
if #specNames > 0 then
|
|
if first then
|
|
tooltip:AddLine(L["The following people in your guild also need this item:"], nil, nil, nil, true)
|
|
first = false
|
|
end
|
|
tooltip:AddLine(("- %s (%s)"):format(BestInSlot:GetPlayerString(player), strjoin("/", unpack(specNames))))
|
|
end
|
|
if #specNamesObtained > 0 then
|
|
obtainedPlayers[player] = specNamesObtained
|
|
end
|
|
end
|
|
end
|
|
first = true
|
|
for player, specNames in pairs(obtainedPlayers) do
|
|
if first then
|
|
tooltip:AddLine("")
|
|
tooltip:AddLine(L["The following people in your guild already obtained this item"], nil, nil, nil, true)
|
|
first = false
|
|
end
|
|
tooltip:AddLine(("- %s (%s)"):format(BestInSlot:GetPlayerString(player), strjoin("/", unpack(specNames))))
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Helper for starting an animation
|
|
local function startAnimating()
|
|
animating = true
|
|
end
|
|
|
|
--- Helper for stopping an animation
|
|
local function stopAnimating()
|
|
animating = false
|
|
end
|
|
|
|
--- Set the size of the frame
|
|
-- @param #number targetWidth The new width
|
|
-- @param #number targetHeight the new height
|
|
-- @param #function func The function to run when the frame is done resizing
|
|
function BestInSlot:SetFrameSize(targetWidth, targetHeight, func, ...)
|
|
if not frame then return end
|
|
if self.options.windowFixed then
|
|
frame:SetWidth(getBiggestWidth())
|
|
frame:SetHeight(getBiggestHeight())
|
|
if func and type(func) == "function" then
|
|
func(...)
|
|
end
|
|
return
|
|
end
|
|
local currentWidth, currentHeight = frame.frame.width, frame.frame.height
|
|
if not targetWidth then targetWidth = currentWidth end
|
|
if not targetHeight then targetHeight = currentHeight end
|
|
if targetWidth == currentWidth and targetHeight == currentHeight then
|
|
if type(func) == "function" then
|
|
func(...)
|
|
end
|
|
return
|
|
end
|
|
if self.options.instantAnimation then
|
|
frame:SetHeight(targetHeight)
|
|
frame:SetWidth(targetWidth)
|
|
if type(func) == "function" then
|
|
func(...)
|
|
end
|
|
else
|
|
local ANIMATIONTIME = 500 --milliseconds
|
|
local timer = 0
|
|
local incWidth = (targetWidth - currentWidth) / (ANIMATIONTIME / 80)
|
|
local incHeight = (targetHeight - currentHeight) / (ANIMATIONTIME / 80)
|
|
startAnimating()
|
|
local resizeTimerId
|
|
local funcArgs = {...}
|
|
resizeTimerId = self:ScheduleRepeatingTimer(function()
|
|
if not frame then stopAnimating() BestInSlot:CancelTimer(resizeTimerId) end
|
|
if incHeight < 0 then
|
|
frame:SetHeight(max(frame.frame.height + incHeight, targetHeight))
|
|
else
|
|
frame:SetHeight(min(frame.frame.height + incHeight, targetHeight))
|
|
end
|
|
if incWidth < 0 then
|
|
frame:SetWidth(max(frame.frame.width + incWidth, targetWidth))
|
|
else
|
|
frame:SetWidth(min(frame.frame.width + incWidth, targetWidth))
|
|
end
|
|
if frame.frame.width == targetWidth and frame.frame.height == targetHeight then
|
|
BestInSlot:CancelTimer(resizeTimerId)
|
|
if func and type(func) == "function" then
|
|
stopAnimating()
|
|
func(unpack(funcArgs))
|
|
end
|
|
end
|
|
end,
|
|
0.025)
|
|
end
|
|
end
|
|
|
|
--- Sets the content to the supplied menuWidget
|
|
-- @param #table menuWidget An AceGUI Widget with the UserData "id" set to the id of the menu to set the content to
|
|
local function SetContent(menuWidget)
|
|
local id = menuWidget:GetUserData("id")
|
|
if not id then error("The menu widget that has been made didn't provide an id") end
|
|
if animating then return false end
|
|
return BestInSlot:SetMenu(id)
|
|
end
|
|
|
|
function BestInSlot:SetMenu(id, ...)
|
|
local args = {...}
|
|
local item = menuItems[id]
|
|
if selectedMenuId then
|
|
local selectedItem = menuItems[selectedMenuId]
|
|
selectedItem:Close()
|
|
BestInSlot:HideItemTooltip()
|
|
wipe(container:GetUserDataTable())
|
|
local menuLabel = menuLabels[selectedMenuId]
|
|
if menuLabel then
|
|
menuLabel:SetColor(0.8941, 0.73725, 0.01961)
|
|
menuLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
end
|
|
end
|
|
local menuLabel = menuLabels[id]
|
|
if menuLabel then
|
|
menuLabel:SetColor(1,1,1)
|
|
menuLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
|
|
end
|
|
container:ReleaseChildren()
|
|
container:SetLayout("Flow")
|
|
self:SetFrameSize(getWidthForModule(item), getHeightForModule(item), function() container:ResumeLayout() item:Draw(container, unpack(args)) BestInSlot:CheckTutorials(item.Description) end)
|
|
selectedMenuId = id
|
|
return true
|
|
end
|
|
|
|
function BestInSlot:HideItemTooltip()
|
|
if GameTooltip:IsShown() then
|
|
GameTooltip.itemInfo = nil
|
|
GameTooltip:Hide()
|
|
end
|
|
if BISComparisonTooltip then
|
|
BISComparisonTooltip:Hide()
|
|
end
|
|
end
|
|
|
|
local playerClass
|
|
|
|
function BestInSlot:GetItemTooltip(itemid, difficulty, frame, itemlink)
|
|
if frame then
|
|
GameTooltip:SetOwner(frame.frame)
|
|
GameTooltip:SetAnchorType("ANCHOR_NONE")
|
|
GameTooltip:SetPoint("TOPLEFT", frame.frame, "TOPRIGHT", 10, 0)
|
|
else
|
|
GameTooltip:SetOwner(UIParent)
|
|
end
|
|
if not self:ItemExists(itemid) and itemlink then
|
|
GameTooltip:SetHyperlink(itemlink)
|
|
end
|
|
local item = self:GetItem(itemid, difficulty)
|
|
if not item then return end
|
|
playerClass = playerClass or select(3, UnitClass("player"))
|
|
GameTooltip:SetHyperlink(item.link, playerClass, select(2, self:GetSelected(self.SPECIALIZATION)))
|
|
GameTooltip.itemInfo = item
|
|
if self.options.DEBUG then
|
|
GameTooltip:AddLine("itemid: "..tostring(itemid))
|
|
GameTooltip:AddLine("dungeon: "..tostring(item.dungeon))
|
|
GameTooltip:AddLine("bossid: "..tostring(item.bossid))
|
|
GameTooltip:AddLine("link: "..tostring(item.link))
|
|
GameTooltip:AddLine("difficulty: "..tostring(item.difficulty))
|
|
GameTooltip:AddLine("equipslot: "..tostring(item.equipSlot))
|
|
if item.misc then
|
|
GameTooltip:AddLine("Item is misc source: "..item.misc)
|
|
end
|
|
if item.tieritem then
|
|
GameTooltip:AddLine("Item is tier item")
|
|
end
|
|
if item.tiertoken then
|
|
GameTooltip:AddLine("Item is tier token")
|
|
end
|
|
end
|
|
GameTooltip:Show()
|
|
return GameTooltip
|
|
end
|
|
|
|
function BestInSlot:GetItemComparisonTooltip(tooltip, selectedItem, difficulty)
|
|
local compareItem = tooltip.itemInfo
|
|
local item = self:GetItem(selectedItem, difficulty)
|
|
if not compareItem then return end
|
|
if compareItem.itemid == item.itemid and compareItem.difficulty == item.difficulty then return end --don't show then it's comparing itself!
|
|
if not BISComparisonTooltip then
|
|
CreateFrame("GameTooltip", "BISComparisonTooltip", tooltip, "GameTooltipTemplate")
|
|
end
|
|
if not BISComparisonTooltip:IsShown() then
|
|
BISComparisonTooltip:SetOwner(BestInSlot.frame.frame)
|
|
BISComparisonTooltip:SetAnchorType("ANCHOR_NONE")
|
|
BISComparisonTooltip:SetPoint("TOPLEFT", GameTooltip, "TOPRIGHT")
|
|
end
|
|
BISComparisonTooltip:SetHyperlink(item.link, playerClass, select(2, self:GetSelected(self.SPECIALIZATION)))
|
|
local statDelta = GetItemStatDelta(item.link, compareItem.link)
|
|
for stat,value in pairs(statDelta) do
|
|
BISComparisonTooltip:AddLine(("%s%d %s"..FONT_COLOR_CODE_CLOSE):format((value > 0 and GREEN_FONT_COLOR_CODE or RED_FONT_COLOR_CODE), value, tostring(_G[stat])))
|
|
end
|
|
BISComparisonTooltip:Show()
|
|
end
|
|
|
|
function BestInSlot:GameTooltip_OnTooltipSetItem(tooltip, ...)
|
|
if self.db.char.options.showBiSTooltip and (self.db.char.options.tooltipCombat or not UnitAffectingCombat("player")) then
|
|
local _, link = tooltip:GetItem()
|
|
if link then
|
|
local itemId, instanceID = self:GetItemInfoFromLink(link)
|
|
local exists, existType = self:ItemExists(itemId)
|
|
if not exists then return end
|
|
if itemId then
|
|
instanceID = tonumber(instanceID)
|
|
instanceID = self:GetDifficultyIdForDungeon(instanceID, nil, true) or 1
|
|
local bisInfo
|
|
if existType == "item" then
|
|
bisInfo = self:IsItemBestInSlot(itemId, instanceID)
|
|
else
|
|
bisInfo = self:IsTokenBestInSlot(itemId, instanceID)
|
|
end
|
|
local isBiS = false --catch empty tables
|
|
if bisInfo and type(bisInfo) == "table" then
|
|
local specsText = ""
|
|
local first = true
|
|
for specId in pairs(bisInfo) do
|
|
isBiS = true
|
|
if not first then
|
|
specsText = specsText.."/"
|
|
end
|
|
specsText = specsText..(type(specId) == "string" and specId or select(2,GetSpecializationInfoByID(specId))) --custom lists can just show as custom list, otherwise lookup spec name
|
|
first = false
|
|
end
|
|
if isBiS then
|
|
local color = self.colorHighlight
|
|
if self:HasItem(itemId, instanceID, true) then
|
|
color = GREEN_FONT_COLOR_CODE
|
|
end
|
|
tooltip:AddLine(("%1$s (%2$s)"):format(color.."BestInSlot"..self.colorNormal, color..specsText.."|r"))
|
|
end
|
|
end
|
|
local item = self:GetItem(itemId)
|
|
if item and (self.db.char.options.tooltipSource or self.options.DEBUG) then
|
|
if item.multiplesources then
|
|
tooltip:AddLine(SOURCES..":")
|
|
for dungeonname,v in pairs(item.multiplesources) do
|
|
for bossid in pairs(v) do
|
|
tooltip:AddLine(("%s%s %s(%s)|r"):format(self.colorHighlight, self:GetDescription(self.BOSS, dungeonname, bossid), self.colorNormal, dungeonname))
|
|
end
|
|
end
|
|
elseif item.dungeon and item.bossid then
|
|
tooltip:AddLine(("%s %s%s"):format(SOURCE, self.colorHighlight, self:GetDescription(self.BOSS, item.dungeon, item.bossid)))
|
|
elseif item.misc then
|
|
tooltip:AddLine(("%s %s%s"):format(SOURCE, self.colorHighlight, item.misc))
|
|
end
|
|
end
|
|
if self:ItemExists(itemId) then
|
|
addGuildInfoToTooltip(tooltip, itemId, instanceID)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function helperOnEnter(widget, ...)
|
|
local itemid = widget:GetUserData("itemid")
|
|
local itemlink = widget:GetUserData("itemlink")
|
|
local callbacks = widget:GetUserData("callbacks")
|
|
local difficulty = widget:GetUserData("difficulty")
|
|
if not itemid then return end
|
|
BestInSlot:GetItemTooltip(itemid,difficulty, BestInSlot.frame, itemlink)
|
|
if IsControlKeyDown() then
|
|
SetCursor("INSPECT_CURSOR")
|
|
else
|
|
ResetCursor()
|
|
end
|
|
|
|
if callbacks.OnEnter then
|
|
for i=1,#callbacks.OnEnter do
|
|
callbacks.OnEnter[i](widget, ...)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function helperOnLeave(widget, ...)
|
|
ResetCursor()
|
|
local callbacks = widget:GetUserData("callbacks")
|
|
BestInSlot:HideItemTooltip()
|
|
if callbacks.OnLeave then
|
|
for i=1,#callbacks.OnLeave do
|
|
callbacks.OnLeave[i](widget, ...)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function helperOnClick(widget, ...)
|
|
local itemlink = widget:GetUserData("itemlink")
|
|
local callbacks = widget:GetUserData("callbacks")
|
|
if not itemlink then return end
|
|
if IsShiftKeyDown() then
|
|
if not ChatEdit_InsertLink(itemlink) then
|
|
ChatFrame_OpenChat(itemlink)
|
|
end
|
|
elseif IsControlKeyDown() then
|
|
DressUpItemLink(itemlink)
|
|
end
|
|
if callbacks.OnClick then
|
|
for i=1,#callbacks.OnClick do
|
|
callbacks.OnClick[i](widget, ...)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function itemLinkCallback(self, name, func)
|
|
if type(func) ~= "function" then error("2nd parameter should be function") end
|
|
local callbacks = self:GetUserData("callbacks")
|
|
if name == "OnEnter" or name == "OnLeave" or name == "OnClick" then
|
|
if not callbacks[name] then
|
|
callbacks[name] = {}
|
|
end
|
|
tinsert(callbacks[name], func)
|
|
else
|
|
error("Invalid callback method")
|
|
end
|
|
end
|
|
|
|
function BestInSlot:GetItemLinkLabel(itemid, difficulty, withIcon)
|
|
if not withIcon and withIcon ~= false then
|
|
withIcon = true
|
|
end
|
|
if not itemid then
|
|
error("GetItemLinkLabel cannot be called without itemid argument")
|
|
return
|
|
end
|
|
local label = AceGUI:Create("InteractiveLabel")
|
|
label:SetUserData("itemid", itemid)
|
|
label:SetUserData("difficulty", difficulty)
|
|
label:SetCallback("OnEnter", helperOnEnter)
|
|
label:SetCallback("OnLeave", helperOnLeave)
|
|
label:SetCallback("OnClick", helperOnClick)
|
|
if itemid ~= "PLACEHOLDER" then
|
|
local item = self:GetItem(itemid, difficulty)
|
|
if not item then
|
|
label:SetText("Undefined itemid")
|
|
else
|
|
label:SetUserData("itemlink", item.link)
|
|
label:SetText(item.link)
|
|
end
|
|
if withIcon then
|
|
label:SetImage(GetItemIcon(itemid))
|
|
end
|
|
else
|
|
label:SetText("")
|
|
end
|
|
label:SetUserData("callbacks", {})
|
|
return label
|
|
end
|
|
|
|
local UISpecialFramesId
|
|
local function frameClosed(widget)
|
|
if UISpecialFrames[UISpecialFramesId] == frameName then
|
|
tremove(UISpecialFrames, UISpecialFramesId)
|
|
end
|
|
BestInSlot:HideFrame()
|
|
end
|
|
|
|
local function GetFrame()
|
|
if frame then return end
|
|
frame = AceGUI:Create("Frame")
|
|
frame.frame:SetFrameStrata("HIGH")
|
|
BestInSlot.frame = frame
|
|
|
|
--make closable with escape
|
|
_G[frameName] = frame.frame
|
|
tinsert(UISpecialFrames, frameName)
|
|
UISpecialFramesId = #UISpecialFrames
|
|
frame:SetTitle("Best In Slot Redux "..GetAddOnMetadata("BestInSlotRedux", "Version"))
|
|
frame:SetStatusText(("By: %s, %s, %s, %s, %s."):format(BestInSlot.Author0, BestInSlot.Author4, BestInSlot.Author1, BestInSlot.Author2, BestInSlot.Author3))
|
|
local FrameBackdrop = {
|
|
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
tile = true, tileSize = 32, edgeSize = 32,
|
|
insets = { left = 8, right = 8, top = 8, bottom = 8 }
|
|
}
|
|
frame.frame:SetBackdrop(FrameBackdrop)
|
|
frame:SetCallback("OnClose", frameClosed)
|
|
frame:EnableResize(nil)
|
|
local dimensionsSet = false
|
|
local initItem
|
|
if selectedMenuId then --GUI has been opened before
|
|
local item = menuItems[selectedMenuId]
|
|
initItem = item
|
|
if item.Height then
|
|
frame:SetHeight(getHeightForModule(item))
|
|
frame:SetWidth(getWidthForModule(item))
|
|
dimensionsSet = true
|
|
end
|
|
end
|
|
if not dimensionsSet or BestInSlot.db.char.options.windowFixed then
|
|
if BestInSlot.db.char.options.windowFixed then
|
|
frame:SetHeight(getBiggestHeight())
|
|
frame:SetWidth(getBiggestWidth())
|
|
else
|
|
frame:SetHeight(MINIMUMHEIGHT)
|
|
frame:SetWidth(MINIMUMWIDTH)
|
|
end
|
|
end
|
|
frame:PauseLayout()
|
|
frame:SetPoint("TOPLEFT", UIParent, "TOPLEFT", 200, -250)
|
|
|
|
if not BestInSlot.hasModules then
|
|
local label = AceGUI:Create("Label")
|
|
label:SetText(L["This addon requires atleast 1 expansion module! Please enable one!"])
|
|
label:SetFullWidth(true)
|
|
label:SetPoint("TOPLEFT", frame.frame, "TOPLEFT")
|
|
frame:AddChild(label)
|
|
return
|
|
end
|
|
|
|
menuContainer = AceGUI:Create("SimpleGroup")
|
|
menuContainer:SetPoint("TOPLEFT", frame.frame, "TOPLEFT", 10, -20)
|
|
menuContainer:SetPoint("BOTTOMRIGHT", frame.frame, "BOTTOMLEFT", 160, 50)
|
|
menuContainer:PauseLayout()
|
|
local previousLabel
|
|
menuLabels = {}
|
|
for i=1,#menuItems do
|
|
local item = menuItems[i]
|
|
if item.IsShown() then
|
|
local interactiveLabel = AceGUI:Create("InteractiveLabel")
|
|
interactiveLabel:SetText(item.Description)
|
|
interactiveLabel:SetWidth(140)
|
|
interactiveLabel:SetFont(GameFontNormalSmall:GetFont(), 14, nil)
|
|
interactiveLabel:SetUserData("name", item.Description)
|
|
interactiveLabel:SetUserData("id", i)
|
|
|
|
if selectedMenuId and selectedMenuId == i then
|
|
interactiveLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestLogTitleHighlight")
|
|
interactiveLabel:SetColor(1,1,1)
|
|
else
|
|
interactiveLabel:SetColor(0.8941, 0.73725, 0.01961)
|
|
interactiveLabel:SetHighlight("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
end
|
|
interactiveLabel:SetCallback("OnClick", SetContent)
|
|
if not previousLabel then
|
|
interactiveLabel:SetPoint("TOPLEFT", menuContainer.frame, "TOPLEFT", 10, -10)
|
|
interactiveLabel:SetPoint("TOPRIGHT", menuContainer.frame, "TOPRIGHT", -10, -10)
|
|
else
|
|
interactiveLabel:SetPoint("TOPLEFT", previousLabel.frame, "BOTTOMLEFT", 0, -10)
|
|
interactiveLabel:SetPoint("TOPRIGHT", previousLabel.frame, "BOTTOMRIGHT", 0, -10)
|
|
end
|
|
previousLabel = interactiveLabel
|
|
menuLabels[i] = interactiveLabel
|
|
menuContainer:AddChild(interactiveLabel)
|
|
end
|
|
end
|
|
|
|
frame:AddChild(menuContainer)
|
|
frame:SetUserData("menu", menuContainer)
|
|
|
|
container = AceGUI:Create("SimpleGroup")
|
|
container:SetPoint("TOPRIGHT", frame.frame, "TOPRIGHT", -20, -20)
|
|
container:SetPoint("BOTTOMLEFT", menuContainer.frame, "BOTTOMRIGHT", 5, 0)
|
|
container:SetLayout("Flow")
|
|
frame:AddChild(container)
|
|
frame:SetUserData("content", container)
|
|
|
|
if initItem then
|
|
initItem:Draw(container)
|
|
BestInSlot:CheckTutorials()
|
|
end
|
|
|
|
local windowpos = BestInSlot.db.char.windowpos
|
|
if windowpos.point then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(windowpos.point, windowpos.relativeTo, windowpos.relativePoint, windowpos.xOffset, windowpos.yOffset)
|
|
end
|
|
end
|
|
|
|
function BestInSlot:HideFrame()
|
|
if frame then
|
|
local point, relativeTo, relativePoint, xOffset, yOffset = frame.frame:GetPoint(1)
|
|
self.db.char.windowpos = {
|
|
point = point,
|
|
relativeTo = type(relativeTo) == "table" and relativeTo:GetName() or relativeTo,
|
|
relativePoint = relativePoint,
|
|
xOffset = xOffset,
|
|
yOffset = yOffset
|
|
}
|
|
if selectedMenuId then
|
|
local item = menuItems[selectedMenuId]
|
|
if item then
|
|
item:Close()
|
|
end
|
|
end
|
|
frame.frame:SetBackdrop({ --set backdrop back to the original backdrop for other users
|
|
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background",
|
|
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
|
|
tile = true, tileSize = 32, edgeSize = 32,
|
|
insets = { left = 8, right = 8, top = 8, bottom = 8 }
|
|
})
|
|
frame.frame:SetFrameStrata("FULLSCREEN_DIALOG")
|
|
frame:Release()
|
|
frame = nil
|
|
container = nil
|
|
menuContainer = nil
|
|
PlaySound(SOUNDKIT.IG_CHARACTER_INFO_CLOSE)
|
|
if BestInSlotTutorialFrame:IsVisible() then
|
|
BestInSlotTutorialFrame:Hide()
|
|
end
|
|
BestInSlot.frame = nil
|
|
menuLabels = nil
|
|
end
|
|
end
|
|
|
|
function BestInSlot:GetSelectedMenuItem()
|
|
return menuItems[selectedMenuId].Description
|
|
end
|
|
|
|
local function defaultDraw(menu, container)
|
|
local label = AceGUI:Create("Label")
|
|
label:SetText("This menu has not been implemented yet")
|
|
container:AddChild(label)
|
|
end
|
|
|
|
local function defaultClose() end
|
|
local function defaultIsShown() return true end
|
|
-- {
|
|
-- descr = "Description",
|
|
-- draw = drawFunction(),
|
|
-- [width = 100,]
|
|
-- [height = 100,]
|
|
-- [close = closeFunction(),]
|
|
-- [isShown = isShownFunction(),]
|
|
-- }
|
|
local menuPrototype = setmetatable({
|
|
Draw = defaultDraw,
|
|
Width = 500,
|
|
Height = 600,
|
|
Close = defaultClose,
|
|
IsShown = defaultIsShown,
|
|
},
|
|
{__index = BestInSlot})
|
|
function BestInSlot:GetMenuPrototype(menuName)
|
|
if not menuName or type(menuName) ~= "string" then error("GetMenuPrototype must always have a name in the form of a string") end
|
|
local menuIndex = #menuItems + 1
|
|
local prototype = setmetatable({Description = menuName, menuindex = menuIndex}, {__index = menuPrototype})
|
|
menuItems[menuIndex] = prototype
|
|
return prototype
|
|
end
|
|
|
|
-------------------------------------------------
|
|
-- Functions for add-on wide selection values
|
|
--
|
|
local selected
|
|
|
|
function BestInSlot:InitSelectedSettings()
|
|
if selected == nil then
|
|
selected = self.db.char.selected
|
|
local verify = {self.EXPANSION, self.RAIDTIER, self.INSTANCE, self.DIFFICULTY, self.SPECIALIZATION}
|
|
local verified = true
|
|
for _, type in pairs(verify) do
|
|
if not selected[type] then
|
|
verified = false
|
|
break
|
|
end
|
|
end
|
|
if not verified or not tContains(self:GetInstances(), selected[self.INSTANCE]) then
|
|
selected = {} --when not verified, reset selected
|
|
self:SetSelected(self.EXPANSION, self:GetLatest(self.EXPANSION))
|
|
self:SetSelected(self.SPECIALIZATION)
|
|
else
|
|
self:ValidateSelected(self.INSTANCE)
|
|
end
|
|
end
|
|
end
|
|
|
|
function BestInSlot:GetSelected(bisType)
|
|
if not selected then
|
|
self:InitSelectedSettings()
|
|
end
|
|
if not selected[self.SPECIALIZATION] or selected[self.SPECIALIZATION] == -1 then
|
|
self:SetSelected(self.SPECIALIZATION)
|
|
end
|
|
if bisType then
|
|
if selected[bisType] then
|
|
if bisType == self.SPECIALIZATION then
|
|
return selected[bisType], type(selected[bisType]) == "string" and self:GetSpecForCustomList(selected[bisType]) or selected[bisType]
|
|
end
|
|
return selected[bisType]
|
|
else
|
|
error("You supplied an invalid type!")
|
|
end
|
|
else
|
|
return {expansion = selected[self.EXPANSION], raidtier = selected[self.RAIDTIER], instance = selected[self.INSTANCE], difficulty = selected[self.DIFFICULTY], specialization = selected[self.SPECIALIZATION]}
|
|
end
|
|
end
|
|
|
|
function BestInSlot:ValidateSelected(type)
|
|
if type == self.RAIDTIER then
|
|
local expansion = self:GetExpansions(type, selected[type])
|
|
if selected[self.EXPANSION] ~= expansion then
|
|
self:SetSelected(self.EXPANSION, expansion)
|
|
end
|
|
local difficulties = self:GetDifficulties(type, selected[type])
|
|
if not difficulties[selected[self.DIFFICULTY]] then
|
|
self:SetSelected(self.DIFFICULTY, difficulties[#difficulties])
|
|
end
|
|
elseif type == self.INSTANCE then
|
|
local raidTier = self:GetRaidTiers(type, selected[type])
|
|
if selected[self.RAIDTIER] ~= raidTier then
|
|
self:SetSelected(self.RAIDTIER, raidTier)
|
|
end
|
|
elseif type == self.DIFFICULTY then
|
|
local difficulties = self:GetDifficulties(self.RAIDTIER, selected[self.RAIDTIER])
|
|
if selected[type] > #difficulties then
|
|
self:SetSelected(type, #difficulties)
|
|
end
|
|
end
|
|
self.db.char.selected = selected
|
|
end
|
|
|
|
local eventBuffer = {}
|
|
local eventTimer
|
|
local function sendEvents()
|
|
for k,v in pairs(eventBuffer) do
|
|
BestInSlot:SendEvent("SelectedChange", k, v)
|
|
end
|
|
wipe(eventBuffer)
|
|
eventTimer = nil
|
|
end
|
|
function BestInSlot:SetSelected(type, value)
|
|
if not selected then
|
|
self:InitSelectedSettings()
|
|
end
|
|
if value and selected[type] == value then return end
|
|
if value then
|
|
selected[type] = value
|
|
end
|
|
if type == self.EXPANSION then
|
|
self:SetSelected(self.RAIDTIER, self:GetLatest(self.RAIDTIER, self.EXPANSION, value))
|
|
elseif type == self.RAIDTIER then
|
|
self:SetSelected(self.INSTANCE, self:GetLatest(self.INSTANCE, self.RAIDTIER, value))
|
|
elseif type == self.INSTANCE then
|
|
if (not selected[self.DIFFICULTY]) or #(self:GetDifficulties(type, selected[type])) < selected[self.DIFFICULTY] then
|
|
self:SetSelected(self.DIFFICULTY, self:GetLatest(self.DIFFICULTY, self.INSTANCE, value))
|
|
end
|
|
elseif type == self.SPECIALIZATION then
|
|
if not value then
|
|
selected[self.SPECIALIZATION] = GetSpecializationInfo(self:GetSpecialization())
|
|
end
|
|
end
|
|
eventBuffer[type] = value
|
|
if not eventTimer then
|
|
eventTimer = C_Timer.After(0.001, sendEvents)
|
|
end
|
|
self:ValidateSelected(type)
|
|
self.db.char.selected = selected
|
|
end
|
|
|
|
local function getRaidTierDropdown(dropdown)
|
|
local order = BestInSlot:GetRaidTiers()
|
|
local list = {}
|
|
for i=1,#order do
|
|
list[order[i]] = BestInSlot:GetDescription(BestInSlot.RAIDTIER, order[i])
|
|
end
|
|
dropdown:SetList(list)
|
|
local value = BestInSlot:GetSelected(BestInSlot.RAIDTIER)
|
|
dropdown:SetValue(value)
|
|
dropdown:SetLabel(L["Raid Tier"])
|
|
dropdown:SetUserData(BestInSlot.RAIDTIER, value)
|
|
return dropdown
|
|
end
|
|
|
|
local function getDifficultyDropdown(dropdown)
|
|
local raidTierValue = BestInSlot:GetSelected(BestInSlot.RAIDTIER)
|
|
dropdown:SetList(BestInSlot:GetDifficulties(BestInSlot.RAIDTIER, raidTierValue))
|
|
local value = BestInSlot:GetSelected(BestInSlot.DIFFICULTY)
|
|
dropdown:SetValue(value)
|
|
dropdown:SetLabel(L["Difficulty"])
|
|
dropdown:SetUserData(BestInSlot.DIFFICULTY, value)
|
|
dropdown:SetUserData(BestInSlot.RAIDTIER, raidTierValue)
|
|
return dropdown
|
|
end
|
|
|
|
local function getSpecializationDropdown(dropdown)
|
|
local customLists = BestInSlot.db.char.customlists
|
|
local hasCustomLists = #customLists > 0
|
|
dropdown:SetList({})
|
|
if hasCustomLists then
|
|
dropdown:AddItem("_header1", L["Specialization"])
|
|
dropdown:SetItemDisabled("_header1", true)
|
|
end
|
|
for i=1,GetNumSpecializations() do
|
|
local id, name = GetSpecializationInfo(i)
|
|
dropdown:AddItem(id, name)
|
|
end
|
|
dropdown:SetLabel(L["Specialization"])
|
|
if #customLists > 0 then
|
|
dropdown:AddItem("_spacer", "")
|
|
dropdown:SetItemDisabled("_spacer", true)
|
|
dropdown:AddItem("_header", L["Custom Lists"])
|
|
dropdown:SetItemDisabled("_header", true)
|
|
for i=1,#customLists do
|
|
dropdown:AddItem(customLists[i].name, customLists[i].name)
|
|
end
|
|
end
|
|
local value = BestInSlot:GetSelected(BestInSlot.SPECIALIZATION)
|
|
dropdown:SetValue(value)
|
|
dropdown:SetUserData(BestInSlot.SPECIALIZATION, value)
|
|
end
|
|
|
|
local function getDungeonDropdown(dropdown)
|
|
dropdown:SetList({})
|
|
local options = {}
|
|
local instances = BestInSlot:GetInstances()
|
|
tsort(instances, function(a,b) return BestInSlot:GetRaidTiers(BestInSlot.INSTANCE, a) < BestInSlot:GetRaidTiers(BestInSlot.INSTANCE, b) end)
|
|
local selectedDungeon = BestInSlot:GetSelected(BestInSlot.INSTANCE)
|
|
local lastExpansion
|
|
for i=1,#instances do
|
|
local expansion = BestInSlot:GetExpansions(BestInSlot.INSTANCE, instances[i])
|
|
if expansion ~= lastExpansion then
|
|
local name = BestInSlot:GetDescription(BestInSlot.EXPANSION, expansion)
|
|
dropdown:AddItem(name, " "..name)
|
|
dropdown:SetItemDisabled(name, true)
|
|
lastExpansion = expansion
|
|
|
|
end
|
|
dropdown:AddItem(instances[i], BestInSlot:GetDescription(BestInSlot.INSTANCE, instances[i]))
|
|
end
|
|
dropdown:SetLabel(L["Raid Instance"])
|
|
dropdown:SetValue(selectedDungeon)
|
|
dropdown:SetUserData(BestInSlot.INSTANCE, selectedDungeon)
|
|
end
|
|
|
|
local function dropDownCallback(dropdown, event, value, ...)
|
|
local udt = dropdown:GetUserDataTable()
|
|
BestInSlot:SetSelected(udt.type, value)
|
|
if udt.customcallback then udt.customcallback(dropdown, event, value, ...) end
|
|
end
|
|
|
|
local function dropdownReleased(dropdown)
|
|
BestInSlot:UnregisterEvent("SelectedChange", dropdown:GetUserData("eventid"))
|
|
end
|
|
|
|
local dropdownConstructors = {
|
|
[BestInSlot.RAIDTIER] = getRaidTierDropdown,
|
|
[BestInSlot.INSTANCE] = getDungeonDropdown,
|
|
[BestInSlot.DIFFICULTY] = getDifficultyDropdown,
|
|
[BestInSlot.SPECIALIZATION] = getSpecializationDropdown
|
|
}
|
|
|
|
local function verifyRequireChange(widget, typeChanged, value)
|
|
local udt = widget:GetUserDataTable()
|
|
return udt[typeChanged] and udt[typeChanged] ~= value or false
|
|
end
|
|
|
|
local function defaultOnChange(event, typeChanged, valueChanged, dropdown, ...)
|
|
if verifyRequireChange(dropdown, typeChanged, valueChanged) then
|
|
dropdown:SetValue(valueChanged)
|
|
dropdown:SetUserData(typeChanged, valueChanged)
|
|
end
|
|
end
|
|
|
|
local function difficultyOnChange(event, typeChanged, valueChanged, dropdown, ...)
|
|
if typeChanged == BestInSlot.RAIDTIER or typeChanged == BestInSlot.INSTANCE then
|
|
dropdownConstructors[BestInSlot.DIFFICULTY](dropdown)
|
|
elseif typeChanged == BestInSlot.DIFFICULTY then
|
|
dropdown:SetValue(valueChanged)
|
|
dropdown:SetUserData(typeChanged, valueChanged)
|
|
end
|
|
end
|
|
|
|
local dropdownOnChange = {
|
|
[BestInSlot.RAIDTIER] = defaultOnChange,
|
|
[BestInSlot.INSTANCE] = defaultOnChange,
|
|
[BestInSlot.DIFFICULTY] = difficultyOnChange,
|
|
[BestInSlot.SPECIALIZATION] = defaultOnChange
|
|
}
|
|
|
|
function BestInSlot:GetDropdown(type, dropdown, callback)
|
|
local dropdown = dropdown or AceGUI:Create("Dropdown")
|
|
if not dropdownConstructors[type] then error("Can't make a dropdown of that type!") end
|
|
dropdownConstructors[type](dropdown)
|
|
dropdown:SetUserData("type", type)
|
|
dropdown:SetUserData("customcallback", callback)
|
|
dropdown:SetUserData("eventid",self:RegisterEvent("SelectedChange", dropdownOnChange[type], dropdown))
|
|
dropdown:SetCallback("OnValueChanged", dropDownCallback)
|
|
dropdown:SetCallback("OnRelease", dropdownReleased)
|
|
return dropdown
|
|
end
|
|
|
|
local function uneditableOnTextChanged(widget)
|
|
widget:SetText(widget:GetUserData("contents"))
|
|
end
|
|
|
|
function BestInSlot:CreateUneditableTextbox(contents, description, type)
|
|
local editBox = AceGUI:Create(type or "EditBox")
|
|
if description then
|
|
editBox:SetLabel(description)
|
|
end
|
|
editBox:SetText(contents)
|
|
editBox:SetUserData("contents", contents)
|
|
editBox:SetFullWidth(true)
|
|
editBox:SetCallback("OnTextChanged", uneditableOnTextChanged)
|
|
return editBox
|
|
end
|
|
|
|
function BestInSlot:QuickCreate(widgtype, options, addTo, point, ...)
|
|
local result = AceGUI:Create(widgtype)
|
|
|
|
if options then
|
|
for k,v in pairs(options) do
|
|
if type(v) == "table" then
|
|
result[k](result, unpack(v))
|
|
else
|
|
result[k](result, v)
|
|
end
|
|
end
|
|
end
|
|
|
|
if addTo then
|
|
if point then
|
|
result:SetPoint(point, addTo.frame, ...)
|
|
end
|
|
addTo:AddChild(result)
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
function BestInSlot:ShowFrame()
|
|
if not frame then
|
|
GetFrame()
|
|
PlaySound(SOUNDKIT.IG_CHARACTER_INFO_OPEN)
|
|
end
|
|
end
|
|
|
|
function BestInSlot:ToggleFrame()
|
|
if not frame then
|
|
self:ShowFrame()
|
|
else
|
|
self:HideFrame()
|
|
end
|
|
end
|
|
BestInSlot:RegisterSlashCmd("show", (L["%s or %s"]):format("/bis", "/bestinslot"), function() BestInSlot:ShowFrame() end, 1)
|
|
BestInSlot:RegisterSlashCmd("reset", ("/bis %s - %s"):format("reset", L["Resets the window to it's original position"]), function() if frame then BestInSlot:HideFrame() end wipe(BestInSlot.db.char.windowpos) end)
|
|
|