884 lines
29 KiB
Lua
884 lines
29 KiB
Lua
--[[
|
|
ScrollSheet
|
|
Version: 5.9.4961 (WhackyWallaby)
|
|
Revision: $Id: ScrollSheet.lua 271 2010-09-04 15:49:38Z kandoko $
|
|
URL: http://auctioneeraddon.com/dl/
|
|
|
|
License:
|
|
This library is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU Lesser General Public
|
|
License as published by the Free Software Foundation; either
|
|
version 2.1 of the License, or (at your option) any later version.
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301 USA
|
|
|
|
Additional:
|
|
Regardless of any other conditions, you may freely use this code
|
|
within the World of Warcraft game client.
|
|
--]]
|
|
|
|
local LIBRARY_VERSION_MAJOR = "ScrollSheet"
|
|
local LIBRARY_VERSION_MINOR = 18
|
|
|
|
--[[-----------------------------------------------------------------
|
|
|
|
LibStub is a simple versioning stub meant for use in Libraries.
|
|
See <http://www.wowwiki.com/LibStub> for more info.
|
|
LibStub is hereby placed in the Public Domain.
|
|
Credits:
|
|
Kaelten, Cladhaire, ckknight, Mikk, Ammo, Nevcairiel, joshborke
|
|
|
|
--]]-----------------------------------------------------------------
|
|
do
|
|
local LIBSTUB_MAJOR, LIBSTUB_MINOR = "LibStub", 2
|
|
local LibStub = _G[LIBSTUB_MAJOR]
|
|
|
|
if not LibStub or LibStub.minor < LIBSTUB_MINOR then
|
|
LibStub = LibStub or {libs = {}, minors = {} }
|
|
_G[LIBSTUB_MAJOR] = LibStub
|
|
LibStub.minor = LIBSTUB_MINOR
|
|
|
|
function LibStub:NewLibrary(major, minor)
|
|
assert(type(major) == "string", "Bad argument #2 to `NewLibrary' (string expected)")
|
|
minor = assert(tonumber(strmatch(minor, "%d+")), "Minor version must either be a number or contain a number.")
|
|
|
|
local oldminor = self.minors[major]
|
|
if oldminor and oldminor >= minor then return nil end
|
|
self.minors[major], self.libs[major] = minor, self.libs[major] or {}
|
|
return self.libs[major], oldminor
|
|
end
|
|
|
|
function LibStub:GetLibrary(major, silent)
|
|
if not self.libs[major] and not silent then
|
|
error(("Cannot find a library instance of %q."):format(tostring(major)), 2)
|
|
end
|
|
return self.libs[major], self.minors[major]
|
|
end
|
|
|
|
function LibStub:IterateLibraries() return pairs(self.libs) end
|
|
setmetatable(LibStub, { __call = LibStub.GetLibrary })
|
|
end
|
|
end
|
|
--[End of LibStub]---------------------------------------------------
|
|
|
|
local lib = LibStub:NewLibrary(LIBRARY_VERSION_MAJOR, LIBRARY_VERSION_MINOR)
|
|
if not lib then return end
|
|
|
|
LibStub("LibRevision"):Set("$URL: http://svn.norganna.org/libs/trunk/Configator/ScrollSheet.lua $","$Rev: 271 $","5.1.DEV.", 'auctioneer', 'libs')
|
|
|
|
local GSC_GOLD="ffd100"
|
|
local GSC_SILVER="e6e6e6"
|
|
local GSC_COPPER="c8602c"
|
|
|
|
local GSC_3 = "|cff%s%d|cff000000.|cff%s%02d|cff000000.|cff%s%02d|r"
|
|
local GSC_2 = "|cff%s%d|cff000000.|cff%s%02d|r"
|
|
local GSC_1 = "|cff%s%d|r"
|
|
|
|
local iconpath = "Interface\\MoneyFrame\\UI-"
|
|
local goldicon = "%d|T"..iconpath.."GoldIcon:0|t"
|
|
local silvericon = "%s|T"..iconpath.."SilverIcon:0|t"
|
|
local coppericon = "%s|T"..iconpath.."CopperIcon:0|t"
|
|
|
|
-- Table management functions:
|
|
local function replicate(source, depth, history)
|
|
if type(source) ~= "table" then return source end
|
|
assert(depth==nil or tonumber(depth), "Unknown depth: " .. tostring(depth))
|
|
if not depth then depth = 0 history = {} end
|
|
assert(history, "Have depth but without history")
|
|
assert(depth < 100, "Structure is too deep")
|
|
local dest = {} history[source] = dest
|
|
for k, v in pairs(source) do
|
|
if type(v) == "table" then
|
|
if history[v] then dest[k] = history[v]
|
|
else dest[k] = replicate(v, depth+1, history) end
|
|
else dest[k] = v end
|
|
end
|
|
return dest
|
|
end
|
|
local function empty(item)
|
|
if type(item) ~= 'table' then return end
|
|
for k,v in pairs(item) do item[k] = nil end
|
|
end
|
|
local function fill(item, ...)
|
|
if type(item) ~= 'table' then return end
|
|
if (#item > 0) then empty(item) end
|
|
local n = select('#', ...)
|
|
for i = 1,n do item[i] = select(i, ...) end
|
|
end
|
|
-- End table management functions
|
|
|
|
local function coins(money, graphic)
|
|
money = math.floor(tonumber(money) or 0)
|
|
local g = math.floor(money / 10000)
|
|
local s = math.floor(money % 10000 / 100)
|
|
local c = money % 100
|
|
|
|
if not graphic then
|
|
if (g>0) then
|
|
return (GSC_3):format(GSC_GOLD, g, GSC_SILVER, s, GSC_COPPER, c)
|
|
elseif (s>0) then
|
|
return (GSC_2):format(GSC_SILVER, s, GSC_COPPER, c)
|
|
end
|
|
return (GSC_1):format(GSC_COPPER, c)
|
|
else
|
|
if g > 0 then
|
|
return goldicon:format(g)..silvericon:format("%02d"):format(s)..coppericon:format("%02d"):format(c)
|
|
elseif s > 0 then
|
|
return silvericon:format("%d"):format(s)..coppericon:format("%02d"):format(c)
|
|
else
|
|
return coppericon:format("%d"):format(c)
|
|
end
|
|
end
|
|
end
|
|
|
|
local kit = {}
|
|
|
|
|
|
--[[
|
|
Format: SetData(input, [inputStyle])
|
|
Where:
|
|
input = {
|
|
{ cellValue, cellValue, ..., styleKey=styleData, ... },
|
|
{ cellValue, cellValue, ..., styleKey=styleData, ... },
|
|
...
|
|
}
|
|
inputStyle = {
|
|
{ { styleKey=styleData, ... }, { styleKey=styleData, ... }, ... },
|
|
{ { styleKey=styleData, ... }, { styleKey=styleData, ... }, ... },
|
|
...
|
|
}
|
|
cellValue = value or { value, styleKey=styleData, ... }
|
|
styleKey = (string) The style type that affects the cell in question.
|
|
styleData = (any type) The data that is to be used by the renderer for this cell.
|
|
|
|
Note:
|
|
There are many ways to represent the style for a given cell.
|
|
]]
|
|
|
|
function kit:SetData(input, instyle)
|
|
local sort = self.sort
|
|
local n = #sort
|
|
for i=n, 1, -1 do
|
|
sort[i] = nil
|
|
end
|
|
|
|
local nRows = #input
|
|
local nCols = self.hSize
|
|
|
|
local data = self.data
|
|
local style = self.style
|
|
local n = #data
|
|
|
|
-- Clean up existing data cells
|
|
for i = n, 1, -1 do
|
|
data[i] = nil
|
|
style[i] = nil
|
|
end
|
|
|
|
-- Copy the data portion of the input table into the data table,
|
|
-- and the style portion into the style table.
|
|
local pos, content
|
|
for i = 1, nRows do
|
|
sort[i] = i -- Initialize sort table to natural order
|
|
|
|
if input[i] then
|
|
for k,v in pairs(input[i]) do
|
|
if type(k) == "string" and type(v) == "table" and #v > 0 then
|
|
style[pos][k] = replicate(v)
|
|
end
|
|
end
|
|
end
|
|
for j = 1, nCols do
|
|
pos = (i-1)*nCols+j
|
|
|
|
if input[i] and input[i][j] then
|
|
content = input[i][j] -- temporary, no need to replicate here
|
|
else
|
|
content = nil
|
|
end
|
|
if type(content) == "table" then
|
|
data[pos] = replicate(content[1]) -- just in case, replicate it
|
|
for k,v in pairs(content) do
|
|
if type(k) == "string" then
|
|
if not style[pos] then style[pos] = {} end
|
|
style[pos][k] = replicate(v)
|
|
end
|
|
end
|
|
else
|
|
data[pos] = content or "NIL" -- non-table, no need to replicate
|
|
end
|
|
|
|
if instyle and instyle[i] and instyle[i][j] and type(instyle[i][j]) == "table" then
|
|
for k,v in pairs(instyle[i][j]) do
|
|
if not style[pos] then style[pos] = {} end
|
|
style[pos][k] = replicate(v)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
--flag for column rearrangement code to know when we have a fresh data table. Needs to be before self:PerformSort() or the flag is set to late
|
|
self.newdata = true
|
|
--reset to top
|
|
if self.vScrollReset then
|
|
self.panel.vScroll:SetValue(0)--always reset scroll to vertical home position when new data is set.
|
|
end
|
|
|
|
self.panel.vSize = nRows
|
|
self:PerformSort()
|
|
end
|
|
|
|
--This function only enables the display of the selected row. The row still gets selected, and kit:GetSelection() will still work
|
|
function kit:EnableSelect(enable)
|
|
if enable then
|
|
self.enableselect = true
|
|
else
|
|
self.enableselect = false
|
|
end
|
|
end
|
|
|
|
function kit:GetSelection()
|
|
local selection = {}
|
|
if self.selected then
|
|
if not self.order then
|
|
for i = 1, self.hSize do
|
|
local pos = i + ((self.selected-1)*self.hSize)
|
|
selection[i] = self.data[pos]
|
|
end
|
|
else--reorginize data so the calling module gets them back in expected order
|
|
for i = 1, self.hSize do
|
|
local pos = i + ((self.selected-1)*self.hSize)
|
|
local name = self.order[i]
|
|
local index = self.order[name][3]
|
|
selection[index] = self.data[pos]
|
|
end
|
|
end
|
|
end
|
|
return selection
|
|
end
|
|
|
|
function kit:RowSelect(row, mouseButton)
|
|
if mouseButton == "RightButton" then
|
|
return
|
|
end
|
|
local selected
|
|
if row then
|
|
selected = row + math.floor(self.panel.vPos)
|
|
if self.selected ~= self.sort[selected] then
|
|
self.selected = self.sort[selected]
|
|
else
|
|
self.selected = nil
|
|
end
|
|
end
|
|
|
|
for i = 1, #self.rows do
|
|
self.rows[i]["highlight"]:SetAlpha(0)
|
|
end
|
|
if self.enableselect and self.selected then
|
|
if not row then
|
|
for i = 1, #self.sort do
|
|
if self.sort[i] == self.selected then
|
|
selected = i
|
|
end
|
|
end
|
|
if selected then
|
|
row = selected - math.floor(self.panel.vPos)
|
|
end
|
|
end
|
|
if row and (row > 0) and (row <= #self.rows) then
|
|
self.rows[row]["highlight"]:SetAlpha(.5)
|
|
end
|
|
end
|
|
end
|
|
|
|
function kit:ButtonClick(column, mouseButton)
|
|
if mouseButton == "RightButton" then lib.moveColumn(self, column) return end
|
|
|
|
if (self.curSort == column) then
|
|
self.curDir = self.curDir * -1
|
|
else
|
|
self.curSort = column
|
|
self.curDir = 1
|
|
if self.labels[column]
|
|
and self.labels[column].sort
|
|
and self.labels[column].sort.DESCENDING
|
|
then
|
|
self.curDir = -1
|
|
end
|
|
end
|
|
self:PerformSort()
|
|
end
|
|
|
|
local function sortDataSet(data, sort, width, column, dir)
|
|
assert(column <= width)
|
|
assert(dir == -1 or dir == 1)
|
|
table.sort(sort, function(a,b)
|
|
local aPos = (a-1)*width+column
|
|
local bPos = (b-1)*width+column
|
|
if dir < 0 then
|
|
return (data[aPos] > data[bPos])
|
|
end
|
|
local dataA, dataB = data[aPos], data[bPos]
|
|
return (dataA < dataB) or (dataA == dataB and a < b)
|
|
end)
|
|
end
|
|
|
|
function kit:PerformSort()
|
|
if not self.curSort then
|
|
for i=1, #self.labels do
|
|
if self.labels[i].sort and self.labels[i].sort.DEFAULT then
|
|
self.curSort = i
|
|
if self.labels[i].sort.DESCENDING then
|
|
self.curDir = -1
|
|
else
|
|
self.curDir = 1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if not self.curSort then
|
|
self.curSort = 1
|
|
self.curDir = 1
|
|
end
|
|
for i=1, #self.labels do -- Removes the previous Columns arrows before we create the new arrows
|
|
self.labels[i].texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
self.labels[i].sortTexture:Hide()
|
|
end
|
|
|
|
if self.curDir == 1 then
|
|
self.labels[self.curSort].sortTexture:SetTexCoord(0,0.55,0.9,0.2)
|
|
self.labels[self.curSort].sortTexture:SetVertexColor(1,0.2,0)
|
|
self.labels[self.curSort].sortTexture:Show()
|
|
elseif self.curDir == -1 then
|
|
self.labels[self.curSort].sortTexture:SetTexCoord(0,0.55,0.2,0.9)
|
|
self.labels[self.curSort].sortTexture:SetVertexColor(0.2,1,0)
|
|
self.labels[self.curSort].sortTexture:Show()
|
|
end
|
|
|
|
-- Allow modules to use their own custom sorter
|
|
-- The module can create a self.CustomSort() function that will provide any special needs. ie proper itemlink sorting
|
|
if self.CustomSort then
|
|
self.CustomSort(self.data, self.sort, self.hSize, self.curSort, self.curDir)
|
|
else
|
|
sortDataSet(self.data, self.sort, self.hSize, self.curSort, self.curDir)
|
|
end
|
|
lib.Processor("ColumnSort", self, nil, self.curSort, nil, nil, self.curDir )
|
|
|
|
self.panel:Update()
|
|
end
|
|
-- if a scroll frame flags this as false we will not reset scroll position to 0,0 on new data renders
|
|
function kit:EnableVerticalScrollReset(enable)
|
|
if enable then
|
|
self.vScrollReset = true
|
|
else
|
|
self.vScrollReset = false
|
|
end
|
|
end
|
|
--is stored order table valid
|
|
local function checkValidOrder(text, saved)
|
|
for i,v in ipairs(saved) do
|
|
if v == text then
|
|
return true
|
|
end
|
|
end
|
|
return false
|
|
end
|
|
--use stored order table if provided or create new order table
|
|
function kit:SetOrder(saved)
|
|
if saved and type(saved) == "table" then
|
|
local passed = false
|
|
--check if # of entries match, fail immediately if they do not. Otherwise check each value
|
|
if #saved[1] == #self.labels then
|
|
for i,v in pairs(self.labels) do
|
|
local text = v:GetText()
|
|
--if unnamed column, create the null fake name
|
|
if text == nil then text = "null "..v.button:GetID() end
|
|
passed = checkValidOrder(text, saved[1])
|
|
if not passed then
|
|
break
|
|
end
|
|
end
|
|
end
|
|
--check that the stored data is valid for use and no changes to the original scrollsheet has occured due to upgrades
|
|
if passed then
|
|
self.order = saved[1]
|
|
self.lastOrder = saved[2]
|
|
for i, name in ipairs(self.order) do
|
|
self.labels[i]:SetText(name)
|
|
self.labels[i].button:SetWidth(self.order[name][4])
|
|
end
|
|
self:ChangeOrder() --apply saved order changes
|
|
else
|
|
self.order = nil --trash the saved table and start fresh
|
|
end
|
|
end
|
|
if not self.order then
|
|
self.order ={}
|
|
self.lastOrder = {}
|
|
for i,v in ipairs(self.labels) do
|
|
local layout = self.rows[1][i].layout
|
|
local justify = self.rows[1][i]:GetJustifyH()
|
|
local name = v:GetText()
|
|
if not name or name == "" then name = ("null "..i) v:SetText(name) end--Need to create a useful name for unnamed buttons used for "hidden" data
|
|
self.order[name] = {layout, justify, v.button:GetID(), v.button:GetWidth() or 80}
|
|
self.order[i] = name --used as a list of names to allow a column to smoothly be inserted
|
|
self.lastOrder[name] = i --Stores the "current" self.data changes so we know where to remap from after initial changes until a new data table is sent
|
|
end
|
|
end
|
|
end
|
|
--rearrange and set data based on column order
|
|
function kit:ChangeOrder()
|
|
for i, name in ipairs(self.order) do
|
|
self.labels[i]:SetText(name)
|
|
self.labels[i].button:SetWidth(self.order[name][4])
|
|
|
|
for index, cell in pairs(self.rows) do
|
|
cell[i].layout = self.order[name][1]
|
|
cell[i]:SetJustifyH(self.order[name][2])
|
|
if self.order[name][1] == "TOOLTIP" then
|
|
cell[i].button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
elseif cell[i].button:GetHighlightTexture() then --if it had a highlight but is not tooltip type anymore nil it
|
|
cell[i].button:SetHighlightTexture(nil)
|
|
end
|
|
end
|
|
end
|
|
self.rearrange = true
|
|
self:Render()
|
|
end
|
|
|
|
local empty = {}
|
|
function kit:Render()
|
|
local vPos = math.floor(self.panel.vPos)
|
|
local vSize = self.panel.vSize
|
|
local hSize = self.hSize
|
|
|
|
local rows = self.rows
|
|
local data = self.data
|
|
local sort = self.sort
|
|
local style = self.style
|
|
--if user has rearranged the columns we need to change data, style to match. Only done once per "fresh data, replaces internal stored data, style
|
|
if (self.rearrange or self.newdata) and self.order then
|
|
data, style = lib.dataToColumn(self, data, style)
|
|
self.data = data
|
|
self.style = style
|
|
self:PerformSort()--sort our rearranged data
|
|
end
|
|
for i = 1, #rows do
|
|
local rowNum = sort[vPos+i]
|
|
local rowPos = nil
|
|
if rowNum then rowPos = (rowNum-1)*hSize end
|
|
|
|
local cells = rows[i]
|
|
local direction, rowR, rowG, rowB, rowA1, rowA2 = "Horizontal", 1, 1, 1, 0, 0 --row level coloring used for gradiants
|
|
for j = 1, hSize do
|
|
local cell = cells[j]
|
|
if rowPos then
|
|
local pos = rowPos + j
|
|
local text = data[pos] or ""
|
|
local settings = style[pos] or empty
|
|
local red,green,blue = 0.8,0.8,0.8
|
|
|
|
if cell.layout == "COIN" then
|
|
text = coins(data[pos])
|
|
end
|
|
|
|
if settings["textColor"] then
|
|
red, green, blue = unpack(settings['textColor'])
|
|
elseif settings["date"] then
|
|
text = date(settings["date"], text)
|
|
elseif settings["rowColor"] then
|
|
rowR, rowG, rowB, rowA1, rowA2, direction = unpack(settings['rowColor'])
|
|
end
|
|
|
|
cell:SetTextColor(red,green,blue)
|
|
cell:SetText(text)
|
|
cell:Show()
|
|
else
|
|
cell:Hide()
|
|
end
|
|
end
|
|
rows[i].colorTex:SetGradientAlpha(direction, rowR, rowG, rowB, rowA1, rowR, rowG, rowB, rowA2)--row color to apply
|
|
end
|
|
self:RowSelect()
|
|
end
|
|
|
|
local PanelScroller = LibStub:GetLibrary("PanelScroller")
|
|
|
|
function lib:Create(frame, layout, onEnter, onLeave, onClick, onResize, onSelect)
|
|
local sheet
|
|
local name = (frame:GetName() or "").."ScrollSheet"
|
|
|
|
local id = 1
|
|
while (_G[name..id]) do
|
|
id = id + 1
|
|
end
|
|
name = name..id
|
|
|
|
local parentHeight = frame:GetHeight()
|
|
local content = CreateFrame("Frame", name.."Content", frame)
|
|
content:SetHeight(parentHeight - 30)
|
|
|
|
local panel = PanelScroller:Create(name.."ScrollPanel", frame)
|
|
panel:SetPoint("TOPLEFT", frame, "TOPLEFT", 5,-5)
|
|
panel:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -25,25)
|
|
panel:SetScrollChild(name.."Content")
|
|
panel:SetScrollBarVisible("VERTICAL","FAUX")
|
|
panel.vSize = 0
|
|
|
|
local totalWidth = 0;
|
|
|
|
local labels = {}
|
|
for i = 1, #layout do
|
|
local button = CreateFrame("Button", nil, content)
|
|
if i == 1 then
|
|
button:SetPoint("TOPLEFT", content, "TOPLEFT", 5,0)
|
|
totalWidth = totalWidth + 5
|
|
else
|
|
button:SetPoint("TOPLEFT", labels[i-1].button, "TOPRIGHT", 3,0)
|
|
totalWidth = totalWidth + 3
|
|
end
|
|
|
|
local label = content:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
|
|
label:SetText(layout[i][1])
|
|
local colWidth = layout[i][3] or 30 --Never us a nil width, causes issues with overlay highlight
|
|
|
|
totalWidth = totalWidth + colWidth
|
|
button:SetWidth(colWidth)
|
|
button:SetHeight(16)
|
|
button:SetResizable(true)
|
|
button:SetMaxResize(400, 16)
|
|
button:SetMinResize(13, 16) --Makes the nice ... elipsies line up
|
|
|
|
button:SetID(i)
|
|
button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
button:SetScript("OnMouseDown", function(self, ...) sheet:ButtonClick(self:GetID(), ...) end)
|
|
|
|
local texture = content:CreateTexture(nil, "ARTWORK")
|
|
texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
texture:SetTexCoord(0.1, 0.8, 0, 1)
|
|
texture:SetAllPoints(button)
|
|
button.texture = texture
|
|
|
|
local sortTexture = button:CreateTexture(nil, "ARTWORK")
|
|
sortTexture:SetTexture("Interface\\Buttons\\UI-SortArrow")
|
|
sortTexture:SetPoint("TOPRIGHT", button, "TOPRIGHT", 0,0)
|
|
sortTexture:SetPoint("BOTTOM", button, "BOTTOM", 0,0)
|
|
sortTexture:SetWidth(12)
|
|
sortTexture:Hide()
|
|
button.sortTexture = sortTexture
|
|
|
|
local background = content:CreateTexture(nil, "ARTWORK")
|
|
background:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
background:SetTexCoord(0.2, 0.9, 0, 0.9)
|
|
background:SetPoint("TOPLEFT", button, "BOTTOMLEFT", 0,0)
|
|
background:SetPoint("TOPRIGHT", button, "BOTTOMRIGHT", 0,0)
|
|
background:SetPoint("BOTTOM", content, "BOTTOM", 0,0)
|
|
background:SetAlpha(0.2)
|
|
|
|
--small button in the gap between lables allows resizing the button its anchored too
|
|
--we use very small columns to store extra data thats not used in rendering. We dont want the player to be able to resize em
|
|
if colWidth > 1 then
|
|
local nub = CreateFrame("Button", nil, content)
|
|
nub:SetPoint("TOPLEFT", button, "TOPRIGHT", 0,0)
|
|
nub:SetHighlightTexture("Interface\\BUTTONS\\YELLOWORANGE64")
|
|
nub:SetAlpha(0.5)
|
|
nub:SetWidth(3)
|
|
nub:SetHeight(content:GetHeight())
|
|
nub:SetScript("OnEnter", function(self) self:LockHighlight() end)
|
|
nub:SetScript("OnLeave", function(self) self:UnlockHighlight() end)
|
|
--buttons lose the proper anchor when resized, havnt figured out why. So store and then reattach
|
|
nub:SetScript("OnMouseDown", function() nub.point, nub.relativeTo, nub.relativePoint, nub.xOfs, nub.yOfs = button:GetPoint() button:StartSizing() end )
|
|
nub:SetScript("OnMouseUp", function() button:StopMovingOrSizing() button:SetPoint(nub.point, nub.relativeTo, nub.relativePoint, nub.xOfs, nub.yOfs) lib.Processor("ColumnWidthSet", sheet, button, i) end )
|
|
end
|
|
label:SetPoint("TOPLEFT", button, "TOPLEFT", 0,0)
|
|
label:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", 0,0)
|
|
label:SetJustifyH("CENTER")
|
|
label:SetJustifyV("CENTER")
|
|
label:SetTextColor(0.8,0.8,0.8)
|
|
|
|
label.button = button
|
|
label.texture = texture
|
|
label.nub = nub
|
|
label.sortTexture = sortTexture
|
|
label.background = background
|
|
label.sort = layout[i][4]
|
|
labels[i] = label
|
|
end
|
|
totalWidth = totalWidth + 5
|
|
|
|
local rows = {}
|
|
local rowNum = 1
|
|
local maxHeight = content:GetHeight()
|
|
local totalHeight = 16
|
|
while (totalHeight + 14 < maxHeight) do
|
|
local row = {}
|
|
for i = 1, #layout do
|
|
local cell = content:CreateFontString(nil, "OVERLAY", "GameFontHighlightSmall")
|
|
local button = CreateFrame("Button", nil, content)
|
|
if rowNum == 1 then
|
|
cell:SetPoint("TOPLEFT", labels[i], "BOTTOMLEFT", 0,0)
|
|
cell:SetPoint("TOPRIGHT", labels[i], "BOTTOMRIGHT", 0,0)
|
|
|
|
local row, index = rowNum, i
|
|
button:SetHeight(totalHeight)
|
|
button:SetAllPoints(cell)
|
|
button:SetID(rowNum)
|
|
button:SetScript("OnMouseDown", function(self, ...) sheet:RowSelect(self:GetID(), ...) lib.Processor("OnMouseDownCell", sheet, self, index, row, nil, ...) end)
|
|
button:SetScript("OnClick", function(self, ...) lib.Processor("OnClickCell", sheet, self, index, row, nil, ...) end)
|
|
button:SetScript("OnEnter", function(self, ...) lib.Processor("OnEnterCell", sheet, self, index, row, nil, ...) end)
|
|
button:SetScript("OnLeave", function(self, ...) lib.Processor("OnLeaveCell", sheet, self, index, row, nil, ...) end)
|
|
|
|
if (layout[i][2] == "TOOLTIP") then
|
|
button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
end
|
|
cell.button = button --store in cell so we can refrence the button
|
|
else
|
|
cell:SetPoint("TOPLEFT", rows[rowNum-1][i], "BOTTOMLEFT", 0,0)
|
|
cell:SetPoint("TOPRIGHT", rows[rowNum-1][i], "BOTTOMRIGHT", 0,0)
|
|
|
|
local row, index = rowNum, i
|
|
button:SetHeight(totalHeight)
|
|
button:SetAllPoints(cell)
|
|
button:SetID(rowNum)
|
|
button:SetScript("OnMouseDown", function(self, ...) sheet:RowSelect(self:GetID(), ...) lib.Processor("OnMouseDownCell", sheet, self, index, row, nil, ...) end)
|
|
button:SetScript("OnClick", function(self, ...) lib.Processor("OnClickCell", sheet, self, index, row, nil, ...) end)
|
|
button:SetScript("OnEnter", function(self, ...) lib.Processor("OnEnterCell", sheet, self, index, row, nil, ...) end)
|
|
button:SetScript("OnLeave", function(self, ...) lib.Processor("OnLeaveCell", sheet, self, index, row, nil, ...) end)
|
|
|
|
if (layout[i][2] == "TOOLTIP") then
|
|
button:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
end
|
|
cell.button = button
|
|
end
|
|
|
|
cell:SetHeight(14)
|
|
cell:SetJustifyV("TOP")
|
|
if (layout[i][2] == "TEXT") then
|
|
cell:SetJustifyH("LEFT")
|
|
elseif (layout[i][2] == "TOOLTIP") then
|
|
cell:SetJustifyH("LEFT")
|
|
elseif (layout[i][2] == "INT") then
|
|
cell:SetJustifyH("RIGHT")
|
|
elseif (layout[i][2] == "COIN") then
|
|
cell:SetJustifyH("RIGHT")
|
|
end
|
|
cell.layout = layout[i][2]
|
|
cell:SetTextColor(0.9, 0.9, 0.9)
|
|
row[i] = cell
|
|
end
|
|
--create a color texture for row color gradiants
|
|
local colorTex = content:CreateTexture()
|
|
colorTex:SetPoint("TOPLEFT", row[1], "TOPLEFT", 0,0)
|
|
colorTex:SetPoint("BOTTOMRIGHT", row[#layout], "BOTTOMRIGHT", 0, 1)
|
|
colorTex:SetTexture(1, 1, 1)
|
|
row.colorTex = colorTex
|
|
--create a highlight texture for row selection, replaces the per cell highlight system
|
|
local highlight = content:CreateTexture()
|
|
highlight:SetPoint("TOPLEFT", row[1], "TOPLEFT", 0,0)
|
|
highlight:SetPoint("BOTTOMRIGHT", row[#layout], "BOTTOMRIGHT", 0, 1)
|
|
highlight:SetAlpha(0)
|
|
highlight:SetTexture(.8, .6, 0)
|
|
row.highlight = highlight
|
|
|
|
rows[rowNum] = row
|
|
rowNum = rowNum + 1
|
|
totalHeight = totalHeight + 14
|
|
end
|
|
|
|
content:SetWidth(totalWidth)
|
|
panel:UpdateScrollChildRect()
|
|
panel:Update()
|
|
|
|
--Used for compatible with older versions that lacked the General Processor callback
|
|
local compatibility = nil
|
|
if onEnter or onLeave or onClick or onResize or onSelect then
|
|
compatibility = {onEnter, onLeave, onClick, onResize, onSelect}
|
|
end
|
|
sheet = {
|
|
name = name,
|
|
content = content,
|
|
panel = panel,
|
|
labels = labels,
|
|
rows = rows,
|
|
hSize = #labels,
|
|
data = {},
|
|
style = {},
|
|
sort = {},
|
|
vScrollReset = true,
|
|
compatibility = compatibility,
|
|
}
|
|
for k,v in pairs(kit) do
|
|
sheet[k] = v
|
|
end
|
|
panel.callback = function() sheet:Render() end
|
|
|
|
_G[name] = sheet
|
|
|
|
return sheet
|
|
end
|
|
|
|
function lib.Processor(type, self, button, column, row, order, curDir, ...)
|
|
--Use old callbacks for modules not using the general Processor
|
|
if self.compatibility then
|
|
if type == "OnEnterCell" and self.compatibility[1] then
|
|
self.compatibility[1](button, row, column) --onEnter(button, row, index)
|
|
elseif type == "OnLeaveCell" and self.compatibility[2] then
|
|
self.compatibility[2](button, row, column) --onLeave(button, row, index)
|
|
elseif type == "OnClickCell" and self.compatibility[3] then
|
|
self.compatibility[3](button, row, column)--onClick(button, row, index)
|
|
elseif type == "ColumnWidthSet" and self.compatibility[4] then
|
|
self.compatibility[4](self, column, button:GetWidth() ) --onResize(self, column, )
|
|
elseif type == "ColumnWidthReset" and self.compatibility[4] then
|
|
self.compatibility[4](self, column, nil ) --onResize(self, column, )
|
|
elseif type == "OnMouseDownCell" and self.compatibility[5] then
|
|
self.compatibility[5]() --onSelect()
|
|
end
|
|
return
|
|
end
|
|
|
|
if not self.Processor then return end
|
|
self.Processor( type, self, button, column, row, order, curDir, ...)
|
|
end
|
|
|
|
function lib.moveColumn(self, column)
|
|
if self and column then
|
|
if IsControlKeyDown() then --reset column to default
|
|
lib.Processor("ColumnWidthReset", self, self.labels[column].button, column)
|
|
else
|
|
local fakeButton = lib.fakeButton
|
|
local width, height, text = self.labels[column]:GetWidth(), self.labels[column]:GetHeight(), self.labels[column]:GetText()
|
|
fakeButton:SetWidth(width)
|
|
fakeButton:SetHeight(height)
|
|
fakeButton.Text:SetText(text)
|
|
fakeButton:ClearAllPoints()
|
|
fakeButton:SetPoint("BOTTOM", self.labels[column], "TOP", 0,5)
|
|
fakeButton:Show()
|
|
if not self.order then
|
|
self:SetOrder()
|
|
end
|
|
self.moving = {["button"] = self.labels[column].button, ["movingFrom"] = self.labels[column].button:GetID()}
|
|
self.labels[column].button:SetScript("OnUpdate", function() lib.changeColumns(self, column, GetMouseFocus()) end)
|
|
end
|
|
end
|
|
end
|
|
function lib.changeColumns(self, column, button)
|
|
local fakeButton = lib.fakeButton
|
|
--if mouse down we store button we are moving column too
|
|
if IsMouseButtonDown() and self.moving then
|
|
local ID = GetMouseFocus():GetID()
|
|
if ID then
|
|
fakeButton:ClearAllPoints()
|
|
fakeButton:SetPoint("BOTTOM", self.labels[ID], "TOP", 0,5)
|
|
end
|
|
return
|
|
end
|
|
-- setup column switch in here if these are not met then script will end
|
|
if not IsMouseButtonDown() and self.moving and GetMouseFocus():GetID() > 0 then
|
|
local movingTo = GetMouseFocus():GetID()
|
|
local movingFrom = self.moving["movingFrom"]
|
|
|
|
--switch buttons text: used to rearrange the (data, style) tables to new column order
|
|
local movingToText = self.labels[movingTo]:GetText()
|
|
local movingFromText = self.labels[movingFrom]:GetText()
|
|
table.remove(self.order, movingFrom)
|
|
table.insert(self.order, movingTo, movingFromText)
|
|
|
|
--Apply column specific data to rearrangement
|
|
self:ChangeOrder()
|
|
end
|
|
--Only keep the order table if columns are not in default state, otherwise clear
|
|
local default = true
|
|
for i, name in ipairs(self.order) do
|
|
if self.order[name][3] ~= i then
|
|
default = false
|
|
end
|
|
end
|
|
--Inform module of change
|
|
if default then
|
|
lib.Processor("ColumnOrder", self)
|
|
self.order, self.lastOrder = nil, nil
|
|
else
|
|
lib.Processor("ColumnOrder", self, nil, nil, nil, {self.order, self.lastOrder})
|
|
end
|
|
--clear OnUpdate script
|
|
self.moving["button"]:SetScript("OnUpdate", nil)
|
|
self.moving = nil
|
|
fakeButton:Hide()
|
|
end
|
|
--takes the data set sent by addon, rearranges it to match users changed column layout
|
|
function lib.dataToColumn(self, data, style)
|
|
--[[take the self.data table 1, 2, 3 ....10000 serial table and break it into column segments of data. Each column's data is grouped then we simply rearrange column order and reserialize the table
|
|
self.style is stored in a non sync index array where the index == the data index. Merge style into data array for rearrangement
|
|
]]
|
|
local temp = {}
|
|
local step = 1
|
|
for a, b in ipairs(data) do
|
|
if not temp[step] then temp[step] = {} end
|
|
table.insert(temp[step], {b, ["style"] = style[a]})
|
|
step = step + 1
|
|
if step == #self.labels + 1 then
|
|
step = 1
|
|
end
|
|
end
|
|
--if we have a new SetData() call we need to resync the self.lastOrder with self.order since we have self.data in teh starting layout
|
|
if self.newdata then
|
|
for i,v in ipairs(self.order) do
|
|
self.lastOrder[v] = self.order[v][3]
|
|
end
|
|
end
|
|
--rearrange to match current layout
|
|
local newData = {}
|
|
for i,v in ipairs(temp) do
|
|
--need to find what data i should have
|
|
local name = self.order[i]
|
|
--this column is currenty maped to..
|
|
local index = self.lastOrder[name]
|
|
--insert this data into appropriate changed area
|
|
newData[i] = temp[index]
|
|
--store changes to self.data so we know where we maped it to
|
|
self.lastOrder[name] = i
|
|
end
|
|
--Take the now rearranged data and reserialize the self.data and extract self.style to match new index positions
|
|
data, style = {}, {}
|
|
if #newData > 0 then --if no data to render skip it all
|
|
for i = 1, #newData[1] do
|
|
for index = 1, #newData do
|
|
table.insert(data, newData[index][i][1])
|
|
style[#data] = newData[index][i].style
|
|
end
|
|
end
|
|
end
|
|
--after we have changed the internal self.data we will not need to change unless a new :SetData() or we change the column order again
|
|
self.rearrange = false
|
|
self.newdata = nil
|
|
return data, style
|
|
end
|
|
|
|
--this is our fake button for column movements
|
|
local fakeButton = CreateFrame("Button", nil, UIParent)
|
|
fakeButton:SetMovable(true)
|
|
fakeButton:SetHighlightTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
fakeButton:SetFrameStrata("DIALOG")
|
|
fakeButton:Show()
|
|
|
|
local texture = fakeButton:CreateTexture(nil, "ARTWORK")
|
|
texture:SetTexture("Interface\\QuestFrame\\UI-QuestTitleHighlight")
|
|
texture:SetTexCoord(0.1, 0.8, 0, 1)
|
|
texture:SetAllPoints(fakeButton)
|
|
fakeButton.texture = texture
|
|
|
|
fakeButton.Text = fakeButton:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
|
|
fakeButton.Text:SetPoint("BOTTOMRIGHT", fakeButton, "BOTTOMRIGHT", 0,0)
|
|
fakeButton.Text:SetJustifyH("CENTER")
|
|
fakeButton.Text:SetJustifyV("CENTER")
|
|
fakeButton.Text:SetTextColor(0.8,0.8,0.8)
|
|
fakeButton.Text:SetText("")
|
|
|
|
lib.fakeButton = fakeButton
|