Implement overview scrolling
This commit is contained in:
+240
-22
@@ -1,10 +1,11 @@
|
|||||||
local mon = peripheral.find("monitor")
|
local mon = peripheral.find("monitor")
|
||||||
assert(mon, "Kein Monitor gefunden")
|
assert(mon, "Kein Monitor gefunden")
|
||||||
local monName = peripheral.getName(mon)
|
local monName = peripheral.getName(mon)
|
||||||
local SCREEN_WIDTH = 40
|
|
||||||
|
|
||||||
mon.setTextScale(0.5)
|
mon.setTextScale(0.5)
|
||||||
|
|
||||||
|
local SCREEN_WIDTH, SCREEN_HEIGHT = mon.getSize()
|
||||||
|
|
||||||
local oldTerm = term.redirect(mon)
|
local oldTerm = term.redirect(mon)
|
||||||
|
|
||||||
local Pine3D = require("Pine3D")
|
local Pine3D = require("Pine3D")
|
||||||
@@ -20,6 +21,8 @@ local itemById
|
|||||||
local defaultBaseId
|
local defaultBaseId
|
||||||
local fallbackIcon = false
|
local fallbackIcon = false
|
||||||
local backButton = false
|
local backButton = false
|
||||||
|
local scrollUpButton = false
|
||||||
|
local scrollDownButton = false
|
||||||
local drawOverview
|
local drawOverview
|
||||||
|
|
||||||
local function ensureChainsLoaded()
|
local function ensureChainsLoaded()
|
||||||
@@ -233,6 +236,30 @@ local function getBackButton()
|
|||||||
return backButton
|
return backButton
|
||||||
end
|
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 function imageSize(img)
|
||||||
local w, h = 0, 0
|
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 dy = 0, scaleY - 1 do
|
||||||
for dx = 0, scaleX - 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
|
end
|
||||||
end
|
end
|
||||||
@@ -304,6 +336,31 @@ local function drawNfpScaled(buffer, img, x, y, scaleX, scaleY)
|
|||||||
end
|
end
|
||||||
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 function assertBufferValid(frame)
|
||||||
local valid = {
|
local valid = {
|
||||||
[colors.white] = true,
|
[colors.white] = true,
|
||||||
@@ -335,10 +392,10 @@ local function assertBufferValid(frame)
|
|||||||
end
|
end
|
||||||
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.
|
-- Normale Monitor-Zellen in Pine3D-Teletext-Pixel umrechnen.
|
||||||
local x = (cellX - 1) * 2 + 1
|
local x = cellToPixelX(cellX)
|
||||||
local y = (cellY - 1) * 3 + 1
|
local y = cellToPixelY(cellY) + (offsetY or 0)
|
||||||
|
|
||||||
local imgW, imgH = 0, 0
|
local imgW, imgH = 0, 0
|
||||||
|
|
||||||
@@ -347,7 +404,9 @@ local function drawItem(img, cellX, cellY, count)
|
|||||||
|
|
||||||
if img then
|
if img then
|
||||||
imgW, imgH = imageSize(img)
|
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
|
end
|
||||||
|
|
||||||
local text = formatCount(count)
|
local text = formatCount(count)
|
||||||
@@ -366,11 +425,13 @@ local function drawItem(img, cellX, cellY, count)
|
|||||||
dy = 1,
|
dy = 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- Schatten
|
if bottomY >= 1 and y <= frame.buffer.height then
|
||||||
mf.writeOn(frame, text, colors.black, rightX + 1, bottomY + 1, options)
|
-- Schatten
|
||||||
|
mf.writeOn(frame, text, colors.black, rightX + 1, bottomY + 1, options)
|
||||||
|
|
||||||
-- Weißer Count
|
-- Weißer Count
|
||||||
mf.writeOn(frame, text, colors.white, rightX, bottomY, options)
|
mf.writeOn(frame, text, colors.white, rightX, bottomY, options)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local function drawPage(base_id)
|
local function drawPage(base_id)
|
||||||
@@ -421,10 +482,69 @@ end
|
|||||||
drawOverview = function()
|
drawOverview = function()
|
||||||
local items = getBaseItemIds()
|
local items = getBaseItemIds()
|
||||||
local sortedItemIds = {}
|
local sortedItemIds = {}
|
||||||
|
local itemCounts = {}
|
||||||
local visibleItemCount = 0
|
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 function renderOverview()
|
||||||
local itemCounts = getMeItemCounts(items)
|
itemCounts = getMeItemCounts(items)
|
||||||
|
|
||||||
for i = 1, #items do
|
for i = 1, #items do
|
||||||
sortedItemIds[i] = items[i]
|
sortedItemIds[i] = items[i]
|
||||||
@@ -441,23 +561,59 @@ drawOverview = function()
|
|||||||
return a < b
|
return a < b
|
||||||
end)
|
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()
|
frame.buffer:clear()
|
||||||
|
|
||||||
for i = 1, visibleItemCount do
|
for i = firstVisibleIndex, lastVisibleIndex do
|
||||||
local itemId = sortedItemIds[i]
|
local itemId = sortedItemIds[i]
|
||||||
local item = getItemById(itemId)
|
local item = getItemById(itemId)
|
||||||
local icon = getItemIcon(item) or getFallbackIcon()
|
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
|
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)
|
assertBufferValid(frame)
|
||||||
frame:drawBuffer()
|
frame:drawBuffer()
|
||||||
end
|
end
|
||||||
|
|
||||||
renderOverview()
|
renderOverview()
|
||||||
local refreshTimer = os.startTimer(30)
|
refreshTimer = os.startTimer(30)
|
||||||
|
|
||||||
while true do
|
while true do
|
||||||
local event, p1, x, y = os.pullEvent()
|
local event, p1, x, y = os.pullEvent()
|
||||||
@@ -465,16 +621,78 @@ drawOverview = function()
|
|||||||
if event == "timer" and p1 == refreshTimer then
|
if event == "timer" and p1 == refreshTimer then
|
||||||
renderOverview()
|
renderOverview()
|
||||||
refreshTimer = os.startTimer(30)
|
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
|
elseif event == "monitor_touch" and p1 == monName then
|
||||||
local col = math.floor((x - 4) / 12)
|
if x >= scrollbarX and x < scrollbarX + scrollbarWidth then
|
||||||
local row = math.floor((y - 2) / 6)
|
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
|
if touchPixelY < thumbY then
|
||||||
local index = row * 3 + col + 1
|
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
|
if col >= 0 and col < columns and row >= 0 and row < rowsPerView + 1 then
|
||||||
drawPage(sortedItemIds[index])
|
local index = (firstVisibleRow + row) * columns + col + 1
|
||||||
return
|
|
||||||
|
if index >= 1 and index <= #sortedItemIds and index <= firstVisibleRow * columns + visibleItemCount + columns then
|
||||||
|
drawPage(sortedItemIds[index])
|
||||||
|
return
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user