@@ -9,6 +9,7 @@ local t_insert = table.insert
99local 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
1213end )
1314
1415function RowListClass :BuildRows (filter )
4445
4546function 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
5562end
5663
5764function 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
7885end
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
0 commit comments