Compare commits

...

6 Commits

Author SHA1 Message Date
Gabriel Tofvesson
3f0487bb48 Initial item controller implementation 2024-10-04 00:33:55 +02:00
Gabriel Tofvesson
552c19db47 Only render when dirty 2024-10-04 00:29:14 +02:00
Gabriel Tofvesson
3d1da9177a Fix padding implementation 2024-10-04 00:05:45 +02:00
Gabriel Tofvesson
925501a486 Add display name getter 2024-10-03 23:22:03 +02:00
Gabriel Tofvesson
0865e40758 Fix module requirement names 2024-10-03 23:03:02 +02:00
Gabriel Tofvesson
54f64b5342 Cache ItemStack display name 2024-10-03 23:02:22 +02:00
6 changed files with 255 additions and 21 deletions

View File

@ -1,4 +1,4 @@
local Event = require("event")
local Event = require("gfx.event")
local Element = {
x = 1,
@ -31,11 +31,15 @@ function Element:new(o)
end
function Element:draw()
local win = self:_getWindow()
-- Calling draw() without a valid window is a logical error
---@diagnostic disable-next-line: need-check-nil
win.setCursorPos(1, 1)
local dirty = self:_isDirty()
if dirty then
local win = self:_getWindow()
-- Calling draw() without a valid window is a logical error
---@diagnostic disable-next-line: need-check-nil
win.setCursorPos(1, 1)
end
self.dirty = false
return dirty
end
function Element:getX()
@ -76,6 +80,7 @@ function Element:setParent(parent)
if self.parent ~= parent then
self:setDirty()
self.parent = parent
self:_reload()
end
end
@ -115,6 +120,10 @@ function Element:setVisible(visible)
self:_getWindow().setVisible(visible)
end
function Element:_isDirty()
return self.dirty
end
function Element:resize(opts)
if (opts.width ~= nil and self.x ~= opts.width) or (opts.height ~= nil and self.y ~= opts.height) then
self:setDirty()
@ -189,10 +198,12 @@ function Element:setOnClick(onClick)
end
function Element:_reload()
local window = window.create(self.parent, self:getX(), self:getY(), self:getWidth(), self:getHeight(), self:isVisible())
window.setBackgroundColor(self:getBgColor())
window.setTextColor(self:getFgColor())
self:_setWindow(window)
if self.parent ~= nil then
local window = window.create(self.parent, self:getX(), self:getY(), self:getWidth(), self:getHeight(), self:isVisible())
window.setBackgroundColor(self:getBgColor())
window.setTextColor(self:getFgColor())
self:_setWindow(window)
end
end
return Element

View File

@ -1,4 +1,4 @@
local Element = require("element")
local Element = require("gfx.element")
local List = Element:new{
children = {},
vertical = false
@ -21,9 +21,22 @@ end
function List:insertChild(child, atIndex)
local index = math.min(math.max(1, atIndex or #self.children), #self.children)
table.insert(self.children, index, child)
-- Update window references
self:_reload()
-- Update render positions
adjustPositions(self.children, self:isVertical(), index)
end
function List:setParent(parent)
Element.setParent(self, parent)
local win = self:_getWindow()
for _,child in ipairs(self.children) do
child:setParent(win)
end
end
function List:removeChild(child)
local index
local searchType = type(child)
@ -51,6 +64,7 @@ function List:removeChild(child)
end
local removed = table.remove(self.children, index)
self:_reload()
if index <= #self.children then
adjustPositions(self.children, self:isVertical(), index)
end
@ -67,11 +81,14 @@ function List:isHorizontal()
end
function List:draw()
Element.draw(self)
self:_getWindow().clear()
for _,v in ipairs(self.children) do
v:draw()
local dirty = Element.draw(self)
if dirty then
self:_getWindow().clear()
for _,child in ipairs(self.children) do
child:draw()
end
end
return dirty
end
function List:getHeight()
@ -105,4 +122,29 @@ function List:findById(id)
return nil
end
function List:_isDirty()
if Element._isDirty(self) then
return true
end
for _,child in ipairs(self.childrent) do
if child:_isDirty() then
return true
end
end
return false
end
function List:_reload()
Element._reload(self)
-- Reload child windows
local win = self:_getWindow()
for _,child in ipairs(self.children) do
if child:_getWindow() ~= win then
child:setParent(win)
end
end
end
return List

View File

@ -1,5 +1,5 @@
local Event = require("event")
local Element = require("element")
local Event = require("gfx.event")
local Element = require("gfx.element")
local Padding = Element:new{
left = 0,
right = 0,
@ -8,6 +8,17 @@ local Padding = Element:new{
element = nil
}
function Padding:new(opts)
local obj = Element.new(self, opts)
obj.element:setPos(obj:getPaddingLeft(), obj:getPaddingTop())
obj:resize{
width = obj:getWidth(),
height = obj:getHeight()
}
obj.element:setParent(obj:_getWindow())
return obj
end
function Padding:resize(opts)
-- Un-pad dimensions and pass to child element
return self.element:resize{
@ -16,6 +27,16 @@ function Padding:resize(opts)
}
end
function Padding:draw()
if Element.draw(self) or self.element:_isDirty() then
local win = self:_getWindow()
win.clear()
self.element:draw()
return true
end
return false
end
function Padding:getPaddingLeft()
return self.left
end

View File

@ -1,4 +1,4 @@
local Element = require("element")
local Element = require("gfx.element")
local Text = Element:new{ text = "" }
function Text:new(o)
@ -26,8 +26,11 @@ function Text:getText()
end
function Text:draw()
Element.draw(self)
self:_getWindow().write(self:getText())
local dirty = Element.draw(self)
if dirty then
self:_getWindow().write(self:getText())
end
return dirty
end
function Text:getHeight()

146
itemcontroller.lua Normal file
View File

@ -0,0 +1,146 @@
local Chest = require("storage.chest")
local Storage = require("storage")
local Text = require("gfx.text")
local List = require("gfx.list")
local Padding = require("gfx.padding")
local CACHE_FILE = ".storage.cache"
local LOCK_FILE = ".storage.lock"
local function lock()
if fs.exists(LOCK_FILE) then
return false
end
-- Create lock file
return fs.open(LOCK_FILE, "w+").close()
end
local function unlock()
if not fs.exists(LOCK_FILE) then
return false
end
-- Delete lock file
local result, reason = pcall(fs.delete, LOCK_FILE)
if not result then
print("ERROR: Lock file could not be deleted: " .. reason)
end
return result
end
local function isLocked()
return fs.exists(LOCK_FILE)
end
local function saveState(fname, ctrl)
local ser = ctrl:toSerializable()
local file = fs.open(fname, "w+")
file.write(textutils.serialize(ser))
file.close()
end
local function loadState(fname, node)
local controller = nil
if not isLocked() then
local file = fs.open(fname, "r")
if file ~= nil then
local ser = textutils.unserialize(file.readAll())
file.close()
return Storage:fromSerializable(ser)
end
controller = loadState(CACHE_FILE)
end
if controller == nil then
local nodeName = peripheral.getName(node)
local storageChests = {peripheral.find("inventory")}
for i,v in ipairs(storageChests) do
if peripheral.getName(v) == nodeName then
table.remove(storageChests, i)
break
end
end
controller = Storage:fromPeripherals(Storage.assumeHomogeneous(storageChests))
saveState(controller, CACHE_FILE)
end
return controller
end
local accessNode = Chest:fromPeripheral("minecraft:trapped_chest")
local controller = loadState(CACHE_FILE, accessNode)
local monitor = peripheral.find("monitor")
local width, height = monitor.getSize()
local function itemList(stacks, wBudget)
local bgColors = {
colors.gray,
colors.black
}
local entries = {}
for i=1,#stacks do
local stack = stacks[i]
local text = stack:getDisplayName()
local count = tostring(stack:getCount())
-- Fit text inside of width budget
local countLen = #count
if countLen + 2 > wBudget then
error("Width budget is too small")
end
local textBudget = wBudget - 2 - countLen
if #text > textBudget then
-- Truncate to available budget
text = text:sub(1,textBudget)
end
local textLabel = Text:new{ text = text, bgColor = bgColors[i % 2] }
local countLabel = Text:new{ text = count, bgColor = bgColors[i % 2] }
local paddedText = Padding:new{
left = 0,
right = wBudget - #count - #text,
top = 0,
bottom = 0,
bgColor = bgColors[i % 2],
element = textLabel
}
local list = List:new{
children = {
paddedText,
countLabel
},
vertical = false,
onClick = function(table, x, y, source)
print("Clicked: "..stack:getDisplayName())
end
}
table.insert(entries, list)
end
return List:new{
children = entries,
vertical = true
}
end
local count = 0
local found = controller:find(function(stack)
local match = not stack:isEmpty()
if match then
count = count + 1
end
return match and count <= 10
end)
local listResult = itemList(found, width)
listResult:setParent(monitor)
listResult:draw()

View File

@ -16,6 +16,7 @@ function ItemStack:fromDetail(inv, detail, slot)
obj.count = detail.count
obj.maxCount = detail.maxCount
obj.enchantments = detail.enchantments
obj.displayName = detail.displayName
end
setmetatable(obj, self)
@ -33,7 +34,8 @@ function ItemStack:clone(withSlot)
maxDamage = self.maxDamage,
count = self.count,
maxCount = self.maxCount,
enchantments = self.enchantments
enchantments = self.enchantments,
displayName = self.displayName
}
setmetatable(obj, self)
@ -55,6 +57,7 @@ function ItemStack:toSerializable()
ser.maxDamage = self.maxDamage
ser.count = self.count
ser.enchantments = self.enchantments
ser.displayName = self.displayName
end
return ser
@ -105,6 +108,10 @@ function ItemStack:isEmpty()
return self.count == nil or self.count == 0
end
function ItemStack:getDisplayName()
return self.displayName
end
function ItemStack:hasChanged(listObj, thorough)
local listItem = listObj[self.slot]
if listItem == nil or listItem.name ~= self.name or listItem.count ~= self.count then
@ -129,6 +136,7 @@ function ItemStack:_modify(countDelta, stack)
self.count = nil
self.maxCount = nil
self.enchantments = nil
self.displayName = nil
else
-- If stack is empty, copy stack data from source
if self:isEmpty() then
@ -138,6 +146,7 @@ function ItemStack:_modify(countDelta, stack)
self.maxDamage = stack.maxDamage
self.maxCount = stack.maxCount
self.enchantments = stack.enchantments
self.displayName = stack.displayName
end
self.count = newCount
@ -206,6 +215,7 @@ function ItemStack:canTransfer(stack)
return self.name == stack.name and
self.damage == stack.damage and
self.maxDamage == stack.maxDamage and
self.displayName == stack.displayName and
objEquals(self.enchantments, stack.enchantments)
end
@ -234,7 +244,8 @@ function ItemStack:matches(query)
queryField(query.count, self.count) and
queryField(query.maxDamage, self.maxDamage) and
queryField(query.enchantments, self.enchantments) and
queryField(query.maxCount, self.maxCount)
queryField(query.maxCount, self.maxCount) and
queryField(query.displayName, self.displayName)
end
return ItemStack