Skip to content

Commit 9b201c2

Browse files
authored
Add Search and sort to exporter (#1147)
* Search for .dat files * Highlight selected column, and sort by clicking column header * removed comments * Resize columns with scroll wheel and middle click when hovering column header * Sort blank items at the bottom * Ctrl+f to jump to dat search box * Switch buttons to ALT for raw data. * Escape button closes dat view so script output is visible * Add clear button to search box * Auto clear checkbox for script output
1 parent a683199 commit 9b201c2

File tree

5 files changed

+337
-9
lines changed

5 files changed

+337
-9
lines changed

src/Classes/ListControl.lua

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,10 +346,12 @@ function ListClass:OnKeyDown(key, doubleClick)
346346
newSelect = index
347347
end
348348
else
349+
local scrollOffsetH = self.controls.scrollBarH.offset
349350
for colIndex, column in ipairs(self.colList) do
350351
local relX = cursorX - (x + 2)
351352
local relY = cursorY - (y + 2)
352-
local mOver = relX >= column._offset and relX <= column._offset + column._width and relY >= 0 and relY <= 18
353+
local adjustedRelX = relX + scrollOffsetH
354+
local mOver = adjustedRelX >= column._offset and adjustedRelX <= column._offset + column._width and relY >= 0 and relY <= 18
353355
if self:GetColumnProperty(column, "sortable") and mOver and self.ReSort then
354356
self:ReSort(colIndex)
355357
end

src/Export/Classes/DatListControl.lua

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,27 @@
44
-- Dat list control.
55
--
66
local DatListClass = newClass("DatListControl", "ListControl", function(self, anchor, rect)
7-
self.ListControl(anchor, rect, 14, "VERTICAL", false, main.datFileList)
7+
self.originalList = main.datFileList
8+
self.searchBuf = ""
9+
self.filteredList = self.originalList
10+
self.ListControl(anchor, rect, 14, "VERTICAL", false, self.filteredList)
811
end)
912

13+
function DatListClass:BuildFilteredList()
14+
local search = self.searchBuf:lower()
15+
if search == "" then
16+
self.filteredList = self.originalList
17+
else
18+
self.filteredList = {}
19+
for _, file in ipairs(self.originalList) do
20+
if file.name:lower():find(search, 1, true) then
21+
table.insert(self.filteredList, file)
22+
end
23+
end
24+
end
25+
self.list = self.filteredList
26+
end
27+
1028
function DatListClass:GetRowValue(column, index, datFile)
1129
if column == 1 then
1230
return "^7"..datFile.name

src/Export/Classes/RowListControl.lua

Lines changed: 288 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ local t_insert = table.insert
99
local RowListClass = newClass("RowListControl", "ListControl", function(self, anchor, rect)
1010
self.ListControl(anchor, rect, 14, "HORIZONTAL", false, { })
1111
self.colLabels = true
12+
self._autoSizeToggleState = {} -- internal toggle memory, not saved to spec
1213
end)
1314

1415
function RowListClass:BuildRows(filter)
@@ -44,21 +45,27 @@ end
4445

4546
function RowListClass:BuildColumns()
4647
wipeTable(self.colList)
47-
self.colList[1] = { width = 50, label = "#", font = "FIXED" }
48+
self.colList[1] = { width = 50, label = "#", font = "FIXED", sortable = true }
4849
for _, specCol in ipairs(main.curDatFile.spec) do
49-
t_insert(self.colList, { width = specCol.width, label = specCol.name, font = function() return IsKeyDown("CTRL") and "FIXED" or "VAR" end })
50+
t_insert(self.colList, {
51+
width = specCol.width,
52+
specColRef = specCol, -- Link to the original data
53+
label = specCol.name,
54+
font = function() return IsKeyDown("ALT") and "FIXED" or "VAR" end,
55+
sortable = true
56+
})
5057
end
5158
local short = main.curDatFile.rowSize - main.curDatFile.specSize
5259
if short > 0 then
53-
t_insert(self.colList, { width = short * DrawStringWidth(self.rowHeight, "FIXED", "00 "), font = "FIXED" })
60+
t_insert(self.colList, { width = short * DrawStringWidth(self.rowHeight, "FIXED", "00 "), font = "FIXED", sortable = true })
5461
end
5562
end
5663

5764
function RowListClass:GetRowValue(column, index, row)
5865
if column == 1 then
5966
return string.format("%5d", row)
6067
end
61-
if not main.curDatFile.spec[column - 1] or IsKeyDown("CTRL") then
68+
if not main.curDatFile.spec[column - 1] or IsKeyDown("ALT") then
6269
local out = { main.curDatFile:ReadCellRaw(row, column - 1) }
6370
for i, b in ipairs(out) do
6471
out[i] = string.format("%02X", b)
@@ -76,3 +83,280 @@ function RowListClass:GetRowValue(column, index, row)
7683
end
7784
end
7885
end
86+
87+
function RowListClass:Draw(viewPort)
88+
local x, y = self:GetPos()
89+
local width, height = self:GetSize()
90+
local rowHeight = self.rowHeight
91+
local list = self.list
92+
93+
local colOffset = 0
94+
for index, column in ipairs(self.colList) do
95+
column._offset = colOffset
96+
column._width = self:GetColumnProperty(column, "width") or (index == #self.colList and self.scroll and width - 20 or width - colOffset) or 0
97+
colOffset = colOffset + column._width
98+
end
99+
100+
local scrollBarV = self.controls.scrollBarV
101+
local rowRegion = self:GetRowRegion()
102+
scrollBarV:SetContentDimension(#list * rowHeight, rowRegion.height)
103+
local scrollOffsetV = scrollBarV.offset
104+
local scrollBarH = self.controls.scrollBarH
105+
local lastCol = self.colList[#self.colList]
106+
scrollBarH:SetContentDimension(lastCol._offset + lastCol._width, rowRegion.width)
107+
local scrollOffsetH = scrollBarH.offset
108+
109+
local cursorX, cursorY = GetCursorPos()
110+
111+
local label = self:GetProperty("label")
112+
if label then
113+
DrawString(x + self.labelPositionOffset[1], y - 20 + self.labelPositionOffset[2], "LEFT", 16, self.font, label)
114+
end
115+
if self.hasFocus then
116+
SetDrawColor(1, 1, 1)
117+
else
118+
SetDrawColor(0.5, 0.5, 0.5)
119+
end
120+
DrawImage(nil, x, y, width, height)
121+
SetDrawColor(0, 0, 0)
122+
DrawImage(nil, x + 1, y + 1, width - 2, height - 2)
123+
self:DrawControls(viewPort)
124+
125+
SetViewport(x + 2, y + 2, self.scroll and width - 20 or width, height - 4 - (self.scroll and self.scrollH and 16 or 0))
126+
local textOffsetY = self.showRowSeparators and 2 or 0
127+
local textHeight = rowHeight - textOffsetY * 2
128+
local minIndex = math.floor(scrollOffsetV / rowHeight + 1)
129+
local maxIndex = math.min(math.floor((scrollOffsetV + height) / rowHeight + 1), #list)
130+
for colIndex, column in ipairs(self.colList) do
131+
local colFont = self:GetColumnProperty(column, "font") or "VAR"
132+
local clipWidth = DrawStringWidth(textHeight, colFont, "...")
133+
colOffset = column._offset - scrollOffsetH
134+
local colWidth = column._width
135+
local relX = cursorX - (x + 2)
136+
local relY = cursorY - (y + 2)
137+
for index = minIndex, maxIndex do
138+
local lineY = rowHeight * (index - 1) - scrollOffsetV + (self.colLabels and 18 or 0)
139+
local value = list[index]
140+
local text = self:GetRowValue(colIndex, index, value)
141+
local textWidth = DrawStringWidth(textHeight, colFont, text)
142+
if textWidth > colWidth - 2 then
143+
local clipIndex = DrawStringCursorIndex(textHeight, colFont, text, colWidth - clipWidth - 2, 0)
144+
text = text:sub(1, clipIndex - 1) .. "..."
145+
textWidth = DrawStringWidth(textHeight, colFont, text)
146+
end
147+
if self.showRowSeparators then
148+
if self.hasFocus and value == self.selValue then
149+
SetDrawColor(1, 1, 1)
150+
else
151+
SetDrawColor(0.5, 0.5, 0.5)
152+
end
153+
DrawImage(nil, colOffset, lineY, not self.scroll and colWidth - 4 or colWidth, rowHeight)
154+
if index % 2 == 0 then
155+
SetDrawColor(0.05, 0.05, 0.05)
156+
else
157+
SetDrawColor(0, 0, 0)
158+
end
159+
DrawImage(nil, colOffset, lineY + 1, not self.scroll and colWidth - 4 or colWidth, rowHeight - 2)
160+
elseif value == self.selValue then
161+
if self.hasFocus and value == self.selValue then
162+
SetDrawColor(1, 1, 1)
163+
else
164+
SetDrawColor(0.5, 0.5, 0.5)
165+
end
166+
DrawImage(nil, colOffset, lineY, not self.scroll and colWidth - 4 or colWidth, rowHeight)
167+
SetDrawColor(0.15, 0.15, 0.15)
168+
DrawImage(nil, colOffset, lineY + 1, not self.scroll and colWidth - 4 or colWidth, rowHeight - 2)
169+
end
170+
if not self.SetHighlightColor or not self:SetHighlightColor(index, value) then
171+
SetDrawColor(1, 1, 1)
172+
end
173+
DrawString(colOffset, lineY + textOffsetY, "LEFT", textHeight, colFont, text)
174+
end
175+
if self.colLabels then
176+
local mOver = relX >= colOffset and relX <= colOffset + colWidth and relY >= 0 and relY <= 18
177+
178+
local isSelected = (colIndex - 1) == main.curSpecColIndex
179+
local outerColor
180+
if mOver then
181+
outerColor = {1, 1, 1}
182+
elseif isSelected then
183+
outerColor = {1, 0.3, 0.2}
184+
else
185+
outerColor = {0.5, 0.5, 0.5}
186+
end
187+
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})
188+
189+
SetDrawColor(unpack(outerColor))
190+
DrawImage(nil, colOffset, 1, colWidth, 18)
191+
SetDrawColor(unpack(innerColor))
192+
DrawImage(nil, colOffset + 1, 2, colWidth - 2, 16)
193+
194+
local label = self:GetColumnProperty(column, "label")
195+
if label and #label > 0 then
196+
SetDrawColor(1, 1, 1)
197+
DrawString(colOffset + colWidth/2, 4, "CENTER_X", 12, "VAR", label)
198+
end
199+
end
200+
end
201+
if #self.list == 0 and self.defaultText then
202+
SetDrawColor(1, 1, 1)
203+
DrawString(2, 2, "LEFT", 14, self.font, self.defaultText)
204+
end
205+
SetViewport()
206+
end
207+
208+
function RowListClass:ReSort(colIndex)
209+
local asc = true
210+
if self.lastSortedCol == colIndex then
211+
asc = not self.sortAsc
212+
end
213+
214+
table.sort(self.list, function(a, b)
215+
local valA = self:GetRowValue(colIndex, nil, a)
216+
local valB = self:GetRowValue(colIndex, nil, b)
217+
218+
local isBlankA = valA == nil or valA == "" or tostring(valA):match("^%s*$")
219+
local isBlankB = valB == nil or valB == "" or tostring(valB):match("^%s*$")
220+
221+
-- Always put blank items at the bottom
222+
if isBlankA and not isBlankB then
223+
return false
224+
elseif not isBlankA and isBlankB then
225+
return true
226+
elseif isBlankA and isBlankB then
227+
return false
228+
end
229+
230+
local numA = tonumber(valA)
231+
local numB = tonumber(valB)
232+
233+
if numA and numB then
234+
if asc then
235+
return numA < numB
236+
else
237+
return numA > numB
238+
end
239+
else
240+
valA = tostring(valA or "")
241+
valB = tostring(valB or "")
242+
if asc then
243+
return valA < valB
244+
else
245+
return valA > valB
246+
end
247+
end
248+
end)
249+
250+
self.lastSortedCol = colIndex
251+
self.sortAsc = asc
252+
end
253+
254+
function RowListClass:OnKeyUp(key, doubleClick)
255+
if not self:IsShown() or not self:IsEnabled() then
256+
return
257+
end
258+
259+
local function isScrollKey(k)
260+
if k == "WHEELUP" then return true, -1, 10 end
261+
if k == "WHEELDOWN" then return true, 1, -10 end
262+
return false, 0, 0
263+
end
264+
265+
local mOverControl = self:GetMouseOverControl()
266+
if mOverControl and mOverControl.OnKeyDown then
267+
return mOverControl:OnKeyDown(key)
268+
end
269+
270+
if not self:IsMouseOver() and key:match("BUTTON") then
271+
return
272+
end
273+
274+
-- Get cursor info
275+
local x, y = self:GetPos()
276+
local cursorX, cursorY = GetCursorPos()
277+
local scrollOffsetH = self.controls.scrollBarH and self.controls.scrollBarH.offset or 0
278+
local relX = cursorX - (x + 2)
279+
local relY = cursorY - (y + 2)
280+
local adjustedRelX = relX + scrollOffsetH
281+
282+
-- Middle-click resets column width
283+
if key == "MIDDLEBUTTON" then
284+
for colIndex, column in ipairs(self.colList) do
285+
local colOffset = column._offset
286+
local colWidth = column.width or column._width
287+
if colOffset and colWidth then
288+
local mOver = adjustedRelX >= colOffset and adjustedRelX <= colOffset + colWidth and relY >= 0 and relY <= 18
289+
if mOver then
290+
-- Initialize if not present
291+
self._autoSizeToggleState[colIndex] = not self._autoSizeToggleState[colIndex]
292+
293+
local newWidth
294+
if self._autoSizeToggleState[colIndex] then
295+
-- First toggle: size to contents
296+
local maxWidth = 0
297+
for _, rowIndex in ipairs(self.list) do
298+
local val = self:GetRowValue(colIndex, nil, rowIndex)
299+
if val ~= nil then
300+
local width = DrawStringWidth(self.rowHeight, "FIXED", tostring(val))
301+
maxWidth = math.max(maxWidth, width)
302+
end
303+
end
304+
local labelWidth = DrawStringWidth(self.rowHeight, "FIXED", tostring(column.label or ""))
305+
newWidth = math.max(40, math.max(maxWidth, labelWidth) + 10)
306+
else
307+
-- Second toggle: reset to label or 150, whichever is greater
308+
local labelWidth = DrawStringWidth(self.rowHeight, "FIXED", tostring(column.label or ""))
309+
newWidth = math.max(150, labelWidth + 10)
310+
end
311+
312+
column.width = newWidth
313+
314+
if column.specColRef then
315+
column.specColRef.width = newWidth
316+
main.curSpecCol = column.specColRef
317+
main.controls.colWidth:SetText(newWidth)
318+
end
319+
320+
self:BuildColumns()
321+
return self
322+
end
323+
end
324+
end
325+
return self
326+
end
327+
-- Scroll behavior
328+
local isScroll, scrollStep, colDelta = isScrollKey(key)
329+
if isScroll then
330+
local overColumnHeader = false
331+
for _, column in ipairs(self.colList) do
332+
local colOffset = column._offset
333+
local colWidth = column.width or column._width
334+
if colOffset and colWidth then
335+
local mOver = adjustedRelX >= colOffset and adjustedRelX <= colOffset + colWidth and relY >= 0 and relY <= 18
336+
if mOver then
337+
-- Widen column if hovering over it
338+
overColumnHeader = true
339+
local newWidth = math.max(40, colWidth + colDelta)
340+
column.width = newWidth
341+
if column.specColRef then
342+
column.specColRef.width = newWidth
343+
main.curSpecCol = column.specColRef
344+
main.controls.colWidth:SetText(newWidth)
345+
end
346+
self:BuildColumns()
347+
break
348+
end
349+
end
350+
end
351+
-- Scroll vertically or horizontally if not resizing column
352+
if not overColumnHeader then
353+
if self.scroll and self.scrollH and IsKeyDown("SHIFT") then
354+
self.controls.scrollBarH:Scroll(scrollStep)
355+
else
356+
self.controls.scrollBarV:Scroll(scrollStep)
357+
end
358+
end
359+
return self
360+
end
361+
return self
362+
end

src/Export/Classes/ScriptListControl.lua

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ end
1515

1616
function ScriptListClass:OnSelClick(index, script, doubleClick)
1717
if doubleClick then
18+
if main.controls.clearAutoClearOutput.state then
19+
wipeTable(main.scriptOutput)
20+
end
1821
local errMsg = PLoadModule("Scripts/"..script..".lua")
1922
if errMsg then
2023
print(errMsg)

0 commit comments

Comments
 (0)