local backingList = {} local subFrames = {} local length = 0 local frame = term.current() local drawEntry = nil local debugLog = fs.open("listframes.debug", "w") -- pagination start line (not number of entries!) local paginationPos = 1 local function setDrawEntryFunc(drawLineFunc) drawEntry = drawLineFunc end local function repositionEntriesAfter(idx) local tw, th = frame.getSize() local entryFrame = subFrames[idx] assert(entryFrame ~= nil, "provided entry at idx=" .. idx .. " has no frame") local xPos, yPos = entryFrame.getPosition() local xSize, ySize = entryFrame.getSize() local yStartNext = yPos + ySize while yStartNext <= th do idx = idx + 1 entryFrame = subFrames[idx] if entryFrame == nil then break end xPos, yPos = entryFrame.getPosition() xSize, ySize = entryFrame.getSize() if yPos == yStartNext then break else entryFrame.reposition(1, yStartNext, tw, ySize, frame) entryFrame.setVisible(true) end yStartNext = yPos + ySize end idx = idx + 1 entryFrame = subFrames[idx] while entryFrame ~= nil and entryFrame.isVisible() do entryFrame.setVisible(false) idx = idx + 1 entryFrame = subFrames[idx] end end local function redrawEntry(idx) local entryFrame = subFrames[idx] if entryFrame ~= nil then entryFrame.setCursorPos(1,1) if drawEntry then drawEntry(entryFrame, backingList[idx]) else entryFrame.setBackgroundColor(colors.red) entryFrame.clearLine() entryFrame.setBackgroundColor(colors.black) end local tw, th = frame.getSize() local xPos, yPos = entryFrame.getPosition() local xSize, ySize = entryFrame.getSize() local visible = (yPos+ySize-1) >= paginationPos and yPos <= th debugLog.writeLine(string.format("[redrawEntry] idx=%d, yPos=%d, visible=%s", idx, yPos, tostring(visible))) debugLog.flush() entryFrame.setVisible(visible) end end local function repositionOrCreateEntryFrame(idx) local previousFrame = subFrames[idx-1] local yPos = 2-paginationPos if previousFrame ~= nil then local xPos xPos, yPos = previousFrame.getPosition() local xSize, ySize = previousFrame.getSize() yPos = yPos + ySize end local tw, th = frame.getSize() local entryFrame = subFrames[idx] if entryFrame == nil then subFrames[idx] = window.create(frame, 1, yPos, tw, 1, false) else entryFrame.setVisible(false) local xSize, ySize = entryFrame.getSize() entryFrame.reposition(1, yPos, tw, ySize, frame) end end local function updateItemAt(idx, item) backingList[idx] = item repositionOrCreateEntryFrame(idx) if idx > length then length = idx end redrawEntry(idx) repositionEntriesAfter(idx) end local function clearFrom(idx) local tw, th = frame.getSize() for j = length, idx, -1 do backingList[j] = nil if subFrames[j] ~= nil then subFrames[j].setVisible(false) end end length = idx-1 for y = idx-paginationPos+1, th, 1 do frame.setCursorPos(1, y) frame.clearLine() end end local function redraw() if drawEntry then local tw, th = frame.getSize() local yPos = 2-paginationPos for idx = 1, length, 1 do assert(backingList[idx] ~= nil, "nil element at idx="..idx) if yPos <= th then repositionOrCreateEntryFrame(idx) redrawEntry(idx) local xSize, ySize = subFrames[idx].getSize() yPos = yPos + ySize elseif subFrames[idx] ~= nil then subFrames[idx].setVisible(false) end end else frame.setBackgroundColor(colors.red) frame.clear() frame.setBackgroundColor(colors.black) end end local function setTerm(termlike) frame = termlike -- reposition visible subFrames redraw() end local function updatePage(scrollPos) if scrollPos ~= paginationPos then paginationPos = scrollPos redraw() end end local function estimatedHeight() local firstEntryFrame = subFrames[1] local lastEntryFrame = subFrames[length] if firstEntryFrame ~= nil and lastEntryFrame ~= nil then local firstXPos, firstYPos = firstEntryFrame.getPosition() local xPos, yPos = lastEntryFrame.getPosition() local xSize, ySize = lastEntryFrame.getSize() return yPos-firstYPos+ySize else return length end end return { setTerm=setTerm, setDrawEntryFunc=setDrawEntryFunc, updateItemAt=updateItemAt, clearFrom=clearFrom, redraw=redraw, updatePage=updatePage, estimatedHeight=estimatedHeight, }