milo: categories, better listing page separation

This commit is contained in:
kepler155c@gmail.com
2018-12-01 00:52:39 -05:00
parent c5d209161e
commit e1084422e5
17 changed files with 146 additions and 96 deletions

View File

@@ -33,24 +33,43 @@ if multishell then
multishell.setTitle(multishell.getCurrent(), 'Milo') multishell.setTitle(multishell.getCurrent(), 'Milo')
end end
local config = { local nodes = Config.load('milo', { })
nodes = { },
}
Config.load('milo', config)
-- TODO: remove - temporary -- TODO: remove - temporary
if config.remoteDefaults then if nodes.remoteDefaults then
config.nodes = config.remoteDefaults nodes.nodes = nodes.remoteDefaults
config.remoteDefaults = nil nodes.remoteDefaults = nil
end end
-- TODO: remove - temporary -- TODO: remove - temporary
for _, node in pairs(config.nodes) do if nodes.nodes then
if node.lock and type(node.lock) == 'string' then local categories = {
node.lock = { input = 'custom',
[ node.lock ] = true, trash = 'custom',
} machine = 'machine',
brewingStand = 'custom',
activity = 'display',
jobs = 'display',
ignore = 'ignore',
hidden = 'ignore',
manipulator = 'custom',
storage = 'storage',
}
for _, node in pairs(nodes.nodes) do
if node.lock and type(node.lock) == 'string' then
node.lock = {
[ node.lock ] = true,
}
end
if not node.category then
node.category = categories[node.mtype]
if not node.category then
Util.print(node)
error('invalid node')
end
end
end end
nodes = nodes.nodes
end end
local function Syntax(msg) local function Syntax(msg)
@@ -87,7 +106,7 @@ if not device.workbench then
end end
local context = { local context = {
config = config, nodes = nodes,
resources = Util.readTable(Milo.RESOURCE_FILE) or { }, resources = Util.readTable(Milo.RESOURCE_FILE) or { },
state = { }, state = { },
@@ -97,7 +116,7 @@ local context = {
queue = { }, queue = { },
localName = modem.getNameLocal(), localName = modem.getNameLocal(),
storage = Storage(config), storage = Storage(nodes),
turtleInventory = introspection.getInventory(), turtleInventory = introspection.getInventory(),
} }

View File

@@ -10,16 +10,15 @@ local os = _G.os
local Storage = class() local Storage = class()
function Storage:init(args) function Storage:init(nodes)
local defaults = { local defaults = {
nodes = { }, nodes = nodes or { },
dirty = true, dirty = true,
activity = { }, activity = { },
storageOnline = true, storageOnline = true,
lastRefresh = os.clock(), lastRefresh = os.clock(),
} }
Util.merge(self, defaults) Util.merge(self, defaults)
Util.merge(self, args)
local modem = Peripheral.get('wired_modem') or error('Wired modem not attached') local modem = Peripheral.get('wired_modem') or error('Wired modem not attached')
self.localName = modem.getNameLocal() self.localName = modem.getNameLocal()

View File

@@ -262,7 +262,7 @@ function page:eventHandler(event)
return true return true
end end
function page:enable() function page:enable(args)
local function updateStatus() local function updateStatus()
self.statusBar.storageStatus.value = self.statusBar.storageStatus.value =
context.storage:isOnline() and '' or 'offline' context.storage:isOnline() and '' or 'offline'
@@ -292,6 +292,15 @@ function page:enable()
end) end)
end) end)
if args and args.filter then
self.filter = args.filter
self.statusBar.filter.value = args.filter
end
if args and args.message then
self.notification:success(args.message)
end
self:setFocus(self.statusBar.filter) self:setFocus(self.statusBar.filter)
UI.Page.enable(self) UI.Page.enable(self)
end end

View File

@@ -16,15 +16,15 @@ local nodeWizard
local function saveConfig() local function saveConfig()
local t = { } local t = { }
for k,v in pairs(context.config.nodes) do for k,v in pairs(context.nodes) do
t[k] = v.adapter t[k] = v.adapter
v.adapter = nil v.adapter = nil
end end
Config.update('milo', context.config) Config.update('milo', context.nodes)
for k,v in pairs(t) do for k,v in pairs(t) do
context.config.nodes[k].adapter = v context.nodes[k].adapter = v
end end
context.storage:initStorage() context.storage:initStorage()
end end
@@ -43,12 +43,13 @@ local networkPage = UI.Page {
}, },
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 2, ey = -3, y = 2, ey = -3,
values = context.config.nodes, values = context.nodes,
columns = { columns = {
{ key = 'suffix', width = 4, justify = 'right' }, { key = 'suffix', width = 4, justify = 'right' },
{ heading = 'Name', key = 'displayName' }, { heading = 'Name', key = 'displayName' },
{ heading = 'Type', key = 'mtype', width = 4 }, { heading = 'Type', key = 'mtype', width = 4 },
{ heading = 'Pri', key = 'priority', width = 3 }, { heading = 'Cat', key = 'category', width = 3 },
--{ heading = 'Pri', key = 'priority', width = 3 },
}, },
sortColumn = 'displayName', sortColumn = 'displayName',
help = 'Select Node', help = 'Select Node',
@@ -96,16 +97,26 @@ function networkPage.grid:getRowTextColor(row, selected)
return UI.Grid:getRowTextColor(row, selected) return UI.Grid:getRowTextColor(row, selected)
end end
function networkPage.grid:sortCompare(a, b)
if self.sortColumn == 'displayName' then
local an = a.displayName or a.name
local bn = b.displayName or b.name
return an:lower() < bn:lower()
end
return UI.Grid.sortCompare(self, a, b)
end
function networkPage:getList() function networkPage:getList()
for _, v in pairs(device) do for _, v in pairs(device) do
if not context.config.nodes[v.name] then if not context.nodes[v.name] then
local node = { local node = {
name = v.name, name = v.name,
mtype = 'ignore', mtype = 'ignore',
category = 'ignore',
} }
for _, page in pairs(nodeWizard.wizard.pages) do for _, page in pairs(nodeWizard.wizard.pages) do
if page.isValidType and page:isValidType(node) then if page.isValidType and page:isValidType(node) then
context.config.nodes[v.name] = node context.nodes[v.name] = node
break break
end end
end end
@@ -146,7 +157,7 @@ function networkPage:disable()
end end
function networkPage:applyFilter() function networkPage:applyFilter()
local t = Util.filter(context.config.nodes, function(v) local t = Util.filter(context.nodes, function(v)
return v.mtype ~= 'hidden' return v.mtype ~= 'hidden'
end) end)
@@ -173,7 +184,7 @@ function networkPage:eventHandler(event)
elseif event.type == 'remove_node' then elseif event.type == 'remove_node' then
local node = self.grid:getSelected() local node = self.grid:getSelected()
if node then if node then
context.config.nodes[node.name] = nil context.nodes[node.name] = nil
saveConfig() saveConfig()
end end
self:applyFilter() self:applyFilter()
@@ -412,6 +423,9 @@ end
function nodeWizard.wizard.pages.general:validate() function nodeWizard.wizard.pages.general:validate()
if self.form:save() then if self.form:save() then
_G._p3 = nodeWizard.choices
_G._p4 = nodeWizard.node
nodeWizard.node.category = Util.find(nodeWizard.choices, 'value', nodeWizard.node.mtype).category
for _, page in pairs(nodeWizard.wizard.pages) do for _, page in pairs(nodeWizard.wizard.pages) do
page.index = nil page.index = nil
end end
@@ -442,20 +456,20 @@ function nodeWizard:enable(node)
self.node.adapter = adapter self.node.adapter = adapter
node.adapter = adapter node.adapter = adapter
local choices = { self.choices = {
{ name = 'Ignore', value = 'ignore', '' }, { name = 'Ignore', value = 'ignore', category = 'ignore' },
{ name = 'Hidden', value = 'hidden', help = 'Do not show in list' }, { name = 'Hidden', value = 'hidden', category = 'ignore', help = 'Do not show in list' },
} }
for _, page in pairs(self.wizard.pages) do for _, page in pairs(self.wizard.pages) do
if page.isValidType then if page.isValidType then
local choice = page:isValidType(self.node) local choice = page:isValidType(self.node)
if choice and not Util.find(choices, 'value', choice.value) then if choice and not Util.find(self.choices, 'value', choice.value) then
table.insert(choices, 2, choice) table.insert(self.choices, 2, choice)
end end
end end
end end
self.wizard.pages.general.form[1].shadowText = self.node.name self.wizard.pages.general.form[1].shadowText = self.node.name
self.wizard.pages.general.form[2].choices = choices self.wizard.pages.general.form[2].choices = self.choices
self.wizard.pages.general.form:setValues(self.node) self.wizard.pages.general.form:setValues(self.node)
self.wizard.pages.general:showInventory(self.node) self.wizard.pages.general:showInventory(self.node)
@@ -491,9 +505,9 @@ function nodeWizard:eventHandler(event)
return true return true
end) end)
Util.clear(context.config.nodes[self.node.name]) Util.clear(context.nodes[self.node.name])
Util.merge(context.config.nodes[self.node.name], self.node) Util.merge(context.nodes[self.node.name], self.node)
context.config.nodes[self.node.name].adapter = adapter context.nodes[self.node.name].adapter = adapter
saveConfig() saveConfig()

View File

@@ -31,6 +31,7 @@ function activityWizardPage:isValidType(node)
return m and m.type == 'monitor' and { return m and m.type == 'monitor' and {
name = 'Activity Monitor', name = 'Activity Monitor',
value = 'activity', value = 'activity',
category = 'display',
help = 'Display storage activity' help = 'Display storage activity'
} }
end end

View File

@@ -30,6 +30,7 @@ function brewingStandView:isValidType(node)
return m and m.type == 'minecraft:brewing_stand'and { return m and m.type == 'minecraft:brewing_stand'and {
name = 'Brewing Stand', name = 'Brewing Stand',
value = 'brewingStand', value = 'brewingStand',
category = 'custom',
help = 'Auto-learning brewing stand', help = 'Auto-learning brewing stand',
} }
end end

View File

@@ -46,6 +46,7 @@ function exportView:isValidType(node)
return m and m.pullItems and { return m and m.pullItems and {
name = 'Generic Inventory', name = 'Generic Inventory',
value = 'machine', value = 'machine',
category = 'machine',
help = 'Chest, furnace... (has an inventory)' help = 'Chest, furnace... (has an inventory)'
} }
end end

View File

@@ -46,6 +46,7 @@ function importView:isValidType(node)
return m and m.pullItems and { return m and m.pullItems and {
name = 'Generic Inventory', name = 'Generic Inventory',
value = 'machine', value = 'machine',
category = 'machine',
help = 'Chest, furnace... (has an inventory)', help = 'Chest, furnace... (has an inventory)',
} }
end end

View File

@@ -26,6 +26,7 @@ function inputChestWizardPage:isValidType(node)
return m and m.pullItems and { return m and m.pullItems and {
name = 'Input Chest', name = 'Input Chest',
value = 'input', value = 'input',
category = 'custom',
help = 'Sends all items to storage', help = 'Sends all items to storage',
} }
end end

View File

@@ -10,7 +10,7 @@ local device = _G.device
local context = Milo:getContext() local context = Milo:getContext()
local itemPage = UI.Page { local page = UI.Page {
titleBar = UI.TitleBar { titleBar = UI.TitleBar {
title = 'Limit Resource', title = 'Limit Resource',
previousPage = true, previousPage = true,
@@ -46,7 +46,7 @@ local itemPage = UI.Page {
x = 2, y = -2, width = 10, x = 2, y = -2, width = 10,
formLabel = 'Machine', formLabel = 'Machine',
event = 'select_machine', event = 'select_machine',
text = 'Configure', text = 'Assign',
}, },
infoButton = UI.Button { infoButton = UI.Button {
x = 2, y = -2, x = 2, y = -2,
@@ -108,7 +108,6 @@ local itemPage = UI.Page {
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 2, ey = -5, y = 2, ey = -5,
disableHeader = true, disableHeader = true,
values = context.config.nodes,
columns = { columns = {
{ heading = 'Name', key = 'displayName'}, { heading = 'Name', key = 'displayName'},
}, },
@@ -142,42 +141,48 @@ local itemPage = UI.Page {
notification = UI.Notification { }, notification = UI.Notification { },
} }
function itemPage:enable(item) function page:enable(item)
self.origItem = item self.origItem = item
self.item = Util.shallowCopy(item) self.item = Util.shallowCopy(item)
self.res = item.resource or { } self.res = item.resource or { }
self.res.displayName = self.item.displayName self.res.displayName = self.item.displayName
self.form:setValues(self.res) self.form:setValues(self.res)
self.titleBar.title = item.displayName or item.name self.titleBar.title = item.displayName or item.name
self.form.machineButton.inactive = not Craft.machineLookup[self.item.key]
local machine = Craft.machineLookup[self.item.key]
self.form.machineButton.inactive = not machine
if machine then
self:filterMachines(machine)
end
UI.Page.enable(self) UI.Page.enable(self)
self:focusFirst() self:focusFirst()
end end
function itemPage.machines.grid:isRowValid(_, value) function page:filterMachines(machine)
local ignores = Util.transpose({ 'storage', 'ignore', 'hidden' }) local t = Util.filter(context.storage.nodes, function(node)
if not ignores[value.mtype] then if node.category == 'machine' then
local node = context.storage.nodes[value.name] return node.adapter and node.adapter.online and node.adapter.pushItems
return node and node.adapter and node.adapter.online and node.adapter.pushItems end
end end)
self.machines.grid:setValues(t)
self.machines.grid:setSelected('name', machine)
end end
function itemPage.machines.grid:getDisplayValues(row) function page.machines.grid:getDisplayValues(row)
row = Util.shallowCopy(row) row = Util.shallowCopy(row)
row.displayName = row.displayName or row.name row.displayName = row.displayName or row.name
return row return row
end end
function itemPage.machines.grid:getRowTextColor(row, selected) function page.machines.grid:getRowTextColor(row, selected)
if row.name == Craft.machineLookup[itemPage.item.key] then if row.name == Craft.machineLookup[page.item.key] then
return colors.yellow return colors.yellow
end
return UI.Grid:getRowTextColor(row, selected)
end end
return UI.Grid:getRowTextColor(row, selected)
end
function itemPage.rsControl:enable() function page.rsControl:enable()
local devices = self.form[2].choices local devices = self.form[2].choices
Util.clear(devices) Util.clear(devices)
for _,dev in pairs(device) do for _,dev in pairs(device) do
@@ -193,7 +198,7 @@ function itemPage.rsControl:enable()
UI.SlideOut.enable(self) UI.SlideOut.enable(self)
end end
function itemPage.rsControl:eventHandler(event) function page.rsControl:eventHandler(event)
if event.type == 'form_cancel' then if event.type == 'form_cancel' then
self:hide() self:hide()
elseif event.type == 'form_complete' then elseif event.type == 'form_complete' then
@@ -204,7 +209,7 @@ function itemPage.rsControl:eventHandler(event)
return true return true
end end
function itemPage:eventHandler(event) function page:eventHandler(event)
if event.type == 'form_cancel' then if event.type == 'form_cancel' then
UI:setPreviousPage() UI:setPreviousPage()
@@ -212,8 +217,6 @@ function itemPage:eventHandler(event)
self.rsControl:show() self.rsControl:show()
elseif event.type == 'select_machine' then elseif event.type == 'select_machine' then
self.machines.grid:update()
self.machines.grid:setIndex(1)
self.machines:show() self.machines:show()
elseif event.type == 'reset' then elseif event.type == 'reset' then
@@ -334,4 +337,4 @@ function itemPage:eventHandler(event)
return true return true
end end
UI:addPage('item', itemPage) UI:addPage('item', page)

View File

@@ -27,6 +27,7 @@ function wizardPage:isValidType(node)
return m and m.type == 'monitor' and { return m and m.type == 'monitor' and {
name = 'Crafting Monitor', name = 'Crafting Monitor',
value = 'jobs', value = 'jobs',
category = 'display',
help = 'Display crafting progress / jobs' help = 'Display crafting progress / jobs'
} }
end end

View File

@@ -6,11 +6,15 @@ local context = Milo:getContext()
local turtle = _G.turtle local turtle = _G.turtle
local learnPage = UI.Dialog { local learnPage = UI.Dialog {
height = 6, width = UI.term.width - 6, height = 9, width = UI.term.width - 6,
title = 'Learn Recipe', title = 'Learn Recipe',
chooser = UI.Chooser { grid = UI.ScrollingGrid {
x = 8, y = 3, x = 2, ex = -2, y = 3, height = 4,
width = 20, disableHeader = true,
columns = {
{ heading = 'Name', key = 'name'},
},
sortColumn = 'name',
}, },
cancel = UI.Button { cancel = UI.Button {
x = 3, y = -2, x = 3, y = -2,
@@ -24,17 +28,15 @@ local learnPage = UI.Dialog {
} }
function learnPage:enable() function learnPage:enable()
self.chooser.choices = { } local t = { }
for k in pairs(context.learnTypes) do for k in pairs(context.learnTypes) do
table.insert(self.chooser.choices, { table.insert(t, {
name = k, name = k,
value = k, value = k,
}) })
end end
self.chooser.value = self.grid:setValues(t)
Milo:getState('learnType') or self.grid:setSelected('name', Milo:getState('learnType') or '')
self.chooser.choices[1].value
Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' })
sync.lock(turtle) sync.lock(turtle)
@@ -53,12 +55,11 @@ function learnPage:eventHandler(event)
Milo:resumeCrafting({ key = 'gridInUse' }) Milo:resumeCrafting({ key = 'gridInUse' })
UI:setPreviousPage() UI:setPreviousPage()
elseif event.type == 'accept' then elseif event.type == 'accept' or event.type == 'grid_select' then
local choice = self.chooser.value local choice = self.grid:getSelected().value
Milo:setState('learnType', choice) Milo:setState('learnType', choice)
UI:setPage(context.learnTypes[choice]) UI:setPage(context.learnTypes[choice])
else else
return UI.Dialog.eventHandler(self, event) return UI.Dialog.eventHandler(self, event)
end end

View File

@@ -11,7 +11,7 @@ local turtle = _G.turtle
local context = Milo:getContext() local context = Milo:getContext()
local machineLearnWizard = UI.Page { local machineLearnWizard = UI.Page {
titleBar = UI.TitleBar { title = 'Learn a crafting recipe' }, titleBar = UI.TitleBar { title = 'Select machine' },
wizard = UI.Wizard { wizard = UI.Wizard {
y = 2, ey = -2, y = 2, ey = -2,
pages = { pages = {
@@ -19,7 +19,6 @@ local machineLearnWizard = UI.Page {
index = 1, index = 1,
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 2, ey = -2, y = 2, ey = -2,
values = context.config.nodes,
columns = { columns = {
{ heading = 'Name', key = 'displayName' }, { heading = 'Name', key = 'displayName' },
}, },
@@ -47,10 +46,6 @@ Example: Slot 1 is the top slot in a furnace.]],
local pages = machineLearnWizard.wizard.pages local pages = machineLearnWizard.wizard.pages
local machine local machine
function pages.machines.grid:isRowValid(_, value)
return value.mtype == 'machine' and value.adapter and value.adapter.online
end
function pages.machines.grid:getDisplayValues(row) function pages.machines.grid:getDisplayValues(row)
row = Util.shallowCopy(row) row = Util.shallowCopy(row)
row.displayName = row.displayName or row.name row.displayName = row.displayName or row.name
@@ -58,7 +53,12 @@ function pages.machines.grid:getDisplayValues(row)
end end
function pages.machines:enable() function pages.machines:enable()
self.grid:update() local t = Util.filter(context.storage.nodes, function(node)
if node.category == 'machine' then
return node.adapter and node.adapter.online and node.adapter.pushItems
end
end)
self.grid:setValues(t)
UI.Window.enable(self) UI.Window.enable(self)
end end
@@ -122,15 +122,12 @@ function pages.confirmation:validate()
Milo:saveMachineRecipe(recipe, result, machine.name) Milo:saveMachineRecipe(recipe, result, machine.name)
local listingPage = UI:getPage('listing')
local displayName = itemDB:getName(result) local displayName = itemDB:getName(result)
listingPage.statusBar.filter:setValue(displayName) UI:setPage('listing', {
listingPage.notification:success('Learned: ' .. displayName) filter = displayName,
listingPage.filter = displayName message = 'Learned: ' .. displayName,
listingPage:refresh() })
listingPage.grid:draw()
return true return true
end end
@@ -141,7 +138,7 @@ function machineLearnWizard:disable()
end end
function machineLearnWizard:eventHandler(event) function machineLearnWizard:eventHandler(event)
if event.type == 'cancel' or event.type == 'accept' then if event.type == 'cancel' then
turtle.emptyInventory() turtle.emptyInventory()
UI:setPage('listing') UI:setPage('listing')
else else

View File

@@ -38,6 +38,7 @@ function wizardPage:isValidType(node)
{ {
name = 'Manipulator', name = 'Manipulator',
value = 'manipulator', value = 'manipulator',
category = 'custom',
help = 'Manipulator w/bound introspection mod' help = 'Manipulator w/bound introspection mod'
} }
end end

View File

@@ -48,6 +48,7 @@ function storageView:isValidType(node)
return m and m.pullItems and { return m and m.pullItems and {
name = 'Storage', name = 'Storage',
value = 'storage', value = 'storage',
category = 'storage',
help = 'Use for item storage', help = 'Use for item storage',
} }
end end
@@ -117,6 +118,7 @@ function lockView:isValidType(node)
return m and m.pullItems and { return m and m.pullItems and {
name = 'Storage', name = 'Storage',
value = 'storage', value = 'storage',
category = 'storage',
help = 'Use for item storage', help = 'Use for item storage',
} }
end end

View File

@@ -58,6 +58,7 @@ function wizardPage:isValidType(node)
return m and m.pullItems and { return m and m.pullItems and {
name = 'Trashcan', name = 'Trashcan',
value = 'trashcan', value = 'trashcan',
category = 'custom',
help = 'An inventory to send unwanted items', help = 'An inventory to send unwanted items',
} }
end end

View File

@@ -133,15 +133,12 @@ function turtleLearnWizard.wizard.pages.confirmation:validate()
local recipe, msg = learnRecipe(self) local recipe, msg = learnRecipe(self)
if recipe then if recipe then
local listingPage = UI:getPage('listing')
local displayName = itemDB:getName(recipe) local displayName = itemDB:getName(recipe)
listingPage.statusBar.filter:setValue(displayName) UI:setPage('listing', {
listingPage.notification:success('Learned: ' .. displayName) filter = displayName,
listingPage.filter = displayName message = 'Learned: ' .. displayName,
listingPage:refresh() })
listingPage.grid:draw()
return true return true
else else
turtleLearnWizard.notification:error(msg) turtleLearnWizard.notification:error(msg)
@@ -149,7 +146,8 @@ function turtleLearnWizard.wizard.pages.confirmation:validate()
end end
function turtleLearnWizard:eventHandler(event) function turtleLearnWizard:eventHandler(event)
if event.type == 'cancel' or event.type == 'accept' then if event.type == 'cancel' then
turtle.emptyInventory()
UI:setPage('listing') UI:setPage('listing')
else else
return UI.Page.eventHandler(self, event) return UI.Page.eventHandler(self, event)