Skip to content

Add Search and sort to exporter #1147

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 11 commits into from
Jun 13, 2025
4 changes: 3 additions & 1 deletion src/Classes/ListControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -346,10 +346,12 @@ function ListClass:OnKeyDown(key, doubleClick)
newSelect = index
end
else
local scrollOffsetH = self.controls.scrollBarH.offset
for colIndex, column in ipairs(self.colList) do
local relX = cursorX - (x + 2)
local relY = cursorY - (y + 2)
local mOver = relX >= column._offset and relX <= column._offset + column._width and relY >= 0 and relY <= 18
local adjustedRelX = relX + scrollOffsetH
local mOver = adjustedRelX >= column._offset and adjustedRelX <= column._offset + column._width and relY >= 0 and relY <= 18
if self:GetColumnProperty(column, "sortable") and mOver and self.ReSort then
self:ReSort(colIndex)
end
Expand Down
20 changes: 19 additions & 1 deletion src/Export/Classes/DatListControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,27 @@
-- Dat list control.
--
local DatListClass = newClass("DatListControl", "ListControl", function(self, anchor, rect)
self.ListControl(anchor, rect, 14, "VERTICAL", false, main.datFileList)
self.originalList = main.datFileList
self.searchBuf = ""
self.filteredList = self.originalList
self.ListControl(anchor, rect, 14, "VERTICAL", false, self.filteredList)
end)

function DatListClass:BuildFilteredList()
local search = self.searchBuf:lower()
if search == "" then
self.filteredList = self.originalList
else
self.filteredList = {}
for _, file in ipairs(self.originalList) do
if file.name:lower():find(search, 1, true) then
table.insert(self.filteredList, file)
end
end
end
self.list = self.filteredList
end

function DatListClass:GetRowValue(column, index, datFile)
if column == 1 then
return "^7"..datFile.name
Expand Down
292 changes: 288 additions & 4 deletions src/Export/Classes/RowListControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ local t_insert = table.insert
local RowListClass = newClass("RowListControl", "ListControl", function(self, anchor, rect)
self.ListControl(anchor, rect, 14, "HORIZONTAL", false, { })
self.colLabels = true
self._autoSizeToggleState = {} -- internal toggle memory, not saved to spec
end)

function RowListClass:BuildRows(filter)
Expand Down Expand Up @@ -44,21 +45,27 @@ end

function RowListClass:BuildColumns()
wipeTable(self.colList)
self.colList[1] = { width = 50, label = "#", font = "FIXED" }
self.colList[1] = { width = 50, label = "#", font = "FIXED", sortable = true }
for _, specCol in ipairs(main.curDatFile.spec) do
t_insert(self.colList, { width = specCol.width, label = specCol.name, font = function() return IsKeyDown("CTRL") and "FIXED" or "VAR" end })
t_insert(self.colList, {
width = specCol.width,
specColRef = specCol, -- Link to the original data
label = specCol.name,
font = function() return IsKeyDown("ALT") and "FIXED" or "VAR" end,
sortable = true
})
end
local short = main.curDatFile.rowSize - main.curDatFile.specSize
if short > 0 then
t_insert(self.colList, { width = short * DrawStringWidth(self.rowHeight, "FIXED", "00 "), font = "FIXED" })
t_insert(self.colList, { width = short * DrawStringWidth(self.rowHeight, "FIXED", "00 "), font = "FIXED", sortable = true })
end
end

function RowListClass:GetRowValue(column, index, row)
if column == 1 then
return string.format("%5d", row)
end
if not main.curDatFile.spec[column - 1] or IsKeyDown("CTRL") then
if not main.curDatFile.spec[column - 1] or IsKeyDown("ALT") then
local out = { main.curDatFile:ReadCellRaw(row, column - 1) }
for i, b in ipairs(out) do
out[i] = string.format("%02X", b)
Expand All @@ -76,3 +83,280 @@ function RowListClass:GetRowValue(column, index, row)
end
end
end

function RowListClass:Draw(viewPort)
local x, y = self:GetPos()
local width, height = self:GetSize()
local rowHeight = self.rowHeight
local list = self.list

local colOffset = 0
for index, column in ipairs(self.colList) do
column._offset = colOffset
column._width = self:GetColumnProperty(column, "width") or (index == #self.colList and self.scroll and width - 20 or width - colOffset) or 0
colOffset = colOffset + column._width
end

local scrollBarV = self.controls.scrollBarV
local rowRegion = self:GetRowRegion()
scrollBarV:SetContentDimension(#list * rowHeight, rowRegion.height)
local scrollOffsetV = scrollBarV.offset
local scrollBarH = self.controls.scrollBarH
local lastCol = self.colList[#self.colList]
scrollBarH:SetContentDimension(lastCol._offset + lastCol._width, rowRegion.width)
local scrollOffsetH = scrollBarH.offset

local cursorX, cursorY = GetCursorPos()

local label = self:GetProperty("label")
if label then
DrawString(x + self.labelPositionOffset[1], y - 20 + self.labelPositionOffset[2], "LEFT", 16, self.font, label)
end
if self.hasFocus then
SetDrawColor(1, 1, 1)
else
SetDrawColor(0.5, 0.5, 0.5)
end
DrawImage(nil, x, y, width, height)
SetDrawColor(0, 0, 0)
DrawImage(nil, x + 1, y + 1, width - 2, height - 2)
self:DrawControls(viewPort)

SetViewport(x + 2, y + 2, self.scroll and width - 20 or width, height - 4 - (self.scroll and self.scrollH and 16 or 0))
local textOffsetY = self.showRowSeparators and 2 or 0
local textHeight = rowHeight - textOffsetY * 2
local minIndex = math.floor(scrollOffsetV / rowHeight + 1)
local maxIndex = math.min(math.floor((scrollOffsetV + height) / rowHeight + 1), #list)
for colIndex, column in ipairs(self.colList) do
local colFont = self:GetColumnProperty(column, "font") or "VAR"
local clipWidth = DrawStringWidth(textHeight, colFont, "...")
colOffset = column._offset - scrollOffsetH
local colWidth = column._width
local relX = cursorX - (x + 2)
local relY = cursorY - (y + 2)
for index = minIndex, maxIndex do
local lineY = rowHeight * (index - 1) - scrollOffsetV + (self.colLabels and 18 or 0)
local value = list[index]
local text = self:GetRowValue(colIndex, index, value)
local textWidth = DrawStringWidth(textHeight, colFont, text)
if textWidth > colWidth - 2 then
local clipIndex = DrawStringCursorIndex(textHeight, colFont, text, colWidth - clipWidth - 2, 0)
text = text:sub(1, clipIndex - 1) .. "..."
textWidth = DrawStringWidth(textHeight, colFont, text)
end
if self.showRowSeparators then
if self.hasFocus and value == self.selValue then
SetDrawColor(1, 1, 1)
else
SetDrawColor(0.5, 0.5, 0.5)
end
DrawImage(nil, colOffset, lineY, not self.scroll and colWidth - 4 or colWidth, rowHeight)
if index % 2 == 0 then
SetDrawColor(0.05, 0.05, 0.05)
else
SetDrawColor(0, 0, 0)
end
DrawImage(nil, colOffset, lineY + 1, not self.scroll and colWidth - 4 or colWidth, rowHeight - 2)
elseif value == self.selValue then
if self.hasFocus and value == self.selValue then
SetDrawColor(1, 1, 1)
else
SetDrawColor(0.5, 0.5, 0.5)
end
DrawImage(nil, colOffset, lineY, not self.scroll and colWidth - 4 or colWidth, rowHeight)
SetDrawColor(0.15, 0.15, 0.15)
DrawImage(nil, colOffset, lineY + 1, not self.scroll and colWidth - 4 or colWidth, rowHeight - 2)
end
if not self.SetHighlightColor or not self:SetHighlightColor(index, value) then
SetDrawColor(1, 1, 1)
end
DrawString(colOffset, lineY + textOffsetY, "LEFT", textHeight, colFont, text)
end
if self.colLabels then
local mOver = relX >= colOffset and relX <= colOffset + colWidth and relY >= 0 and relY <= 18

local isSelected = (colIndex - 1) == main.curSpecColIndex
local outerColor
if mOver then
outerColor = {1, 1, 1}
elseif isSelected then
outerColor = {1, 0.3, 0.2}
else
outerColor = {0.5, 0.5, 0.5}
end
local innerColor = isSelected and {0.6, 0.25, 0.2} or (mOver and self:GetColumnProperty(column, "sortable") and {0.33, 0.33, 0.33} or {0.15, 0.15, 0.15})

SetDrawColor(unpack(outerColor))
DrawImage(nil, colOffset, 1, colWidth, 18)
SetDrawColor(unpack(innerColor))
DrawImage(nil, colOffset + 1, 2, colWidth - 2, 16)

local label = self:GetColumnProperty(column, "label")
if label and #label > 0 then
SetDrawColor(1, 1, 1)
DrawString(colOffset + colWidth/2, 4, "CENTER_X", 12, "VAR", label)
end
end
end
if #self.list == 0 and self.defaultText then
SetDrawColor(1, 1, 1)
DrawString(2, 2, "LEFT", 14, self.font, self.defaultText)
end
SetViewport()
end

function RowListClass:ReSort(colIndex)
local asc = true
if self.lastSortedCol == colIndex then
asc = not self.sortAsc
end

table.sort(self.list, function(a, b)
local valA = self:GetRowValue(colIndex, nil, a)
local valB = self:GetRowValue(colIndex, nil, b)

local isBlankA = valA == nil or valA == "" or tostring(valA):match("^%s*$")
local isBlankB = valB == nil or valB == "" or tostring(valB):match("^%s*$")

-- Always put blank items at the bottom
if isBlankA and not isBlankB then
return false
elseif not isBlankA and isBlankB then
return true
elseif isBlankA and isBlankB then
return false
end

local numA = tonumber(valA)
local numB = tonumber(valB)

if numA and numB then
if asc then
return numA < numB
else
return numA > numB
end
else
valA = tostring(valA or "")
valB = tostring(valB or "")
if asc then
return valA < valB
else
return valA > valB
end
end
end)

self.lastSortedCol = colIndex
self.sortAsc = asc
end

function RowListClass:OnKeyUp(key, doubleClick)
if not self:IsShown() or not self:IsEnabled() then
return
end

local function isScrollKey(k)
if k == "WHEELUP" then return true, -1, 10 end
if k == "WHEELDOWN" then return true, 1, -10 end
return false, 0, 0
end

local mOverControl = self:GetMouseOverControl()
if mOverControl and mOverControl.OnKeyDown then
return mOverControl:OnKeyDown(key)
end

if not self:IsMouseOver() and key:match("BUTTON") then
return
end

-- Get cursor info
local x, y = self:GetPos()
local cursorX, cursorY = GetCursorPos()
local scrollOffsetH = self.controls.scrollBarH and self.controls.scrollBarH.offset or 0
local relX = cursorX - (x + 2)
local relY = cursorY - (y + 2)
local adjustedRelX = relX + scrollOffsetH

-- Middle-click resets column width
if key == "MIDDLEBUTTON" then
for colIndex, column in ipairs(self.colList) do
local colOffset = column._offset
local colWidth = column.width or column._width
if colOffset and colWidth then
local mOver = adjustedRelX >= colOffset and adjustedRelX <= colOffset + colWidth and relY >= 0 and relY <= 18
if mOver then
-- Initialize if not present
self._autoSizeToggleState[colIndex] = not self._autoSizeToggleState[colIndex]

local newWidth
if self._autoSizeToggleState[colIndex] then
-- First toggle: size to contents
local maxWidth = 0
for _, rowIndex in ipairs(self.list) do
local val = self:GetRowValue(colIndex, nil, rowIndex)
if val ~= nil then
local width = DrawStringWidth(self.rowHeight, "FIXED", tostring(val))
maxWidth = math.max(maxWidth, width)
end
end
local labelWidth = DrawStringWidth(self.rowHeight, "FIXED", tostring(column.label or ""))
newWidth = math.max(40, math.max(maxWidth, labelWidth) + 10)
else
-- Second toggle: reset to label or 150, whichever is greater
local labelWidth = DrawStringWidth(self.rowHeight, "FIXED", tostring(column.label or ""))
newWidth = math.max(150, labelWidth + 10)
end

column.width = newWidth

if column.specColRef then
column.specColRef.width = newWidth
main.curSpecCol = column.specColRef
main.controls.colWidth:SetText(newWidth)
end

self:BuildColumns()
return self
end
end
end
return self
end
-- Scroll behavior
local isScroll, scrollStep, colDelta = isScrollKey(key)
if isScroll then
local overColumnHeader = false
for _, column in ipairs(self.colList) do
local colOffset = column._offset
local colWidth = column.width or column._width
if colOffset and colWidth then
local mOver = adjustedRelX >= colOffset and adjustedRelX <= colOffset + colWidth and relY >= 0 and relY <= 18
if mOver then
-- Widen column if hovering over it
overColumnHeader = true
local newWidth = math.max(40, colWidth + colDelta)
column.width = newWidth
if column.specColRef then
column.specColRef.width = newWidth
main.curSpecCol = column.specColRef
main.controls.colWidth:SetText(newWidth)
end
self:BuildColumns()
break
end
end
end
-- Scroll vertically or horizontally if not resizing column
if not overColumnHeader then
if self.scroll and self.scrollH and IsKeyDown("SHIFT") then
self.controls.scrollBarH:Scroll(scrollStep)
else
self.controls.scrollBarV:Scroll(scrollStep)
end
end
return self
end
return self
end
3 changes: 3 additions & 0 deletions src/Export/Classes/ScriptListControl.lua
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ end

function ScriptListClass:OnSelClick(index, script, doubleClick)
if doubleClick then
if main.controls.clearAutoClearOutput.state then
wipeTable(main.scriptOutput)
end
local errMsg = PLoadModule("Scripts/"..script..".lua")
if errMsg then
print(errMsg)
Expand Down
Loading