Files
coa-bagnon/Bagnon/components/item.lua
T

692 lines
15 KiB
Lua

--[[
item.lua
An item slot button
--]]
local Bagnon = LibStub('AceAddon-3.0'):GetAddon('Bagnon')
local ItemSlot = Bagnon.Classy:New('Button')
ItemSlot:Hide()
Bagnon.ItemSlot = ItemSlot
local ItemSearch = LibStub('LibItemSearch-1.0')
local function hasBlizzQuestHighlight()
return GetContainerItemQuestInfo and true or false
end
--[[
The item widget
--]]
--[[ ItemSlot Constructor ]]
--
function ItemSlot:New(bag, slot, frameID, parent)
local item = self:Restore() or self:Create()
item:SetParent(item:GetDummyBag(parent, bag))
item:SetID(slot)
item:SetFrameID(frameID)
if item:IsVisible() then
item:Update()
else
item:Show()
end
return item
end
--constructs a brand new item slot
function ItemSlot:Create()
local id = self:GetNextItemSlotID()
local item = self:Bind(self:GetBlizzardItemSlot(id) or self:ConstructNewItemSlot(id))
item:Hide()
--add a quality border texture
item.questBorder = _G[item:GetName() .. 'IconQuestTexture']
local border = item:CreateTexture(nil, 'OVERLAY')
border:SetWidth(67)
border:SetHeight(67)
border:SetPoint('CENTER', item)
border:SetTexture([[Interface\Buttons\UI-ActionButton-Border]])
border:SetBlendMode('ADD')
border:Hide()
item.border = border
--hack, make sure the cooldown model stays visible
item.cooldown = _G[item:GetName() .. 'Cooldown']
--get rid of any registered frame events, and use my own
item:SetScript('OnEvent', nil)
item:SetScript('OnEnter', item.OnEnter)
item:SetScript('OnLeave', item.OnLeave)
item:SetScript('OnShow', item.OnShow)
item:SetScript('OnHide', item.OnHide)
item:SetScript('PostClick', item.PostClick)
item.UpdateTooltip = nil
return item
end
--creates a new item slot for <id>
function ItemSlot:ConstructNewItemSlot(id)
return CreateFrame('Button', 'BagnonItemSlot' .. id, nil, 'ContainerFrameItemButtonTemplate')
end
--returns an available blizzard item slot for <id>
function ItemSlot:GetBlizzardItemSlot(id)
--only allow reuse of blizzard frames if all frames are enabled
if not self:CanReuseBlizzardBagSlots() then
return nil
end
local bag = math.ceil(id / MAX_CONTAINER_ITEMS)
local slot = (id - 1) % MAX_CONTAINER_ITEMS + 1
local item = _G[format('ContainerFrame%dItem%d', bag, slot)]
if item then
item:SetID(0)
item:ClearAllPoints()
return item
end
end
function ItemSlot:CanReuseBlizzardBagSlots()
return Bagnon.Settings:AreAllFramesEnabled() and (not Bagnon.Settings:IsBlizzardBagPassThroughEnabled())
end
--returns the next available item slot
function ItemSlot:Restore()
local item = ItemSlot.unused and next(ItemSlot.unused)
if item then
ItemSlot.unused[item] = nil
return item
end
end
--gets the next unique item slot id
do
local id = 1
function ItemSlot:GetNextItemSlotID()
local nextID = id
id = id + 1
return nextID
end
end
--[[ ItemSlot Destructor ]]
--
function ItemSlot:Free()
self:Hide()
self:SetParent(nil)
self:UnregisterAllEvents()
self:UnregisterAllMessages()
ItemSlot.unused = ItemSlot.unused or {}
ItemSlot.unused[self] = true
end
--[[ Events ]]
--
function ItemSlot:ITEM_SLOT_UPDATE(msg, bag, slot)
self:Update()
end
function ItemSlot:ITEM_LOCK_CHANGED(event, bag, slot)
self:UpdateLocked()
end
function ItemSlot:ITEM_SLOT_UPDATE_COOLDOWN(msg, bag, slot)
self:UpdateCooldown()
end
function ItemSlot:TEXT_SEARCH_UPDATE(msg, frameID, search)
self:UpdateSearch()
end
function ItemSlot:BAG_SEARCH_UPDATE(msg, frameID, search)
if self:GetFrameID() == frameID then
self:UpdateBagSearch()
end
end
function ItemSlot:ITEM_HIGHLIGHT_QUALITY_UPDATE(msg, enable)
self:UpdateBorder()
end
function ItemSlot:ITEM_HIGHLIGHT_QUEST_UPDATE(msg, enable)
self:UpdateBorder()
end
function ItemSlot:ITEM_HIGHLIGHT_OPACITY_UPDATE(msg, opacity)
self:UpdateBorder()
end
function ItemSlot:SHOW_EMPTY_ITEM_SLOT_TEXTURE_UPDATE(msg, enable)
self:Update()
end
function ItemSlot:ITEM_SLOT_COLOR_ENABLED_UPDATE(msg, type, r, g, b)
self:Update()
end
function ItemSlot:ITEM_SLOT_COLOR_UPDATE(msg, type, r, g, b)
self:Update()
end
function ItemSlot:QUEST_ACCEPTED()
self:UpdateBorder()
end
function ItemSlot:UNIT_QUEST_LOG_CHANGED()
self:UpdateBorder()
end
function ItemSlot:HandleEvent(msg, ...)
local action = self[msg]
if action then
action(self, msg, ...)
end
end
--[[ Frame Events ]]
--
function ItemSlot:OnShow()
self:Update()
end
function ItemSlot:OnHide()
self:HideStackSplitFrame()
end
function ItemSlot:OnDragStart()
if self:IsCached() and CursorHasItemSlot() then
ClearCursor()
end
end
function ItemSlot:OnModifiedClick(button)
local link = self:IsCached() and self:GetItem()
if link then
HandleModifiedItemClick(link)
end
end
function ItemSlot:OnEnter()
local dummySlot = self:GetDummyItemSlot()
if self:IsCached() then
dummySlot:SetParent(self)
dummySlot:SetAllPoints(self)
dummySlot:Show()
else
dummySlot:Hide()
if self:IsBank() then
if self:GetItem() then
self:AnchorTooltip()
GameTooltip:SetInventoryItem('player', BankButtonIDToInvSlotID(self:GetID()))
GameTooltip:Show()
CursorUpdate(self)
end
else
ContainerFrameItemButton_OnEnter(self)
end
end
end
function ItemSlot:OnLeave()
GameTooltip:Hide()
ResetCursor()
end
--[[ Update Methods ]]
--
-- Update the texture, lock status, and other information about an item
function ItemSlot:Update()
if not self:IsVisible() then return end
local texture, count, locked, quality, readable, lootable, link = self:GetItemSlotInfo()
self:SetItem(link)
self:SetTexture(texture)
self:SetCount(count)
self:SetLocked(locked)
self:SetReadable(readable)
self:SetBorderQuality(quality)
self:UpdateCooldown()
self:UpdateSlotColor()
self:UpdateSearch()
self:UpdateBagSearch()
if GameTooltip:IsOwned(self) then
self:UpdateTooltip()
end
end
--item link
function ItemSlot:SetItem(itemLink)
self.hasItem = itemLink or nil
end
function ItemSlot:GetItem()
return self.hasItem
end
--item texture
function ItemSlot:SetTexture(texture)
SetItemButtonTexture(self, texture or self:GetEmptyItemTexture())
end
function ItemSlot:GetEmptyItemTexture()
if self:ShowingEmptyItemSlotTexture() then
return [[Interface\PaperDoll\UI-Backpack-EmptySlot]]
end
return nil
end
--item slot color
function ItemSlot:UpdateSlotColor()
local normalTexture = self:GetNormalTexture() -- elvui nukes this function (in a bad way)
local r, g, b = 1, 1, 1
if (not self:GetItem()) and self:ColoringBagSlots() then
if self:IsKeyRingSlot() then
r, g, b = self:GetKeyringSlotColor()
elseif self:IsAmmoBagSlot() then
r, g, b = self:GetAmmoSlotColor()
elseif self:IsTradeBagSlot() then
r, g, b = self:GetTradeSlotColor()
elseif self:IsShardBagSlot() then
r, g, b = self:GetShardSlotColor()
end
end
SetItemButtonTextureVertexColor(self, r, g, b)
if normalTexture then
normalTexture:SetVertexColor(r, g, b)
end
end
--item count
function ItemSlot:SetCount(count)
SetItemButtonCount(self, count)
end
--readable status
function ItemSlot:SetReadable(readable)
self.readable = readable
end
--locked status
function ItemSlot:SetLocked(locked)
SetItemButtonDesaturated(self, locked)
end
function ItemSlot:UpdateLocked()
self:SetLocked(self:IsLocked())
end
-- returns true if the slot is locked, and false otherwise
function ItemSlot:IsLocked()
return Bagnon.ItemSlotInfo:IsLocked(self:GetPlayer(), self:GetBag(), self:GetID())
end
-- colors the item border based on the quality of the item. hides it for common/poor items
if hasBlizzQuestHighlight() then
function ItemSlot:SetBorderQuality(quality)
local border = self.border
local qBorder = self.questBorder
if self:HighlightingQuestItems() then
local isQuestItem, isQuestStarter = self:IsQuestItem()
if isQuestItem then
qBorder:SetTexture(TEXTURE_ITEM_QUEST_BORDER)
qBorder:SetAlpha(self:GetHighlightAlpha())
qBorder:Show()
border:Hide()
return
end
if isQuestStarter then
qBorder:SetTexture(TEXTURE_ITEM_QUEST_BANG)
qBorder:SetAlpha(self:GetHighlightAlpha())
qBorder:Show()
border:Hide()
return
end
end
if self:HighlightingItemsByQuality() then
if self:GetItem() and quality and quality > 1 then
local r, g, b = GetItemQualityColor(quality)
border:SetVertexColor(r, g, b, self:GetHighlightAlpha())
border:Show()
qBorder:Hide()
return
end
end
qBorder:Hide()
border:Hide()
end
else
function ItemSlot:SetBorderQuality(quality)
local border = self.border
if self:HighlightingItemsByQuality() then
if self:GetItem() and quality and quality > 1 then
local r, g, b = GetItemQualityColor(quality)
border:SetVertexColor(r, g, b, self:GetHighlightAlpha())
border:Show()
return
end
end
if self:HighlightingQuestItems() then
if self:IsQuestItem() then
border:SetVertexColor(1, 1, 0, self:GetHighlightAlpha())
border:Show()
return
end
end
border:Hide()
end
end
function ItemSlot:UpdateBorder()
local texture, count, locked, quality = self:GetItemSlotInfo()
self:SetBorderQuality(quality)
end
--cooldown
function ItemSlot:UpdateCooldown()
if self:GetItem() and (not self:IsCached()) then
ContainerFrame_UpdateCooldown(self:GetBag(), self)
else
CooldownFrame_SetTimer(self.cooldown, 0, 0, 0)
SetItemButtonTextureVertexColor(self, 1, 1, 1)
end
end
--stack split frame
function ItemSlot:HideStackSplitFrame()
if self.hasStackSplit and self.hasStackSplit == 1 then
StackSplitFrame:Hide()
end
end
--tooltip methods
ItemSlot.UpdateTooltip = ItemSlot.OnEnter
function ItemSlot:AnchorTooltip()
if self:GetRight() >= (GetScreenWidth() / 2) then
GameTooltip:SetOwner(self, 'ANCHOR_LEFT')
else
GameTooltip:SetOwner(self, 'ANCHOR_RIGHT')
end
end
--search
function ItemSlot:UpdateSearch()
local shouldFade = false
local search = self:GetItemSearch()
if search and search ~= '' then
local itemLink = self:GetItem()
shouldFade = not (itemLink and (ItemSearch:Find(itemLink, search)))
end
if shouldFade then
self:SetAlpha(0.4)
SetItemButtonDesaturated(self, true)
self.border:Hide()
else
self:SetAlpha(1)
self:UpdateLocked()
self:UpdateBorder()
self:UpdateSlotColor()
end
end
function ItemSlot:GetItemSearch()
return Bagnon.Settings:GetTextSearch()
end
--bag search
function ItemSlot:UpdateBagSearch()
local search = self:GetBagSearch()
if self:GetBag() == search then
self:LockHighlight()
else
self:UnlockHighlight()
end
end
function ItemSlot:GetBagSearch()
return self:GetSettings():GetBagSearch()
end
--[[ Accessor Methods ]]
--
function ItemSlot:SetFrameID(frameID)
if self:GetFrameID() ~= frameID then
self.frameID = frameID
self:Update()
end
end
function ItemSlot:GetFrameID()
return self.frameID
end
function ItemSlot:GetSettings()
return Bagnon.FrameSettings:Get(self:GetFrameID())
end
function ItemSlot:GetPlayer()
return self:GetSettings():GetPlayerFilter()
end
function ItemSlot:GetBag()
return self:GetParent() and self:GetParent():GetID() or 1
end
function ItemSlot:IsSlot(bag, slot)
return self:GetBag() == bag and self:GetID() == slot
end
function ItemSlot:IsCached()
return Bagnon.BagSlotInfo:IsCached(self:GetPlayer(), self:GetBag())
end
function ItemSlot:IsBank()
return Bagnon.BagSlotInfo:IsBank(self:GetBag())
end
function ItemSlot:IsBankSlot()
local bag = self:GetBag()
return Bagnon.BagSlotInfo:IsBank(bag) or Bagnon.BagSlotInfo:IsBankBag(bag)
end
function ItemSlot:AtBank()
return Bagnon.PlayerInfo:AtBank()
end
function ItemSlot:GetItemSlotInfo()
local texture, count, locked, quality, readable, lootable, link = Bagnon.ItemSlotInfo:GetItemInfo(self:GetPlayer(),
self:GetBag(), self:GetID())
return texture, count, locked, quality, readable, lootable, link
end
--[[ Item Type Highlighting ]]
--
function ItemSlot:HighlightingItemsByQuality()
return Bagnon.Settings:HighlightingItemsByQuality()
end
function ItemSlot:HighlightingQuestItems()
return Bagnon.Settings:HighlightingQuestItems()
end
function ItemSlot:GetHighlightAlpha()
return Bagnon.Settings:GetHighlightOpacity()
end
-- returns true if the item is a quest item or not
-- in 3.3, includes a second return to determine if the item is a quest starter for a quest the player lacks
local QUEST_ITEM_SEARCH = string.format('t:%s|%s', select(12, GetAuctionItemClasses()), 'quest')
if hasBlizzQuestHighlight() then
function ItemSlot:IsQuestItem()
local itemLink = self:GetItem()
if not itemLink then
return false, false
end
if self:IsCached() then
return ItemSearch:Find(itemLink, QUEST_ITEM_SEARCH), false
else
local isQuestItem, questID, isActive = GetContainerItemQuestInfo(self:GetBag(), self:GetID())
return isQuestItem, (questID and not isActive)
end
end
else
function ItemSlot:IsQuestItem()
local itemLink = self:GetItem()
if not itemLink then
return false
end
return ItemSearch:Find(itemLink, QUEST_ITEM_SEARCH)
end
end
--[[ Item Slot Coloring ]]
--
function ItemSlot:IsAmmoBagSlot()
return Bagnon.BagSlotInfo:IsAmmoBag(self:GetPlayer(), self:GetBag())
end
function ItemSlot:GetAmmoSlotColor()
return Bagnon.Settings:GetItemSlotColor('ammo')
end
function ItemSlot:IsTradeBagSlot()
return Bagnon.BagSlotInfo:IsTradeBag(self:GetPlayer(), self:GetBag())
end
function ItemSlot:GetTradeSlotColor()
return Bagnon.Settings:GetItemSlotColor('trade')
end
function ItemSlot:IsShardBagSlot()
return Bagnon.BagSlotInfo:IsShardBag(self:GetPlayer(), self:GetBag())
end
function ItemSlot:GetShardSlotColor()
return Bagnon.Settings:GetItemSlotColor('shard')
end
function ItemSlot:IsKeyRingSlot()
return Bagnon.BagSlotInfo:IsKeyRing(self:GetBag())
end
function ItemSlot:GetKeyringSlotColor()
return Bagnon.Settings:GetItemSlotColor('keyring')
end
function ItemSlot:ColoringBagSlots()
return Bagnon.Settings:ColoringBagSlots()
end
--[[ Empty Slot Visibility ]]
--
function ItemSlot:ShowingEmptyItemSlotTexture()
return Bagnon.Settings:ShowingEmptyItemSlotTextures()
end
--[[ Delicious Hacks ]]
--
-- dummy slot - A hack, used to provide a tooltip for cached items without tainting other item code
function ItemSlot:GetDummyItemSlot()
ItemSlot.dummySlot = ItemSlot.dummySlot or ItemSlot:CreateDummyItemSlot()
return ItemSlot.dummySlot
end
function ItemSlot:CreateDummyItemSlot()
local slot = CreateFrame('Button')
slot:RegisterForClicks('anyUp')
slot:SetToplevel(true)
slot:Hide()
local function Slot_OnEnter(self)
local parent = self:GetParent()
parent:LockHighlight()
if parent:IsCached() and parent:GetItem() then
ItemSlot.AnchorTooltip(self)
GameTooltip:SetHyperlink(parent:GetItem())
GameTooltip:Show()
end
end
local function Slot_OnLeave(self)
GameTooltip:Hide()
self:Hide()
end
local function Slot_OnHide(self)
local parent = self:GetParent()
if parent then
parent:UnlockHighlight()
end
end
local function Slot_OnClick(self, button)
self:GetParent():OnModifiedClick(button)
end
slot.UpdateTooltip = Slot_OnEnter
slot:SetScript('OnClick', Slot_OnClick)
slot:SetScript('OnEnter', Slot_OnEnter)
slot:SetScript('OnLeave', Slot_OnLeave)
slot:SetScript('OnShow', Slot_OnEnter)
slot:SetScript('OnHide', Slot_OnHide)
return slot
end
-- dummy bag, a hack to enforce the internal blizzard rule that item:GetParent():GetID() == bagID
function ItemSlot:GetDummyBag(parent, bag)
local dummyBags = parent.dummyBags
-- metatable magic to create a new frame on demand
if not dummyBags then
dummyBags = setmetatable({}, {
__index = function(t, k)
local f = CreateFrame('Frame', nil, parent)
f:SetID(k)
t[k] = f
return f
end
})
parent.dummyBags = dummyBags
end
return dummyBags[bag]
end