ListFrames = { frame = term.current(), drawEntry = nil, backingList = {}, subFrames = {}, length = 0, estimatedTotalHeight = 0, -- pagination start line (not number of entries!) paginationPos = 1, } function ListFrames:new(o) o = o or {} setmetatable(o, self) self.__index = self return o end function ListFrames:repositionEntriesAfter(idx) local tw, th = self.frame.getSize() local entryFrame = self.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 = self.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, self.frame) entryFrame.setVisible(true) end yStartNext = yPos + ySize end idx = idx + 1 entryFrame = self.subFrames[idx] while entryFrame ~= nil and entryFrame.isVisible() do entryFrame.setVisible(false) idx = idx + 1 entryFrame = self.subFrames[idx] end end function ListFrames:redrawEntry(idx) local entryFrame = self.subFrames[idx] if entryFrame ~= nil then entryFrame.setCursorPos(1,1) if self.drawEntry then self.drawEntry(entryFrame, self.backingList[idx]) else entryFrame.setBackgroundColor(colors.red) entryFrame.clearLine() entryFrame.setBackgroundColor(colors.black) end local tw, th = self.frame.getSize() local xPos, yPos = entryFrame.getPosition() local xSize, ySize = entryFrame.getSize() local yEnd = yPos+ySize-1 entryFrame.setVisible(yEnd > 0 and yPos <= th) yEnd = yEnd+self.paginationPos-1 if yEnd > self.estimatedTotalHeight then self.estimatedTotalHeight = yEnd end end end function ListFrames:repositionOrCreateEntryFrame(idx) local previousFrame = self.subFrames[idx-1] local yPos = 2-self.paginationPos if previousFrame ~= nil then local xPos xPos, yPos = previousFrame.getPosition() local xSize, ySize = previousFrame.getSize() yPos = yPos + ySize end local tw, th = self.frame.getSize() local entryFrame = self.subFrames[idx] if entryFrame == nil then self.subFrames[idx] = window.create(self.frame, 1, yPos, tw, 1, false) else entryFrame.setVisible(false) local xSize, ySize = entryFrame.getSize() entryFrame.reposition(1, yPos, tw, ySize, self.frame) end end function ListFrames:updateItemAt(idx, item) self.backingList[idx] = item self:repositionOrCreateEntryFrame(idx) if idx > self.length then self.length = idx end self:redrawEntry(idx) self:repositionEntriesAfter(idx) end function ListFrames:clearFrom(idx) self.estimatedTotalHeight = idx-1 local tw, th = self.frame.getSize() for j = self.length, idx, -1 do self.backingList[j] = nil if self.subFrames[j] ~= nil then self.subFrames[j].setVisible(false) end end self.length = idx-1 for y = idx-self.paginationPos+1, th, 1 do self.frame.setCursorPos(1, y) self.frame.clearLine() end end function ListFrames:redraw() if self.drawEntry then self.frame.clear() local tw, th = self.frame.getSize() local yPos = 2-self.paginationPos for idx = 1, self.length, 1 do assert(self.backingList[idx] ~= nil, "nil element at idx="..idx) if yPos <= th then self:repositionOrCreateEntryFrame(idx) self:redrawEntry(idx) local xSize, ySize = self.subFrames[idx].getSize() yPos = yPos + ySize elseif self.subFrames[idx] ~= nil then self.subFrames[idx].setVisible(false) end end else self.frame.setBackgroundColor(colors.red) self.frame.clear() self.frame.setBackgroundColor(colors.black) end end function ListFrames:updatePage(scrollPos) if scrollPos ~= self.paginationPos then self.paginationPos = scrollPos self:redraw() end end function ListFrames:estimatedHeight() local lastEntryFrame = self.subFrames[self.length] if lastEntryFrame ~= nil and lastEntryFrame.isVisible() then local xPos, yPos = lastEntryFrame.getPosition() local xSize, ySize = lastEntryFrame.getSize() self.estimatedTotalHeight = yPos+ySize-1+self.paginationPos-1 end return self.estimatedTotalHeight end return ListFrames