Implement overview scrolling
This commit is contained in:
+240
-22
@@ -1,10 +1,11 @@
|
||||
local mon = peripheral.find("monitor")
|
||||
assert(mon, "Kein Monitor gefunden")
|
||||
local monName = peripheral.getName(mon)
|
||||
local SCREEN_WIDTH = 40
|
||||
|
||||
mon.setTextScale(0.5)
|
||||
|
||||
local SCREEN_WIDTH, SCREEN_HEIGHT = mon.getSize()
|
||||
|
||||
local oldTerm = term.redirect(mon)
|
||||
|
||||
local Pine3D = require("Pine3D")
|
||||
@@ -20,6 +21,8 @@ local itemById
|
||||
local defaultBaseId
|
||||
local fallbackIcon = false
|
||||
local backButton = false
|
||||
local scrollUpButton = false
|
||||
local scrollDownButton = false
|
||||
local drawOverview
|
||||
|
||||
local function ensureChainsLoaded()
|
||||
@@ -233,6 +236,30 @@ local function getBackButton()
|
||||
return backButton
|
||||
end
|
||||
|
||||
local function getScrollUpButton()
|
||||
if scrollUpButton == false then
|
||||
scrollUpButton = parseNfpImage(table.concat({
|
||||
" 00 ",
|
||||
"0000",
|
||||
"0 0",
|
||||
}, "\n"))
|
||||
end
|
||||
|
||||
return scrollUpButton
|
||||
end
|
||||
|
||||
local function getScrollDownButton()
|
||||
if scrollDownButton == false then
|
||||
scrollDownButton = parseNfpImage(table.concat({
|
||||
"0 0",
|
||||
"0000",
|
||||
" 00 ",
|
||||
}, "\n"))
|
||||
end
|
||||
|
||||
return scrollDownButton
|
||||
end
|
||||
|
||||
local function imageSize(img)
|
||||
local w, h = 0, 0
|
||||
|
||||
@@ -296,7 +323,12 @@ local function drawNfpScaled(buffer, img, x, y, scaleX, scaleY)
|
||||
|
||||
for dy = 0, scaleY - 1 do
|
||||
for dx = 0, scaleX - 1 do
|
||||
buffer:setPixel(px + dx, py + dy, col)
|
||||
local drawX = px + dx
|
||||
local drawY = py + dy
|
||||
|
||||
if drawX >= 1 and drawX <= buffer.width and drawY >= 1 and drawY <= buffer.height then
|
||||
buffer:setPixel(drawX, drawY, col)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -304,6 +336,31 @@ local function drawNfpScaled(buffer, img, x, y, scaleX, scaleY)
|
||||
end
|
||||
end
|
||||
|
||||
local function fillRect(buffer, x, y, width, height, color)
|
||||
local startX = math.max(1, x)
|
||||
local startY = math.max(1, y)
|
||||
local endX = math.min(buffer.width, x + width - 1)
|
||||
local endY = math.min(buffer.height, y + height - 1)
|
||||
|
||||
if startX > endX or startY > endY then
|
||||
return
|
||||
end
|
||||
|
||||
for py = startY, endY do
|
||||
for px = startX, endX do
|
||||
buffer:setPixel(px, py, color)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function cellToPixelX(cellX)
|
||||
return (cellX - 1) * 2 + 1
|
||||
end
|
||||
|
||||
local function cellToPixelY(cellY)
|
||||
return (cellY - 1) * 3 + 1
|
||||
end
|
||||
|
||||
local function assertBufferValid(frame)
|
||||
local valid = {
|
||||
[colors.white] = true,
|
||||
@@ -335,10 +392,10 @@ local function assertBufferValid(frame)
|
||||
end
|
||||
end
|
||||
|
||||
local function drawItem(img, cellX, cellY, count)
|
||||
local function drawItem(img, cellX, cellY, count, offsetY)
|
||||
-- Normale Monitor-Zellen in Pine3D-Teletext-Pixel umrechnen.
|
||||
local x = (cellX - 1) * 2 + 1
|
||||
local y = (cellY - 1) * 3 + 1
|
||||
local x = cellToPixelX(cellX)
|
||||
local y = cellToPixelY(cellY) + (offsetY or 0)
|
||||
|
||||
local imgW, imgH = 0, 0
|
||||
|
||||
@@ -347,7 +404,9 @@ local function drawItem(img, cellX, cellY, count)
|
||||
|
||||
if img then
|
||||
imgW, imgH = imageSize(img)
|
||||
drawNfpScaled(frame.buffer, img, x, y, scaleX, scaleY)
|
||||
if y <= frame.buffer.height and y + imgH * scaleY - 1 >= 1 then
|
||||
drawNfpScaled(frame.buffer, img, x, y, scaleX, scaleY)
|
||||
end
|
||||
end
|
||||
|
||||
local text = formatCount(count)
|
||||
@@ -366,11 +425,13 @@ local function drawItem(img, cellX, cellY, count)
|
||||
dy = 1,
|
||||
}
|
||||
|
||||
-- Schatten
|
||||
mf.writeOn(frame, text, colors.black, rightX + 1, bottomY + 1, options)
|
||||
if bottomY >= 1 and y <= frame.buffer.height then
|
||||
-- Schatten
|
||||
mf.writeOn(frame, text, colors.black, rightX + 1, bottomY + 1, options)
|
||||
|
||||
-- Weißer Count
|
||||
mf.writeOn(frame, text, colors.white, rightX, bottomY, options)
|
||||
-- Weißer Count
|
||||
mf.writeOn(frame, text, colors.white, rightX, bottomY, options)
|
||||
end
|
||||
end
|
||||
|
||||
local function drawPage(base_id)
|
||||
@@ -421,10 +482,69 @@ end
|
||||
drawOverview = function()
|
||||
local items = getBaseItemIds()
|
||||
local sortedItemIds = {}
|
||||
local itemCounts = {}
|
||||
local visibleItemCount = 0
|
||||
local totalRows = 0
|
||||
local maxScrollRow = 0
|
||||
local currentScrollRow = 0
|
||||
local targetScrollRow = 0
|
||||
local refreshTimer
|
||||
local animationTimer
|
||||
|
||||
local columns = 3
|
||||
local rowsPerView = 3
|
||||
local baseCellX = 4
|
||||
local baseCellY = 2
|
||||
local colStepCells = 12
|
||||
local rowStepCells = 6
|
||||
local rowStepPixels = rowStepCells * 3
|
||||
local scrollbarX = SCREEN_WIDTH - 1
|
||||
local scrollbarWidth = 2
|
||||
local scrollbarButtonHeight = 3
|
||||
local scrollbarTrackY = scrollbarButtonHeight + 1
|
||||
local scrollbarTrackHeight = SCREEN_HEIGHT - scrollbarButtonHeight * 2
|
||||
local scrollbarPixelX = cellToPixelX(scrollbarX)
|
||||
local scrollbarPixelWidth = scrollbarWidth * 2
|
||||
local scrollbarTrackPixelY = cellToPixelY(scrollbarTrackY)
|
||||
local scrollbarTrackPixelHeight = math.max(0, scrollbarTrackHeight * 3)
|
||||
|
||||
local function clampScrollRow(row)
|
||||
return math.max(0, math.min(maxScrollRow, row))
|
||||
end
|
||||
|
||||
local function getThumbMetrics()
|
||||
if scrollbarTrackPixelHeight <= 0 then
|
||||
return scrollbarTrackPixelY, 0
|
||||
end
|
||||
|
||||
if totalRows <= rowsPerView or maxScrollRow == 0 then
|
||||
return scrollbarTrackPixelY, scrollbarTrackPixelHeight
|
||||
end
|
||||
|
||||
local thumbHeight = math.max(4, math.floor(scrollbarTrackPixelHeight * rowsPerView / totalRows + 0.5))
|
||||
local thumbTravel = scrollbarTrackPixelHeight - thumbHeight
|
||||
local thumbY = scrollbarTrackPixelY + math.floor((currentScrollRow / maxScrollRow) * thumbTravel + 0.5)
|
||||
|
||||
return thumbY, thumbHeight
|
||||
end
|
||||
|
||||
local function scheduleAnimation()
|
||||
if not animationTimer and math.abs(targetScrollRow - currentScrollRow) > 0.001 then
|
||||
animationTimer = os.startTimer(0.05)
|
||||
end
|
||||
end
|
||||
|
||||
local function scrollTo(row)
|
||||
local clampedRow = clampScrollRow(row)
|
||||
|
||||
if clampedRow ~= targetScrollRow then
|
||||
targetScrollRow = clampedRow
|
||||
scheduleAnimation()
|
||||
end
|
||||
end
|
||||
|
||||
local function renderOverview()
|
||||
local itemCounts = getMeItemCounts(items)
|
||||
itemCounts = getMeItemCounts(items)
|
||||
|
||||
for i = 1, #items do
|
||||
sortedItemIds[i] = items[i]
|
||||
@@ -441,23 +561,59 @@ drawOverview = function()
|
||||
return a < b
|
||||
end)
|
||||
|
||||
visibleItemCount = math.min(#sortedItemIds, 9)
|
||||
totalRows = math.ceil(#sortedItemIds / columns)
|
||||
maxScrollRow = math.max(0, totalRows - rowsPerView)
|
||||
currentScrollRow = clampScrollRow(currentScrollRow)
|
||||
targetScrollRow = clampScrollRow(targetScrollRow)
|
||||
|
||||
local firstVisibleRow = math.floor(currentScrollRow)
|
||||
local rowOffsetPixels = -math.floor((currentScrollRow - firstVisibleRow) * rowStepPixels + 0.5)
|
||||
local firstVisibleIndex = firstVisibleRow * columns + 1
|
||||
local lastVisibleIndex = math.min(#sortedItemIds, (firstVisibleRow + rowsPerView + 1) * columns)
|
||||
|
||||
frame.buffer:clear()
|
||||
|
||||
for i = 1, visibleItemCount do
|
||||
for i = firstVisibleIndex, lastVisibleIndex do
|
||||
local itemId = sortedItemIds[i]
|
||||
local item = getItemById(itemId)
|
||||
local icon = getItemIcon(item) or getFallbackIcon()
|
||||
drawItem(icon, 4+(8+4)*((i-1)%3), 2+(5+1)*math.floor((i-1)/3), itemCounts[itemId] or 0)
|
||||
local relativeIndex = i - firstVisibleIndex
|
||||
local col = relativeIndex % columns
|
||||
local row = math.floor(relativeIndex / columns)
|
||||
drawItem(
|
||||
icon,
|
||||
baseCellX + colStepCells * col,
|
||||
baseCellY + rowStepCells * row,
|
||||
itemCounts[itemId] or 0,
|
||||
rowOffsetPixels
|
||||
)
|
||||
end
|
||||
|
||||
visibleItemCount = math.max(0, math.min(#sortedItemIds - firstVisibleRow * columns, rowsPerView * columns))
|
||||
|
||||
local upButtonY = cellToPixelY(1)
|
||||
local downButtonY = cellToPixelY(SCREEN_HEIGHT - scrollbarButtonHeight + 1)
|
||||
local upColor = targetScrollRow > 0 and colors.gray or colors.lightGray
|
||||
local downColor = targetScrollRow < maxScrollRow and colors.gray or colors.lightGray
|
||||
local thumbY, thumbHeight = getThumbMetrics()
|
||||
|
||||
fillRect(frame.buffer, scrollbarPixelX, upButtonY, scrollbarPixelWidth, scrollbarButtonHeight * 3, upColor)
|
||||
fillRect(frame.buffer, scrollbarPixelX, downButtonY, scrollbarPixelWidth, scrollbarButtonHeight * 3, downColor)
|
||||
|
||||
if scrollbarTrackPixelHeight > 0 then
|
||||
fillRect(frame.buffer, scrollbarPixelX, scrollbarTrackPixelY, scrollbarPixelWidth, scrollbarTrackPixelHeight, colors.gray)
|
||||
fillRect(frame.buffer, scrollbarPixelX, thumbY, scrollbarPixelWidth, thumbHeight, colors.white)
|
||||
end
|
||||
|
||||
drawNfpScaled(frame.buffer, getScrollUpButton(), scrollbarPixelX, upButtonY + 2, 1, 1)
|
||||
drawNfpScaled(frame.buffer, getScrollDownButton(), scrollbarPixelX, downButtonY + 3, 1, 1)
|
||||
|
||||
assertBufferValid(frame)
|
||||
frame:drawBuffer()
|
||||
end
|
||||
|
||||
renderOverview()
|
||||
local refreshTimer = os.startTimer(30)
|
||||
refreshTimer = os.startTimer(30)
|
||||
|
||||
while true do
|
||||
local event, p1, x, y = os.pullEvent()
|
||||
@@ -465,16 +621,78 @@ drawOverview = function()
|
||||
if event == "timer" and p1 == refreshTimer then
|
||||
renderOverview()
|
||||
refreshTimer = os.startTimer(30)
|
||||
scheduleAnimation()
|
||||
elseif event == "timer" and p1 == animationTimer then
|
||||
local delta = targetScrollRow - currentScrollRow
|
||||
|
||||
if math.abs(delta) <= 0.001 then
|
||||
currentScrollRow = targetScrollRow
|
||||
animationTimer = nil
|
||||
else
|
||||
local step = delta * 0.35
|
||||
|
||||
if step > 0 then
|
||||
step = math.max(0.08, math.min(step, 0.45))
|
||||
else
|
||||
step = math.min(-0.08, math.max(step, -0.45))
|
||||
end
|
||||
|
||||
if math.abs(step) >= math.abs(delta) then
|
||||
currentScrollRow = targetScrollRow
|
||||
animationTimer = nil
|
||||
else
|
||||
currentScrollRow = currentScrollRow + step
|
||||
animationTimer = os.startTimer(0.05)
|
||||
end
|
||||
end
|
||||
|
||||
renderOverview()
|
||||
elseif event == "monitor_touch" and p1 == monName then
|
||||
local col = math.floor((x - 4) / 12)
|
||||
local row = math.floor((y - 2) / 6)
|
||||
if x >= scrollbarX and x < scrollbarX + scrollbarWidth then
|
||||
if y <= scrollbarButtonHeight then
|
||||
scrollTo(targetScrollRow - 1)
|
||||
elseif y > SCREEN_HEIGHT - scrollbarButtonHeight then
|
||||
scrollTo(targetScrollRow + 1)
|
||||
elseif scrollbarTrackHeight > 0 then
|
||||
local touchPixelY = cellToPixelY(y) + 1
|
||||
local thumbY, thumbHeight = getThumbMetrics()
|
||||
|
||||
if col >= 0 and col < 3 and row >= 0 and row < 3 then
|
||||
local index = row * 3 + col + 1
|
||||
if touchPixelY < thumbY then
|
||||
if maxScrollRow > 0 then
|
||||
local thumbTravel = scrollbarTrackPixelHeight - thumbHeight
|
||||
local targetPixelY = math.max(
|
||||
scrollbarTrackPixelY,
|
||||
math.min(scrollbarTrackPixelY + thumbTravel, touchPixelY - math.floor(thumbHeight / 2))
|
||||
)
|
||||
local progress = (targetPixelY - scrollbarTrackPixelY) / math.max(1, thumbTravel)
|
||||
scrollTo(progress * maxScrollRow)
|
||||
end
|
||||
elseif touchPixelY > thumbY + thumbHeight - 1 then
|
||||
if maxScrollRow > 0 then
|
||||
local thumbTravel = scrollbarTrackPixelHeight - thumbHeight
|
||||
local targetPixelY = math.max(
|
||||
scrollbarTrackPixelY,
|
||||
math.min(scrollbarTrackPixelY + thumbTravel, touchPixelY - math.floor(thumbHeight / 2))
|
||||
)
|
||||
local progress = (targetPixelY - scrollbarTrackPixelY) / math.max(1, thumbTravel)
|
||||
scrollTo(progress * maxScrollRow)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local col = math.floor((x - baseCellX) / colStepCells)
|
||||
local firstVisibleRow = math.floor(currentScrollRow)
|
||||
local rowOffsetPixels = (currentScrollRow - firstVisibleRow) * rowStepPixels
|
||||
local touchPixelY = cellToPixelY(y) + 1
|
||||
local row = math.floor((touchPixelY - cellToPixelY(baseCellY) + rowOffsetPixels) / rowStepPixels)
|
||||
|
||||
if index <= visibleItemCount then
|
||||
drawPage(sortedItemIds[index])
|
||||
return
|
||||
if col >= 0 and col < columns and row >= 0 and row < rowsPerView + 1 then
|
||||
local index = (firstVisibleRow + row) * columns + col + 1
|
||||
|
||||
if index >= 1 and index <= #sortedItemIds and index <= firstVisibleRow * columns + visibleItemCount + columns then
|
||||
drawPage(sortedItemIds[index])
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user