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.

1990 lines
75 KiB

local _, S = ...
local pairs, ipairs, string, type, time = pairs, ipairs, string, type, time
local COLUMN_HEADING_HEIGHT = 24
local numFilteredItems = 0
local COLUMN_HISTORY_STATES = 10
local function GetSortArrow(ascending)
local s = "|TInterface\\Addons\\Sorted\\Textures\\Sort-Arrow"
if S.Settings.Get("fontOutline") > 0 then
s = s.."-Outline"
if S.Settings.Get("fontOutline") > 1 then
s = s..":0:0:-5"
else
s = s..":0:0:-2"
end
else
s = s..":0:0:-1"
end
if not ascending then
s = s..":-1:16:16:0:16:0:16|t"
else
s = s..":0:16:16:0:16:16:0|t"
end
return s
end
local function GetSortMethod(self)
local columnSettings = self:GetColumnSettings()
if not self.columns[columnSettings.selectedColumn] then
columnSettings.selectedColumn = S.Settings.defaults[self.columnSettingsKey].selectedColumn
end
return self.columns[columnSettings.selectedColumn].sortMethods[columnSettings["sortMethod"]], columnSettings["sortAsc"]
end
local function GetSortMethodHistory(self)
return self:GetColumnSettings().historyStates
end
local function ToggleSortMethod(self, columnKey)
local cs = self:GetColumnSettings()
local col = self.columns[columnKey]
if col.sortMethods then
-- Toggle asc/desc, or switch to next sort method
if cs.selectedColumn == columnKey then
if not cs.sortAsc then
cs.sortAsc = true
else
cs.sortAsc = false
cs.sortMethod = cs.sortMethod + 1
if not col.sortMethods[cs.sortMethod] then
cs.sortMethod = 1
end
-- Remember the last used sort method
if not cs.lastSortMethod then
cs.lastSortMethod = {}
end
cs.lastSortMethod[columnKey] = cs.sortMethod
end
-- Update history
if not cs.historyStates then cs.historyStates = {} end
cs.historyStates[1] = {
["key"] = columnKey,
["sortMethod"] = cs.sortMethod,
["asc"] = cs.sortAsc
}
-- Switch to a different column, and add to history
else
if not cs.historyStates then
cs.historyStates = {}
end
table.insert(cs.historyStates, 1, {
["key"] = columnKey,
["sortMethod"] = 1,
["asc"] = false
})
if #cs.historyStates > COLUMN_HISTORY_STATES then -- Cap the number of history states
table.remove(cs.historyStates, COLUMN_HISTORY_STATES + 1)
end
cs.selectedColumn = columnKey
cs.sortAsc = false
-- Restore the previous sort method
if cs.lastSortMethod and cs.lastSortMethod[columnKey] then
cs.sortMethod = cs.lastSortMethod[columnKey]
else
cs.sortMethod = 1
end
end
elseif columnKey == "FAVORITES" then
cs.favoritesOnTop = not cs.favoritesOnTop
S.Utils.TriggerEvent("SortingChanged")
S.Utils.TriggerEvent("ColumnsChanged")
end
S.Utils.TriggerEvent("SortingChanged")
end
local function FilterEntries(self)
return
end
-- God I wish I had better names for these functions, but here goes...
-- EntryHasData returns true when the entry refers to an object that exists
-- For example, a currency with 0 quantity, or an empty item slot in a bag, should return false
local function EntryHasData(self, entry)
return true
end
-- GetDataForEntry returns the data referenced by the entry
-- For example, an item entry doesn't store all the data about an item, but has the attributes 'bag' and 'slot'
-- This function should pass those attributes to S.Data.GetItem to get the actual data
local function GetDataForEntry(self, entry)
return {}
end
-- For each entry in .entryData, adds attributes:
-- .data referencing the data from GetDataForEntry
-- .hasData, a boolean indicating whether the entry should be displayed
local function UpdateEntryData(self)
for k,v in pairs(self.entryData) do
v.data = self:GetDataForEntry(v)
v.hasData = self:EntryHasData(v)
end
end
-- Creates a table which can be iterated through in order to produce the displayed list
-- Copied from .entryData, but also:
-- Is sorted
-- Includes group headings
-- Doesn't include empty item slots
-- Filters items
-- Combine item stacks
local function UpdateDisplayedEntryData(self)
local entryDataTable = self.entryData
-- Reset entry data
for k,v in pairs(self.entryData) do
v.isCombined = nil
v.otherLocations = nil
end
for k,v in pairs(self.entryButtons) do
v.isCombined = nil
v.otherLocations = nil
end
-- Load up each entry with its data
self:UpdateEntryData()
-- Filter items
if self.FilterEntries then
self:FilterEntries()
end
-- Combine stacks in a new entryDataTable
-- Makes a table 'items' which is indexed by item key
-- Then adds them to entryDataTable indexed by number
local doCombineStacks = self.canCombineStacks and S.Settings.Get("combineStacks") == 1
if doCombineStacks then
entryDataTable = {}
local unfilteredItems, filteredItems = {}, {}
for _, entry in pairs(self.entryData) do
if entry.hasData then
local key = entry.data.key
local items
if entry.filtered then
items = filteredItems
else
items = unfilteredItems
end
if items[key] then
-- Combine entry with the existing entry
items[key].data.combinedCount = items[key].data.combinedCount + entry.data.count
items[key].isCombined = true
if not items[key].otherLocations then
items[key].otherLocations = {}
end
local t = {}
for k,v in pairs(entry) do
t[k] = v
end
t.isCombined = false
t.data.combinedCount = t.data.count
table.insert(items[key].otherLocations, t)
else
local t = {}
for k,v in pairs(entry) do
t[k] = v
end
t.data.combinedCount = entry.data.count
t.isCombined = false
items[key] = t
end
end
end
local i = 1
for _, item in pairs(unfilteredItems) do
entryDataTable[i] = item
i = i + 1
end
for _, item in pairs(filteredItems) do
entryDataTable[i] = item
i = i + 1
end
else
for _, entry in pairs(self.entryData) do
entry.data.combinedCount = entry.data.count
end
end
-- Sort .entryData
self:SortTable(entryDataTable)
--Insert extra entries from combined stacks that the user has expanded
if doCombineStacks then
local i = 1
while i <= #entryDataTable do
local entry = entryDataTable[i]
if entry.isCombined then
if self.expandedCombinedItems[entry.data.key] then
entry.data.combinedCount = entry.data.count
for k,v in pairs(entry.otherLocations) do
i = i + 1
table.insert(entryDataTable, i, v)
end
end
end
i = i + 1
end
end
-- Create .displayedEntryData
self.displayedEntryData = {}
-- Iterate over entryData and build displayedEntryData
-- i is the current index of entryData
-- j is the current index of displayedEntryData
local i,j = 1,1
local grouping = self:GetGrouping()
local heading, headingCollapsed, foundFilteredItem = nil, nil, nil
local groupName, groupOrder = nil, nil
local groupEntriesCount, lastGroupHeadingIndex = 0, nil
local entryData = nil
numFilteredItems = 0
while i <= #entryDataTable do
entryData = entryDataTable[i]
if not entryData.hasData then
i = i + 1
elseif entryData.filtered then
-- Add a heading for greyed out filtered items
if not foundFilteredItem and entryData.filtered then
foundFilteredItem = true
-- Add line break
if j > 1 then
if self.gridView then
-- Get to the next row
while j % self.numEntriesAcross ~= 1 do
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
else
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
end
self.displayedEntryData[j] = {
["isFilteredHeading"] = true
}
j = j + 1
if self.gridView then
-- Get to the next row
while j % self.numEntriesAcross ~= 1 do
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
end
else
if not S.Settings.Get("filteredCollapsed") then
self.displayedEntryData[j] = entryData
j = j + 1
end
numFilteredItems = numFilteredItems + 1
i = i + 1
end
else--[[if grouping then]]
local prevGroupOrder = groupOrder
groupName, groupOrder = self:GetEntryGroup(entryData)--self.groups[grouping].func(entryData.data)
-- Add a new group heading if this entry is in a new group
-- Don't add a heading for greyed out, filtered items at the bottom of the list
-- Also don't add a heading for OTHER items, if it's the only group
if not entryData.filtered and heading ~= groupName and not (groupOrder == 0 and not heading) then
if lastGroupHeadingIndex then
self.displayedEntryData[lastGroupHeadingIndex].numEntries = groupEntriesCount -- Count the number of entries in the previous group
end
groupEntriesCount = 0
lastGroupHeadingIndex = j
if self.gridView then
-- Get to the next row
while j % self.numEntriesAcross ~= 1 do
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
end
-- Add a blank row below new/recently unequippd items
if (prevGroupOrder and type(prevGroupOrder) == "number" and prevGroupOrder < 0) and (type(groupOrder) ~= "number" or groupOrder >= 0) then
if self.gridView then
-- Get to the next row
while j % self.numEntriesAcross ~= 1 do
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
else
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
end
heading = groupName
headingCollapsed = self:GetGroupHeadingCollapsed(groupName)
if groupOrder ~= 0 then
self.displayedEntryData[j] = {
["isGroupHeading"] = true,
["group"] = groupName,
["groupOrder"] = groupOrder
}
j = j + 1
end
if self.gridView then
-- Get to the next row
while j % self.numEntriesAcross ~= 1 do
self.displayedEntryData[j] = {
["isEmpty"] = true
}
j = j + 1
end
end
else
if not headingCollapsed then
self.displayedEntryData[j] = entryData
j = j + 1
end
groupEntriesCount = groupEntriesCount + 1
i = i + 1
end
--[[else
self.displayedEntryData[j] = entryData
i = i + 1
j = j + 1]]
end
end
if lastGroupHeadingIndex then
self.displayedEntryData[lastGroupHeadingIndex].numEntries = groupEntriesCount -- Count the number of entries in the last group
end
end
local sortingArgs = {}
local function SortFunction(entry1, entry2)
if not entry1.hasData then
return false
elseif not entry2.hasData then
return true
end
if entry1.filtered and not entry2.filtered then
return false
elseif not entry1.filtered and entry2.filtered then
return true
end
--[[if sortingArgs.pinNew then -- new items are now assigned a group with order -100, so they can be sorted by that instead
if entry1.isNew ~= entry2.isNew then
return entry1.isNew == true
end
end]]
if not entry1.filtered then
if entry1.group ~= entry2.group then
return entry1.group < entry2.group
end
end
if sortingArgs.favoriting then
if entry1.favorited ~= entry2.favorited then
if not entry1.favorited then return false elseif not entry2.favorited then return true end
return entry1.favorited < entry2.favorited
end
end
-- Use column sorting
local result = sortingArgs.sortMethod.func(sortingArgs.asc, entry1.data, entry2.data)
if result == 0 and sortingArgs.historyStates then -- Still identical, go through the historical columns
local i = 2
while i <= #sortingArgs.historyStates and result == 0 do
if sortingArgs.historyFuncs[i] then
result = sortingArgs.historyFuncs[i].func(sortingArgs.historyStates[i].asc, entry1.data, entry2.data)
end
i = i + 1
end
end
-- Legacy support for outdated plugins that return true/false instead of 1/0/-1
if type(result) == "boolean" then
return result
end
if result == 0 then -- Still identical, use default function
return sortingArgs.defaultFunc(entry1.data, entry2.data)
end
return result < 0
end
-- Sorts table 't' using SortFunction
local function SortTable(self, t)
-- Load up the sortingArgs table
sortingArgs.defaultFunc = self.DefaultSortFunc
--sortingArgs.grouping = self:GetGrouping()
sortingArgs.favoriting = self:GetColumnSettings().favoritesOnTop
sortingArgs.sortMethod, sortingArgs.asc = self:GetSortMethod()
sortingArgs.pinNew = S.Settings.Get("newOnTop") == 1
-- Add grouping and favoriting information
for k,v in pairs(t) do
_, v.group = self:GetEntryGroup(v)
if sortingArgs.favoriting then
v.favorited = self:GetEntryFavorited(v.data)
end
if S.IsPlayingCharacterSelected() then
if sortingArgs.pinNew then
v.isNew = self:GetEntryNew(v.data)
end
else
v.new = false
end
end
-- Add historical sort methods
sortingArgs.historyStates = self:GetSortMethodHistory()
sortingArgs.historyFuncs = {}
if sortingArgs.historyStates then
for i, v in ipairs(sortingArgs.historyStates) do
if self.columns[v.key] then -- Check column still exists, in case it was provided by a plugin which has since been disabled
sortingArgs.historyFuncs[i] = self.columns[v.key].sortMethods[v.sortMethod]
else
sortingArgs.historyFuncs[i] = nil
end
end
end
table.sort(t, SortFunction)
end
local function AddEntry(self, listEntry)
table.insert(self.entryData, listEntry)
end
local function EntryExists(self, entryDataIndex)
if self.entryData[entryDataIndex] and self:EntryHasData(self.entryData[entryDataIndex]) then
return true
end
return false
end
--[[
-- Make sure to only run UpdateGroups after sorting!
local function UpdateGroups(self)
self.groupHeadings = {}
self.entriesWithGroupHeadings = {}
local grouping = self:GetGrouping()
local currentGroup = nil
-- i iterates through the original entryData table
-- j counts the number of entries in the new table, which includes group headings
local i, j = 1, 1
while i <= #self.entryData do
local entry = self.entryData[i]
local groupName, _ = self.groups[grouping].func(self:GetDataForEntry(entry))
if not self:EntryHasData(entry) then
i = i + 1
elseif entry.filtered then
self.entriesWithGroupHeadings[j] = entry
j = j + 1
i = i + 1
elseif currentGroup ~= groupName then
self.groupHeadings[#self.groupHeadings + 1] = groupName
self.entriesWithGroupHeadings[j] = {
["isGroupHeading"] = true,
["group"] = groupName,
["groupHeadingIndex"] = j
}
j = j + 1
currentGroup = groupName
else
entry.group = groupName
if not self:GetGroupHeadingCollapsed(groupName) then
self.entriesWithGroupHeadings[j] = entry
j = j + 1
end
i = i + 1
end
end
end
local function UpdateGroupsGrid(self)
self.groupHeadings = {}
self.entriesWithGroupHeadings = {}
local grouping = self:GetGrouping()
local currentGroup = nil
local foundFilteredItem = false
-- i iterates through the original entryData table
-- j counts the number of entries in the new table, which includes group headings
local i, j = 1, 1
while i <= #self.entryData do
local entry = self.entryData[i]
local groupName, _ = self.groups[grouping].func(self:GetDataForEntry(entry))
if not self:EntryHasData(entry) then
i = i + 1
elseif entry.filtered then
if not foundFilteredItem then
foundFilteredItem = true
while j % self.numEntriesAcross ~= 1 do
self.entriesWithGroupHeadings[j] = {
["empty"] = true
}
j = j + 1
end
end
self.entriesWithGroupHeadings[j] = entry
j = j + 1
i = i + 1
elseif currentGroup ~= groupName then
while j % self.numEntriesAcross ~= 1 do
self.entriesWithGroupHeadings[j] = {
["empty"] = true
}
j = j + 1
end
self.groupHeadings[#self.groupHeadings + 1] = groupName
self.entriesWithGroupHeadings[j] = {
["isGroupHeading"] = true,
["group"] = groupName,
["groupHeadingIndex"] = math.floor(j / self.numEntriesAcross) + 1
}
j = j + 1
while j % self.numEntriesAcross ~= 1 do
self.entriesWithGroupHeadings[j] = {
["empty"] = true
}
j = j + 1
end
currentGroup = groupName
else
entry.group = groupName
if not self:GetGroupHeadingCollapsed(groupName) then
self.entriesWithGroupHeadings[j] = entry
j = j + 1
end
i = i + 1
end
end
end
local function UpdateEntryButtonsWithGrouping(self)
UpdateGroups(self)
local isAvailable = self:IsAvailable()
local topEntryIndex = floor(self.scrollPos)
local i = 1
while i <= self:GetNumVisibleEntries() do
local entryButton = self.entryButtons[i]
local headingButton = self.headingButtons[i]
if not self.entriesWithGroupHeadings[topEntryIndex + i] then
entryButton:Hide()
headingButton:Hide()
else
local entryData = self.entriesWithGroupHeadings[topEntryIndex + i]
if entryData.empty then
entryButton:Hide()
headingButton:Hide()
elseif entryData.isGroupHeading then
headingButton.group = entryData.group
headingButton.nameString:SetText(entryData.group)
headingButton:Show()
headingButton:SetCollapsed(self:GetGroupHeadingCollapsed(entryData.group))
entryButton:Hide()
elseif entryData and self:EntryHasData(entryData) then
-- Add keys from the entryData table to the entry object before updating it
for k,w in pairs(entryData) do
entryButton[k] = w
end
entryButton:Show()
entryButton:Update()
if isAvailable then
--entryButton:SetAlpha(1)
entryButton:Enable()
else
--entryButton:SetAlpha(0.8)
entryButton:Disable()
end
headingButton:Hide()
else
entryButton:Hide()
headingButton:Hide()
end
end
i = i + 1
end
end
local function UpdateEntryButtonsWithGroupingGrid(self)
UpdateGroupsGrid(self)
local isAvailable = self:IsAvailable()
local topEntryIndex = floor(self.scrollPos) * self.numEntriesAcross
for k,v in pairs(self.headingButtons) do
v:Hide()
end
local i = 1
while i <= self:GetNumVisibleEntries() do
local entryButton = self.entryButtons[i]
if not self.entriesWithGroupHeadings[topEntryIndex + i] then
entryButton:Hide()
else
local entryData = self.entriesWithGroupHeadings[topEntryIndex + i]
if entryData.empty then
entryButton:Hide()
elseif entryData.isGroupHeading then
local headingButton = self.headingButtons[entryData.groupHeadingIndex - floor(self.scrollPos)]
if headingButton then
headingButton.group = entryData.group
headingButton.nameString:SetText(entryData.group)
headingButton:Show()
headingButton:SetCollapsed(self:GetGroupHeadingCollapsed(entryData.group))
end
entryButton:Hide()
elseif entryData and self:EntryHasData(entryData) then
-- Add keys from the entryData table to the entry object before updating it
for k,w in pairs(entryData) do
entryButton[k] = w
end
entryButton:Show()
entryButton:Update()
if isAvailable then
--entryButton:SetAlpha(1)
entryButton:Enable()
else
--entryButton:SetAlpha(0.8)
entryButton:Disable()
end
else
entryButton:Hide()
end
end
i = i + 1
end
end
local function UpdateEntryButtons(self)
if self:GetGrouping() then
if self.gridView then
UpdateEntryButtonsWithGroupingGrid(self)
else
UpdateEntryButtonsWithGrouping(self)
end
return
end
local isAvailable = self:IsAvailable()
local entryIndex
if self.gridView then
entryIndex = floor(self.scrollPos) * self.numEntriesAcross + 1
else
entryIndex = floor(self.scrollPos) + 1
end
for i = 1, self:GetNumVisibleEntries() do
self.headingButtons[i]:Hide()
local entry = self.entryButtons[i]
local entryData = self.entryData[entryIndex]
if entryData and self:EntryHasData(entryData) then
-- Add keys from the entryData table to the button before updating it
for k,w in pairs(entryData) do
entry[k] = w
end
entry:Show()
entry:Update()
if isAvailable then
--v:SetAlpha(1)
entry:Enable()
else
--v:SetAlpha(0.8)
entry:Disable()
end
entryIndex = entryIndex + 1
else
entry:Hide()
entryIndex = entryIndex + 1
end
end
end]]
local function UpdateEntryButtonIcons(self)
local iconSize, borderThickness, iconShape, iconBorders
if self.gridView then
iconSize = S.Settings.Get("iconSizeGrid")
borderThickness = S.Settings.Get("iconBorderThicknessGrid")
iconShape = S.Settings.Get("iconShapeGrid")
iconBorders = S.Settings.Get("iconBordersGrid")
else
iconSize = S.Settings.Get("iconSize")
borderThickness = S.Settings.Get("iconBorderThickness")
iconShape = S.Settings.Get("iconShape")
iconBorders = S.Settings.Get("iconBorders")
end
for _, v in pairs(self.entryButtons) do
v:UpdateIcons(iconSize, borderThickness, iconShape, iconBorders)
end
end
local function UpdateEntryButtons(self)
local isAvailable = self:IsAvailable()
local eb, ed, hb -- entryButton, entryData, headingButton
-- Iterate over the tables
local bi = 1 -- index in entryButtons
local di -- index in displayedEntryData
if self.gridView then
di = floor(self.scrollPos) * self.numEntriesAcross + 1
else
di = floor(self.scrollPos) + 1
end
for _, hb in pairs(self.headingButtons) do
hb:Hide()
end
for _, eb in pairs(self.entryButtons) do
eb:Hide()
end
while bi <= self:GetNumVisibleEntries() do
eb = self.entryButtons[bi]
if self.gridView then
hb = self.headingButtons[floor(bi / self.numEntriesAcross) + 1]
else
hb = self.headingButtons[bi]
end
if di > #self.displayedEntryData then
else
ed = self.displayedEntryData[di]
if ed then
if ed.isEmpty then
elseif ed.isGroupHeading then
if ed.group then
-- Display the group heading
hb.group = ed.group
hb.isForFilteredItems = false
if ed.numEntries then
hb.nameString:SetText(ed.group.. " |cff555555(|cffbbbbbb"..ed.numEntries.."|cff555555)")
else
hb.nameString:SetText(ed.group.. " |cff555555(|cffbbbbbb0|cff555555)")
end
if type(ed.groupOrder) == "number" and ed.groupOrder <= 0 then
hb:SetCollapsed(false)
hb:SetCollapsible(false)
else
hb:SetCollapsed(self:GetGroupHeadingCollapsed(ed.group))
hb:SetCollapsible(true)
end
hb.button:GetNormalTexture():SetVertexColor(1, 1, 1)
hb.button:GetNormalTexture():SetDesaturated(false)
hb.button:GetHighlightTexture():SetDesaturated(false)
hb.button:GetPushedTexture():SetDesaturated(false)
hb:Show()
end
elseif ed.isFilteredHeading then
-- Display the filtered heading
hb.nameString:SetText("|cff666666"..S.Localize("FILTERED_ITEMS").." |cff444444(|cff666666"..numFilteredItems.."|cff444444)")
hb.isForFilteredItems = true
hb:SetCollapsed(S.Settings.Get("filteredCollapsed"))
hb.button:GetNormalTexture():SetDesaturated(true)
hb.button:GetNormalTexture():SetVertexColor(0.6, 0.6, 0.6)
hb.button:GetHighlightTexture():SetDesaturated(true)
hb.button:GetPushedTexture():SetDesaturated(true)
hb:Show()
else
--hb:Hide()
-- Add keys from the entry data table to the button before updating it
-- ed.data points to a data table in Sorted_Data. DON'T use this.
-- Instead, point back to the entry button's data table and populate it
-- This table will be getting extra data that shouldn't be saved between sessions
local ebData = eb.data
for k,v in pairs(ed) do
eb[k] = v
end
--eb.data = ebData
eb.data = {}
for k,v in pairs(ed.data) do
eb.data[k] = v
end
eb:Show()
eb:Update()
--eb.button:SetEnabled(isAvailable)
end
end
end
bi = bi + 1
di = di + 1
end
end
local function CreateGroupHeading(list)
local self = CreateFrame("FRAME", nil, list.listFrame)
self:ClearAllPoints()
self.list = list
self:SetPoint("LEFT")
self:SetPoint("RIGHT")
self:SetHeight(20)
self.nameString = self:CreateFontString(nil, "OVERLAY", "SortedFont")
self.nameString:SetTextScale(1.1)
self.nameString:SetPoint("LEFT", 28, 0)
self.nameString:SetTextColor(S.Utils.GetButtonTextColor():GetRGB())
self.button = CreateFrame("BUTTON", nil, self)
self.button.parent = self
self.button:SetAllPoints()
self.button:SetNormalTexture("Interface\\Addons\\Sorted\\Textures\\Expand-Button")
self.button:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Close-Button-Highlight")
self.button:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Expand-Button")
self.button:GetNormalTexture():ClearAllPoints()
self.button:GetNormalTexture():SetPoint("LEFT", 2, 0)
self.button:GetNormalTexture():SetSize(22, 22)
self.button:GetHighlightTexture():ClearAllPoints()
self.button:GetHighlightTexture():SetPoint("LEFT", 2, 0)
self.button:GetHighlightTexture():SetSize(22, 22)
self.button:GetPushedTexture():ClearAllPoints()
self.button:GetPushedTexture():SetPoint("LEFT", 2, 0)
self.button:GetPushedTexture():SetSize(22, 22)
function self:SetCollapsed(collapsed)
self.collapsed = collapsed
if collapsed then
self.button:GetNormalTexture():SetTexCoord(0, 0.375, 0, 0.375)
self.button:GetPushedTexture():SetTexCoord(0.375, 0.75, 0, 0.375)
else
self.button:GetNormalTexture():SetTexCoord(0, 0.375, 0.375, 0.75)
self.button:GetPushedTexture():SetTexCoord(0.375, 0.75, 0.375, 0.75)
end
end
function self:SetCollapsible(collapsible)
self.button:SetEnabled(collapsible)
if collapsible then
--self.button:GetNormalTexture():SetDesaturated(false)
self.button:SetAlpha(1)
else
--self.button:GetNormalTexture():SetDesaturated(true)
self.button:SetAlpha(0.3)
end
end
self.button:SetScript("OnClick", function(self)
if self.parent.isForFilteredItems then
S.Settings.Set("filteredCollapsed", not S.Settings.Get("filteredCollapsed"))
S.Utils.TriggerEvent("GroupingChanged")
else
self.parent.list:ToggleGroupHeading(self.parent.group)
end
end)
self:SetCollapsed(false)
return self
end
local function GetNumVisibleEntries(self)
return self.numVisibleEntries
end
local function OnResize(self)
local entryHeight = S.Settings.Get("iconSize") + S.Settings.Get("padding") * 2
local y = 0
-- Create entries to fill frame
local i = 0
while y < self.listFrame:GetHeight() + entryHeight do
i = i + 1
if not self.entryButtons[i] then
self.entryButtons[i] = self.CreateEntry(self, self.listFrame)
self.headingButtons[i] = CreateGroupHeading(self)
end
self.entryButtons[i]:SetParent(self.entryButtons[i].parentFrame)
if not self.entryButtons[i]:IsShown() then
self.entryButtons[i]:Show()
end
y = y + entryHeight
end
self.numVisibleEntries = i
-- Hide entries below bottom of frame
i = i + 1
while i <= #self.entryButtons do
self.entryButtons[i]:Hide()
self.entryButtons[i]:ClearAllPoints()
self.entryButtons[i]:SetParent(nil)
self.headingButtons[i]:Hide()
i = i + 1
end
self:PositionEntryButtons()
self:UpdateEntryButtonIcons()
self:UpdateEntryButtons()
end
local function OnResizeGrid(self)
local size = S.Settings.Get("iconSizeGrid") + S.Settings.Get("paddingGrid") * 2
local x,y = 0,0
local listWidth = self:GetWidth() - self.scrollBar:GetWidth()
self.numEntriesAcross = math.floor(listWidth / size)
-- Create entries to fill frame
local i = 1
while y < self.listFrame:GetHeight() + size do
if not self.entryButtons[i] then
self.entryButtons[i] = self.CreateEntry(self, self.listFrame)
self.headingButtons[i] = CreateGroupHeading(self)
end
self.entryButtons[i]:SetParent(self.entryButtons[i].parentFrame)
if not self.entryButtons[i]:IsShown() then
self.entryButtons[i]:Show()
end
x = x + size
if x > listWidth - size then
x = 0
y = y + size
end
i = i + 1
end
self.numVisibleEntries = i - 1
-- Hide entries below bottom of frame
while i <= #self.entryButtons do
self.entryButtons[i]:Hide()
self.entryButtons[i]:ClearAllPoints()
self.entryButtons[i]:SetParent(nil)
self.headingButtons[i]:Hide()
i = i + 1
end
self:PositionEntryButtons()
self:UpdateEntryButtonIcons()
end
local function ScrollToTop(self)
if S.Settings.Get("smoothingAmount") == 0 then
self.scrollPos = 0
self:UpdateEntryButtons()
self:PositionEntryButtons()
else
self.scrollBar.doSmoothing = true
self.scrollBar.smoothOppositeCurve = false
self.scrollBar.smoothStart = self.scrollPos
self.scrollBar.smoothTime = GetTime() - GetTickTime()
self.scrollBar.smoothTarget = 0
end
end
local function UpdateScrollBarMax(self)
local entryHeight
if self.gridView then
entryHeight = S.Settings.Get("iconSizeGrid") + S.Settings.Get("paddingGrid") * 2
else
entryHeight = S.Settings.Get("iconSize") + S.Settings.Get("padding") * 2
end
local numEntries = #self.displayedEntryData
local max
if self.gridView then
max = (math.floor(numEntries / self.numEntriesAcross) + 2) - self.listFrame:GetHeight() / entryHeight
else
max = numEntries - self.listFrame:GetHeight() / entryHeight
end
if max > 0 then
self.scrollBar:SetMinMaxValues(0, max)
self.scrollBar.ThumbTexture:Show()
self.scrollBar.ScrollUpButton:Enable()
self.scrollBar.ScrollDownButton:Enable()
else
max = 0
self.scrollBar:SetMinMaxValues(0, 0)
self.scrollBar.ThumbTexture:Hide()
self.scrollBar.ScrollUpButton:Disable()
self.scrollBar.ScrollDownButton:Disable()
end
if self.scrollPos > max then
if S.Settings.Get("smoothingAmount") == 0 then
self.scrollPos = max
self:UpdateEntryButtons()
self:PositionEntryButtons()
elseif not self.scrollBar.doSmoothing or self.scrollBar.smoothTarget > max then
self.scrollBar.doSmoothing = true
self.scrollBar.smoothOppositeCurve = true
self.scrollBar.smoothStart = self.scrollPos
self.scrollBar.smoothTime = GetTime() - GetTickTime()
self.scrollBar.smoothTarget = max
end
end
end
local entryButtonHeightCached
local function PositionEntryButtons(self)
local entryHeight = S.Settings.Get("iconSize") + S.Settings.Get("padding") * 2
entryButtonHeightCached = entryHeight
local y = -(self.scrollPos - floor(self.scrollPos)) * entryHeight
for i = 1, self:GetNumVisibleEntries() do
self.entryButtons[i]:SetHeight(entryHeight)
self.entryButtons[i]:SetPoint("LEFT")
self.entryButtons[i]:SetPoint("RIGHT")
self.entryButtons[i]:SetPoint("TOP", self.listFrame, "TOP", 0, -y)
self.headingButtons[i]:SetHeight(entryHeight)
self.headingButtons[i]:SetPoint("LEFT")
self.headingButtons[i]:SetPoint("RIGHT")
self.headingButtons[i]:SetPoint("TOP", self.listFrame, "TOP", 0, -y)
y = y + entryHeight
end
end
-- Doesn't change the size of the buttons, just quickly repositions them. For use with smooth scrolling
local function PositionEntryButtonsFast(self)
local y = -(self.scrollPos - floor(self.scrollPos)) * entryButtonHeightCached
for i = 1, self:GetNumVisibleEntries() do
self.entryButtons[i]:SetPoint("TOP", self.listFrame, "TOP", 0, -y)
self.headingButtons[i]:SetPoint("TOP", self.listFrame, "TOP", 0, -y)
y = y + entryButtonHeightCached
end
end
local function PositionEntryButtonsGrid(self)
local size = S.Settings.Get("iconSizeGrid") + S.Settings.Get("paddingGrid") * 2
local x, y = 0, 0
local scrollOffset = (self.scrollPos - floor(self.scrollPos)) * size
local listWidth = self:GetWidth() - self.scrollBar:GetWidth()
for i = 1, #self.headingButtons do
self.headingButtons[i]:SetHeight(size)
self.headingButtons[i]:SetPoint("LEFT")
self.headingButtons[i]:SetPoint("RIGHT")
self.headingButtons[i]:SetPoint("TOP", self.listFrame, "TOP", 0, -i * size + size + scrollOffset)
end
for i = 1, self:GetNumVisibleEntries() do
self.entryButtons[i]:ClearAllPoints()
self.entryButtons[i]:SetSize(size, size)
self.entryButtons[i]:SetPoint("TOPLEFT", x, -y + scrollOffset)
x = x + size
if x > listWidth - size then
x = 0
y = y + size
end
end
end
local function GetGroupingSettings(self)
return S.Settings.Get(self.groupingSettings)
end
local function SelectGrouping(self, key)
local settings = GetGroupingSettings(self)
settings.selectedGrouping = key
--settings.collapsedGroups = {} -- Collapsed groups used to be tied to settings, now they're tied to the list object and aren't saved between sessions
self.collapsedGroups = {}
S.Utils.TriggerEvent("GroupingChanged")
end
local function DeselectGrouping(self)
GetGroupingSettings(self).selectedGrouping = nil
S.Utils.TriggerEvent("GroupingChanged")
end
local function GetGrouping(self)
return GetGroupingSettings(self).selectedGrouping
end
local function GetEntryGroup(self, entryData)
if S.Settings.Get("newOnTop") == 1 and self:GetEntryNew(entryData) then
return "|cff99ff00"..NEW, -100
end
local grouping = self:GetGrouping()
if grouping then
return self.groups[self:GetGrouping()].func(entryData.data)
else
return OTHER, 0
end
end
local function ToggleGroupHeading(self, group)
--local settings = GetGroupingSettings(self)
if self.collapsedGroups[group] then
self.collapsedGroups[group] = nil
else
self.collapsedGroups[group] = true
end
S.Utils.TriggerEvent("GroupingChanged")
end
local function GetGroupHeadingCollapsed(self, group)
return self.collapsedGroups[group]
end
-- Returns the width of a column, either using the column's own function,
-- or the width set by the user
local function GetColumnWidth(self, key)
if self.columns[key].GetWidth then
return self.columns[key]:GetWidth()
end
local settings = self:GetColumnSettings()
if settings.widths and settings.widths[key] then
return settings.widths[key]
end
return self.columns[key].width
end
local function UpdateColumnsSortArrows(self)
local cs = self:GetColumnSettings()
for k,v in pairs(self.columnHeadings) do
local col = self.columns[k]
if k == cs.selectedColumn then
local sortMethod = col.sortMethods[cs.sortMethod]
if sortMethod.inverse then
v.nameString:SetText(sortMethod.title..GetSortArrow(not cs.sortAsc))
else
v.nameString:SetText(sortMethod.title..GetSortArrow(cs.sortAsc))
end
else
if col.sortMethods then
if cs.lastSortMethod and cs.lastSortMethod[v.key] then
v.nameString:SetText(col.sortMethods[cs.lastSortMethod[v.key]].title)
else
v.nameString:SetText(col.sortMethods[1].title)
end
end
end
end
end
local function PositionColumns(self)
local nameColumnIndex = 0
local nameColumnButton = nil
local doCombineStacks = self.canCombineStacks and S.Settings.Get("combineStacks") == 1
-- Find the middle 'name' column which expands to fill the remaining space
for i,v in ipairs(self:GetColumnOrder()) do
local key = self:GetColumnOrder()[i]
if key == "NAME" then
nameColumnIndex = i
nameColumnButton = self.columnHeadings[v]
nameColumnButton:SetPoint("TOP")
nameColumnButton:SetPoint("BOTTOM")
end
end
local lastEnabledColumnButton = nil
for i = 1, nameColumnIndex - 1, 1 do
local key = self:GetColumnOrder()[i]
if self:ColumnEnabled(key) then
local button = self.columnHeadings[key]
local column = self.columns[key]
button:ClearAllPoints()
button:SetPoint("TOP", self.head)
button:SetPoint("BOTTOM", self.head)
-- Resize handle goes on the right
if button.resizeButton then
button.resizeButton:SetPoint("LEFT", button, "RIGHT", -3, 0)
button.resizeButton.invert = false
end
if lastEnabledColumnButton then
button:SetPoint("LEFT", lastEnabledColumnButton, "RIGHT")
else
if doCombineStacks then
button:SetPoint("LEFT", self.head, 20, 0)
else
button:SetPoint("LEFT", self.head)
end
end
button:SetWidth(self:GetColumnWidth(key))
lastEnabledColumnButton = button
end
end
if lastEnabledColumnButton then
nameColumnButton:SetPoint("LEFT", lastEnabledColumnButton, "RIGHT")
else
nameColumnButton:SetPoint("LEFT")
end
lastEnabledColumnButton = nil
for i = #self:GetColumnOrder(), nameColumnIndex + 1, -1 do
local key = self:GetColumnOrder()[i]
if self:ColumnEnabled(key) then
local button = self.columnHeadings[key]
local column = self.columns[key]
button:ClearAllPoints()
button:SetPoint("TOP", self.head)
button:SetPoint("BOTTOM", self.head)
-- Resize handle goes on the left
if button.resizeButton then
button.resizeButton:SetPoint("LEFT", -3, 0)
button.resizeButton.invert = true
end
if lastEnabledColumnButton then
button:SetPoint("RIGHT", lastEnabledColumnButton, "LEFT")
else
button:SetPoint("RIGHT", self.head)
end
button:SetWidth(self:GetColumnWidth(key))
lastEnabledColumnButton = button
end
end
if lastEnabledColumnButton then
nameColumnButton:SetPoint("RIGHT", lastEnabledColumnButton, "LEFT")
else
nameColumnButton:SetPoint("RIGHT")
end
end
local function UpdateColumns(self)
for k,v in pairs(self.columns) do
if not self.gridView then
for i = 1, self:GetNumVisibleEntries() do
local entry = self.entryButtons[i]
if entry.columnElements[k] then
entry.columnElements[k]:SetShown(self:ColumnEnabled(k))
end
end
end
self.columnHeadings[k]:SetShown(self:ColumnEnabled(k))
end
if self:GetColumnSettings().favoritesOnTop then
self.columnHeadings["FAVORITES"].favoriteIcon:SetTexCoord(0, 0.21875, 0, 0.21875)
else
self.columnHeadings["FAVORITES"].favoriteIcon:SetTexCoord(0, 0.21875, 0.21875 * 2, 0.21875 * 3)
end
self:PositionColumns()
end
local function EnableColumn(self, columnKey)
self:GetColumnSettings().enabledColumns[columnKey] = true
-- Add column to the columnOrder only if it isn't there already
for i, v in ipairs(self:GetColumnOrder()) do
if v == columnKey then
S.Utils.TriggerEvent("MinSizeChanged")
S.Utils.TriggerEvent("ColumnsChanged")
return
end
end
table.insert(self:GetColumnOrder(), columnKey)
S.Utils.TriggerEvent("MinSizeChanged")
S.Utils.TriggerEvent("ColumnsChanged")
end
local function DisableColumn(self, columnKey)
self:GetColumnSettings().enabledColumns[columnKey] = false
S.Utils.TriggerEvent("MinSizeChanged")
S.Utils.TriggerEvent("ColumnsChanged")
end
local function ColumnEnabled(self, columnKey)
return self.columns[columnKey] and self:GetColumnSettings().enabledColumns[columnKey]
end
local function OnDropdownColumnEntryClick(self)
S.Settings.Set("columnSettingsHaveChanged", true)
if self.checked then
EnableColumn(self.data1, self.data2)
else
DisableColumn(self.data1, self.data2)
end
end
local function OnDropdownGroupingEntryClick(self)
S.Settings.Set("columnSettingsHaveChanged", true)
if self.checked then
SelectGrouping(self.data1, self.data2)
else
DeselectGrouping(self.data1, self.data2)
end
end
local function ColumnOnClick(self, button, down)
-- Don't trigger a click if the column has been dragged around
if not self.list.movedColumn then
if button == "LeftButton" then
ToggleSortMethod(self.list, self.key)
elseif button == "RightButton" then
-- Dropdown menu
if S.Dropdown.IsShown() then
S.Dropdown.Hide()
else
S.Dropdown.Reset()
S.Dropdown.AddEntry(S.Localize("CONFIG_GROUPING"), nil, nil, nil, S.Utils.GetButtonTextColor())
S.Dropdown.SetHeading()
local dropdownEntries = {}
for k,v in pairs(self.list.groups) do
table.insert(dropdownEntries, {
["key"] = k,
["name"] = v.name
})
end
table.sort(dropdownEntries, function(a,b) return a.name < b.name end)
for i,v in ipairs(dropdownEntries) do
S.Dropdown.AddEntry(v.name, OnDropdownGroupingEntryClick, self.list, v.key)
S.Dropdown.AddRadioButton(self.list:GetGrouping() == v.key)
end
S.Dropdown.AddEntry(S.Localize("CONFIG_COLUMNS"), nil, nil, nil, S.Utils.GetButtonTextColor())
S.Dropdown.SetHeading()
dropdownEntries = {}
for k,v in pairs(self.list.columns) do
if k ~= "NAME" then -- Don't allow the name column to be disabled
local name = v.name
if v.sortMethods then
if v.sortMethods[1].title ~= name and #v.sortMethods[1].title > 0 then
name = name.." ("..v.sortMethods[1].title..")"
end
end
table.insert(dropdownEntries, {
["key"] = k,
["name"] = name
})
end
end
table.sort(dropdownEntries, function(a,b) return a.name < b.name end)
for i,v in ipairs(dropdownEntries) do
S.Dropdown.AddEntry(v.name, OnDropdownColumnEntryClick, self.list, v.key)
S.Dropdown.AddCheckbox(self.list:ColumnEnabled(v.key))
end
local x,y = GetCursorPosition()
x,y = x / S.dropdownMenu:GetEffectiveScale(), y / S.dropdownMenu:GetEffectiveScale()
S.Dropdown.Show(UIParent, "TOPLEFT", "BOTTOMLEFT", x, y)
end
end
end
end
local function GetColumnSettings(self)
return S.Settings.Get(self.columnSettingsKey)
end
local function GetColumnOrder(self)
return GetColumnSettings(self).order
end
local function GetEntryFavorited(self, entryData)
return false
end
local function GetEntryNew(self, entryData)
return false
end
local function GetMinWidth(self)
local width = 100 -- Effectively the minimum width of the name column
for key, col in pairs(self.columns) do
if self:ColumnEnabled(key) then
local colWidth = self:GetColumnWidth(key)
if colWidth then
width = width + colWidth
end
end
end
if width > self.minWidth then
return width
else
return self.minWidth
end
end
local function SetMinimised(self, minimised)
end
local function IsAvailable(self)
return S.IsPlayingCharacterSelected()
end
local function ToggleGridView(self)
self.gridView = not self.gridView
if self.gridView then
self.OnResize = OnResizeGrid
self:OnResize()
self.PositionEntryButtons = PositionEntryButtonsGrid
self.PositionEntryButtonsFast = PositionEntryButtonsGrid
for k,v in pairs(self.entryButtons) do
v:DetachFramesFromColumns()
v.columnElements["ICON"]:SetAllPoints()
v.columnElements["ICON"]:Show()
v.columnElements["QUANTITY"]:SetParent(v.columnElements["ICON"])
v.columnElements["QUANTITY"]:SetPoint("BOTTOMRIGHT")
v.columnElements["QUANTITY"]:SetPoint("TOPLEFT", v, "RIGHT")
v.columnElements["QUANTITY"]:Show()
v.button:SetPushedTexture("")
end
else
self.OnResize = OnResize
self:OnResize()
self.PositionEntryButtons = PositionEntryButtons
self.PositionEntryButtonsFast = PositionEntryButtonsFast
for k,v in pairs(self.entryButtons) do
v:ClearAllPoints()
v:SetPoint("LEFT")
v:SetPoint("RIGHT")
v.columnElements["QUANTITY"]:SetParent(v)
v.columnElements["ICON"]:ClearAllPoints()
v.columnElements["QUANTITY"]:ClearAllPoints()
v:AttachFramesToColumns()
v.button:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\UI-Highlight")
if k > self:GetNumVisibleEntries() then
v:Hide()
end
end
end
self:PositionEntryButtons()
self:UpdateEntryButtonIcons()
self:ScheduleUpdate(true, true)
S.Utils.TriggerEvent("LayoutChanged")
end
local function CreateColumnButton(self, key)
local b = CreateFrame("BUTTON", "", self.head)
b.normalTex = b:CreateTexture(nil, "BACKGROUND")
b.normalTex:SetTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading")
b.normalTex:SetTexCoord(0.2, 0.8, 0, 1)
b.normalTex:SetPoint("TOPLEFT", 4, 0)
b.normalTex:SetPoint("BOTTOMRIGHT", -4, 0)
b.normalTexL = b:CreateTexture(nil, "BACKGROUND")
b.normalTexL:SetTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading")
b.normalTexL:SetTexCoord(0, 0.2, 0, 1)
b.normalTexL:SetPoint("TOPLEFT")
b.normalTexL:SetPoint("BOTTOMRIGHT", b, "BOTTOMLEFT", 4, 0)
b.normalTexR = b:CreateTexture(nil, "BACKGROUND")
b.normalTexR:SetTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading")
b.normalTexR:SetTexCoord(0.8, 1, 0, 1)
b.normalTexR:SetPoint("TOPLEFT", b, "TOPRIGHT", -4, 0)
b.normalTexR:SetPoint("BOTTOMRIGHT")
b:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading-Highlight")
b:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading-Highlight")
b.nameString = b:CreateFontString(nil, "OVERLAY", "SortedFont")
b.nameString:SetPoint("CENTER")
b.nameString:SetHeight(1)
if self.columns[key].width then
b.resizeButton = CreateFrame("BUTTON", "", b)
b.resizeButton:SetPoint("TOP")
b.resizeButton:SetPoint("BOTTOM")
b.resizeButton:SetWidth(6)
b.resizeButton:SetFrameLevel(b:GetFrameLevel() + 10)
b.resizeButton:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Close-Button-Highlight")
b.resizeButton:GetHighlightTexture():SetTexCoord(0, 1, 0.3, 0.7)
b.resizeButton:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Close-Button-Highlight")
b.resizeButton:GetPushedTexture():SetBlendMode("ADD")
b.resizeButton:GetPushedTexture():SetTexCoord(0, 1, 0.3, 0.7)
b.resizeButton.key = key
b.resizeButton.list = self
b.resizeButton:SetScript("OnMouseDown", function(self)
local settings = self.list:GetColumnSettings()
if not settings.widths then
settings.widths = {}
end
if not settings.widths[self.key] then
settings.widths[self.key] = self.list.columns[self.key].width
end
self.x, self.y = GetCursorPosition()
self.x = self.x / self:GetEffectiveScale()
self.y = self.y / self:GetEffectiveScale()
self.startWidth = settings.widths[self.key]
self:SetScript("OnUpdate", function(self)
local x, y = GetCursorPosition()
x = x / self:GetEffectiveScale()
y = y / self:GetEffectiveScale()
if self.invert then
settings.widths[self.key] = self.startWidth - (x - self.x)
else
settings.widths[self.key] = self.startWidth + (x - self.x)
end
if settings.widths[self.key] < 16 then
settings.widths[self.key] = 16
end
self.list:PositionColumns()
end)
end)
b.resizeButton:SetScript("OnMouseUp", function(self)
self:SetScript("OnUpdate", nil)
S.primaryFrame:UpdateMinSize()
S.primaryFrame.sideFrame:UpdateMinSize()
S.Utils.TriggerEvent("ColumnResized")
end)
if key == "FAVORITES" then
b.favoriteIcon = b:CreateTexture(nil, "OVERLAY")
b.favoriteIcon:SetTexture("Interface\\Addons\\Sorted\\Textures\\Favorite-Icons")
b.favoriteIcon:SetPoint("CENTER")
b.favoriteIcon:SetSize(20, 20)
end
end
b:RegisterForClicks("LeftButtonUp", "RightButtonUp")
--b:RegisterForDrag("LeftButton")
-- Column dragging
b:SetScript("OnClick", ColumnOnClick)
b:SetScript("OnMouseDown", function(self)
self.list.dragging = self.key
self.list.movedColumn = false
end)
b:SetScript("OnMouseUp", function(self)
self.list.dragging = nil
end)
b:SetScript("OnEnter", function(self)
if self.list.dragging then
self.list.movedColumn = true
for i,v in ipairs(self.list:GetColumnOrder()) do
if v == self.key then
self.list:GetColumnOrder()[i] = self.list.dragging
elseif v == self.list.dragging then
self.list:GetColumnOrder()[i] = self.key
end
end
self.list:UpdateColumns()
else
-- "Pin favorites" tooltip
if self.key == "FAVORITES" then
S.Tooltip.CreateLocalized(self, "ANCHOR_TOPLEFT", "CONFIG_BEHAVIOR_ON_OPEN_PIN_FAVORITES")
else
-- "Sort by" tooltip. Don't show after player knows how to change column settings
if not S.Settings.Get("columnSettingsHaveChanged") then
S.Tooltip.Schedule(function()
GameTooltip:SetOwner(self, "ANCHOR_TOPLEFT")
GameTooltip:ClearLines()
GameTooltip:AddLine(S.Localize("TOOLTIP_SORT_BY", self.list.columns[self.key].name))
if BANK_TAB_TOOLTIP_CLICK_INSTRUCTION then
GameTooltip:AddLine(BANK_TAB_TOOLTIP_CLICK_INSTRUCTION, 0, 1, 0)
else
GameTooltip:AddLine(S.Localize("TOOLTIP_CLICK_INSTRUCTION_SETTINGS"), 0, 1, 0)
end
GameTooltip:Show()
end)
end
end
end
end)
b:SetScript("OnLeave", S.Tooltip.Cancel)
self.columnHeadings[key] = b
b.key = key
b.list = self
return b
end
local lastUpdateTime = 0
local function ScheduleUpdate(self, resize, sort)
if resize then
-- Resize is no longer scheduled with other updates. Instead it's done the same way as Sorted tooltips
-- This is due to ScheduledUpdates only happening when the list is shown.
-- By performing the resize when the list is hidden, it doesn't need to run OnShow, causing noticeable lag when opening bags
--self.resizeScheduled = true
if not self.lastResizeID then
self.lastResizeID = 0
else
self.lastResizeID = self.lastResizeID + 1
end
local thisID = self.lastResizeID
C_Timer.After(0.01, function()
if self.lastResizeID == thisID then
self:OnResize()
self:ScheduleUpdate(false, false)
end
end)
end
if sort then
self.sortingScheduled = true
end
self.updateScheduled = true
end
local function OnUpdate(self)
if self.updateScheduled and GetTime() > lastUpdateTime + 0.1 then
--[[if self.resizeScheduled then
self:OnResize()
self:UpdateEntryButtonIcons()
end]]
if self.sortingScheduled then
self:UpdateDisplayedEntryData()
--return
--self:Sort()
end
self:UpdateColumnsSortArrows()
self:UpdateColumns()
self:UpdateEntryButtons()
self:UpdateScrollBarMax()
self.updateScheduled = false
self.resizeScheduled = false
self.sortingScheduled = false
self.dontFilter = false
lastUpdateTime = GetTime()
end
end
local function AddColumn(self, key)
CreateColumnButton(self, key)
self:UpdateColumns()
for _, entryButton in pairs(self.entryButtons) do
entryButton:AddColumn(key)
end
end
local smoothingAmount
local function SmoothScrollFrame_OnUpdate(self)
local f = self:GetParent()
local smoothingAmount = smoothingAmount
if self.smoothOppositeCurve then
smoothingAmount = smoothingAmount + 0.1
end
if self.doSmoothing then
local progress = (GetTime() - GetTickTime() - self.smoothTime) / smoothingAmount
if progress > 1 then
progress = 1
else
-- Curve it
if not self.smoothOppositeCurve then
progress = 1 - progress
progress = progress * progress
progress = 1 - progress
else
progress = (math.cos((1 - progress) * math.pi) + 1) / 2
end
end
local smoothTarget = self.smoothTarget
local _, max = self:GetMinMaxValues()
-- Allow scrolling a bit past the end of the list
if smoothTarget > max then
smoothTarget = max + math.pow((smoothTarget - max) * smoothingAmount * 0.4, 0.3)
elseif smoothTarget < 0 then
smoothTarget = -math.pow(math.abs(smoothTarget) * smoothingAmount * 0.4, 0.3)
end
local distance = smoothTarget - self.smoothStart
f.scrollPos = self.smoothStart + distance * progress
self:SetValue(f.scrollPos)
if f.lastScrollPosInteger ~= floor(f.scrollPos) then
f.lastScrollPosInteger = floor(f.scrollPos)
f:UpdateEntryButtons()
end
f:PositionEntryButtonsFast()
-- At end of animation, stop or go back to min/max value
if progress == 1 then
if self.smoothTarget > max then
self.smoothTime = GetTime() - GetTickTime()
self.smoothStart = smoothTarget
self.smoothTarget = max
self.smoothOppositeCurve = true
elseif self.smoothTarget < 0 then
self.smoothTime = GetTime() - GetTickTime()
self.smoothStart = smoothTarget
self.smoothTarget = 0
self.smoothOppositeCurve = true
else
self.doSmoothing = false
end
end
end
end
function S.CreateList(parent, entryConstructor, minWidth, tColumns, sColumnSettings, bColumnHeadings, tGroups, groupingSettings)
local self = CreateFrame("SCROLLFRAME", "", parent)
self.gridView = false
self.ToggleGridView = ToggleGridView
self.entryButtons = {}
self.entryData = {}
self.EntryExists = EntryExists
self.EntryHasData = EntryHasData
self.GetDataForEntry = GetDataForEntry
self.UpdateEntryData = UpdateEntryData
self.UpdateDisplayedEntryData = UpdateDisplayedEntryData
self.scrollPos = 0
self.lastScrollPosInteger = 0
self.hasColumnHeadings = bColumnHeadings
self.columns = tColumns
self.AddColumn = AddColumn
self.columnSettingsKey = sColumnSettings
self.GetColumnWidth = GetColumnWidth
self.ColumnEnabled = ColumnEnabled
self.GetColumnSettings = GetColumnSettings
self.GetColumnOrder = GetColumnOrder
self.UpdateColumns = UpdateColumns
self.PositionColumns = PositionColumns
self.AddEntry = AddEntry
self.ScheduleUpdate = ScheduleUpdate
self:SetScript("OnUpdate", OnUpdate)
self.UpdateEntryButtons = UpdateEntryButtons
self.UpdateEntryButtonIcons = UpdateEntryButtonIcons
self.PositionEntryButtons = PositionEntryButtons
self.PositionEntryButtonsFast = PositionEntryButtonsFast
self.CreateEntry = entryConstructor
self.OnResize = OnResize
self.SortTable = SortTable
self.FilterEntries = FilterEntries
self.GetSortMethod = GetSortMethod
self.GetSortMethodHistory = GetSortMethodHistory
self.UpdateColumnsSortArrows = UpdateColumnsSortArrows
self.UpdateScrollBarMax = UpdateScrollBarMax
self.ScrollToTop = ScrollToTop
self.minWidth = minWidth
self.GetMinWidth = GetMinWidth
self.groups = tGroups
self.groupHeadings = {}
self.collapsedGroups = {}
self.headingButtons = {}
self.groupingSettings = groupingSettings
self.SelectGrouping = SelectGrouping
self.GetGrouping = GetGrouping
self.GetEntryGroup = GetEntryGroup
self.ToggleGroupHeading = ToggleGroupHeading
self.GetGroupHeadingCollapsed = GetGroupHeadingCollapsed
self.GetEntryFavorited = GetEntryFavorited
self.GetEntryNew = GetEntryNew
self.SetMinimised = SetMinimised
self.IsAvailable = IsAvailable
self.GetNumVisibleEntries = GetNumVisibleEntries
self.numVisibleEntries = 0
local f = self
f:SetAllPoints()
f.scrollBar = CreateFrame("SLIDER", "", f, "MinimalScrollBarTemplate")
f.scrollBar.trackBG:Hide()
f.scrollBar:SetPoint("BOTTOM", 0, 16)
f.scrollBar:SetMinMaxValues(0, 200)
f.scrollBar:SetValue(0)
f.scrollPos = 0
f.scrollBar.Update = function(self)
f.scrollPos = self:GetValue()
if f.lastScrollPosInteger ~= floor(f.scrollPos) then
f.lastScrollPosInteger = floor(f.scrollPos)
f:UpdateEntryButtons()
end
f:PositionEntryButtonsFast()
end
f.scrollBar:SetScript("OnMouseDown", function(self)
self.doSmoothing = false
self:SetScript("OnUpdate", f.scrollBar.Update)
end)
f.scrollBar:SetScript("OnMouseUp", function(self) self:SetScript("OnUpdate", SmoothScrollFrame_OnUpdate) end)
f.scrollBar.ScrollUpButton:SetScript("OnClick", function(self)
self:GetParent().doSmoothing = false
f.scrollBar:SetValue(f.scrollBar:GetValue() - S.Settings.Get("scrollSpeed"))
f.scrollBar:Update()
end)
f.scrollBar.ScrollDownButton:SetScript("OnClick", function(self)
self:GetParent().doSmoothing = false
f.scrollBar:SetValue(f.scrollBar:GetValue() + S.Settings.Get("scrollSpeed"))
f.scrollBar:Update()
end)
f:SetScript("OnMouseWheel", function(self, delta)
local _, max = f.scrollBar:GetMinMaxValues()
if max == 0 then return end
local scrollSpeed = S.Settings.Get("scrollSpeed")
if IsAltKeyDown() then
scrollSpeed = scrollSpeed * 3
end
if S.Settings.Get("smoothingAmount") == 0 then
f.scrollBar:SetValue(f.scrollBar:GetValue() - delta * scrollSpeed)
f.scrollBar:Update()
else
f.scrollBar.doSmoothing = true
f.scrollBar.smoothOppositeCurve = false
f.scrollBar.smoothStart = f.scrollPos
f.scrollBar.smoothTime = GetTime() - GetTickTime()
if f.scrollBar.smoothTarget and ((f.scrollBar.smoothDirection and delta > 0) or (not f.scrollBar.smoothDirection and delta < 0)) then
local prevSmoothTarget = f.scrollBar.smoothTarget
f.scrollBar.smoothTarget = f.scrollBar.smoothTarget - delta * scrollSpeed
else
f.scrollBar.smoothTarget = f.scrollPos - delta * scrollSpeed
end
if delta > 0 then
f.scrollBar.smoothDirection = true
else
f.scrollBar.smoothDirection = false
end
end
end)
local function OnSmoothAmountChanged(self)
smoothingAmount = S.Settings.Get("smoothingAmount")
if smoothingAmount and smoothingAmount > 0 then
if not self.doOnUpdate then
self.doOnUpdate = true
-- Perform smooth scrolling
self:SetScript("OnUpdate", SmoothScrollFrame_OnUpdate)
end
else
if self.doOnUpdate then
self.doOnUpdate = false
self:SetScript("OnUpdate", nil)
end
end
end
S.Utils.RunOnEvent(f.scrollBar, "SettingChanged-smoothingAmount", OnSmoothAmountChanged)
function f.scrollBar.UpdateScrollStep(self)
self.scrollStep = S.Settings.Get("scrollSpeed")
end
S.Utils.RunOnEvent(f.scrollBar, "SettingChanged-scrollSpeed", f.scrollBar.UpdateScrollStep)
f.listFrame = CreateFrame("FRAME", "", self)
f.listFrame:SetClipsChildren(true)
f.listFrame:SetPoint("BOTTOM")
f.listFrame:SetPoint("RIGHT", f.scrollBar, "LEFT", 0, 0)
if not bColumnHeadings then
f.listFrame:SetPoint("TOPLEFT", 0, 0)
f.scrollBar:SetPoint("TOPRIGHT", 0, -18)
else
f.listFrame:SetPoint("TOPLEFT", 0, -COLUMN_HEADING_HEIGHT)
f.scrollBar:SetPoint("TOPRIGHT", 0, -18 - COLUMN_HEADING_HEIGHT)
f.head = CreateFrame("FRAME", "", f)
f.head:SetPoint("TOPLEFT")
f.head:SetPoint("BOTTOM", f, "TOP", 0, -COLUMN_HEADING_HEIGHT)
f.head:SetPoint("RIGHT", f.scrollBar, "LEFT", 0, 0)
f.head.combinedStackSpacer = f.head:CreateTexture("")
f.head.combinedStackSpacer:SetTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading-Gap")
f.head.combinedStackSpacer:SetPoint("TOPLEFT")
f.head.combinedStackSpacer:SetSize(20, 24)
f.head.toggleGridButton = CreateFrame("BUTTON", nil, f.head)
f.head.toggleGridButton.bg = f.head.toggleGridButton:CreateTexture(nil, "BACKGROUND")
f.head.toggleGridButton.bg:SetTexture("Interface\\Addons\\Sorted\\Textures\\Column-Heading-Gap")
f.head.toggleGridButton.bg:SetAllPoints()
f.head.toggleGridButton:SetPoint("TOPLEFT", f.head, "TOPRIGHT")
f.head.toggleGridButton:SetSize(f.scrollBar:GetWidth(), COLUMN_HEADING_HEIGHT)
f.head.toggleGridButton.list = self
function f.head.toggleGridButton:Update()
if self.list.gridView then
self.tooltipText = "TOOLTIP_LAYOUT_LIST"
self:SetNormalTexture("Interface\\Addons\\Sorted\\Textures\\Layout-List-Up")
self:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Layout-List-Up")
self:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Layout-List-Down")
else
self.tooltipText = "TOOLTIP_LAYOUT_GRID"
self:SetNormalTexture("Interface\\Addons\\Sorted\\Textures\\Layout-Grid-Up")
self:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Layout-Grid-Up")
self:SetPushedTexture("Interface\\Addons\\Sorted\\Textures\\Layout-Grid-Down")
end
end
function f.head.toggleGridButton:UpdatePushed()
if self.list.gridView then
self:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Layout-List-Down")
else
self:SetHighlightTexture("Interface\\Addons\\Sorted\\Textures\\Layout-Grid-Down")
end
end
f.head.toggleGridButton:Update()
f.head.toggleGridButton:SetScript("OnMouseDown", f.head.toggleGridButton.UpdatePushed)
f.head.toggleGridButton:SetScript("OnMouseUp", f.head.toggleGridButton.Update)
f.head.toggleGridButton:SetScript("OnClick", function(self)
self.list:ToggleGridView()
self:Update()
S.Tooltip.CreateLocalized(self, "ANCHOR_LEFT", self.tooltipText)
end)
f.head.toggleGridButton:SetScript("OnEnter", function(self)
S.Tooltip.CreateLocalized(self, "ANCHOR_LEFT", self.tooltipText)
end)
f.head.toggleGridButton:SetScript("OnLeave", S.Tooltip.Cancel)
f.columnHeadings = {}
for k, v in pairs(tColumns) do
local b = CreateColumnButton(self, k)
end
end
S.Utils.RunOnEvent(self, "SettingChanged-padding", function(self)
self:ScheduleUpdate(true, false)
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconSize", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconShape", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconBorderThickness", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-paddingGrid", function(self)
self:ScheduleUpdate(true, false)
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconSizeGrid", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconShapeGrid", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconBorderThicknessGrid", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconBorders", function(self)
self:ScheduleUpdate(true, false)
self:UpdateColumns()
end)
S.Utils.RunOnEvent(self, "SettingChanged-iconBordersGrid", function(self)
self:ScheduleUpdate(false, false)
end)
S.Utils.RunOnEvent(self, "SettingChanged-combineStacks", function(self)
self:ScheduleUpdate(true, true)
end)
S.Utils.RunOnEvent(self, "SettingChanged-newOnTop", function(self)
self:ScheduleUpdate(false, true)
end)
S.Utils.RunOnEvent(self, "SettingChanged-pinRecentlyUnequippedItems", function(self)
self:ScheduleUpdate(false, true)
end)
S.Utils.RunOnEvent(self, "ProfileChanged", function(self)
self:ScheduleUpdate(false, true)
end)
S.Utils.RunOnEvent(self, "ColumnsChanged", function(self)
self:UpdateColumns()
self:ScheduleUpdate(true, false)
end)
S.Utils.RunOnEvent(self, "GroupingChanged", function(self)
self:ScheduleUpdate(false, true)
end)
S.Utils.RunOnEvent(self, "FavoriteChanged", function(self)
self:ScheduleUpdate(false, true)
end)
S.Utils.RunOnEvent(self, "SortingChanged", function(self)
self:ScheduleUpdate(false, true)
--self:ScrollToTop()
end)
S.Utils.RunOnEvent(self, "Resized", function(self)
self:ScheduleUpdate(true, false)
end)
S.Utils.RunOnEvent(self, "ColumnResized", function(self)
self:ScheduleUpdate(false, false)
end)
S.Utils.RunOnEvent(self, "EnteredWorld", function(self)
self:UpdateDisplayedEntryData()
self:UpdateColumnsSortArrows()
self:UpdateColumns()
self:UpdateEntryButtons()
self:UpdateScrollBarMax()
end)
-- This is now handled by OnUpdate, seeing as OnUpdate only runs when its shown anyway
-- Update everything on show, since updates are disabled when the frame is hidden for performance
--[[self.OnShow = function(self)
self:ScheduleUpdate(false, true)
--self:ScrollToTop()
end
self:SetScript("OnShow", self.OnShow)]]
return self
end