From 95010cfc078aa0ffc96c39a4afcdec9624fcc561 Mon Sep 17 00:00:00 2001 From: Luca_S Date: Sun, 19 Apr 2020 00:45:33 +0200 Subject: [PATCH 01/24] Add listItemsRaw(), listProviders() and defrag() to storage.lua listItemsRaw provides a raw list of the items in all storage chests listProviders provides a list of items and which chests contain them defrag defragments the storage system --- milo/apis/storage.lua | 83 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 83 insertions(+) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index dfd2061..d6c8df1 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -291,6 +291,89 @@ function Storage:listItems(throttle) return self.cache end +-- provide a raw list of all the items in all storage chests +-- it might be beneficial to move this to the adapter class at some point and cache the raw item list in this class at some point +function Storage:listItemsRaw() + local res = {} + + for _, v in pairs(self.nodes) do + if v.category == "storage" then + local chest = device[v.name] + local items = chest.list() + + for slot, item in pairs(items) do + items[slot] = itemDB:get(item, function() return chest.getItemMeta(slot) end) + end + + res[v.name] = items + end + end + + return res +end + +-- provide a list of items and which chests provide them +function Storage:listProviders() + local res = {} + + local rawItems = self:listItemsRaw() + + for chest, items in pairs(rawItems) do + for slot, item in pairs(items) do + local key = table.concat({item.name, item.damage, item.nbtHash}, ":") + if not res[key] then + res[key] = {} + end + table.insert(res[key], {item = item, device = device[chest], slot = slot}) + end + end + return res +end + +-- defrags the storage system +function Storage:defrag() + local items = self:listProviders() + + for _, providers in pairs(items) do + table.sort(providers, function(a, b) + return a.item.count < b.item.count + end) + + -- We're done when we either compressed the stacks so far, that there's only one left (#providers == 1) + -- Or when we've compressed so far, that the there's only one stack which has a lower count than the maxCount + -- Because of the sorting, we know that this will be the stack in providers[1], so we check if providers[2] is at the maxCount + while #providers > 1 and providers[2].item.count ~= providers[2].item.maxCount do + local from = providers[1] + local to + + -- We're pushing to the highest stack which is still below the maxCount, this way as many slots as possible will be filled + -- This loop is guarenteed to assign a value to "to", as the only cases where it wouldn't (#providers == 1 or no provider with less than maxCount) + -- are ruled out by the condition of the outer while loop + for i = 2, #providers do + if providers[i].item.count < providers[i].item.maxCount then + to = providers[i] + else + -- As this slot is already at maxCount, all the remaining ones will also be due to sorting + break + end + end + + local toMove = math.min(to.item.maxCount - to.item.count, from.item.count) + from.device.pushItems(to.device.name, from.slot, toMove, to.slot) + to.item.count = to.item.count + toMove + from.item.count = from.item.count - toMove + + if from.item.count <= 0 then + table.remove(providers, 1) + end + + table.sort(providers, function(a, b) + return a.item.count < b.item.count + end) + end + end +end + function Storage:updateCache(adapter, item, count) if not adapter.cache then adapter.dirty = true -- 2.49.1 From ca0cb0dabbb52dfce9a825437ab72ff31524b17c Mon Sep 17 00:00:00 2001 From: Luca_S Date: Sun, 19 Apr 2020 00:47:25 +0200 Subject: [PATCH 02/24] Add defrag button --- milo/core/listing.lua | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/milo/core/listing.lua b/milo/core/listing.lua index b19b4d2..26933e4 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -33,6 +33,11 @@ local page = UI.Page { event = 'rescan', help = 'Rescan all inventories' }, + { + text = 'Defragment storage', + event = 'defrag', + help = 'Defragments the storage' + } }, }, }, @@ -250,6 +255,9 @@ function page:eventHandler(event) self.grid:draw() self:setFocus(self.statusBar.filter) + elseif event.type == 'defrag' then + context.storage:defrag() + elseif event.type == 'toggle_display' then displayMode = (displayMode + 1) % 2 Util.merge(event.button, displayModes[displayMode]) -- 2.49.1 From cedb807e89dde4424c3a54a9635e5c38f38c1a5a Mon Sep 17 00:00:00 2001 From: Anavrins Date: Sat, 18 Apr 2020 19:06:50 -0400 Subject: [PATCH 03/24] Add throttling to defragger --- milo/apis/storage.lua | 27 +++++++++++++++------------ milo/core/listing.lua | 11 ++++++++++- 2 files changed, 25 insertions(+), 13 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index d6c8df1..c41ca8b 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -293,30 +293,33 @@ end -- provide a raw list of all the items in all storage chests -- it might be beneficial to move this to the adapter class at some point and cache the raw item list in this class at some point -function Storage:listItemsRaw() +function Storage:listItemsRaw(throttle) local res = {} - for _, v in pairs(self.nodes) do - if v.category == "storage" then - local chest = device[v.name] + throttle = throttle or Util.throttle() + + for _, v in pairs(self.nodes) do + if v.category == "storage" then + local chest = device[v.name] local items = chest.list() for slot, item in pairs(items) do items[slot] = itemDB:get(item, function() return chest.getItemMeta(slot) end) end - res[v.name] = items - end - end + res[v.name] = items + throttle() + end + end - return res + return res end -- provide a list of items and which chests provide them -function Storage:listProviders() +function Storage:listProviders(throttle) local res = {} - local rawItems = self:listItemsRaw() + local rawItems = self:listItemsRaw(throttle) for chest, items in pairs(rawItems) do for slot, item in pairs(items) do @@ -331,8 +334,8 @@ function Storage:listProviders() end -- defrags the storage system -function Storage:defrag() - local items = self:listProviders() +function Storage:defrag(throttle) + local items = self:listProviders(throttle) for _, providers in pairs(items) do table.sort(providers, function(a, b) diff --git a/milo/core/listing.lua b/milo/core/listing.lua index 26933e4..afd359c 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -256,7 +256,8 @@ function page:eventHandler(event) self:setFocus(self.statusBar.filter) elseif event.type == 'defrag' then - context.storage:defrag() + self:defrag() + self:refresh(true) elseif event.type == 'toggle_display' then displayMode = (displayMode + 1) % 2 @@ -365,6 +366,14 @@ function page:refresh(force) self.throttle:disable() end +function page:defrag() + local throttle = function() self.throttle:update() end + + self.throttle:enable() + context.storage:defrag(throttle) + self.throttle:disable() +end + function page:applyFilter() local function filterItems(t, filter) self.grid.sortColumn = Milo:getState('sortColumn') or 'count' -- 2.49.1 From 2f187a196fca687b082ecfdc9d8709030f3d02fb Mon Sep 17 00:00:00 2001 From: Luca_S Date: Mon, 20 Apr 2020 22:47:23 +0200 Subject: [PATCH 04/24] defrag() now returns how many slots were saved --- milo/apis/storage.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index c41ca8b..01aaf18 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -336,6 +336,7 @@ end -- defrags the storage system function Storage:defrag(throttle) local items = self:listProviders(throttle) + local slotsSaved = 0 for _, providers in pairs(items) do table.sort(providers, function(a, b) @@ -368,6 +369,7 @@ function Storage:defrag(throttle) if from.item.count <= 0 then table.remove(providers, 1) + slotsSaved = slotsSaved + 1 end table.sort(providers, function(a, b) @@ -375,6 +377,8 @@ function Storage:defrag(throttle) end) end end + + return slotsSaved end function Storage:updateCache(adapter, item, count) -- 2.49.1 From 1d1a770e4b891966eba69e44754fd9e2344e358f Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 20 Apr 2020 18:08:39 -0400 Subject: [PATCH 05/24] Milo-Defrag: Notify how many slots were freed --- milo/core/listing.lua | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/milo/core/listing.lua b/milo/core/listing.lua index afd359c..5d5c796 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -370,8 +370,9 @@ function page:defrag() local throttle = function() self.throttle:update() end self.throttle:enable() - context.storage:defrag(throttle) + local saved = context.storage:defrag(throttle) self.throttle:disable() + self:notifyInfo(("Saved %d slots"):format(saved)) end function page:applyFilter() -- 2.49.1 From 47e0a90116e9f1d9d38487ffae33512aac1aec02 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Tue, 21 Apr 2020 20:56:21 -0400 Subject: [PATCH 06/24] gpsServer: Reduce amount of peripheral calls Redraw screen every seconds instead of every packets received, yeah that was a bad idea. --- gps/gpsServer.lua | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/gps/gpsServer.lua b/gps/gpsServer.lua index 5c60926..58461d6 100644 --- a/gps/gpsServer.lua +++ b/gps/gpsServer.lua @@ -246,8 +246,6 @@ local function server(mode) computers[computerId] = nil page.grid.values = positions page.grid:update() - page.grid:draw() - page.grid:sync() end end @@ -267,22 +265,17 @@ local function server(mode) end) Event.onInterval(1, function() - local resync = false for _, detail in pairs(positions) do if os.clock() - detail.lastChanged > 10 then detail.changed = false - resync = true end if os.clock() - detail.timestamp > 60 and detail.alive then detail.alive = false detail.hbeat = false - resync = true end end - if resync then - page:draw() - page:sync() - end + page:draw() + page:sync() end) end @@ -304,6 +297,7 @@ elseif args[1] == 'snmp' then table.insert(page.grid.columns, { heading = 'Label', key = 'label', textColor = colors.cyan } ) + page.grid.sortColumn = 'label' page.grid:adjustWidth() server('snmp') -- 2.49.1 From 45769697396f2288020aa6f6001bd00dd8966d5f Mon Sep 17 00:00:00 2001 From: kepler155c Date: Tue, 21 Apr 2020 22:40:47 -0600 Subject: [PATCH 07/24] Ui enhancements 2.0 (#29) * canvas overhaul * editor 2.0 * more tweaks * more editor work * completions + refactor * cleanup + editor additions * cleanup + undo overhaul * editor recent/peripherals/redo + cleanup * editor path issues * cleanup * changes for deprecated ui methods - recolor milo - make turtle scripts run again - mob rancher improvements * can now use named colors --- builder/builder.lua | 15 +- ccemux/etc/apps.db | 12 + ccemux/system/ccemux.lua | 8 +- common/Appstore.lua | 121 +-- common/Devices.lua | 74 +- common/DiskCopy.lua | 7 +- common/Events.lua | 127 ++- common/Follow.lua | 3 +- common/SoundPlayer.lua | 2 +- common/Turtles.lua | 324 +++---- common/edit.lua | 1392 ++++++++++++++++++---------- common/etc/apps.db | 15 +- common/etc/scripts/goHome | 2 +- common/etc/scripts/moveTo | 29 - common/etc/scripts/moveTo.lua | 17 + common/etc/scripts/setHome | 2 +- common/etc/scripts/summon | 6 +- common/recorder.lua | 107 ++- examples/.package | 6 - examples/grid.lua | 44 - gps/gpsServer.lua | 2 +- ignore/Music.lua | 2 - ignore/passthrough.lua | 174 ++++ ignore/twm.lua | 62 +- lzwfs/system/lzwfs.lua | 13 +- milo/MiloLocal.lua | 2 +- milo/MiloRemote.lua | 12 +- milo/apis/fuzzyMatch.lua | 19 - milo/apis/massAdapter.lua | 53 -- milo/core/listing.lua | 10 +- milo/core/machines.lua | 17 +- milo/plugins/activityView.lua | 1 - milo/plugins/backupView.lua | 2 - milo/plugins/brewingStandView.lua | 1 - milo/plugins/emitterView.lua | 2 - milo/plugins/inputChestView.lua | 2 - milo/plugins/item.lua | 1 - milo/plugins/item/infoTab.lua | 5 +- milo/plugins/item/machinesTab.lua | 1 - milo/plugins/item/recipeTab.lua | 3 - milo/plugins/item/resetTab.lua | 2 +- milo/plugins/jobMonitor.lua | 1 - milo/plugins/massStorageView.lua | 43 - milo/plugins/remote/setup.lua | 23 +- milo/plugins/speakerView.lua | 1 - milo/plugins/statsView.lua | 7 +- milo/plugins/storageView.lua | 2 - milo/plugins/transferView.lua | 1 - milo/plugins/trashcanView.lua | 1 - miners/scanningMiner.lua | 3 +- monitor/mwm.lua | 5 +- neural/Equipment.lua | 142 +++ neural/Scanner.lua | 4 +- neural/Sensor.lua | 4 +- neural/etc/apps.db | 6 + neural/mobRancher.lua | 116 ++- recipeBook/recipeBook.lua | 15 +- screenSaver/autorun/saver.lua | 2 +- screenSaver/system/saver.lua | 50 +- secure/autorun/lock.lua | 4 +- secure/system/secure.lua | 15 +- swshop/Shoplogs.lua | 2 +- swshop/shopConfig.lua | 4 +- swshop/shopTab.lua | 2 +- turtle/{autorun => init}/6.tl3.lua | 0 65 files changed, 1842 insertions(+), 1310 deletions(-) create mode 100644 ccemux/etc/apps.db delete mode 100644 common/etc/scripts/moveTo create mode 100644 common/etc/scripts/moveTo.lua delete mode 100644 examples/.package delete mode 100644 examples/grid.lua create mode 100644 ignore/passthrough.lua delete mode 100644 milo/apis/fuzzyMatch.lua delete mode 100644 milo/apis/massAdapter.lua delete mode 100644 milo/plugins/massStorageView.lua create mode 100644 neural/Equipment.lua rename turtle/{autorun => init}/6.tl3.lua (100%) diff --git a/builder/builder.lua b/builder/builder.lua index 0c39cb8..c021169 100644 --- a/builder/builder.lua +++ b/builder/builder.lua @@ -236,10 +236,7 @@ function substitutionPage.info:draw() end self:clear() - self:setCursorPos(1, 1) - self:print(' Replace ' .. inName .. '\n') - --self:print(' ' .. sub.id .. ':' .. sub.dmg .. '\n', nil, colors.yellow) - self:print(' With ' .. outName) + self:print(' Replace ' .. inName .. '\n' .. ' With ' .. outName) end function substitutionPage:enable() @@ -536,7 +533,7 @@ local startPage = UI.Page { event = 'setStartLevel', cancelEvent = 'slide_hide', text = UI.Text { - x = 5, y = 1, width = 20, + x = 5, y = 1, width = 10, textColor = colors.gray, }, textEntry = UI.TextEntry { @@ -554,7 +551,7 @@ local startPage = UI.Page { event = 'setStartBlock', cancelEvent = 'slide_hide', text = UI.Text { - x = 2, y = 1, width = 20, + x = 2, y = 1, width = 13, textColor = colors.gray, }, textEntry = UI.TextEntry { @@ -727,8 +724,7 @@ function startPage:eventHandler(event) Builder:begin() elseif event.type == 'quit' then - UI.term:reset() - Event.exitPullEvents() + UI:quit() end return UI.Page.eventHandler(self, event) @@ -772,5 +768,4 @@ UI:setPages({ }) UI:setPage('start') - -UI:pullEvents() +UI:start() diff --git a/ccemux/etc/apps.db b/ccemux/etc/apps.db new file mode 100644 index 0000000..d94fb3d --- /dev/null +++ b/ccemux/etc/apps.db @@ -0,0 +1,12 @@ +{ + [ "87e89abb4c1c551fe08d355d097f18b8de78edca5f556997085681662fce8eed" ] = { + title = "Config", + category = "CCEmuX", + run = "emu config", + }, + [ "cec3a9b89b2e391393d0f68e4bc12a9fa6cf358b3cdf79496dc442d52b8dd528" ] = { + title = "Data", + category = "CCEmuX", + run = "emu data", + }, +} diff --git a/ccemux/system/ccemux.lua b/ccemux/system/ccemux.lua index 0948583..f05df3d 100644 --- a/ccemux/system/ccemux.lua +++ b/ccemux/system/ccemux.lua @@ -9,7 +9,7 @@ local tab = UI.Tab { tabTitle = 'CCEmuX', description = 'CCEmuX peripherals', form = UI.Form { - x = 2, ex = -2, y = 1, ey = 4, + x = 2, ex = -2, y = 2, ey = 5, values = { side = 'bottom', type = 'wireless_modem', @@ -28,7 +28,7 @@ local tab = UI.Tab { }, }, drive_id = UI.TextEntry { - x = 20, y = 3, + x = 19, y = 3, formKey = 'drive_id', shadowText = 'id', width = 5, @@ -36,13 +36,13 @@ local tab = UI.Tab { transform = 'number', }, add = UI.Button { - x = 28, y = 3, + x = -6, y = 3, width = 5, text = 'Add', event = 'form_ok', help = 'Add items to turtle to add to filter', }, }, grid = UI.Grid { - x = 3, ex = -3, y = 6, ey = -2, + x = 2, ex = -2, y = 7, ey = -2, columns = { { heading = 'Side', key = 'side', width = 8 }, { heading = 'Type', key = 'type' }, diff --git a/common/Appstore.lua b/common/Appstore.lua index c489696..7576f5f 100644 --- a/common/Appstore.lua +++ b/common/Appstore.lua @@ -8,10 +8,10 @@ local http = _G.http local multishell = _ENV.multishell local os = _G.os local shell = _ENV.shell +local colors = _G.colors local REGISTRY_DIR = 'usr/.registry' - -- FIX SOMEDAY local function registerApp(app, key) app.key = SHA.compute(key) @@ -27,7 +27,6 @@ local function unregisterApp(key) end end - local sandboxEnv = Util.shallowCopy(_ENV) setmetatable(sandboxEnv, { __index = _G }) @@ -36,21 +35,10 @@ UI:configure('Appstore', ...) local APP_DIR = 'usr/apps' -local sources = { - - { text = "STD Default", - event = 'source', - url = "https://github.com/LDDestroier/STD-GUI/raw/master/list.lua" }, --stock ---[[ - { text = "Discover", - event = 'source', - generateName = true, - url = "http://pastebin.com/raw/9bXfCz6M" }, --owned by dannysmc95 - - { text = "Opus", - event = 'source', - url = "http://pastebin.com/raw/ajQ91Rmn" }, -]] +local source = { + text = "STD Default", + event = 'source', + url = "https://github.com/LDDestroier/STD-GUI/raw/master/list.lua", } shell.setDir(APP_DIR) @@ -87,14 +75,14 @@ local function runApp(app, checkExists, ...) error('Failed to download') end - local fn = loadstring(program, app.name) + fn = _G.loadstring(program, app.name) if not fn then error('Failed to download') end - setfenv(fn, sandboxEnv) - fn(unpack(args)) + _G.setfenv(fn, sandboxEnv) + fn(table.unpack(args)) end end @@ -134,16 +122,16 @@ local viewApp = function(app) return true end -local getSourceListing = function(source) +local getSourceListing = function() local contents = http.get(source.url) if contents then - local fn = loadstring(contents.readAll(), source.text) + local fn = _G.loadstring(contents.readAll(), source.text) contents.close() local env = { std = { } } setmetatable(env, { __index = _G }) - setfenv(fn, env) + _G.setfenv(fn, env) fn() if env.contextualGet then @@ -172,9 +160,28 @@ local getSourceListing = function(source) end end +getSourceListing() + +if not source.storeURLs then + error('Unable to download application list') +end + +local buttons = { } +for k,v in Util.spairs(source.storeCatagoryNames, + function(a, b) return a:lower() < b:lower() end) do + + if v ~= 'Operating System' then + table.insert(buttons, { + text = v, + event = 'category', + index = k, + }) + end +end +source.index, source.name = Util.first(source.storeCatagoryNames) + local appPage = UI.Page { menuBar = UI.MenuBar { --- showBackButton = not pocket, buttons = { { text = '\027', event = 'back' }, { text = 'Install', event = 'install' }, @@ -204,16 +211,14 @@ function appPage.container.viewport:draw() Ansi.yellow .. app.description .. Ansi.reset) self:clear() - self:setCursorPos(1, 1) self:print(str) - self.ymax = self.cursorY if appPage.notification.enabled then appPage.notification:draw() end end -function appPage:enable(source, app) +function appPage:enable(app) self.source = source self.app = app UI.Page.enable(self) @@ -293,8 +298,7 @@ end local categoryPage = UI.Page { menuBar = UI.MenuBar { buttons = { - { text = 'Catalog', dropdown = sources }, - { text = 'Category', name = 'categoryButton', dropdown = { } }, + { text = 'Category', name = 'categoryButton', dropdown = buttons }, }, }, grid = UI.ScrollingGrid { @@ -309,62 +313,21 @@ local categoryPage = UI.Page { l = 'lua', [ 'control-q' ] = 'quit', }, + source = source, } -function categoryPage:setCategory(source, name, index) +function categoryPage:setCategory(name, index) self.grid.values = { } for _,v in pairs(source.storeURLs) do if index == 0 or index == v.catagory then table.insert(self.grid.values, v) end end - self.statusBar:setStatus(string.format('%s: %s', source.text, name)) + self.statusBar:setStatus(string.format('%s: %s', self.source.text or '', name)) self.grid:update() self.grid:setIndex(1) end -function categoryPage:setSource(source) - - if not source.categoryMenu then - - self.statusBar:setStatus('Loading...') - self.statusBar:draw() - self:sync() - - getSourceListing(source) - - if not source.storeURLs then - error('Unable to download application list') - end - - local buttons = { } - for k,v in Util.spairs(source.storeCatagoryNames, - function(a, b) return a:lower() < b:lower() end) do - - if v ~= 'Operating System' then - table.insert(buttons, { - text = v, - event = 'category', - index = k, - }) - end - end - - source.categoryMenu = UI.DropMenu({ - buttons = buttons, - }) - source.index, source.name = Util.first(source.storeCatagoryNames) - - categoryPage.menuBar.categoryButton:add({ - categoryMenu = source.categoryMenu - }) - end - - self.source = source - self.menuBar.categoryButton.dropmenu = source.categoryMenu - categoryPage:setCategory(source, source.name, source.index) -end - function categoryPage.grid:sortCompare(a, b) return a.ltitle < b.ltitle end @@ -377,12 +340,11 @@ function categoryPage.grid:getRowTextColor(row, selected) end function categoryPage:eventHandler(event) - if event.type == 'grid_select' or event.type == 'select' then - UI:setPage(appPage, self.source, self.grid:getSelected()) + UI:setPage(appPage, self.grid:getSelected()) elseif event.type == 'category' then - self:setCategory(self.source, event.button.text, event.button.index) + self:setCategory(event.button.text, event.button.index) self:setFocus(self.grid) self:draw() @@ -392,7 +354,7 @@ function categoryPage:eventHandler(event) self:draw() elseif event.type == 'quit' then - UI:exitPullEvents() + UI:quit() else return UI.Page.eventHandler(self, event) @@ -401,8 +363,7 @@ function categoryPage:eventHandler(event) end print("Retrieving catalog list") -categoryPage:setSource(sources[1]) +categoryPage:setCategory(source.name, source.index) UI:setPage(categoryPage) -UI:pullEvents() -UI.term:reset() +UI:start() diff --git a/common/Devices.lua b/common/Devices.lua index 6ad2f08..fc2cecd 100644 --- a/common/Devices.lua +++ b/common/Devices.lua @@ -3,7 +3,6 @@ local Event = require('opus.event') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local peripheral = _G.peripheral --[[ -- PeripheralsPage -- ]] -- @@ -16,6 +15,20 @@ local peripheralsPage = UI.Page { }, sortColumn = 'type', autospace = true, + enable = function(self) + local sides = peripheral.getNames() + + Util.clear(self.values) + for _,side in pairs(sides) do + table.insert(self.values, { + type = peripheral.getType(side), + side = side + }) + end + self:update() + self:adjustWidth() + UI.Grid.enable(self) + end, }, statusBar = UI.StatusBar { values = 'Select peripheral', @@ -23,52 +36,35 @@ local peripheralsPage = UI.Page { accelerators = { [ 'control-q' ] = 'quit', }, + updatePeripherals = function(self) + if UI:getCurrentPage() == self then + self.grid:draw() + self:sync() + end + end, + eventHandler = function(self, event) + if event.type == 'quit' then + UI:quit() + + elseif event.type == 'grid_select' then + UI:setPage('methods', event.selected) + + end + return UI.Page.eventHandler(self, event) + end, } -function peripheralsPage.grid:enable() - local sides = peripheral.getNames() - - Util.clear(self.values) - for _,side in pairs(sides) do - table.insert(self.values, { - type = peripheral.getType(side), - side = side - }) - end - self:update() - self:adjustWidth() - UI.Grid.enable(self) -end - -function peripheralsPage:updatePeripherals() - if UI:getCurrentPage() == self then - self.grid:draw() - self:sync() - end -end - -function peripheralsPage:eventHandler(event) - if event.type == 'quit' then - Event.exitPullEvents() - - elseif event.type == 'grid_select' then - UI:setPage('methods', event.selected) - - end - return UI.Page.eventHandler(self, event) -end - --[[ -- MethodsPage -- ]] -- local methodsPage = UI.Page { - backgroundColor = colors.black, doc = UI.TextArea { - backgroundColor = colors.black, - x = 2, y = 2, ex = -1, ey = -7, + backgroundColor = 'black', + ey = -7, + marginLeft = 1, marginTop = 1, }, grid = UI.ScrollingGrid { y = -6, ey = -2, columns = { - { heading = 'Name', key = 'name', width = UI.term.width } + { heading = 'Name', key = 'name' } }, sortColumn = 'name', }, @@ -198,4 +194,4 @@ UI:setPages({ methods = methodsPage, }) -UI:pullEvents() +UI:start() diff --git a/common/DiskCopy.lua b/common/DiskCopy.lua index 849dbe3..b3efc4e 100644 --- a/common/DiskCopy.lua +++ b/common/DiskCopy.lua @@ -122,7 +122,6 @@ function page:drawInfo(drive, textArea) return isValid(drive) and fs.getFreeSpace(drive.getMountPath()) or 0 end - textArea:setCursorPos(1, 1) textArea:print(string.format('Drive: %s%s%s\nLabel: %s%s%s\nUsed: %s%s%s\nFree: %s%s%s', Ansi.yellow, drive.name, Ansi.reset, isValid(drive) and Ansi.yellow or Ansi.orange, getLabel():sub(1, 10), Ansi.reset, @@ -138,6 +137,7 @@ function page:scan() self.copyButton.inactive = not valid self:draw() + self.progress:clear() self.progress:centeredWrite(1, 'Analyzing Disks..') self.progress:sync() @@ -167,6 +167,7 @@ function page:copy() throttle() end + self.progress:clear() self.progress:centeredWrite(1, 'Computing..') self.progress:sync() @@ -211,6 +212,8 @@ function page:copy() self.progress:clear() rawCopy(sdrive.getMountPath(), tdrive.getMountPath()) cleanup() + + self.progress:clear() self.progress:centeredWrite(1, 'Copy Complete', colors.lime, colors.black) self.progress:sync() @@ -270,4 +273,4 @@ Event.onTimeout(.2, function() end) UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/common/Events.lua b/common/Events.lua index 6a0dd2c..783e38c 100644 --- a/common/Events.lua +++ b/common/Events.lua @@ -27,6 +27,26 @@ local page = UI.Page { }, autospace = true, disableHeader = true, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + + local function tovalue(s) + if type(s) == 'table' then + return 'table' + end + return s + end + + for k,v in pairs(row) do + row[k] = tovalue(v) + end + + return row + end, + draw = function(self) + self:adjustWidth() + UI.Grid.draw(self) + end, }, accelerators = { f = 'filter', @@ -36,81 +56,48 @@ local page = UI.Page { [ 'control-q' ] = 'quit', }, filtered = { }, -} + eventHandler = function(self, event) + if event.type == 'filter' then + local entry = self.grid:getSelected() + self.filtered[entry.event] = true -function page:eventHandler(event) + elseif event.type == 'toggle' then + self.paused = not self.paused + if self.paused then + self.menuBar.pauseButton.text = 'Resume' + else + self.menuBar.pauseButton.text = 'Pause ' + end + self.menuBar:draw() - if event.type == 'filter' then - local entry = self.grid:getSelected() - self.filtered[entry.event] = true + elseif event.type == 'grid_select' then + multishell.openTab({ + path = 'sys/apps/Lua.lua', + args = { event.selected }, + focused = true, + }) - elseif event.type == 'toggle' then - self.paused = not self.paused - if self.paused then - self.menuBar.pauseButton.text = 'Resume' - else - self.menuBar.pauseButton.text = 'Pause ' - end - self.menuBar:draw() - - elseif event.type == 'grid_select' then - multishell.openTab({ - path = 'sys/apps/Lua.lua', - args = { event.selected }, - focused = true, - }) - - elseif event.type == 'reset' then - self.filtered = { } - self.grid:setValues({ }) - self.grid:draw() - if self.paused then - self:emit({ type = 'toggle' }) - end - - elseif event.type == 'clear' then - self.grid:setValues({ }) - self.grid:draw() - - elseif event.type == 'quit' then - UI:exitPullEvents() - - --[[ - elseif event.type == 'focus_change' then - if event.focused == self.grid then - if not self.paused then + elseif event.type == 'reset' then + self.filtered = { } + self.grid:setValues({ }) + self.grid:draw() + if self.paused then self:emit({ type = 'toggle' }) end + + elseif event.type == 'clear' then + self.grid:setValues({ }) + self.grid:draw() + + elseif event.type == 'quit' then + UI:quit() + + else + return UI.Page.eventHandler(self, event) end - --]] - - else - return UI.Page.eventHandler(self, event) - end - return true -end - -function page.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - - local function tovalue(s) - if type(s) == 'table' then - return 'table' - end - return s - end - - for k,v in pairs(row) do - row[k] = tovalue(v) - end - - return row -end - -function page.grid:draw() - self:adjustWidth() - UI.Grid.draw(self) -end + return true + end, +} local updated = false local timerId = os.startTimer(1) @@ -150,6 +137,6 @@ end kernel.hook('*', hookFunction) UI:setPage(page) -UI:pullEvents() +UI:start() kernel.unhook('*', hookFunction) diff --git a/common/Follow.lua b/common/Follow.lua index 3ad0007..6c2e84d 100644 --- a/common/Follow.lua +++ b/common/Follow.lua @@ -42,7 +42,6 @@ local page = UI.Page { }, range = UI.SlideOut { y = -7, height = 7, - backgroundColor = colors.cyan, titleBar = UI.TitleBar { event = 'cancel', title = 'Enter range', @@ -239,6 +238,6 @@ Event.addRoutine(function() end) UI:setPage(page) -UI:pullEvents() +UI:start() swarm:stop() diff --git a/common/SoundPlayer.lua b/common/SoundPlayer.lua index bcce449..bb36029 100644 --- a/common/SoundPlayer.lua +++ b/common/SoundPlayer.lua @@ -60,4 +60,4 @@ function page:eventHandler(event) end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/common/Turtles.lua b/common/Turtles.lua index f27a004..cc48825 100644 --- a/common/Turtles.lua +++ b/common/Turtles.lua @@ -2,7 +2,6 @@ local Config = require('opus.config') local Event = require('opus.event') local itemDB = require('core.itemDB') local Socket = require('opus.socket') -local Terminal = require('opus.terminal') local UI = require('opus.ui') local Util = require('opus.util') @@ -11,10 +10,7 @@ local fs = _G.fs local multishell = _ENV.multishell local network = _G.network local os = _G.os -local shell = _ENV.shell -local term = _G.term ---UI.Button.defaults.focusIndicator = ' ' UI:configure('Turtles', ...) local config = { } @@ -31,13 +27,26 @@ local options = { local SCRIPTS_PATH = 'packages/common/etc/scripts' -local nullTerm = Terminal.getNullTerm(term.current()) -local socket +local socket, turtle, page -local page = UI.Page { +page = UI.Page { coords = UI.Window { backgroundColor = colors.black, height = 3, + marginTop = 1, marginLeft = 1, + draw = function(self) + local t = turtle + self:clear() + if t then + self:setCursorPos(2, 2) + local ind = 'GPS' + if not t.point.gps then + ind = 'REL' + end + self:print(string.format('%s : %d,%d,%d', + ind, t.point.x, t.point.y, t.point.z)) + end + end, }, tabs = UI.Tabs { x = 1, y = 4, ey = -2, @@ -50,6 +59,23 @@ local page = UI.Page { disableHeader = true, sortColumn = 'label', autospace = true, + draw = function(self) + Util.clear(self.values) + local files = fs.list(SCRIPTS_PATH) + for _,path in pairs(files) do + table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) + end + self:update() + UI.ScrollingGrid.draw(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + page:runScript(event.selected.label) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true + end, }, turtles = UI.ScrollingGrid { tabTitle = 'Select', @@ -63,6 +89,41 @@ local page = UI.Page { disableHeader = true, sortColumn = 'label', autospace = true, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + if row.fuel then + row.fuel = Util.toBytes(row.fuel) + end + if row.distance then + row.distance = Util.round(row.distance, 1) + end + return row + end, + draw = function(self) + Util.clear(self.values) + for _,v in pairs(network) do + if v.fuel then + table.insert(self.values, v) + end + end + self:update() + UI.ScrollingGrid.draw(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + turtle = event.selected + config.id = event.selected.id + Config.update('Turtles', config) + multishell.setTitle(multishell.getCurrent(), turtle.label) + if socket then + socket:close() + socket = nil + end + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true + end, }, inventory = UI.ScrollingGrid { backgroundColor = colors.cyan, @@ -70,10 +131,58 @@ local page = UI.Page { columns = { { heading = '', key = 'index', width = 2 }, { heading = '', key = 'count', width = 2 }, - { heading = 'Inventory', key = 'key', width = UI.term.width - 7 }, + { heading = 'Inventory', key = 'key' }, }, disableHeader = true, sortColumn = 'index', + getRowTextColor = function(self, row, selected) + if turtle and row.selected then + return colors.yellow + end + return UI.ScrollingGrid.getRowTextColor(self, row, selected) + end, + draw = function(self) + local t = turtle + Util.clear(self.values) + if t then + for k,v in pairs(t.inv or { }) do -- new method (less data) + local index, count = k:match('(%d+),(%d+)') + v = { + index = tonumber(index), + key = v, + count = tonumber(count), + } + table.insert(self.values, v) + end + + for _,v in pairs(t.inventory or { }) do + if v.count > 0 then + table.insert(self.values, v) + end + end + + for _,v in pairs(self.values) do + if v.index == t.slotIndex then + v.selected = true + end + if v.key then + v.key = itemDB:getName(v.key) + end + end + end + self:adjustWidth() + self:update() + UI.ScrollingGrid.draw(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + local fn = string.format('turtle.select(%d)', event.selected.index) + page:runFunction(fn) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true + end, }, --[[ policy = UI.ScrollingGrid { @@ -134,6 +243,15 @@ local page = UI.Page { { key = 'distance', width = 6 }, { key = 'fuel', width = 6 }, }, + draw = function(self) + local t = turtle + if t then + self.values.status = t.status + self.values.distance = t.distance and Util.round(t.distance, 2) + self.values.fuel = Util.toBytes(t.fuel) + end + UI.StatusBar.draw(self) + end, }, notification = UI.Notification(), accelerators = { @@ -141,15 +259,10 @@ local page = UI.Page { }, } -function page:enable(turtle) - self.turtle = turtle - UI.Page.enable(self) -end - function page:runFunction(script, nowrap) for _ = 1, 2 do if not socket then - socket = Socket.connect(self.turtle.id, 161) + socket = Socket.connect(turtle.id, 161) end if socket then @@ -170,149 +283,52 @@ function page:runFunction(script, nowrap) end function page:runScript(scriptName) - if self.turtle then + if turtle then self.notification:info('Connecting') self:sync() - local cmd = string.format('Script %d %s', self.turtle.id, scriptName) - local ot = term.redirect(nullTerm) - pcall(function() shell.run(cmd) end) - term.redirect(ot) - self.notification:success('Sent') - end -end - -function page.coords:draw() - local t = self.parent.turtle - self:clear() - if t then - self:setCursorPos(2, 2) - local ind = 'GPS' - if not t.point.gps then - ind = 'REL' + local script = Util.readFile(fs.combine(SCRIPTS_PATH, scriptName)) + if not script then + print('Unable to read script file') end - self:print(string.format('%s : %d,%d,%d', - ind, t.point.x, t.point.y, t.point.z)) - end -end ---[[ Inventory Tab ]]-- -function page.tabs.inventory:getRowTextColor(row, selected) - if page.turtle and row.selected then - return colors.yellow - end - return UI.ScrollingGrid.getRowTextColor(self, row, selected) -end - -function page.tabs.inventory:draw() - local t = page.turtle - Util.clear(self.values) - if t then - for k,v in pairs(t.inv or { }) do -- new method (less data) - local index, count = k:match('(%d+),(%d+)') - v = { - index = tonumber(index), - key = v, - count = tonumber(count), + local function processVariables() + local variables = { + COMPUTER_ID = os.getComputerID, + GPS = function() + local pt = require('opus.gps').getPoint() + if not pt then + error('Unable to determine location') + end + return _G.textutils.serialize(pt) + end, } - table.insert(self.values, v) - end - - for _,v in pairs(t.inventory or { }) do - if v.count > 0 then - table.insert(self.values, v) + for k,v in pairs(variables) do + local token = string.format('{%s}', k) + if script:find(token, 1, true) then + local s, m = pcall(v) + if not s then + self.notification:error(m) + return + end + script = script:gsub(token, m) + end end + return true end - for _,v in pairs(self.values) do - if v.index == t.slotIndex then - v.selected = true + if processVariables(script) then + local socket = Socket.connect(turtle.id, 161) + if not socket then + self.notification:error('Unable to connect') + return end - if v.key then - v.key = itemDB:getName(v.key) - end - end - end - self:adjustWidth() - self:update() - UI.ScrollingGrid.draw(self) -end - -function page.tabs.inventory:eventHandler(event) - if event.type == 'grid_select' then - local fn = string.format('turtle.select(%d)', event.selected.index) - page:runFunction(fn) - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true -end - -function page.tabs.scripts:draw() - Util.clear(self.values) - local files = fs.list(SCRIPTS_PATH) - for _,path in pairs(files) do - table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) - end - self:update() - UI.ScrollingGrid.draw(self) -end - -function page.tabs.scripts:eventHandler(event) - if event.type == 'grid_select' then - page:runScript(event.selected.label) - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true -end - -function page.tabs.turtles:getDisplayValues(row) - row = Util.shallowCopy(row) - if row.fuel then - row.fuel = Util.toBytes(row.fuel) - end - if row.distance then - row.distance = Util.round(row.distance, 1) - end - return row -end - -function page.tabs.turtles:draw() - Util.clear(self.values) - for _,v in pairs(network) do - if v.fuel then - table.insert(self.values, v) - end - end - self:update() - UI.ScrollingGrid.draw(self) -end - -function page.tabs.turtles:eventHandler(event) - if event.type == 'grid_select' then - page.turtle = event.selected - config.id = event.selected.id - Config.update('Turtles', config) - multishell.setTitle(multishell.getCurrent(), page.turtle.label) - if socket then + socket:write({ type = 'script', args = script }) socket:close() - socket = nil - end - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true -end -function page.statusBar:draw() - local t = self.parent.turtle - if t then - self.values.status = t.status - self.values.distance = t.distance and Util.round(t.distance, 2) - self.values.fuel = Util.toBytes(t.fuel) + self.notification:success('Sent') + end end - UI.StatusBar.draw(self) end function page:showBlocks() @@ -335,7 +351,7 @@ end function page:eventHandler(event) if event.type == 'quit' then - UI:exitPullEvents() + UI:quit() elseif event.type == 'tab_select' then config.tab = event.button.text @@ -354,19 +370,14 @@ function page:eventHandler(event) return true end -function page:enable() - UI.Page.enable(self) --- self.tabs:activateTab(page.tabs.turtles) -end - if not Util.getOptions(options, { ... }, true) then return end if options.turtle.value >= 0 then for _ = 1, 10 do - page.turtle = _G.network[options.turtle.value] - if page.turtle then + turtle = _G.network[options.turtle.value] + if turtle then break end os.sleep(1) @@ -374,9 +385,9 @@ if options.turtle.value >= 0 then end Event.onInterval(1, function() - if page.turtle then - local t = _G.network[page.turtle.id] - page.turtle = t + if turtle then + --local t = _G.network[turtle.id] + --turtle = t page:draw() page:sync() end @@ -387,5 +398,4 @@ if config.tab then end UI:setPage(page) - -UI:pullEvents() +UI:start() diff --git a/common/edit.lua b/common/edit.lua index f74bdb6..804d40c 100644 --- a/common/edit.lua +++ b/common/edit.lua @@ -1,6 +1,10 @@ -local input = require('opus.input') +local Array = require('opus.array') +local Config = require('opus.config') +local fuzzy = require('opus.fuzzy') +local UI = require('opus.ui') +local Util = require('opus.util') -local colors = _G.colors +local device = _G.device local fs = _G.fs local multishell = _ENV.multishell local os = _G.os @@ -8,27 +12,15 @@ local shell = _ENV.shell local term = _G.term local textutils = _G.textutils -shell.setCompletionFunction(shell.getRunningProgram(), function(_, index, text) - if index == 1 then - return fs.complete(text, shell.dir(), true, false) - end -end) +local _format = string.format +local _rep = string.rep +local _sub = string.sub +local _concat = table.concat +local _insert = table.insert +local _remove = table.remove +local _unpack = table.unpack -local tArgs = { ... } -if #tArgs == 0 then - error( "Usage: edit " ) -end - --- Error checking -local sPath = shell.resolve(tArgs[1]) -local bReadOnly = fs.isReadOnly(sPath) -if fs.exists(sPath) and fs.isDir(sPath) then - error( "Cannot edit a directory." ) -end - -if multishell then - multishell.setTitle(multishell.getCurrent(), fs.getName(sPath)) -end +local config = Config.load('editor') local x, y = 1, 1 local w, h = term.getSize() @@ -36,39 +28,35 @@ local scrollX = 0 local scrollY = 0 local lastPos = { x = 1, y = 1 } local tLines = { } -local bRunning = true -local sStatus = "" -local isError local fileInfo -local lastAction - +local actions +local lastSave local dirty = { y = 1, ey = h } local mark = { } local searchPattern -local undo = { chain = { }, pointer = 0 } -local complete = { } +local undo = { chain = { }, redo = { } } +h = h - 1 + +local bgColor = 'gray' local color = { - textColor = '0', - keywordColor = '4', - commentColor = 'd', - stringColor = 'e', - bgColor = colors.black, - highlightColor = colors.orange, - cursorColor = colors.lime, - errorBackground = colors.red, + text = '0', + keyword = '2', + comment = 'd', + string = '1', + mark = '8', + bg = '7', } if not term.isColor() then + bgColor = 'black' color = { - textColor = '0', - keywordColor = '8', - commentColor = '8', - stringColor = '8', - bgColor = colors.black, - highlightColor = colors.lightGray, - cursorColor = colors.white, - errorBackground = colors.gray, + text = '0', + keyword = '8', + comment = '8', + string = '8', + mark = '7', + bg = 'f', } end @@ -78,10 +66,9 @@ local keyMapping = { down = 'down', left = 'left', right = 'right', - pageUp = 'pageUp', - [ 'control-b' ] = 'pageUp', - pageDown = 'pageDown', --- [ 'control-f' ] = 'pageDown', + pageUp = 'page_up', + [ 'control-b' ] = 'page_up', + pageDown = 'page_down', home = 'home', [ 'end' ] = 'toend', [ 'control-home' ] = 'top', @@ -93,7 +80,7 @@ local keyMapping = { [ 'scroll_down' ] = 'scroll_down', [ 'control-down' ] = 'scroll_down', [ 'mouse_click' ] = 'go_to', - [ 'control-l' ] = 'goto_line', + [ 'control-g' ] = 'goto_line', -- marking [ 'shift-up' ] = 'mark_up', @@ -108,6 +95,8 @@ local keyMapping = { [ 'shift-end' ] = 'mark_end', [ 'shift-home' ] = 'mark_home', [ 'mouse_down' ] = 'mark_anchor', + [ 'mouse_doubleclick' ] = 'mark_current_word', + [ 'mouse_tripleclick' ] = 'mark_line', -- editing delete = 'delete', @@ -117,17 +106,21 @@ local keyMapping = { paste = 'paste', tab = 'tab', [ 'control-z' ] = 'undo', + [ 'control-Z' ] = 'redo', [ 'control-space' ] = 'autocomplete', + [ 'control-shift-space' ] = 'peripheral', -- copy/paste [ 'control-x' ] = 'cut', [ 'control-c' ] = 'copy', --- [ 'control-shift-paste' ] = 'paste_internal', + [ 'control-y' ] = 'paste_internal', -- file [ 'control-s' ] = 'save', + [ 'control-S' ] = 'save_as', [ 'control-q' ] = 'exit', [ 'control-enter' ] = 'run', + [ 'control-p' ] = 'quick_open', -- search [ 'control-f' ] = 'find_prompt', @@ -135,264 +128,613 @@ local keyMapping = { [ 'control-n' ] = 'find_next', -- misc - [ 'control-g' ] = 'status', + [ 'control-i' ] = 'status', [ 'control-r' ] = 'refresh', - [ 'control' ] = 'menu', } -local messages = { - menu = '^s: save, ^q: quit, ^enter: run', - wrapped = 'search hit BOTTOM, continuing at TOP', +local page = UI.Page { + menuBar = UI.MenuBar { + transitionHint = 'slideLeft', + buttons = { + { text = 'File', dropdown = { + { text = 'New ', event = 'menu_action', action = 'file_new' }, + { text = 'Open... ', event = 'menu_action', action = 'file_open' }, + { text = 'Quick Open... ^p', event = 'menu_action', action = 'quick_open' }, + { text = 'Recent... ', event = 'menu_action', action = 'recent' }, + { spacer = true }, + { text = 'Save ^s', event = 'menu_action', action = 'save' }, + { text = 'Save As... ^S', event = 'menu_action', action = 'save_as' }, + { spacer = true }, + { text = 'Quit ^q', event = 'menu_action', action = 'exit' }, + } }, + { text = 'Edit', dropdown = { + { text = 'Cut ^x', event = 'menu_action', action = 'cut' }, + { text = 'Copy ^c', event = 'menu_action', action = 'copy' }, + { text = 'Paste ^y,^V', event = 'menu_action', action = 'paste_internal' }, + { spacer = true }, + { text = 'Find... ^f', event = 'menu_action', action = 'find_prompt' }, + { text = 'Find Next ^n', event = 'menu_action', action = 'find_next' }, + { spacer = true }, + { text = 'Go to line... ^g', event = 'menu_action', action = 'goto_line' }, + { text = 'Mark all ^a', event = 'menu_action', action = 'mark_all' }, + } }, + { text = 'Code', dropdown = { + { text = 'Complete ^space', event = 'menu_action', action = 'autocomplete' }, + { text = 'Run ^enter', event = 'menu_action', action = 'run' }, + { spacer = true }, + { text = 'Peripheral ^SPACE', event = 'menu_action', action = 'peripheral' }, + } }, + }, + status = UI.Text { + textColor = 'gray', + x = -9, width = 9, + align = 'right', + }, + }, + gotoLine = UI.MiniSlideOut { + x = -15, y = -2, + label = 'Line', + lineNo = UI.TextEntry { + x = 7, width = 7, + limit = 5, + transform = 'number', + accelerators = { + [ 'enter' ] = 'accept', + }, + }, + show = function(self) + self.lineNo:reset() + UI.MiniSlideOut.show(self) + end, + eventHandler = function(self, event) + if event.type == 'accept' then + if self.lineNo.value then + actions.process('go_to', 1, self.lineNo.value) + end + self:hide() + return true + end + return UI.MiniSlideOut.eventHandler(self, event) + end, + }, + search = UI.MiniSlideOut { + x = '50%', y = -2, + label = 'Find', + search = UI.TextEntry { + x = 7, ex = -3, + limit = 512, + accelerators = { + [ 'enter' ] = 'accept', + }, + }, + show = function(self) + self.search:markAll() + UI.MiniSlideOut.show(self) + end, + eventHandler = function(self, event) + if event.type == 'accept' then + local text = self.search.value + if text and #text > 0 then + searchPattern = text:lower() + if searchPattern then + actions.unmark() + actions.process('find', searchPattern, x) + end + end + self:hide() + return true + end + return UI.MiniSlideOut.eventHandler(self, event) + end, + }, + save_as = UI.MiniSlideOut { + x = '30%', y = -2, + label = 'Save', + filename = UI.TextEntry { + x = 7, ex = -3, + limit = 512, + accelerators = { + [ 'enter' ] = 'accept', + }, + }, + show = function(self) + self.filename:setValue(fileInfo.path) + self.filename:setPosition(#self.filename.value) + UI.MiniSlideOut.show(self) + end, + eventHandler = function(self, event) + if event.type == 'accept' then + local text = self.filename.value + if text and #text > 0 then + actions.save('/' .. text) + end + self:hide() + return true + end + return UI.MiniSlideOut.eventHandler(self, event) + end, + }, + unsaved = UI.Question { + x = -25, y = -2, + label = 'Save', + cancel = UI.Button { + x = 16, + text = 'Cancel', + backgroundColor = 'primary', + event = 'question_cancel', + }, + show = function(self, action) + self.action = action + UI.MiniSlideOut.show(self) + end, + eventHandler = function(self, event) + if event.type == 'question_yes' then + if actions.save() then + self:hide() + actions.process(self.action) + end + elseif event.type == 'question_no' then + actions.process(self.action, true) + self:hide() + elseif event.type == 'question_cancel' then + self:hide() + end + return UI.MiniSlideOut.eventHandler(self, event) + end, + }, + file_open = UI.FileSelect { + modal = true, + enable = function() end, + show = function(self) + UI.FileSelect.enable(self, fs.getDir(fileInfo.path)) + self:focusFirst() + self:draw() + self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 }) + end, + eventHandler = function(self, event) + if event.type == 'select_cancel' then + self:disable() + elseif event.type == 'select_file' then + self:disable() + actions.process('open', event.file) + end + return UI.FileSelect.eventHandler(self, event) + end, + }, + recent = UI.SlideOut { + grid = UI.Grid { + x = 2, y = 2, ey = -4, ex = -2, + columns = { + { key = 'name', heading = 'Name' }, + { key = 'dir', heading = 'Directory' }, + }, + accelerators = { + backspace = 'slide_hide', + }, + }, + cancel = UI.Button { + x = -9, y = -2, + text = 'Cancel', + event = 'slide_hide', + }, + show = function(self) + local t = { } + for _,v in pairs(config.recent or { }) do + table.insert(t, { name = fs.getName(v), dir = fs.getDir(v), path = v }) + end + self.grid:setValues(t) + self.grid:setIndex(1) + UI.SlideOut.show(self) + self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 }) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + actions.process('open', event.selected.path) + self:hide() + return true + end + return UI.SlideOut.eventHandler(self, event) + end, + }, + quick_open = UI.SlideOut { + filter_entry = UI.TextEntry { + x = 2, y = 2, ex = -2, + limit = 256, + shadowText = 'File name', + accelerators = { + [ 'enter' ] = 'accept', + [ 'up' ] = 'grid_up', + [ 'down' ] = 'grid_down', + }, + }, + grid = UI.ScrollingGrid { + x = 2, y = 3, ex = -2, ey = -4, + disableHeader = true, + columns = { + { key = 'name' }, + { key = 'dir', textColor = 'lightGray' }, + }, + accelerators = { + grid_select = 'accept', + }, + }, + cancel = UI.Button { + x = -9, y = -2, + text = 'Cancel', + event = 'slide_hide', + }, + apply_filter = function(self, filter) + local t = { } + if filter then + filter = filter:lower() + self.grid.sortColumn = 'score' + self.grid.inverseSort = true + + for _,v in pairs(self.listing) do + v.score = fuzzy(v.lname, filter) + if v.score then + _insert(t, v) + end + end + else + self.grid.sortColumn = 'lname' + self.grid.inverseSort = false + t = self.listing + end + + self.grid:setValues(t) + self.grid:setIndex(1) + end, + show = function(self) + local listing = { } + local function recurse(dir) + local files = fs.list(dir) + for _,f in ipairs(files) do + local fullName = fs.combine(dir, f) + if fs.native.isDir(fullName) then -- skip virtual dirs + if f ~= '.git' then recurse(fullName) end + else + _insert(listing, { + name = f, + dir = dir, + lname = f:lower(), + fullName = fullName, + }) + end + end + end + recurse('') + self.listing = listing + self:apply_filter() + self.filter_entry:reset() + UI.SlideOut.show(self) + self:addTransition('expandUp', { easing = 'outBounce', ticks = 12 }) + end, + eventHandler = function(self, event) + if event.type == 'grid_up' then + self.grid:emit({ type = 'scroll_up' }) + + elseif event.type == 'grid_down' then + self.grid:emit({ type = 'scroll_down' }) + + elseif event.type == 'accept' then + local sel = self.grid:getSelected() + if sel then + actions.process('open', sel.fullName) + self:hide() + end + + elseif event.type == 'text_change' then + self:apply_filter(event.text) + self.grid:draw() + + else + return UI.SlideOut.eventHandler(self, event) + end + return true + end, + }, + completions = UI.SlideOut { + x = -12, y = 2, + transitionHint = 'slideLeft', + grid = UI.Grid { + x = 2, y = 2, ey = -2, + columns = { + { key = 'text', heading = 'Completion' }, + }, + accelerators = { + [ ' ' ] = 'down', + backspace = 'slide_hide', + }, + }, + cancel = UI.Button { + y = -1, x = -9, + text = 'Cancel', + backgroundColor = 'black', + backgroundFocusColor = 'black', + textColor = 'lightGray', + event = 'slide_hide', + }, + show = function(self, values) + local m = 12 + for _, v in pairs(values) do + m = #v.text > m and #v.text or m + end + m = m + 3 + m = m > self.parent.width and self.parent.width or m + self.ox = -m + self:resize() + self.grid:setValues(values) + self.grid:setIndex(1) + UI.SlideOut.show(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + actions.process('insertText', x, y, event.selected.complete) + self:hide() + return true + end + return UI.SlideOut.eventHandler(self, event) + end, + }, + peripheral = UI.SlideOut { + x = '20%', y = 2, + transitionHint = 'slideLeft', + grid1 = UI.Grid { + x = 2, y = 2, ey = 5, + sortColumn = 'name', + columns = { + { key = 'name', heading = 'Peripheral' }, + }, + accelerators = { + [ ' ' ] = 'down', + backspace = 'slide_hide', + grid_focus_row = 'select_peripheral', + grid_select = 'complete', + }, + scan = function(self) + self.values = { } + for k, v in pairs(device) do + table.insert(self.values, { name = k, complete = 'peripheral.wrap("' .. v.side .. '")' }) + end + end, + postInit = function(self) + self:scan() + end, + }, + grid2 = UI.Grid { + x = 2, y = 6, ey = -2, + sortColumn = 'method', + columns = { + { key = 'method', heading = 'Method' }, + }, + accelerators = { + [ ' ' ] = 'down', + backspace = 'slide_hide', + grid_select = 'complete', + }, + showMethods = function(self) + local dev = device[self.parent.grid1:getSelected().name] + local t = { } + if dev then + pcall(function() + local docs = dev.getDocs and dev.getDocs() + for k, v in pairs(dev) do + if type(v) == 'function' then + local m = docs and docs[k] and docs[k]:match('^function%((.+)%).+') + table.insert(t, { method = k, complete = k .. '(' .. (m or '') .. ')' }) + end + end + end) + end + self:setValues(t) + end, + enable = function(self) + self:showMethods() + UI.Grid.enable(self) + end, + }, + cancel = UI.Button { + y = -1, x = -9, + text = 'Cancel', + backgroundColor = 'black', + backgroundFocusColor = 'black', + textColor = 'lightGray', + event = 'slide_hide', + }, + eventHandler = function(self, event) + if event.type == 'complete' then + actions.process('insertText', x, y, event.selected.complete) + actions.process('left') + self:hide() + return true + elseif event.type == 'select_peripheral' then + self.grid2:showMethods() + self.grid2:setIndex(1) + self.grid2:update() + self.grid2:draw() + end + return UI.SlideOut.eventHandler(self, event) + end, + }, + editor = UI.Window { + y = 2, + backgroundColor = bgColor, + transitionHint = 'slideRight', + cursorBlink = true, + focus = function(self) + if self.focused then + self:setCursorPos(x - scrollX, y - scrollY) + end + end, + resize = function(self) + UI.Window.resize(self) + + w, h = self.width, self.height + actions.set_cursor(x, y) + actions.dirty_all() + actions.redraw() + end, + draw = function() + actions.redraw() + end, + eventHandler = function(_, event) + if event.ie then + local action, param, param2 + local ie = event.ie + + if ie.code == 'char' then + action = keyMapping.char + param = ie.ch + + elseif ie.code == "mouse_click" or + ie.code == 'mouse_drag' or + ie.code == 'shift-mouse_click' or + ie.code == 'mouse_down' or + ie.code == 'mouse_doubleclick' then + + action = keyMapping[ie.code] + param = ie.x + scrollX + param2 = ie.y + scrollY + + elseif event.type == 'paste' then + action = keyMapping.paste + param = event.text + + else + action = keyMapping[ie.code] + end + + if action then + actions.process(action, param, param2) + return true + end + end + end, + }, + notification = UI.Notification { }, + enable = function(self) + UI.Page.enable(self) + self:setFocus(self.editor) + end, + checkFocus = function(self) + if not self.focused or not self.focused.enabled then + -- if no current focus, set it to the editor + self:setFocus(self.editor) + end + end, + eventHandler = function(self, event) + if event.type == 'menu_action' then + actions.process(event.element.action) + return true + end + return UI.Page.eventHandler(self, event) + end, } -if w < 32 then - messages = { - menu = '^s = save, ^q = quit', - wrapped = 'search wrapped', - } -end local function getFileInfo(path) - local abspath = shell.resolve(path) + path = fs.combine('/', path) local fi = { - abspath = abspath, path = path, - isNew = not fs.exists(abspath), - dirExists = fs.exists(fs.getDir(abspath)), - modified = false, + isNew = not fs.exists(path), + dirExists = fs.exists(fs.getDir(path)), + isReadOnly = fs.isReadOnly(path), } - if fi.isDir then - fi.isReadOnly = true - else - fi.isReadOnly = fs.isReadOnly(fi.abspath) + + if path ~= config.filename then + config.filename = path + config.recent = config.recent or { } + + Array.removeByValue(config.recent, path) + table.insert(config.recent, 1, path) + while #config.recent > 10 do + table.remove(config.recent) + end + + Config.update('editor', config) + end + + if multishell then + multishell.setTitle(multishell.getCurrent(), fs.getName(fi.path)) end return fi end -local function setStatus(pattern, ...) - sStatus = string.format(pattern, ...) -end - -local function setError(pattern, ...) - setStatus(pattern, ...) - isError = true -end - -local function load(path) - tLines = {} - if fs.exists(path) then - local file = io.open(path, "r") - local sLine = file:read() - while sLine do - table.insert(tLines, sLine) - sLine = file:read() - end - file:close() - end - - if #tLines == 0 then - table.insert(tLines, '') - end - - fileInfo = getFileInfo(tArgs[1]) - - local name = fileInfo.path - if w < 32 then - name = fs.getName(fileInfo.path) - end - if fileInfo.isNew then - if not fileInfo.dirExists then - setStatus('"%s" [New DIRECTORY]', name) - else - setStatus('"%s" [New File]', name) - end - elseif fileInfo.isReadOnly then - setStatus('"%s" [readonly] %dL, %dC', - name, #tLines, fs.getSize(fileInfo.abspath)) - else - setStatus('"%s" %dL, %dC', - name, #tLines, fs.getSize(fileInfo.abspath)) - end -end - -local function save( _sPath ) - -- Create intervening folder - local sDir = _sPath:sub(1, _sPath:len() - fs.getName(_sPath):len() ) - if not fs.exists( sDir ) then - fs.makeDir( sDir ) - end - - -- Save - local file = nil - local function innerSave() - file = fs.open( _sPath, "w" ) - if file then - for _,sLine in ipairs( tLines ) do - file.write(sLine .. "\n") - end - else - error( "Failed to open ".._sPath ) - end - end - - local ok, err = pcall( innerSave ) - if file then - file.close() - end - return ok, err -end - -local function split(str, pattern) - pattern = pattern or "(.-)\n" - local t = {} - local function helper(line) table.insert(t, line) return "" end - helper((str:gsub(pattern, helper))) - return t -end - -local tKeywords = { - ["and"] = true, - ["break"] = true, - ["do"] = true, - ["else"] = true, - ["elseif"] = true, - ["end"] = true, - ["false"] = true, - ["for"] = true, - ["function"] = true, - ["if"] = true, - ["in"] = true, - ["local"] = true, - ["nil"] = true, - ["not"] = true, - ["or"] = true, - ["repeat"] = true, - ["return"] = true, - ["then"] = true, - ["true"] = true, - ["until"]= true, - ["while"] = true, +local keywords = Util.transpose { + 'and', 'break', 'do', 'else', 'elseif', 'end', 'false', 'for', 'function', 'if', + 'in', 'local', 'nil', 'not', 'or', 'repeat', 'return', 'then', 'true', 'until', 'while' } -local function writeHighlighted(sLine, ny) - local buffer = { - fg = '', - text = '', - } +local function writeHighlighted(sLine, ny, dy) + local buffer = { fg = { }, text = { } } local function tryWrite(line, regex, fgcolor) local match = line:match(regex) if match then - local fg - if type(fgcolor) == "string" then - fg = fgcolor - else - fg = fgcolor(match) - end - buffer.text = buffer.text .. match - buffer.fg = buffer.fg .. string.rep(fg, #match) - return line:sub(#match + 1) + local fg = type(fgcolor) == "string" and fgcolor or fgcolor(match) + _insert(buffer.text, match) + _insert(buffer.fg, _rep(fg, #match)) + return _sub(line, #match + 1) end return nil end while #sLine > 0 do sLine = - tryWrite(sLine, "^%-%-%[%[.-%]%]", color.commentColor ) or - tryWrite(sLine, "^%-%-.*", color.commentColor ) or - tryWrite(sLine, "^\".-[^\\]\"", color.stringColor ) or - tryWrite(sLine, "^\'.-[^\\]\'", color.stringColor ) or - tryWrite(sLine, "^%[%[.-%]%]", color.stringColor ) or + -- tryWrite(sLine, "^[%\26]", '7' ) or + tryWrite(sLine, "^%-%-%[%[.-%]%]", color.comment ) or + tryWrite(sLine, "^%-%-.*", color.comment ) or + tryWrite(sLine, "^\".-[^\\]\"", color.string ) or + tryWrite(sLine, "^\'.-[^\\]\'", color.string ) or + tryWrite(sLine, "^%[%[.-%]%]", color.string ) or tryWrite(sLine, "^[%w_]+", function(match) - if tKeywords[match] then - return color.keywordColor - end - return color.textColor + return keywords[match] and color.keyword or color.text end) or - tryWrite(sLine, "^[^%w_]", color.textColor) + tryWrite(sLine, "^[^%w_]", color.text) end - buffer.fg = buffer.fg .. '7' - buffer.text = buffer.text .. '.' + buffer.fg = _concat(buffer.fg) .. '7' + buffer.text = _concat(buffer.text) .. '\183' if mark.active and ny >= mark.y and ny <= mark.ey then - local sx = 1 - if ny == mark.y then - sx = mark.x - end - local ex = #buffer.text - if ny == mark.ey then - ex = mark.ex - end - buffer.bg = string.rep('f', sx - 1) .. - string.rep('7', ex - sx) .. - string.rep('f', #buffer.text - ex + 1) - + local sx = ny == mark.y and mark.x or 1 + local ex = ny == mark.ey and mark.ex or #buffer.text + buffer.bg = _rep(color.bg, sx - 1) .. + _rep(color.mark, ex - sx) .. + _rep(color.bg, #buffer.text - ex + 1) else - buffer.bg = string.rep('f', #buffer.text) + buffer.bg = _rep(color.bg, #buffer.text) end - term.blit(buffer.text, buffer.fg, buffer.bg) + page.editor:blit(1 - scrollX, dy, buffer.text, buffer.bg, buffer.fg) end local function redraw() if dirty.y > 0 then - term.setBackgroundColor(color.bgColor) for dy = 1, h do - local sLine = tLines[dy + scrollY] if sLine ~= nil then if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then - term.setCursorPos(1 - scrollX, dy) - term.clearLine() - writeHighlighted(sLine, dy + scrollY) + page.editor:clearLine(dy) + writeHighlighted(sLine, dy + scrollY, dy) end else - term.setCursorPos(1 - scrollX, dy) - term.clearLine() + page.editor:clearLine(dy) end end end - -- Draw status - if #sStatus > 0 then - if isError then - term.setTextColor(colors.white) - term.setBackgroundColor(color.errorBackground) - else - term.setTextColor(color.highlightColor) - term.setBackgroundColor(colors.gray) - end - term.setCursorPos(1, h) - term.clearLine() - term.write(string.format(' %s ', sStatus)) + local modifiedIndicator = undo.chain[#undo.chain] == lastSave and ' ' or '*' + page.menuBar.status.value = _format('%d:%d%s', y, x, modifiedIndicator) + page.menuBar.status:draw() + + if page.editor.focused then + page.editor:setCursorPos(x - scrollX, y - scrollY) end - if not (w < 32 and #sStatus > 0) then - local modifiedIndicator = ' ' - if undo.chain[1] then - modifiedIndicator = '*' - end - - local str = string.format(' %d:%d %s', - y, x, modifiedIndicator) - term.setTextColor(color.highlightColor) - term.setBackgroundColor(colors.gray) - term.setCursorPos(w - #str + 1, h) - term.write(str) - end - - term.setTextColor(color.cursorColor) - term.setCursorPos(x - scrollX, y - scrollY) - dirty.y, dirty.ey = 0, 0 - if #sStatus > 0 then - sStatus = '' - dirty.y = scrollY + h - dirty.ey = dirty.y - end - isError = false end local function nextWord(line, cx) @@ -407,148 +749,98 @@ local function nextWord(line, cx) end end -local function hacky_read() - local _oldSetCursorPos = term.setCursorPos - local _oldGetCursorPos = term.getCursorPos +actions = { + info = function(pattern, ...) + page.notification:info(_format(pattern, ...)) + end, - term.setCursorPos = function(cx) - return _oldSetCursorPos(cx, h) - end - term.getCursorPos = function() - local cx = _oldGetCursorPos() - return cx, 1 - end - - local s, m = pcall(function() return _G.read() end) - term.setCursorPos = _oldSetCursorPos - term.getCursorPos = _oldGetCursorPos - if s then - return m - end - if m == 'Terminated' then - bRunning = false - end - return '' -end - -local actions -local __actions = { - - input = function(prompt) - term.setTextColor(color.highlightColor) - term.setBackgroundColor(colors.gray) - term.setCursorPos(1, h) - term.clearLine() - term.write(prompt) - local str = hacky_read() - term.setCursorBlink(true) - input:reset() - term.setCursorPos(x - scrollX, y - scrollY) - actions.dirty_line(scrollY + h) - return str + error = function(pattern, ...) + page.notification:error(_format(pattern, ...)) end, undo = function() - local last = table.remove(undo.chain) + local last = _remove(undo.chain) if last then undo.active = true - actions[last.action](unpack(last.args)) + table.insert(undo.redo, { }) + for i = #last, 1, -1 do + local u = last[i] + actions[u.action](_unpack(u.args)) + end undo.active = false else - setStatus('Already at oldest change') + actions.info('already at oldest change') end end, - addUndo = function(entry) - local last = undo.chain[#undo.chain] - if last and last.action == entry.action then - if last.action == 'deleteText' then - if last.args[3] == entry.args[1] and - last.args[4] == entry.args[2] then - last.args = { - last.args[1], last.args[2], entry.args[3], entry.args[4], - last.args[5] .. entry.args[5] - } - else - table.insert(undo.chain, entry) - end - else - -- insertText (need to finish) - table.insert(undo.chain, entry) - end + undo_add = function(entry) + if undo.active then + local last = undo.redo[#undo.redo] + table.insert(last, entry) else - table.insert(undo.chain, entry) + if not undo.redo_active then + undo.redo = { } + end + local last = undo.chain[#undo.chain] + if last and undo.continue then + table.insert(last, entry) + else + _insert(undo.chain, { entry }) + end + end + end, + + redo = function() + local last = _remove(undo.redo) + if last then + -- too many flags ! + undo.redo_active = true + undo.continue = false + for i = #last, 1, -1 do + local u = last[i] + actions[u.action](_unpack(u.args)) + undo.continue = true + end + undo.redo_active = false + else + actions.info('already at newest change') end end, autocomplete = function() - if lastAction ~= 'autocomplete' or not complete.results then - local sLine = tLines[y]:sub(1, x - 1) - local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$") - if nStartPos then - sLine = sLine:sub(nStartPos) + local sLine = tLines[y]:sub(1, x - 1):match("[a-zA-Z0-9_%.]+$") + local results = sLine and textutils.complete(sLine, _ENV) or { } + + if #results == 0 then + actions.error('no completions available') + + elseif #results == 1 then + actions.insertText(x, y, results[1]) + + elseif #results > 1 then + local prefix = sLine:match('^.+%.(.*)$') or sLine + for i = 1, #results do + results[i] = { + text = prefix .. results[i], + complete = results[i], + } end - if #sLine > 0 then - complete.results = textutils.complete(sLine) - else - complete.results = { } - end - complete.index = 0 - complete.x = x + page.completions:show(results) end + end, - if #complete.results == 0 then - setError('No completions available') - - elseif #complete.results == 1 then - actions.insertText(x, y, complete.results[1]) - complete.results = nil - - elseif #complete.results > 1 then - local prefix = complete.results[1] - for n = 1, #complete.results do - local result = complete.results[n] - while #prefix > 0 do - if result:find(prefix, 1, true) == 1 then - break - end - prefix = prefix:sub(1, #prefix - 1) - end - end - if #prefix > 0 then - actions.insertText(x, y, prefix) - complete.results = nil - else - if complete.index > 0 then - actions.deleteText(complete.x, y, complete.x + #complete.results[complete.index], y) - end - complete.index = complete.index + 1 - if complete.index > #complete.results then - complete.index = 1 - end - actions.insertText(complete.x, y, complete.results[complete.index]) - end - end + peripheral = function() + page.peripheral:show() end, refresh = function() actions.dirty_all() mark.continue = mark.active - setStatus('refreshed') - end, - - menu = function() - setStatus(messages.menu) - mark.continue = mark.active + actions.info('refreshed') end, goto_line = function() - local lineNo = tonumber(actions.input('Line: ')) - if lineNo then - actions.go_to(1, lineNo) - else - setStatus('Invalid line number') - end + page.gotoLine:show() end, find = function(pattern, sx) @@ -558,19 +850,18 @@ local __actions = { if ny > nLines then ny = ny - nLines end - local nx = tLines[ny]:lower():find(pattern, sx) + local nx = tLines[ny]:lower():find(pattern, sx, true) if nx then if ny < y or ny == y and nx <= x then - setStatus(messages.wrapped) + actions.info('search hit BOTTOM, continuing at TOP') end actions.go_to(nx, ny) actions.mark_to(nx + #pattern, ny) - actions.go_to(nx, ny) return end sx = 1 end - setError('Pattern not found') + actions.error('pattern not found') end, find_next = function() @@ -581,58 +872,174 @@ local __actions = { end, find_prompt = function() - local text = actions.input('/') - if #text > 0 then - searchPattern = text:lower() - if searchPattern then - actions.unmark() - actions.find(searchPattern, x) - end - end + page.search:show() end, - save = function() - if bReadOnly then - setError("Access denied") + quick_open = function(force) + if not force and undo.chain[#undo.chain] ~= lastSave then + page.unsaved:show('quick_open') else - local ok = save(sPath) - if ok then - setStatus('"%s" %dL, %dC written', - fileInfo.path, #tLines, fs.getSize(fileInfo.abspath)) + page.quick_open:show() + end + end, + + file_open = function(force) + if not force and undo.chain[#undo.chain] ~= lastSave then + page.unsaved:show('file_open') + else + page.file_open:show() + end + end, + + recent = function(force) + if not force and undo.chain[#undo.chain] ~= lastSave then + page.unsaved:show('recent') + else + page.recent:show() + end + end, + + file_new = function(force) + if not force and undo.chain[#undo.chain] ~= lastSave then + page.unsaved:show('file_new') + else + actions.open('/untitled.txt') + end + end, + + open = function(filename) + if not actions.load(filename) then + actions.error('unable to load file') + end + end, + + load = function(path) + if not path or (fs.exists(path) and fs.isDir(path)) then + return false + end + fileInfo = getFileInfo(path) + + x, y = 1, 1 + scrollX, scrollY = 0, 0 + lastPos = { x = 1, y = 1 } + lastSave = nil + dirty = { y = 1, ey = h } + mark = { } + undo = { chain = { }, redo = { } } + + tLines = Util.readLines(fileInfo.path) or { } + if #tLines == 0 then + _insert(tLines, '') + end + + --[[ + local function detabify(l) + return l:gsub('\26\26', '\9'):gsub('\26', '\9') + end ]] + + -- since we can't handle tabs, convert them to spaces :( + local t1, t2 = ' ', ' ' + local function tabify(l) + repeat + local i = l:find('\9') + if i then + local tabs = (i - 1) % 2 == 0 and t2 or t1 + l = l:sub(1, i - 1) .. tabs .. l:sub(i + 1) + end + until not i + return l + end + + for k, v in pairs(tLines) do + tLines[k] = tabify(v) + end + + local name = fileInfo.path + if fileInfo.isNew then + if not fileInfo.dirExists then + actions.info('"%s" [New DIRECTORY]', name) else - setError("Error saving to %s", sPath) + actions.info('"%s" [New File]', name) + end + elseif fileInfo.isReadOnly then + actions.info('"%s" [readonly] %dL, %dC', + name, #tLines, fs.getSize(fileInfo.path)) + else + actions.info('"%s" %dL, %dC', + name, #tLines, fs.getSize(fileInfo.path)) + end + + return true + end, + + save = function(filename) + filename = filename or fileInfo.path + if fs.isReadOnly(filename) then + actions.error("access denied") + else + local s, m = pcall(function() + if not Util.writeLines(filename, tLines) then + error("Failed to open " .. filename) + end + end) + + if s then + lastSave = undo.chain[#undo.chain] + fileInfo = getFileInfo(filename) + actions.info('"%s" %dL, %dC written', + fileInfo.path, #tLines, fs.getSize(fileInfo.path)) + return true + else + actions.error(m) end end end, - exit = function() - bRunning = false + save_as = function() + page.save_as:show() + end, + + exit = function(force) + if not force and undo.chain[#undo.chain] ~= lastSave then + page.unsaved:show('exit') + else + UI:quit() + end end, run = function() - input:reset() - local sTempPath = "/.temp" - local ok = save(sTempPath) - if ok then - local nTask = shell.openTab(sTempPath) + if not multishell then + actions.error('open available with multishell') + return + end + if undo.chain[#undo.chain] == lastSave then + local nTask = shell.openTab(fileInfo.path) if nTask then shell.switchTab(nTask) else - setError("Error starting Task") + actions.error("error starting Task") end - os.sleep(0) - fs.delete(sTempPath) else - setError("Error saving to %s", sTempPath) + local fn, msg = load(_concat(tLines, '\n'), fileInfo.path) + if fn then + multishell.openTab({ + fn = fn, + focused = true, + title = fs.getName(fileInfo.path), + }) + else + local ln = msg:match(':(%d+):') + if ln and tonumber(ln) then + actions.go_to(1, tonumber(ln)) + end + actions.error(msg) + end end end, status = function() - local modified = '' - if undo.chain[1] then - modified = '[Modified] ' - end - setStatus('"%s" %s%d lines --%d%%--', + local modified = undo.chain[#undo.chain] == lastSave and '' or '[Modified] ' + actions.info('"%s" %s%d lines --%d%%--', fileInfo.path, modified, #tLines, math.floor((y - 1) / (#tLines - 1) * 100)) end, @@ -738,12 +1145,40 @@ local __actions = { actions.mark_finish() end, + mark_line = function() + actions.home() + actions.mark_begin() + actions.toend() + actions.right() + actions.mark_finish() + end, + mark_word = function() actions.mark_begin() actions.word() actions.mark_finish() end, + mark_current_word = function(cx, cy) + local index = 1 + actions.go_to(cx, cy) + while true do + local s, e = tLines[y]:find('%w+', index) + if not s or s - 1 > x then + break + end + if x >= s and x <= e then + x = s + actions.mark_begin() + x = e + 1 + actions.mark_finish() + x, y = cx, cy + break + end + index = e + 1 + end + end, + mark_backword = function() actions.mark_begin() actions.backword() @@ -773,7 +1208,7 @@ local __actions = { actions.dirty_all() end, - setCursor = function() + set_cursor = function() lastPos.x = x lastPos.y = y @@ -781,18 +1216,18 @@ local __actions = { local screenY = y - scrollY if screenX < 1 then - scrollX = x - 1 + scrollX = math.max(0, x - 4) actions.dirty_all() elseif screenX > w then - scrollX = x - w + scrollX = x - w + 3 actions.dirty_all() end if screenY < 1 then scrollY = y - 1 actions.dirty_all() - elseif screenY > h - 1 then - scrollY = y - (h - 1) + elseif screenY > h then + scrollY = y - h actions.dirty_all() end end, @@ -827,12 +1262,12 @@ local __actions = { actions.insertText(x, y, ' ') end, - pageUp = function() - actions.go_to(x, y - (h - 1)) + page_up = function() + actions.go_to(x, y - h) end, - pageDown = function() - actions.go_to(x, y + (h - 1)) + page_down = function() + actions.go_to(x, y + h) end, home = function() @@ -907,39 +1342,35 @@ local __actions = { actions.dirty_line(y) x = x + #text else - local lines = split(text) + local lines = Util.split(text) local remainder = sLine:sub(x) tLines[y] = sLine:sub(1, x - 1) .. lines[1] actions.dirty_range(y, #tLines + #lines) x = x + #lines[1] for k = 2, #lines do y = y + 1 - table.insert(tLines, y, lines[k]) + _insert(tLines, y, lines[k]) x = #lines[k] + 1 end tLines[y] = tLines[y]:sub(1, x) .. remainder end - if not undo.active then - actions.addUndo( - { action = 'deleteText', args = { sx, sy, x, y, text } }) - end + actions.undo_add( + { action = 'deleteText', args = { sx, sy, x, y } }) end, deleteText = function(sx, sy, ex, ey) x = sx y = sy - if not undo.active then - local text = actions.copyText(sx, sy, ex, ey) - actions.addUndo( - { action = 'insertText', args = { sx, sy, text } }) - end + local text = actions.copyText(sx, sy, ex, ey) + actions.undo_add( + { action = 'insertText', args = { sx, sy, text } }) local front = tLines[sy]:sub(1, sx - 1) local back = tLines[ey]:sub(ex, #tLines[ey]) for _ = 2, ey - sy + 1 do - table.remove(tLines, y + 1) + _remove(tLines, y + 1) end tLines[y] = front .. back if sy ~= ey then @@ -966,10 +1397,10 @@ local __actions = { end local str = line:sub(cx, ex) count = count + #str - table.insert(lines, str) + _insert(lines, str) end end - return table.concat(lines, '\n'), count + return _concat(lines, '\n'), count end, delete = function() @@ -986,9 +1417,7 @@ local __actions = { end, backspace = function() - if mark.active then - actions.delete() - elseif actions.left() then + if mark.active or actions.left() then actions.delete() end end, @@ -1003,7 +1432,7 @@ local __actions = { if mark.active then actions.delete() end - actions.insertText(x, y, '\n' .. string.rep(' ', spaces)) + actions.insertText(x, y, '\n' .. _rep(' ', spaces)) end, char = function(ch) @@ -1016,7 +1445,7 @@ local __actions = { copy_marked = function() local text = actions.copyText(mark.x, mark.y, mark.ex, mark.ey) os.queueEvent('clipboard_copy', text) - setStatus('shift-^v to paste') + actions.info('shift-^v to paste') end, cut = function() @@ -1039,12 +1468,16 @@ local __actions = { end if text then actions.insertText(x, y, text) - setStatus('%d chars added', #text) + actions.info('%d chars added', #text) else - setStatus('Clipboard empty') + actions.info('clipboard empty') end end, + paste_internal = function() + os.queueEvent('clipboard_paste') + end, + go_to = function(cx, cy) y = math.min(math.max(cy, 1), #tLines) x = math.min(math.max(cx, 1), #tLines[y] + 1) @@ -1059,54 +1492,19 @@ local __actions = { end, scroll_down = function() - local nMaxScroll = #tLines - (h-1) + local nMaxScroll = #tLines - h if scrollY < nMaxScroll then scrollY = scrollY + 1 actions.dirty_all() end mark.continue = mark.active end, -} -actions = __actions + redraw = function() + redraw() + end, -load(sPath) -term.setCursorBlink(true) -redraw() - -while bRunning do - local sEvent, param, param2, param3 = os.pullEventRaw() - local action - - if sEvent == 'terminate' then - action = 'exit' - elseif sEvent == 'multishell_focus' then -- opus only event - input:reset() - elseif sEvent == "mouse_click" or - sEvent == 'mouse_drag' or - sEvent == 'mouse_up' or - sEvent == 'mouse_down' then - local ie = input:translate(sEvent, param, param2, param3) - if param3 < h or sEvent == 'mouse_drag' then - if ie.code then - action = keyMapping[ie.code] - param = param2 + scrollX - param2 = param3 + scrollY - end - end - else - local ie = input:translate(sEvent, param, param2) - if ie then - if ie.ch and #ie.ch == 1 then - action = keyMapping.char - param = ie.ch - else - action = keyMapping[ie.code] - end - end - end - - if action then + process = function(action, ...) if not actions[action] then error('Invaid action: ' .. action) end @@ -1114,31 +1512,35 @@ while bRunning do local wasMarking = mark.continue mark.continue = false - actions[action](param, param2) - if action ~= 'menu' then - lastAction = action - end + -- for undo purposes, treat tab and enter as char actions + local a = (action == 'tab' or action == 'enter') and 'char' or action + undo.continue = a == undo.lastAction + + actions[action](...) + + undo.lastAction = a if x ~= lastPos.x or y ~= lastPos.y then - actions.setCursor() + actions.set_cursor() end if not mark.continue and wasMarking then actions.unmark() end - redraw() + actions.redraw() + end, +} - elseif sEvent == "term_resize" then - w,h = term.getSize() - actions.setCursor(x, y) - actions.dirty_all() - redraw() - end +local args = { ... } +local filename = args[1] and shell.resolve(args[1]) +if not (actions.load(filename) or actions.load(config.filename) or actions.load('untitled.txt')) then + error('Error opening file') end --- Cleanup -term.setBackgroundColor(colors.black) -term.setTextColor(colors.white) -term.clear() -term.setCursorBlink(false) -term.setCursorPos(1, 1) +UI:setPage(page) +local s, m = pcall(function() UI:start() end) +if not s then + actions.save('/crash.txt') + print('Editor has crashed. File saved as /crash.txt') + error(m) +end diff --git a/common/etc/apps.db b/common/etc/apps.db index d12829c..d8608bb 100644 --- a/common/etc/apps.db +++ b/common/etc/apps.db @@ -57,7 +57,7 @@ category = "Apps", requires = "advancedComputer", iconExt = "\030 \031 \128\030d\159\030 \031d\140\030d\031 \155\030 \0315\140\0305\031 \155\030 \128\010\030 \031d\136\145\0315\136\145\031d\153\031 \128\0315\153\010\030 \031 \128\031d\130\140\134\0315\140\134\031 \128", - run = "packages/common/hexedit.lua", + run = "fileui --exec=hexedit.lua", }, [ "fb1c39e9f4f3c2628ad173ab401a6e4e4baf783d" ] = { title = "Sounds", @@ -65,4 +65,17 @@ run = "SoundPlayer", iconExt = "\030 \031 \128\0307\159\129\030 \0317\149\0310\144\0300\031 \155\030 \0310\137\144\010\0307\0317\128\128\128\030 \149\0300\031 \149\030 \128\0310\149\0300\031 \149\010\030 \031 \128\0317\130\0307\031 \144\030 \0317\149\0310\129\134\152\129", }, + [ "464c4ffd019e1e9691dcf0537c797353ef2b1c1d4833d3d463e5b74ae4547344" ] = { + title = "Editor", + category = "Apps", + run = "edit", + iconExt = "7\ +¨¨¨¨f€0¨\ +¨¨f€0¨¨f€", + }, + [ "3f00927a719345edd4a8316599d3b328857987547f8884306861161ffa09647e" ] = { + title = "Write", + category = "Apps", + run = "write", + }, } diff --git a/common/etc/scripts/goHome b/common/etc/scripts/goHome index 2749820..94fd138 100644 --- a/common/etc/scripts/goHome +++ b/common/etc/scripts/goHome @@ -1,5 +1,5 @@ _G.requireInjector(_ENV) -local config = require('config').load('gps') +local config = require('opus.config').load('gps') if config.home then if turtle.enableGPS() then return turtle.pathfind(config.home) diff --git a/common/etc/scripts/moveTo b/common/etc/scripts/moveTo deleted file mode 100644 index c68e1db..0000000 --- a/common/etc/scripts/moveTo +++ /dev/null @@ -1,29 +0,0 @@ -turtle.run(function() - - _G.requireInjector(_ENV) - - local GPS = require('gps') - local Socket = require('socket') - - local id = {COMPUTER_ID} - - if not turtle.enableGPS() then - error('turtle: No GPS found') - end - - local socket = Socket.connect(id, 161) - if not socket then - error('turtle: Unable to connect to ' .. id) - end - - socket:write({ type = 'gps' }) - - local pt = socket:read(3) - if not pt then - error('turtle: No GPS response') - end - - if not turtle.pathfind(pt) then - error('Unable to go to location') - end -end) diff --git a/common/etc/scripts/moveTo.lua b/common/etc/scripts/moveTo.lua new file mode 100644 index 0000000..72d597a --- /dev/null +++ b/common/etc/scripts/moveTo.lua @@ -0,0 +1,17 @@ +local turtle = _G.turtle + +turtle.run(function() + _G.requireInjector(_ENV) + + local GPS = require('opus.gps') + + if not turtle.enableGPS() then + error('turtle: No GPS found') + end + + local pt = {GPS} + + if not turtle.pathfind(pt) then + error('Unable to go to location') + end +end) diff --git a/common/etc/scripts/setHome b/common/etc/scripts/setHome index 9f88beb..be69170 100644 --- a/common/etc/scripts/setHome +++ b/common/etc/scripts/setHome @@ -1,5 +1,5 @@ _G.requireInjector(_ENV) -local Config = require('config') +local Config = require('opus.config') local pt = turtle.enableGPS() if pt then local config = Config.load('gps', { }) diff --git a/common/etc/scripts/summon b/common/etc/scripts/summon index 45e0c9e..66dcded 100644 --- a/common/etc/scripts/summon +++ b/common/etc/scripts/summon @@ -2,9 +2,9 @@ local function summon(id) _G.requireInjector(_ENV) - local GPS = require('gps') - local Point = require('point') - local Socket = require('socket') + local GPS = require('opus.gps') + local Point = require('opus.point') + local Socket = require('opus.socket') turtle.setStatus('GPSing') turtle.setPoint({ x = 0, y = 0, z = 0, heading = 0 }) diff --git a/common/recorder.lua b/common/recorder.lua index e6c7a94..4ea8824 100644 --- a/common/recorder.lua +++ b/common/recorder.lua @@ -19,10 +19,14 @@ local Util = require('opus.util') local multishell = _ENV.multishell local os = _G.os -local recTerm, oldTerm, arg, showInput, skipLast, lastDelay, curInput = {}, Util.shallowCopy(multishell.term), {...}, false, false, 2, "" +local colours = _G.colors + +local args, options = Util.parse(...) + +local oldTerm, showInput, skipLast, lastDelay, curInput = Util.shallowCopy(multishell.term), false, false, 2, "" local curBlink, oldBlink, tTerm, buffer, colourNum, xPos, yPos, oldXPos, oldYPos, tCol, bCol, xSize, ySize = false, false, {}, {}, {}, 1, 1, 1, 1, colours.white, colours.black, oldTerm.getSize() local greys, buttons = {["0"] = true, ["7"] = true, ["8"] = true, ["f"] = true}, {"l", "r", "m"} -local charW, charH, chars, resp +local charW, charH, chars local calls = { } local curCalls = { delay = 0 } @@ -32,38 +36,44 @@ local callCount = 0 local function showSyntax() print('Gif Recorder by Bomb Bloke\n') print('Syntax: recGif [-i] [-s] [-ld:] filename') - print(' -i : show input') - print(' -s : skip last') - print(' -ld : last delay') + print(' --showInput : show input') + print(' --skipLast : skip last') + print(' --lastDelay : last delay') + print(' --noResize : dont resize') end -for i = #arg, 1, -1 do - local curArg = arg[i]:lower() - - if curArg == "-i" then - showInput, ySize = true, ySize + 1 - table.remove(arg, i) - elseif curArg == "-s" then - skipLast = true - table.remove(arg, i) - elseif curArg:sub(1, 4) == "-ld:" then - curArg = tonumber(curArg:sub(5)) - if curArg then lastDelay = curArg end - table.remove(arg, i) - elseif curArg == '-?' then - showSyntax() - return - elseif i ~= #arg then - showSyntax() - printError('\nInvalid argument') - return - end +if options.showInput then + showInput, ySize = true, ySize + 1 end -print('Gif Recorder by Bomb Bloke\n') -print('Press control-p to stop recording') +if options.skipLast then + skipLast = true +end -local filename = arg[#arg] +if options.lastDelay then + lastDelay = options.lastDelay +end + +if options.help then + showSyntax() + return +end + +if options.daemon then + device.keyboard.addHotkey('control-P', function() + multishell.openTab({ + path = 'sys/apps/shell.lua', + args = { arg[0], '--noResize', '--rawOutput', 'recorder.gif' }, + }) + end) + return +end + +print('Gif Recorder by Bomb Bloke') +print(version) +print('\nPress control-p to stop recording') + +local filename = args[1] if not filename then print('Enter file name:') filename = read() @@ -131,37 +141,34 @@ end -- Build a terminal that records stuff: -recTerm = multishell.term +local recTerm = multishell.term for key, func in pairs(oldTerm) do - recTerm[key] = function(...) - local result = { func(...) } + if type(func) == 'function' then + recTerm[key] = function(...) + local result = { func(...) } - if callCount == 0 then - os.queueEvent('capture_frame') + if callCount == 0 then + os.queueEvent('capture_frame') + end + callCount = callCount + 1 + curCalls[callCount] = { key, ... } + return unpack(result) end - callCount = callCount + 1 - curCalls[callCount] = { key, ... } - return unpack(result) end end local tabId = multishell.getCurrent() +multishell.hideTab(tabId) + +if not options.noResize then + os.queueEvent('term_resize') +end _G.device.keyboard.addHotkey('control-p', function() os.queueEvent('recorder_stop') end) -local tabs = multishell.getTabs() -for _,tab in pairs(tabs) do - if tab.isOverview then - multishell.hideTab(tabId) - multishell.setFocus(tab.tabId) - os.queueEvent('term_resize') - break - end -end - local curTime = os.clock() - 1 while true do @@ -200,8 +207,12 @@ if skipLast and #calls > 1 then calls[#calls] = nil end calls[#calls].delay = lastDelay +if options.rawOutput then + Util.writeTable('tmp/raw.txt', calls) + return +end + print(string.format("Encoding %d frames...", #calls)) ---Util.writeTable('tmp/raw.txt', calls) -- Perform a quick re-parse of the recorded data (adding frames for when the cursor blinks): diff --git a/examples/.package b/examples/.package deleted file mode 100644 index da8c112..0000000 --- a/examples/.package +++ /dev/null @@ -1,6 +0,0 @@ -{ - title = 'Example programs for coding in Opus', - repository = 'kepler155c/opus-apps/{{OPUS_BRANCH}}/examples', - description = [[Starter/example programs for creating Opus programs.]], - license = 'MIT', -} diff --git a/examples/grid.lua b/examples/grid.lua deleted file mode 100644 index b979a87..0000000 --- a/examples/grid.lua +++ /dev/null @@ -1,44 +0,0 @@ -local UI = require('opus.ui') - -local page = UI.Page { - menuBar = UI.MenuBar { - buttons = { - { text = 'Shuffle', event = 'shuffle' }, - { text = 'Clear', event = 'clear', }, - } - }, - grid = UI.ScrollingGrid { - y = 2, ey = -2, - values = { - { col = 'column1', value = 'value1' }, - { col = 'column2', value = 'value2' }, - { col = 'column3', value = 'value3' }, - }, - columns = { - { heading = 'HDR1', key = 'col' }, - { heading = 'HDR2', key = 'value' }, - } - }, - statusBar = UI.StatusBar { }, -} - -function page:eventHandler(event) - if event.type == 'grid_select' then - self.statusBar:setStatus('Selected: ' .. event.selected.value) - - elseif event.type == 'shuffle' then - for _,v in pairs(self.grid.values) do - v.col = 'column' .. math.random(1,3) - end - self.grid:update() - self.grid:draw() - - elseif event.type == 'clear' then - self.grid:setValues({ }) - self.grid:draw() - end - return UI.Page.eventHandler(self, event) -end - -UI:setPage(page) -UI:pullEvents() diff --git a/gps/gpsServer.lua b/gps/gpsServer.lua index 58461d6..d66b575 100644 --- a/gps/gpsServer.lua +++ b/gps/gpsServer.lua @@ -307,4 +307,4 @@ else end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/ignore/Music.lua b/ignore/Music.lua index 2af2b86..7ff44c2 100644 --- a/ignore/Music.lua +++ b/ignore/Music.lua @@ -250,5 +250,3 @@ turtle.setStatus('Jamming') UI:pullEvents() turtle.setStatus('idle') page:play(false) - -UI.term:reset() diff --git a/ignore/passthrough.lua b/ignore/passthrough.lua new file mode 100644 index 0000000..dae7403 --- /dev/null +++ b/ignore/passthrough.lua @@ -0,0 +1,174 @@ +local _rep = string.rep +local _sub = string.sub +local colors = _G.colors + +local palette = { } + +for n = 1, 16 do + palette[2 ^ (n - 1)] = _sub("0123456789abcdef", n, n) +end + +local swindow = { } + +function swindow.createPassthrough(parent, wx, wy, width, height) + local window = { } + local cx, cy = 1, 1 + local blink = false + local fg = colors.white + local bg = colors.black + + local function crop(text, x) + local w = #text + + if x < 1 then + text = _sub(text, 2 - x) + w = w + x - 1 + x = 1 + end + + if x + w - 1 > width then + text = _sub(text, 1, width - x + 1) + end + + return text + end + + local function blit(text, fg, bg) + parent.setCursorPos(cx + wx - 1, cy + wy - 1) + parent.blit(text, fg, bg) + cx = cx + #text + end + + function window.write(text) + if cy > 0 and cy <= height then + text = crop(tostring(text), cx) + if #text > 0 then + --parent.setCursorPos(cx + wx - 1, cy + wy - 1) + blit(text, _rep(palette[fg], #text), _rep(palette[bg], #text)) + end + end + end + + function window.blit(text, fg, bg) + if cy > 0 and cy <= height then + text = crop(tostring(text), cx) + if #text > 0 then + blit(text, crop(tostring(fg), cx), crop(tostring(bg), cx)) + end + end + end + + function window.clear() + local filler = _rep(' ', width) + for i = 1, height do + parent.setCursorPos(wx, i + wy - 1) + parent.write(filler) + end + end + + function window.clearLine() + if cy > 0 and cy <= height then + local filler = _rep(' ', width) + parent.setCursorPos(cx + wx - 1, cy + wy - 1) + parent.write(filler) + end + end + + function window.getCursorPos() + return cx, cy + end + + function window.setCursorPos(x, y) + cx = math.floor(x) + cy = math.floor(y) + parent.setCursorPos(cx + wx - 1, cy + wy - 1) + end + + function window.setCursorBlink(b) + blink = b + parent.setCursorBlink(b) + end + + function window.getCursorBlink() + return blink + end + + window.isColor = parent.isColor + window.isColour = parent.isColour + window.setPaletteColour = parent.setPaletteColour + window.setPaletteColor = parent.setPaletteColor + window.getPaletteColour = parent.getPaletteColour + window.getPaletteColor = parent.getPaletteColour + window.setBackgroundColor = parent.setBackgroundColor + window.setBackgroundColour = parent.setBackgroundColor + window.getBackgroundColor = parent.getBackgroundColor + window.getBackgroundColour = parent.getBackgroundColor + window.setVisible = parent.setVisible + window.redraw = function() end --parent.redraw + + function window.getTextColor() + return fg + end + window.getTextColour = window.getTextColor + + function window.setTextColor(textColor) + fg = textColor + parent.setTextColor(fg) + end + window.setTextColour = window.setTextColor + + function window.restoreCursor() + parent.setCursorPos(cx + wx - 1, cy + wy - 1) + parent.setTextColor(fg) + parent.setCursorBlink(blink) + end + + function window.getSize() + return width, height + end + + function window.scroll( n ) + if n ~= 0 then + local lines = { } + for i = 1, height do + lines[i] = { parent.getLine(wy + i - 1) } + end + + for newY = 1, height do + local y = newY + n + parent.setCursorPos(wx, wy + newY - 1) + if y >= 1 and y <= height then + parent.blit(table.unpack(lines[y])) + else + parent.blit( + _rep(' ', width), + _rep(palette[fg], width), + _rep(palette[bg], width)) + end + end + parent.setCursorPos(cx + wx - 1, cy + wy - 1) + end + end + + function window.getLine(y) + local t, tc, bc = parent.getLine(y + cy - 1) + return t:sub(1, width), tc:sub(1, width), bc:sub(1, width) + end + + function window.getPosition() + return wx, wy + end + + function window.reposition(nNewX, nNewY, nNewWidth, nNewHeight, newParent) + wx = nNewX + wy = nNewY + width = nNewWidth + height = nNewHeight + + window.restoreCursor() + end + + return window +end + +return swindow diff --git a/ignore/twm.lua b/ignore/twm.lua index 24f908a..07bfc3e 100644 --- a/ignore/twm.lua +++ b/ignore/twm.lua @@ -4,42 +4,21 @@ local Util = require('opus.util') local colors = _G.colors local os = _G.os -local peripheral = _G.peripheral local printError = _G.printError -local shell = _ENV.shell local term = _G.term local window = _G.window -local function syntax() - printError('Syntax:') - error('mwm [--config=filename] [monitor]') -end - -local args = Util.parse(...) local UID = 0 local multishell = { } local processes = { } local parentTerm = term.current() -local sessionFile = args.config or 'usr/config/mwm' -local monName = args[1] +local sessionFile = 'usr/config/twm' local running -local parentMon +local parentMon = term.current() local defaultEnv = Util.shallowCopy(_ENV) defaultEnv.multishell = multishell -if monName == 'terminal' then - parentMon = term.current() -elseif monName then - parentMon = peripheral.wrap(monName) or syntax() -else - parentMon = peripheral.find('monitor') or syntax() -end - -if parentMon.setTextScale then - parentMon.setTextScale(.5) -end - local monDim, termDim = { }, { } monDim.width, monDim.height = parentMon.getSize() termDim.width, termDim.height = parentTerm.getSize() @@ -73,13 +52,10 @@ local function write(win, x, y, text) end local function redraw() - --monitor.clear() monitor.canvas:dirty() - --monitor.setBackgroundColor(colors.gray) monitor.canvas:clear(colors.gray) - for k, process in ipairs(processes) do + for _, process in ipairs(processes) do process.container.canvas:dirty() - process:focus(k == #processes) end end @@ -112,7 +88,8 @@ function Process:new(args) height = args.height + 1, path = args.path, args = args.args or { }, - title = args.title or 'shell', + title = args.title or 'shell', + timestamp = os.clock(), isMoving = false, isResizing = false, }, { __index = Process }) @@ -155,20 +132,12 @@ function Process:new(args) end function Process:focus(focused) - if focused then - self.container.setBackgroundColor(colors.yellow) - else - self.container.setBackgroundColor(colors.lightGray) - end + self.container.setBackgroundColor(focused and colors.yellow or colors.lightGray) self.container.setTextColor(colors.black) write(self.container, 1, 1, string.rep(' ', self.width)) write(self.container, 2, 1, self.title) write(self.container, self.width - 1, 1, '*') write(self.container, self.width - 3, 1, '\029') - - if focused then - self.window.restoreCursor() - end end function Process:drawSizers() @@ -188,17 +157,18 @@ function Process:adjustDimensions() self.y = math.min(self.y, monDim.height - self.height + 1) end -function Process:reposition() +function Process:reposition(resizing) self:adjustDimensions() self.container.reposition(self.x, self.y, self.width, self.height) - self.container.setBackgroundColor(colors.black) - self.container.clear() self.window.reposition(1, 2, self.width, self.height - 1) if self.window ~= self.terminal then if self.terminal.reposition then -- ?? self.terminal.reposition(1, 1, self.width, self.height - 1) end - end + end + if resizing then + self:focus(self == processes[#processes]) + end redraw() end @@ -225,7 +195,7 @@ function Process:resize(x, y) self.height = y - self.isResizing.y + self.isResizing.h self.width = x - self.isResizing.x + self.isResizing.w - self:reposition() + self:reposition(true) self:resume('term_resize') self:drawSizers() multishell.saveSession(sessionFile) @@ -278,7 +248,8 @@ function multishell.setFocus(uid) process.container.canvas:raise() process:focus(true) - process.container.canvas:dirty() + process.container.canvas:dirty() + process.window.restoreCursor() end return true end @@ -423,14 +394,15 @@ function multishell.start() elseif focused.isMoving then focused.x = event[3] - focused.isMoving.x + focused.isMoving.ox focused.y = event[4] - focused.isMoving.y + focused.isMoving.oy - focused:reposition() + focused:reposition(false) end end elseif event[1] == 'char' or event[1] == 'key' or event[1] == 'key_up' or - event[1] == 'paste' then + event[1] == 'paste' or + event[1] == 'mouse_scroll' then local focused = processes[#processes] if focused then diff --git a/lzwfs/system/lzwfs.lua b/lzwfs/system/lzwfs.lua index 2c76f30..3f0a7a2 100644 --- a/lzwfs/system/lzwfs.lua +++ b/lzwfs/system/lzwfs.lua @@ -16,16 +16,19 @@ local config = Config.load('lzwfs', { local tab = UI.Tab { tabTitle = 'Compression', description = 'Disk compression', + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 6, + }, label1 = UI.Text { - x = 2, y = 2, + x = 3, y = 3, value = 'Enable compression', }, checkbox = UI.Checkbox { - x = 20, y = 2, + x = 21, y = 3, value = config.enabled }, entry = UI.TextEntry { - x = 2, y = 4, ex = -2, + x = 3, y = 5 , ex = -3, limit = 256, shadowText = 'enter new path', accelerators = { @@ -34,7 +37,7 @@ local tab = UI.Tab { help = 'add a new path', }, grid = UI.Grid { - x = 2, ex = -2, y = 6, ey = -5, + x = 2, ex = -2, y = 8, ey = -5, disableHeader = true, columns = { { key = 'value' } }, autospace = true, @@ -45,7 +48,7 @@ local tab = UI.Tab { }, }, button = UI.Button { - x = -9, ex = -2, y = -3, + x = -8, ex = -2, y = -3, text = 'Apply', event = 'apply', }, diff --git a/milo/MiloLocal.lua b/milo/MiloLocal.lua index 68b409f..7ba699b 100644 --- a/milo/MiloLocal.lua +++ b/milo/MiloLocal.lua @@ -216,7 +216,7 @@ _G._syslog = function(...) end local s, m = pcall(function() - UI:pullEvents() + UI:start() end) turtle.setStatus('idle') diff --git a/milo/MiloRemote.lua b/milo/MiloRemote.lua index 32cbd06..1f2caa8 100644 --- a/milo/MiloRemote.lua +++ b/milo/MiloRemote.lua @@ -1,6 +1,6 @@ local Config = require('opus.config') local Event = require('opus.event') -local fuzzy = require('milo.fuzzyMatch') +local fuzzy = require('opus.fuzzy') local Sound = require('opus.sound') local Socket = require('opus.socket') local sync = require('opus.sync').sync @@ -63,8 +63,8 @@ local page = UI.Page { x = 1, ex = -13, limit = 50, shadowText = 'filter', - backgroundColor = colors.cyan, - backgroundFocusColor = colors.cyan, + backgroundColor = 'primary', + backgroundFocusColor = 'primary', accelerators = { [ 'enter' ] = 'eject', [ 'up' ] = 'grid_up', @@ -169,7 +169,7 @@ end function page:eventHandler(event) if event.type == 'quit' then - UI:exitPullEvents() + UI:quit() elseif event.type == 'setup' then self.setup.form:setValues(context.state) @@ -517,14 +517,14 @@ local function loadDirectory(dir) }) end end - page.menuBar.config:add({ dropmenu = UI.DropMenu { buttons = dropdown } }) + page.menuBar.config.dropdown = dropdown end local programDir = fs.getDir(shell.getRunningProgram()) loadDirectory(fs.combine(programDir, 'plugins/remote')) UI:setPage(page) -UI:pullEvents() +UI:start() if context.socket then context.socket:close() diff --git a/milo/apis/fuzzyMatch.lua b/milo/apis/fuzzyMatch.lua deleted file mode 100644 index 7a6082d..0000000 --- a/milo/apis/fuzzyMatch.lua +++ /dev/null @@ -1,19 +0,0 @@ --- Based on Squid's fuzzy search --- https://github.com/SquidDev-CC/artist/blob/vnext/artist/lib/match.lua --- --- not very fuzzy anymore - -local SCORE_WEIGHT = 1000 -local LEADING_LETTER_PENALTY = -3 -local LEADING_LETTER_PENALTY_MAX = -9 - -local _find = string.find -local _max = math.max - -return function(str, pattern) - local start = _find(str, pattern, 1, true) - if start then - -- All letters before the current one are considered leading, so add them to our penalty - return SCORE_WEIGHT + _max(LEADING_LETTER_PENALTY * (start - 1), LEADING_LETTER_PENALTY_MAX) - end -end diff --git a/milo/apis/massAdapter.lua b/milo/apis/massAdapter.lua deleted file mode 100644 index f272e4b..0000000 --- a/milo/apis/massAdapter.lua +++ /dev/null @@ -1,53 +0,0 @@ -local class = require('opus.class') -local itemDB = require('core.itemDB') -local Mini = require('milo.miniAdapter') - -local os = _G.os - -local Adapter = class(Mini) - -function Adapter:init(args) - Mini.init(self, args) - - self._rawList = self.list - - function self.list() - -- wait for up to 1 sec until any items that have been inserted - -- into interface are added to the system - for _ = 0, 20 do - if #self._rawList() == 0 then - break - end - os.sleep(0) - end - - local list = { } - for _, v in pairs(self.listAvailableItems()) do - list[itemDB:makeKey(v)] = v - end - return list - end - - function self.getItemMeta(key) - local item = self.findItem(itemDB:splitKey(key)) - if item and item.getMetadata then - return item.getMetadata() - end - end - - function self.pushItems(target, key, amount, slot) - local item = self.findItem(itemDB:splitKey(key)) - if item and item.export then - return item.export(target, amount, slot) - end - return 0 - end - - function self.pullItems(target, key, amount, slot) - _G._syslog({target, key, amount, slot }) - return 0 - end - -end - -return Adapter diff --git a/milo/core/listing.lua b/milo/core/listing.lua index b19b4d2..34f1b6c 100644 --- a/milo/core/listing.lua +++ b/milo/core/listing.lua @@ -1,6 +1,6 @@ local Craft = require('milo.craft2') local Event = require('opus.event') -local fuzzy = require('milo.fuzzyMatch') +local fuzzy = require('opus.fuzzy') local Milo = require('milo') local Sound = require('opus.sound') local UI = require('opus.ui') @@ -54,8 +54,8 @@ local page = UI.Page { limit = 50, shadowText = 'filter', shadowTextColor = colors.gray, - backgroundColor = colors.cyan, - backgroundFocusColor = colors.cyan, + backgroundColor = 'primary', + backgroundFocusColor = 'primary', accelerators = { [ 'enter' ] = 'eject', [ 'up' ] = 'grid_up', @@ -66,7 +66,7 @@ local page = UI.Page { storageStatus = UI.Text { x = -17, ex = -10, textColor = colors.lime, - backgroundColor = colors.cyan, + backgroundColor = 'primary', value = '', }, amount = UI.TextEntry { @@ -208,7 +208,7 @@ end function page:eventHandler(event) if event.type == 'quit' then - UI:exitPullEvents() + UI:quit() elseif event.type == 'eject' or event.type == 'grid_select' then self:eject(1) diff --git a/milo/core/machines.lua b/milo/core/machines.lua index 844e491..b3779db 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -7,7 +7,6 @@ local Util = require('opus.util') local colors = _G.colors local device = _G.device -local turtle = _G.turtle local context = Milo:getContext() @@ -22,8 +21,8 @@ local networkPage = UI.Page { y = -2, x = 1, ex = -9, limit = 50, shadowText = 'filter', - backgroundColor = colors.cyan, - backgroundFocusColor = colors.cyan, + backgroundColor = 'primary', + backgroundFocusColor = 'primary', }, grid = UI.ScrollingGrid { y = 2, ey = -3, @@ -195,7 +194,6 @@ nodeWizard = UI.Page { pages = { general = UI.WizardPage { index = 1, - backgroundColor = colors.cyan, form = UI.Form { x = 2, ex = -2, y = 1, ey = 3, manualControls = true, @@ -236,11 +234,11 @@ The settings will take effect immediately!]], }, }, statusBar = UI.StatusBar { - backgroundColor = colors.cyan, + backgroundColor = 'primary', }, notification = UI.Notification { }, filter = UI.SlideOut { - backgroundColor = colors.cyan, + noFill = true, menuBar = UI.MenuBar { buttons = { { text = 'Save', event = 'save' }, @@ -248,7 +246,8 @@ The settings will take effect immediately!]], }, }, grid = UI.ScrollingGrid { - x = 2, ex = -6, y = 2, ey = -6, + x = 2, ex = -6, y = 3, ey = -7, + disableHeader = true, columns = { { heading = 'Name', key = 'displayName' }, }, @@ -262,7 +261,7 @@ The settings will take effect immediately!]], text = '-', event = 'remove_entry', help = 'Remove', }, form = UI.Form { - x = 2, y = -4, height = 3, + x = 2, y = -5, height = 3, margin = 1, manualControls = true, [1] = UI.Checkbox { @@ -290,7 +289,7 @@ The settings will take effect immediately!]], }, }, statusBar = UI.StatusBar { - backgroundColor = colors.cyan, + backgroundColor = 'primary', }, }, } diff --git a/milo/plugins/activityView.lua b/milo/plugins/activityView.lua index 723d313..1d8cffd 100644 --- a/milo/plugins/activityView.lua +++ b/milo/plugins/activityView.lua @@ -17,7 +17,6 @@ Right-clicking on the activity monitor will reset the totals.]] local wizardPage = UI.WizardPage { title = 'Activity Monitor', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = 6, marginRight = 0, diff --git a/milo/plugins/backupView.lua b/milo/plugins/backupView.lua index f52a00e..f53516a 100644 --- a/milo/plugins/backupView.lua +++ b/milo/plugins/backupView.lua @@ -3,7 +3,6 @@ local Event = require('opus.event') local Milo = require('milo') local UI = require('opus.ui') -local colors = _G.colors local device = _G.device local fs = _G.fs local os = _G.os @@ -23,7 +22,6 @@ Backup configuration files each minecraft day. local wizardPage = UI.WizardPage { title = 'Backup Drive', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, value = string.format(template, Ansi.yellow, Ansi.reset), diff --git a/milo/plugins/brewingStandView.lua b/milo/plugins/brewingStandView.lua index 88b550a..c97004e 100644 --- a/milo/plugins/brewingStandView.lua +++ b/milo/plugins/brewingStandView.lua @@ -18,7 +18,6 @@ Note that you do not need to import items from the brewing stand or export blaze local wizardPage = UI.WizardPage { title = 'Brewing Stand', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, value = string.format(template, Ansi.yellow, Ansi.reset), diff --git a/milo/plugins/emitterView.lua b/milo/plugins/emitterView.lua index f3c51d3..6198a6a 100644 --- a/milo/plugins/emitterView.lua +++ b/milo/plugins/emitterView.lua @@ -4,12 +4,10 @@ local itemDB = require('core.itemDB') local colors = _G.colors local device = _G.device -local context = Milo:getContext() local wizardPage = UI.WizardPage { title = 'Level Emitter', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, y = 1, height = 2, diff --git a/milo/plugins/inputChestView.lua b/milo/plugins/inputChestView.lua index fe0fd9a..5709f3b 100644 --- a/milo/plugins/inputChestView.lua +++ b/milo/plugins/inputChestView.lua @@ -1,7 +1,6 @@ local Ansi = require('opus.ansi') local UI = require('opus.ui') -local colors = _G.colors local device = _G.device --[[ Configuration Screen ]] @@ -14,7 +13,6 @@ Any items placed in this chest will be imported into storage. local inputChestWizardPage = UI.WizardPage { title = 'Input Chest', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, value = string.format(template, Ansi.yellow, Ansi.reset), diff --git a/milo/plugins/item.lua b/milo/plugins/item.lua index c6dcd85..75c2fc4 100644 --- a/milo/plugins/item.lua +++ b/milo/plugins/item.lua @@ -41,7 +41,6 @@ function page:eventHandler(event) elseif event.type == 'focus_change' then self.statusBar:setStatus(event.focused.help) - self.statusBar:draw() elseif event.type == 'success_message' then self.notification:success(event.message) diff --git a/milo/plugins/item/infoTab.lua b/milo/plugins/item/infoTab.lua index 92bb37d..fb96fc1 100644 --- a/milo/plugins/item/infoTab.lua +++ b/milo/plugins/item/infoTab.lua @@ -1,14 +1,11 @@ local Ansi = require('opus.ansi') local UI = require('opus.ui') -local colors = _G.colors - local infoTab = UI.Tab { tabTitle = 'Info', index = 4, - backgroundColor = colors.cyan, textArea = UI.TextArea { - x = 2, ex = -2, y = 2, + x = 2, ex = -2, y = 2, ey = -2, }, } diff --git a/milo/plugins/item/machinesTab.lua b/milo/plugins/item/machinesTab.lua index 4ff7e04..8f52595 100644 --- a/milo/plugins/item/machinesTab.lua +++ b/milo/plugins/item/machinesTab.lua @@ -9,7 +9,6 @@ local context = Milo:getContext() local machinesTab = UI.Tab { tabTitle = 'Machine', index = 3, - backgroundColor = colors.cyan, grid = UI.ScrollingGrid { x = 2, ex = -2, y = 2, ey = -2, disableHeader = true, diff --git a/milo/plugins/item/recipeTab.lua b/milo/plugins/item/recipeTab.lua index 0a08fca..93e321f 100644 --- a/milo/plugins/item/recipeTab.lua +++ b/milo/plugins/item/recipeTab.lua @@ -3,12 +3,9 @@ local itemDB = require('core.itemDB') local Milo = require('milo') local UI = require('opus.ui') -local colors = _G.colors - local recipeTab = UI.Tab { tabTitle = 'Recipe', index = 2, - backgroundColor = colors.cyan, grid = UI.ScrollingGrid { x = 2, ex = -2, y = 2, ey = -4, disableHeader = true, diff --git a/milo/plugins/item/resetTab.lua b/milo/plugins/item/resetTab.lua index a0a12ae..0d9d34d 100644 --- a/milo/plugins/item/resetTab.lua +++ b/milo/plugins/item/resetTab.lua @@ -9,7 +9,7 @@ local context = Milo:getContext() local resetTab = UI.Tab { tabTitle = 'Reset', index = 5, - backgroundColor = colors.cyan, + noFill = true, textArea = UI.TextArea { y = 2, ey = 6, textColor = colors.yellow, diff --git a/milo/plugins/jobMonitor.lua b/milo/plugins/jobMonitor.lua index 846e279..a548fcf 100644 --- a/milo/plugins/jobMonitor.lua +++ b/milo/plugins/jobMonitor.lua @@ -15,7 +15,6 @@ local os = _G.os local wizardPage = UI.WizardPage { title = 'Crafting Monitor', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = 3, marginRight = 0, diff --git a/milo/plugins/massStorageView.lua b/milo/plugins/massStorageView.lua deleted file mode 100644 index e198b3e..0000000 --- a/milo/plugins/massStorageView.lua +++ /dev/null @@ -1,43 +0,0 @@ -local Ansi = require('opus.ansi') -local UI = require('opus.ui') - -local colors = _G.colors -local device = _G.device - ---[[ Configuration Screen ]] -local template = -[[%sWarning%s - -Must an interface for Refined Storage / Applied Energistics. - -Add all speed upgrades possible. -]] - -local wizardPage = UI.WizardPage { - title = 'Mass Storage', - index = 2, - backgroundColor = colors.cyan, - [1] = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = string.format(template, Ansi.red, Ansi.reset), - }, -} - -function wizardPage:isValidFor(node) - if node.mtype == 'storage' then - local m = device[node.name] - return m and m.listAvailableItems - end -end - -function wizardPage:setNode(node) - self.node = node -end - -function wizardPage:validate() - self.node.adapterType = 'massAdapter' - return true -end - --- disable until a way is found to transfer between 2 non-transferrable nodes --- UI:getPage('nodeWizard').wizard:add({ inputChest = wizardPage }) diff --git a/milo/plugins/remote/setup.lua b/milo/plugins/remote/setup.lua index 5d12d21..856278d 100644 --- a/milo/plugins/remote/setup.lua +++ b/milo/plugins/remote/setup.lua @@ -10,12 +10,11 @@ local STARTUP_FILE = 'usr/autorun/miloRemote.lua' local context = ({ ... })[1] local setup = UI.SlideOut { - backgroundColor = colors.cyan, titleBar = UI.TitleBar { title = 'Remote Setup', }, form = UI.Form { - x = 2, ex = -2, y = 2, ey = -1, + y = 2, ey = -1, [1] = UI.TextEntry { formLabel = 'Server', formKey = 'server', help = 'ID for the server', @@ -40,18 +39,18 @@ local setup = UI.SlideOut { formLabel = 'Run on startup', formKey = 'runOnStartup', help = 'Run this program on startup' }, - info = UI.TextArea { - x = 1, ex = -1, y = 6, ey = -4, - textColor = colors.yellow, - marginLeft = 0, - marginRight = 0, - value = [[The Milo turtle must connect to a manipulator with a ]] .. - [[bound introspection module. The neural interface must ]] .. - [[also have an introspection module.]], - }, + }, + info = UI.TextArea { + x = 2, ex = -2, y = 8, ey = -4, + textColor = colors.yellow, + marginLeft = 0, + marginRight = 0, + value = [[The Milo turtle must connect to a manipulator with a ]] .. + [[bound introspection module. The neural interface must ]] .. + [[also have an introspection module.]], }, statusBar = UI.StatusBar { - backgroundColor = colors.cyan, + backgroundColor = 'primary', }, } diff --git a/milo/plugins/speakerView.lua b/milo/plugins/speakerView.lua index c5668dc..1b5cd3c 100644 --- a/milo/plugins/speakerView.lua +++ b/milo/plugins/speakerView.lua @@ -14,7 +14,6 @@ end local wizardPage = UI.WizardPage { title = 'Speaker', index = 2, - backgroundColor = colors.cyan, [1] = UI.Text { x = 2, y = 2, textColor = colors.yellow, diff --git a/milo/plugins/statsView.lua b/milo/plugins/statsView.lua index 9f9c0a2..b6ab90c 100644 --- a/milo/plugins/statsView.lua +++ b/milo/plugins/statsView.lua @@ -18,7 +18,6 @@ Right-clicking on the activity monitor will reset the totals.]] local wizardPage = UI.WizardPage { title = 'Status Monitor', index = 2, - backgroundColor = colors.cyan, [1] = UI.TextArea { x = 2, ex = -2, y = 2, ey = 6, marginRight = 0, @@ -86,6 +85,7 @@ local function createPage(node) [1] = UI.Tab { tabTitle = 'Overview', backgroundColor = colors.black, + noFill = true, onlineLabel = UI.Text { x = 2, y = 2, value = 'Storage Status', @@ -136,12 +136,14 @@ local function createPage(node) }, [2] = UI.Tab { tabTitle = 'Stats', + noFill = true, textArea = UI.TextArea { y = 3, }, }, [3] = UI.Tab { tabTitle = 'Storage', + noFill = true, grid = UI.ScrollingGrid { y = 2, columns = { @@ -156,6 +158,7 @@ local function createPage(node) }, [4] = UI.Tab { tabTitle = 'Offline', + noFill = true, grid = UI.ScrollingGrid { y = 2, columns = { @@ -166,12 +169,14 @@ local function createPage(node) }, [5] = UI.Tab { tabTitle = 'Activity', + noFill = true, term = UI.Embedded { --visible = true, }, }, [6] = UI.Tab { tabTitle = 'Tasks', + noFill = true, grid = UI.ScrollingGrid { y = 2, values = context.tasks, diff --git a/milo/plugins/storageView.lua b/milo/plugins/storageView.lua index b1d51c2..18e2c0e 100644 --- a/milo/plugins/storageView.lua +++ b/milo/plugins/storageView.lua @@ -7,7 +7,6 @@ local device = _G.device local storageView = UI.WizardPage { title = 'Storage Options - General', index = 2, - backgroundColor = colors.cyan, form = UI.Form { x = 2, ex = -2, y = 1, ey = -2, manualControls = true, @@ -62,7 +61,6 @@ UI:getPage('nodeWizard').wizard:add({ storageGeneral = storageView }) local lockView = UI.WizardPage { title = 'Storage Options - Locking', index = 3, - backgroundColor = colors.cyan, form = UI.Form { x = 2, ex = -2, y = 1, ey = 3, manualControls = true, diff --git a/milo/plugins/transferView.lua b/milo/plugins/transferView.lua index 3fa70ff..df6079e 100644 --- a/milo/plugins/transferView.lua +++ b/milo/plugins/transferView.lua @@ -12,7 +12,6 @@ local context = Milo:getContext() local wizardPage = UI.WizardPage { title = 'Transfer Inventory', index = 2, - backgroundColor = colors.cyan, grid = UI.ScrollingGrid { y = 2, ey = -2, values = context.storage.nodes, diff --git a/milo/plugins/trashcanView.lua b/milo/plugins/trashcanView.lua index 2de4a4e..fc1e70f 100644 --- a/milo/plugins/trashcanView.lua +++ b/milo/plugins/trashcanView.lua @@ -9,7 +9,6 @@ local device = _G.device local wizardPage = UI.WizardPage { title = 'Trashcan', index = 2, - backgroundColor = colors.cyan, info = UI.TextArea { x = 1, ex = -1, y = 2, ey = 4, textColor = colors.yellow, diff --git a/miners/scanningMiner.lua b/miners/scanningMiner.lua index 1d5e361..588259b 100644 --- a/miners/scanningMiner.lua +++ b/miners/scanningMiner.lua @@ -637,8 +637,7 @@ Event.addRoutine(function() end) UI:setPage(page) -UI:pullEvents() -UI.term:reset() +UI:start() turtle.reset() diff --git a/monitor/mwm.lua b/monitor/mwm.lua index a1e5eb1..a397606 100644 --- a/monitor/mwm.lua +++ b/monitor/mwm.lua @@ -121,7 +121,10 @@ function Process:new(args) self.terminal = self.window self.container.canvas.parent = monitor.canvas - table.insert(monitor.canvas.layers, 1, self.container.canvas) + if not monitor.canvas.children then + monitor.canvas.children = { } + end + table.insert(monitor.canvas.children, 1, self.container.canvas) self.container.canvas:setVisible(true) --self.container.getSize = self.window.getSize diff --git a/neural/Equipment.lua b/neural/Equipment.lua new file mode 100644 index 0000000..32b849e --- /dev/null +++ b/neural/Equipment.lua @@ -0,0 +1,142 @@ +local Event = require('opus.event') +local itemDB = require('core.itemDB') +local neural = require('neural.interface') +local UI = require('opus.ui') +local Util = require('opus.util') + +local device = _G.device + +neural.assertModules({ + 'plethora:sensor', + 'plethora:kinetic', + 'plethora:introspection', +}) +UI:configure('Equipment', ...) + +local equipment = device.neuralInterface.getEquipment() + +local slots = { + 'primary', + 'offhand', + 'boots', + 'leggings', + 'chest', + 'helmet', +} + +local page = UI.Page { + menuBar = UI.MenuBar { + buttons = { + { text = 'Drop', event = 'drop' }, + { text = 'Suck', event = 'suck' }, + }, + }, + grid = UI.Grid { + y = 2, + columns = { + { heading = 'Slot', key = 'index', width = 7 }, + { heading = 'Name', key = 'displayName' }, + { heading = 'Count', key = 'count', width = 5, align = 'right' }, + }, + sortColumn = 'index', + accelerators = { + grid_select = 'show_detail', + }, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + if row.name then + local item = itemDB:get( + table.concat({ row.name, row.damage, row.nbtHash }, ':'), + function() + return equipment.getItemMeta(row.index) + end) + row.displayName = item.displayName + else + row.displayName = 'empty' + end + row.index = slots[row.index] + return row + end, + }, + accelerators = { + [ 'control-q' ] = 'quit', + }, + detail = UI.SlideOut { + menuBar = UI.MenuBar { + buttons = { + { text = 'Back', event = 'slide_hide' }, + }, + }, + grid = UI.ScrollingGrid { + y = 2, + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Value', key = 'value' }, + }, + sortColumn = 'name', + accelerators = { + grid_select = 'inspect', + }, + }, + show = function(self, slot) + local detail = equipment.getItemMeta(slot.index) + local t = { } + for k,v in pairs(detail) do + table.insert(t, { + name = k, + value = v, + }) + end + self.grid:setValues(t) + self.grid:setIndex(1) + UI.SlideOut.show(self) + end, + }, + enable = function(self) + self:refresh() + UI.Page.enable(self) + end, + refresh = function(self) + local t = { } + local list = equipment.list() + for i = 1, equipment.size() do + local v = list[i] or { } + v.index = i + table.insert(t, v) + end + self.grid:setValues(t) + self.grid:draw() + end, + eventHandler = function(self, event) + if event.type == 'quit' then + UI:quit() + + elseif event.type == 'show_detail' then + if event.selected.name then + self.detail:show(event.selected) + end + + elseif event.type == 'drop' then + local selected = self.grid:getSelected() + equipment.drop(selected.index) + self:refresh() + + elseif event.type == 'suck' then + local selected = self.grid:getSelected() + equipment.suck(selected.index) + self:refresh() + end + + UI.Page.eventHandler(self, event) + end, +} + +Event.onInterval(1, function() + page:refresh() + page:sync() +end) + +UI:setPage(page) +UI:start() + +itemDB:flush() diff --git a/neural/Scanner.lua b/neural/Scanner.lua index 11edab2..4dd6be5 100644 --- a/neural/Scanner.lua +++ b/neural/Scanner.lua @@ -182,7 +182,7 @@ end function page:eventHandler(event) if event.type == 'quit' then - Event.exitPullEvents() + UI:quit() elseif event.type == 'scan' then self:scan() @@ -211,7 +211,7 @@ Event.onTimeout(0, function() page:sync() end) -UI:pullEvents() +UI:start() if canvas then canvas:clear() diff --git a/neural/Sensor.lua b/neural/Sensor.lua index d926c05..42d0b9a 100644 --- a/neural/Sensor.lua +++ b/neural/Sensor.lua @@ -253,7 +253,7 @@ end function page:eventHandler(event) if event.type == 'quit' then - Event.exitPullEvents() + UI:quit() elseif event.type == 'tab_activate' then config.activeTab = event.activated.tabTitle @@ -268,7 +268,7 @@ if config.activeTab then end UI:setPage(page) -UI:pullEvents() +UI:start() if canvas then canvas:clear() diff --git a/neural/etc/apps.db b/neural/etc/apps.db index 61040b2..31c01d7 100644 --- a/neural/etc/apps.db +++ b/neural/etc/apps.db @@ -29,4 +29,10 @@ run = "ores.lua", requires = "neuralInterface", }, + [ "7f2465ac7cefab2766e6ee0714647089df9364b0ff09858c84b21b8a436a845d" ] = { + title = "Equipment", + category = "Neural", + run = "Equipment", + requires = "neuralInterface", + } } diff --git a/neural/mobRancher.lua b/neural/mobRancher.lua index c09d7d3..cef0975 100644 --- a/neural/mobRancher.lua +++ b/neural/mobRancher.lua @@ -1,6 +1,15 @@ --[[ - Breed either cows or sheep. - Must be run on a mob with the same height. + Changed to use a 2-high mob (smaller mobs may work ?) + Updated due to entity.look working correctly now. + + The mob looks head-on to the lever. Make sure the + lever is accessible by the mob. + + Laser is now optional - if no laser, the mobs will be + punched (or provide a stick). Best mob may be a + skeleton (unlimited ammo). + + Feeding hand has been changed to off-hand. ]] local Array = require('opus.array') @@ -21,80 +30,100 @@ local WALK_SPEED = 1.5 neural.assertModules({ 'plethora:sensor', 'plethora:scanner', - 'plethora:laser', + --'plethora:laser', 'plethora:kinetic', 'plethora:introspection', }) +local caninimals = config.animals or { [ config.animal ] = true } +local animals = { } +for k in pairs(caninimals) do + animals[k] = { } +end + local fed = { } local function resupply() - local slot = neural.getEquipment().list()[1] + local slot = neural.getEquipment().list()[2] if slot and slot.count > 32 then return end print('resupplying') for _ = 1, 2 do - local dispenser = Map.find(neural.scan(), 'name', 'minecraft:dispenser') + local dispenser = Map.find(neural.scan(), 'name', 'minecraft:lever') if not dispenser then print('dispenser not found') break end - if math.abs(dispenser.x) <= 1 and math.abs(dispenser.z) <= 1 then - neural.lookAt(dispenser) + if math.abs(dispenser.x) <= 1.2 and math.abs(dispenser.z) <= 1.2 then + neural.lookAt({ x = dispenser.x, y = dispenser.y, z = dispenser.z }) for _ = 1, 8 do - neural.use(0, 'off') + if not neural.use(0, 'off') then + break + end os.sleep(.2) - neural.getEquipment().suck(1, 64) + neural.getEquipment().suck(2, 64) end break else - neural.walkTo({ x = dispenser.x, y = 0, z = dispenser.z }, WALK_SPEED) + neural.walkTo({ x = dispenser.x, y = 0, z = dispenser.z }, WALK_SPEED, .5) end end end local function breed(entity) - print('breeding') - entity.lastFed = os.clock() - fed[entity.id] = entity + print('breeding ' .. entity.name) neural.walkTo(entity, WALK_SPEED, 1) entity = neural.getMetaByID(entity.id) if entity then neural.lookAt(entity) - neural.use(1) + if neural.use(1, 'off') then + entity.lastFed = os.clock() + fed[entity.id] = entity + end os.sleep(.1) end end local function kill(entity) - print('killing') - neural.walkTo(entity, WALK_SPEED, 2.5) + print('killing ' .. entity.name) + neural.walkTo(entity, WALK_SPEED, (neural.fire or neural.shoot) and 2.5 or .5) entity = neural.getMetaByID(entity.id) if entity then neural.lookAt(entity) - neural.fireAt({ x = entity.x, y = 0, z = entity.z }) + if neural.fire or neural.shoot then + neural.shootAt(entity) + else + neural.swing() + end Sound.play('entity.firework.launch') os.sleep(.2) end end -local function getEntities() - local sheep = Array.filter(neural.sense(), function(entity) - if entity.name == 'Sheep' and entity.y > -.5 then - return true - end - end) - if #sheep > config.maxAdults then - return sheep +local function shuffle(tbl) + for i = #tbl, 2, -1 do + local j = math.random(i) + tbl[i], tbl[j] = tbl[j], tbl[i] end + return tbl +end - return Map.filter(neural.sense(), function(entity) - if entity.name == config.animal and entity.y > -.5 then - return true +local function getEntities() + return shuffle(Array.filter(neural.sense(), function(entity) + if animals[entity.name] then + if not animals[entity.name].height then + entity = neural.getMetaByID(entity.id) + if entity and not entity.isChild and entity.motionX == 0 and entity.motionZ == 0 then + animals[entity.name].height = entity.y + return true + end + elseif entity.y == animals[entity.name].height then + return true + end end - end) + end)) end local function getHungry(entities) @@ -105,13 +134,25 @@ local function getHungry(entities) end end -local function randomEntity(entities) - local r = math.random(1, Map.size(entities)) - local i = 1 +local function getCount(entities, name) + local c = 0 for _, v in pairs(entities) do - i = i + 1 - if i > r then - return v + if v.name == name then + c = c + 1 + end + end + print(name .. ' ' .. c) + return c +end + +local function getKillable(entities) + print('map: ' .. Map.size(fed)) + if Map.size(fed) > 1000 then + fed = { } + end + for name in pairs(animals) do + if getCount(entities, name) > config.maxAdults then + return Array.find(entities, 'name', name) end end end @@ -120,9 +161,10 @@ while true do resupply() local entities = getEntities() + local killable = getKillable(entities) - if Map.size(entities) > config.maxAdults then - kill(randomEntity(entities)) + if killable then + kill(killable) else local entity = getHungry(entities) if entity then diff --git a/recipeBook/recipeBook.lua b/recipeBook/recipeBook.lua index 3aa67c3..65d4fc7 100644 --- a/recipeBook/recipeBook.lua +++ b/recipeBook/recipeBook.lua @@ -61,12 +61,12 @@ local page = UI.Page { autospace = true, }, add = UI.SlideOut { - backgroundColor = colors.cyan, + height = 9, y = -9, titleBar = UI.TitleBar { title = 'Add a new book', }, form = UI.Form { - x = 2, ex = -2, y = 2, ey = -1, + y = 2, [1] = UI.TextEntry { formLabel = 'Name', formKey = 'name', shadowText = 'Friendly name', @@ -100,11 +100,10 @@ function page.info:draw() self:clear() if book then - self:setCursorPos(1, 1) self:print( - string.format('Name: %s%s%s\n', Ansi.yellow, book.name, Ansi.reset)) - self:print( - string.format('Version: %s%s%s\n', Ansi.yellow, book.version, Ansi.reset)) + string.format('Name: %s%s%s\nVersion: %s%s%s\n', + Ansi.yellow, book.name, Ansi.reset, + Ansi.yellow, book.version, Ansi.reset)) self.button.text = book.enabled and 'Disable' or 'Enable' self.button:draw() @@ -173,11 +172,11 @@ function page:eventHandler(event) self.info:draw() elseif event.type == 'quit' then - UI:exitPullEvents() + UI:quit() end UI.Page.eventHandler(self, event) end UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/screenSaver/autorun/saver.lua b/screenSaver/autorun/saver.lua index 274b82f..db33bc6 100644 --- a/screenSaver/autorun/saver.lua +++ b/screenSaver/autorun/saver.lua @@ -11,7 +11,7 @@ if not multishell then end local config = Config.load('saver', { - enabled = true, + enabled = false, timeout = 60, random = true, specific = nil, diff --git a/screenSaver/system/saver.lua b/screenSaver/system/saver.lua index b8fa7a3..7391f2d 100644 --- a/screenSaver/system/saver.lua +++ b/screenSaver/system/saver.lua @@ -6,23 +6,26 @@ local config = Config.load('saver', { timeout = 60, }) -local tab = UI.Tab { +return UI.Tab { tabTitle = 'Screen Saver', description = 'Screen saver', + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 5, + }, label1 = UI.Text { - x = 2, y = 3, + x = 3, y = 3, value = 'Enabled', }, checkbox = UI.Checkbox { - x = 20, y = 3, + x = 21, y = 3, value = config.enabled }, label2 = UI.Text { - x = 2, y = 4, + x = 3, y = 4, value = 'Timeout', }, timeout = UI.TextEntry { - x = 20, y = 4, width = 6, + x = 21, y = 4, width = 6, limit = 4, transform = 'number', value = config.timeout, @@ -31,28 +34,25 @@ local tab = UI.Tab { }, }, button = UI.Button { - x = 20, y = 6, - text = 'Update', + x = -8, ex = -2, y = -2, + text = 'Apply', event = 'update', }, -} + eventHandler = function(self, event) + if event.type =='checkbox_change' then + config.enabled = not not event.checked -function tab:eventHandler(event) - if event.type =='checkbox_change' then - config.enabled = not not event.checked + elseif event.type == 'update' then + if self.timeout.value then + config.timeout = self.timeout.value + Config.update('saver', config) - elseif event.type == 'update' then - if self.timeout.value then - config.timeout = self.timeout.value - Config.update('saver', config) - - self:emit({ type = 'success_message', message = 'Settings updated' }) - os.queueEvent('config_update', 'saver', config) - else - self:emit({ type = 'error_message', message = 'Invalid timeout' }) + self:emit({ type = 'success_message', message = 'Settings updated' }) + os.queueEvent('config_update', 'saver', config) + else + self:emit({ type = 'error_message', message = 'Invalid timeout' }) + end end - end - return UI.Tab.eventHandler(self, event) -end - -return tab + return UI.Tab.eventHandler(self, event) + end, +} diff --git a/secure/autorun/lock.lua b/secure/autorun/lock.lua index f951dfc..a8965d7 100644 --- a/secure/autorun/lock.lua +++ b/secure/autorun/lock.lua @@ -49,7 +49,7 @@ local function buildLockScreen() #self.pass.value > 0 and Security.verifyPassword(SHA.compute(self.pass.value)) then - UI:exitPullEvents() -- valid + UI:quit() -- valid else self.notification:error('Invalid password', math.max(counter, 2)) self:sync() @@ -66,7 +66,7 @@ local function buildLockScreen() Event.onTerminate(function() return false end) UI:setPage(page) - UI:pullEvents() + UI:start() -- restart lock timer timer = os.startTimer(config.timeout) diff --git a/secure/system/secure.lua b/secure/system/secure.lua index 113ad9b..9fd0137 100644 --- a/secure/system/secure.lua +++ b/secure/system/secure.lua @@ -9,20 +9,23 @@ local config = Config.load('secure', { local tab = UI.Tab { tabTitle = 'Secure', description = 'Secure options', + [1] = UI.Window { + x = 2, y = 2, ex = -2, ey = 5, + }, label1 = UI.Text { - x = 2, y = 3, + x = 3, y = 3, value = 'Screen Locking', }, checkbox = UI.Checkbox { - x = 20, y = 3, + x = 21, y = 3, value = config.enabled }, label2 = UI.Text { - x = 2, y = 4, + x = 3, y = 4, value = 'Lock timeout', }, timeout = UI.TextEntry { - x = 20, y = 4, width = 6, + x = 21, y = 4, width = 6, limit = 4, transform = 'number', value = config.timeout, @@ -31,8 +34,8 @@ local tab = UI.Tab { }, }, button = UI.Button { - x = 20, y = 6, - text = 'Update', + x = -8, ex = -2, y = -2, + text = 'Apply', event = 'update', }, } diff --git a/swshop/Shoplogs.lua b/swshop/Shoplogs.lua index 5766b08..5a478e3 100644 --- a/swshop/Shoplogs.lua +++ b/swshop/Shoplogs.lua @@ -189,4 +189,4 @@ end) page.grid:loadTransactions() UI:setPage(page) -UI:pullEvents() +UI:start() diff --git a/swshop/shopConfig.lua b/swshop/shopConfig.lua index 026ad16..857d0c1 100644 --- a/swshop/shopConfig.lua +++ b/swshop/shopConfig.lua @@ -9,7 +9,6 @@ local os = _G.os local wizardPage = UI.WizardPage { title = 'Store Front', index = 2, - backgroundColor = colors.cyan, form = UI.Form { x = 2, ex = -2, y = 1, ey = -2, manualControls = true, @@ -90,7 +89,6 @@ end local passwordPage = UI.WizardPage { title = 'Krist Settings', index = 3, - backgroundColor = colors.cyan, form = UI.Form { x = 2, ex = -2, y = 1, ey = -2, manualControls = true, @@ -112,7 +110,7 @@ local passwordPage = UI.WizardPage { preview = UI.TextEntry { formIndex = 4, formLabel = 'Using address', formKey = 'address', - backgroundColor = colors.cyan, + backgroundColor = 'primary', textColor = colors.yellow, inactive = true, }, diff --git a/swshop/shopTab.lua b/swshop/shopTab.lua index 169d6ee..f731ebf 100644 --- a/swshop/shopTab.lua +++ b/swshop/shopTab.lua @@ -10,7 +10,7 @@ local shopTab = UI.Tab { tabTitle = 'Store', index = 2, form = UI.Form { - x = 2, ex = -2, y = 1, ey = -2, + x = 2, ex = -2, y = 2, ey = -2, manualControls = true, [1] = UI.TextEntry { formLabel = 'Name', formKey = 'name', diff --git a/turtle/autorun/6.tl3.lua b/turtle/init/6.tl3.lua similarity index 100% rename from turtle/autorun/6.tl3.lua rename to turtle/init/6.tl3.lua -- 2.49.1 From 8d014c0098ce1003965f6faded64c522db3491a3 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Wed, 22 Apr 2020 23:36:03 -0600 Subject: [PATCH 08/24] remove a few screen savers - some bugfixes --- ccemux/system/ccemux.lua | 7 +++++-- common/DiskCopy.lua | 4 ++-- common/Events.lua | 4 ---- common/edit.lua | 6 +++++- screenSaver/etc/fstab | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) diff --git a/ccemux/system/ccemux.lua b/ccemux/system/ccemux.lua index f05df3d..d2f0e43 100644 --- a/ccemux/system/ccemux.lua +++ b/ccemux/system/ccemux.lua @@ -46,6 +46,7 @@ local tab = UI.Tab { columns = { { heading = 'Side', key = 'side', width = 8 }, { heading = 'Type', key = 'type' }, + { heading = 'ID', key = 'args', width = 4 }, }, }, } @@ -56,7 +57,7 @@ function tab:updatePeripherals(config) table.insert(self.grid.values, { side = k, type = v.type, - args = v.args, + args = v.args and v.args.id, }) end self.grid:update() @@ -83,7 +84,6 @@ function tab:eventHandler(event) self:emit({ type = 'error_message', message = 'Invalid drive ID' }) else ccemux.detach(event.values.side) - ccemux.attach(event.values.side, event.values.type) local config = Config.load('ccemux') config[event.values.side] = { @@ -93,6 +93,9 @@ function tab:eventHandler(event) config[event.values.side].args = { id = event.values.drive_id } + ccemux.attach(event.values.side, event.values.type, { id = event.values.drive_id }) + else + ccemux.attach(event.values.side, event.values.type) end Config.update('ccemux', config) self:updatePeripherals(config) diff --git a/common/DiskCopy.lua b/common/DiskCopy.lua index b3efc4e..cf9d616 100644 --- a/common/DiskCopy.lua +++ b/common/DiskCopy.lua @@ -94,7 +94,7 @@ function page:enable() self.eject.value = config.eject self.automatic.value = config.automatic - self.dir.x = math.floor((self.width / 2) - 3) + 1 + self.dir:move(math.floor((self.width / 2) - 3) + 1, self.dir.y) UI.Page.enable(self) end @@ -218,7 +218,7 @@ function page:copy() self.progress:sync() self.progress.value = 0 - self.progress:clear() +-- self.progress:clear() self:scan() diff --git a/common/Events.lua b/common/Events.lua index 783e38c..ae58ca2 100644 --- a/common/Events.lua +++ b/common/Events.lua @@ -43,10 +43,6 @@ local page = UI.Page { return row end, - draw = function(self) - self:adjustWidth() - UI.Grid.draw(self) - end, }, accelerators = { f = 'filter', diff --git a/common/edit.lua b/common/edit.lua index 804d40c..c2097e3 100644 --- a/common/edit.lua +++ b/common/edit.lua @@ -575,6 +575,10 @@ local page = UI.Page { actions.dirty_all() actions.redraw() end, + setCursorPos = function(self, cx, cy) + self.cursorBlink = cy >= 1 and cy <= self.height + UI.Window.setCursorPos(self, cx, cy) + end, draw = function() actions.redraw() end, @@ -715,7 +719,7 @@ local function redraw() if dirty.y > 0 then for dy = 1, h do local sLine = tLines[dy + scrollY] - if sLine ~= nil then + if sLine and #sLine > 0 then if dy + scrollY >= dirty.y and dy + scrollY <= dirty.ey then page.editor:clearLine(dy) writeHighlighted(sLine, dy + scrollY, dy) diff --git a/screenSaver/etc/fstab b/screenSaver/etc/fstab index e89b176..31905fb 100644 --- a/screenSaver/etc/fstab +++ b/screenSaver/etc/fstab @@ -4,11 +4,11 @@ packages/screenSaver/savers/visualizer.lua urlfs https://raw.githubusercontent.c packages/screenSaver/savers/random.lua urlfs https://pastebin.com/raw/XXW0r5zt packages/screenSaver/savers/melting.lua urlfs http://pastebin.com/raw/raUv6Pap packages/screenSaver/savers/bubbles.lua urlfs https://pastebin.com/raw/JCR8YTww -packages/screenSaver/savers/fire.lua urlfs https://pastebin.com/raw/4CY4AYj3 +#packages/screenSaver/savers/fire.lua urlfs https://pastebin.com/raw/4CY4AYj3 packages/screenSaver/savers/rain.lua urlfs https://pastebin.com/raw/P86Hm99N packages/screenSaver/savers/snow.lua urlfs https://pastebin.com/raw/j1dwdLKw packages/screenSaver/savers/fireworks.lua urlfs https://pastebin.com/raw/Yn5sWt3f packages/screenSaver/savers/starfield.lua urlfs https://pastebin.com/raw/AQm9R6nT -packages/screenSaver/savers/nyan.lua urlfs https://pastebin.com/raw/YJnT6Adu +#packages/screenSaver/savers/nyan.lua urlfs https://pastebin.com/raw/YJnT6Adu packages/screenSaver/savers/bounce.lua urlfs https://pastebin.com/raw/WLrfdyNy packages/screenSaver/savers/antfarm.lua urlfs https://pastebin.com/raw/h9x3h7aw \ No newline at end of file -- 2.49.1 From ef0886ec85cd9003e75c7b0b9fd1f2f0e64d43a0 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sun, 26 Apr 2020 19:39:28 -0600 Subject: [PATCH 09/24] wizard and tab rework --- ccemux/system/ccemux.lua | 2 +- common/Turtles.lua | 336 +++++++++++++------------ common/etc/apps.db | 2 +- common/multiMiner.lua | 6 +- lzwfs/system/lzwfs.lua | 2 +- milo/core/learnWizard.lua | 32 ++- milo/core/machines.lua | 396 ++++++++++++++---------------- milo/plugins/item/infoTab.lua | 2 +- milo/plugins/item/machinesTab.lua | 2 +- milo/plugins/item/manageTab.lua | 2 +- milo/plugins/item/recipeTab.lua | 2 +- milo/plugins/item/resetTab.lua | 2 +- milo/plugins/remote/autostore.lua | 4 +- milo/plugins/statsView.lua | 16 +- neural/Sensor.lua | 8 +- screenSaver/system/saver.lua | 2 +- secure/system/secure.lua | 2 +- shellex/apis/transfer.lua | 2 +- swshop/shopTab.lua | 2 +- turtle/system/turtle.lua | 2 +- 20 files changed, 398 insertions(+), 426 deletions(-) diff --git a/ccemux/system/ccemux.lua b/ccemux/system/ccemux.lua index d2f0e43..d066bab 100644 --- a/ccemux/system/ccemux.lua +++ b/ccemux/system/ccemux.lua @@ -6,7 +6,7 @@ local ccemux = _G.ccemux local sides = { 'bottom', 'top', 'back', 'front', 'right', 'left' } local tab = UI.Tab { - tabTitle = 'CCEmuX', + title = 'CCEmuX', description = 'CCEmuX peripherals', form = UI.Form { x = 2, ex = -2, y = 2, ey = 5, diff --git a/common/Turtles.lua b/common/Turtles.lua index cc48825..07d89e6 100644 --- a/common/Turtles.lua +++ b/common/Turtles.lua @@ -5,7 +5,6 @@ local Socket = require('opus.socket') local UI = require('opus.ui') local Util = require('opus.util') -local colors = _G.colors local fs = _G.fs local multishell = _ENV.multishell local network = _G.network @@ -31,7 +30,7 @@ local socket, turtle, page page = UI.Page { coords = UI.Window { - backgroundColor = colors.black, + backgroundColor = 'black', height = 3, marginTop = 1, marginLeft = 1, draw = function(self) @@ -50,156 +49,149 @@ page = UI.Page { }, tabs = UI.Tabs { x = 1, y = 4, ey = -2, - scripts = UI.ScrollingGrid { - tabTitle = 'Run', - backgroundColor = colors.cyan, - columns = { - { heading = '', key = 'label' }, + UI.Tab { + title = 'Run', + scripts = UI.ScrollingGrid { + backgroundColor = 'primary', + columns = { + { heading = '', key = 'label' }, + }, + disableHeader = true, + sortColumn = 'label', + autospace = true, + draw = function(self) + Util.clear(self.values) + local files = fs.list(SCRIPTS_PATH) + for _,path in pairs(files) do + table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) + end + self:update() + UI.ScrollingGrid.draw(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + page:runScript(event.selected.label) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true + end, }, - disableHeader = true, - sortColumn = 'label', - autospace = true, - draw = function(self) - Util.clear(self.values) - local files = fs.list(SCRIPTS_PATH) - for _,path in pairs(files) do - table.insert(self.values, { label = path, path = fs.combine(SCRIPTS_PATH, path) }) - end - self:update() - UI.ScrollingGrid.draw(self) - end, - eventHandler = function(self, event) - if event.type == 'grid_select' then - page:runScript(event.selected.label) - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true - end, }, - turtles = UI.ScrollingGrid { - tabTitle = 'Select', - backgroundColor = colors.cyan, - columns = { - { heading = 'label', key = 'label' }, - { heading = 'Dist', key = 'distance' }, - { heading = 'Status', key = 'status' }, - { heading = 'Fuel', key = 'fuel' }, - }, - disableHeader = true, - sortColumn = 'label', - autospace = true, - getDisplayValues = function(_, row) - row = Util.shallowCopy(row) - if row.fuel then - row.fuel = Util.toBytes(row.fuel) - end - if row.distance then - row.distance = Util.round(row.distance, 1) - end - return row - end, - draw = function(self) - Util.clear(self.values) - for _,v in pairs(network) do - if v.fuel then - table.insert(self.values, v) + UI.Tab { + title = 'Select', + turtles = UI.ScrollingGrid { + backgroundColor = 'primary', + columns = { + { heading = 'label', key = 'label' }, + { heading = 'Dist', key = 'distance' }, + { heading = 'Status', key = 'status' }, + { heading = 'Fuel', key = 'fuel' }, + }, + disableHeader = true, + sortColumn = 'label', + autospace = true, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + if row.fuel then + row.fuel = Util.toBytes(row.fuel) end - end - self:update() - UI.ScrollingGrid.draw(self) - end, - eventHandler = function(self, event) - if event.type == 'grid_select' then - turtle = event.selected - config.id = event.selected.id - Config.update('Turtles', config) - multishell.setTitle(multishell.getCurrent(), turtle.label) - if socket then - socket:close() - socket = nil + if row.distance then + row.distance = Util.round(row.distance, 1) end - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true - end, - }, - inventory = UI.ScrollingGrid { - backgroundColor = colors.cyan, - tabTitle = 'Inv', - columns = { - { heading = '', key = 'index', width = 2 }, - { heading = '', key = 'count', width = 2 }, - { heading = 'Inventory', key = 'key' }, - }, - disableHeader = true, - sortColumn = 'index', - getRowTextColor = function(self, row, selected) - if turtle and row.selected then - return colors.yellow - end - return UI.ScrollingGrid.getRowTextColor(self, row, selected) - end, - draw = function(self) - local t = turtle - Util.clear(self.values) - if t then - for k,v in pairs(t.inv or { }) do -- new method (less data) - local index, count = k:match('(%d+),(%d+)') - v = { - index = tonumber(index), - key = v, - count = tonumber(count), - } - table.insert(self.values, v) - end - - for _,v in pairs(t.inventory or { }) do - if v.count > 0 then + return row + end, + draw = function(self) + Util.clear(self.values) + for _,v in pairs(network) do + if v.fuel then table.insert(self.values, v) end end - - for _,v in pairs(self.values) do - if v.index == t.slotIndex then - v.selected = true + self:update() + UI.ScrollingGrid.draw(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + turtle = event.selected + config.id = event.selected.id + Config.update('Turtles', config) + multishell.setTitle(multishell.getCurrent(), turtle.label) + if socket then + socket:close() + socket = nil end - if v.key then - v.key = itemDB:getName(v.key) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true + end, + }, + }, + UI.Tab { + title = 'Inv', + inventory = UI.ScrollingGrid { + backgroundColor = 'primary', + columns = { + { heading = '', key = 'index', width = 2 }, + { heading = '', key = 'count', width = 2 }, + { heading = 'Inventory', key = 'key' }, + }, + disableHeader = true, + sortColumn = 'index', + getRowTextColor = function(self, row, selected) + if turtle and row.selected then + return 'yellow' + end + return UI.ScrollingGrid.getRowTextColor(self, row, selected) + end, + draw = function(self) + local t = turtle + Util.clear(self.values) + if t then + for k,v in pairs(t.inv or { }) do -- new method (less data) + local index, count = k:match('(%d+),(%d+)') + v = { + index = tonumber(index), + key = v, + count = tonumber(count), + } + table.insert(self.values, v) + end + + for _,v in pairs(t.inventory or { }) do + if v.count > 0 then + table.insert(self.values, v) + end + end + + for _,v in pairs(self.values) do + if v.index == t.slotIndex then + v.selected = true + end + if v.key then + v.key = itemDB:getName(v.key) + end end end - end - self:adjustWidth() - self:update() - UI.ScrollingGrid.draw(self) - end, - eventHandler = function(self, event) - if event.type == 'grid_select' then - local fn = string.format('turtle.select(%d)', event.selected.index) - page:runFunction(fn) - else - return UI.ScrollingGrid.eventHandler(self, event) - end - return true - end, - }, ---[[ - policy = UI.ScrollingGrid { - tabTitle = 'Mod', - backgroundColor = UI.TabBar.defaults.selectedBackgroundColor, - columns = { - { heading = 'label', key = 'label' }, + self:adjustWidth() + self:update() + UI.ScrollingGrid.draw(self) + end, + eventHandler = function(self, event) + if event.type == 'grid_select' then + local fn = string.format('turtle.select(%d)', event.selected.index) + page:runFunction(fn) + else + return UI.ScrollingGrid.eventHandler(self, event) + end + return true + end, }, - values = policies, - disableHeader = true, - sortColumn = 'label', - autospace = true, }, - ]] - action = UI.Window { - tabTitle = 'Action', - backgroundColor = colors.cyan, + UI.Tab { + title = 'Action', + backgroundColor = 'primary', moveUp = UI.Button { x = 5, y = 2, text = 'up', @@ -233,8 +225,41 @@ page = UI.Page { info = UI.TextArea { x = 15, y = 2, inactive = true, - } + }, + showBlocks = function(self) + local script = [[ + local function inspect(direction) + local s,b = turtle['inspect' .. (direction or '')]() + if not s then + return 'minecraft:air:0' + end + return string.format('%s:%d', b.name, b.metadata) + end + + local bu, bf, bd = inspect('Up'), inspect(), inspect('Down') + return string.format('%s\n%s\n%s', bu, bf, bd) + ]] + + local s, m = self:runFunction(script, true) + self.info:setText(s or m) + end, + eventHandler = function(self, event) + if event.type == 'button_press' then + if event.button.fn then + self:runFunction(event.button.fn, event.button.nowrap) + self:showBlocks() + end + return true + end + return UI.Tab.eventHandler(self, event) + end, }, + enable = function(self) + if config.tab then + self:selectTab(Util.find(self, 'title', config.tab)) + end + UI.Tabs.enable(self) + end }, statusBar = UI.StatusBar { values = { }, @@ -331,24 +356,6 @@ function page:runScript(scriptName) end end -function page:showBlocks() - local script = [[ - local function inspect(direction) - local s,b = turtle['inspect' .. (direction or '')]() - if not s then - return 'minecraft:air:0' - end - return string.format('%s:%d', b.name, b.metadata) - end - - local bu, bf, bd = inspect('Up'), inspect(), inspect('Down') - return string.format('%s\n%s\n%s', bu, bf, bd) - ]] - - local s, m = self:runFunction(script, true) - self.tabs.action.info:setText(s or m) -end - function page:eventHandler(event) if event.type == 'quit' then UI:quit() @@ -357,13 +364,6 @@ function page:eventHandler(event) config.tab = event.button.text Config.update('Turtles', config) - elseif event.type == 'button_press' then - if event.button.fn then - self:runFunction(event.button.fn, event.button.nowrap) - self:showBlocks() - elseif event.button.script then - self:runScript(event.button.script) - end else return UI.Page.eventHandler(self, event) end @@ -393,9 +393,5 @@ Event.onInterval(1, function() end end) -if config.tab then - page.tabs.tabBar:selectTab(config.tab) -end - UI:setPage(page) UI:start() diff --git a/common/etc/apps.db b/common/etc/apps.db index d8608bb..7580ce5 100644 --- a/common/etc/apps.db +++ b/common/etc/apps.db @@ -57,7 +57,7 @@ category = "Apps", requires = "advancedComputer", iconExt = "\030 \031 \128\030d\159\030 \031d\140\030d\031 \155\030 \0315\140\0305\031 \155\030 \128\010\030 \031d\136\145\0315\136\145\031d\153\031 \128\0315\153\010\030 \031 \128\031d\130\140\134\0315\140\134\031 \128", - run = "fileui --exec=hexedit.lua", + run = "fileui --exec=hexedit.lua --title=hexedit", }, [ "fb1c39e9f4f3c2628ad173ab401a6e4e4baf783d" ] = { title = "Sounds", diff --git a/common/multiMiner.lua b/common/multiMiner.lua index d721461..a271261 100644 --- a/common/multiMiner.lua +++ b/common/multiMiner.lua @@ -308,7 +308,7 @@ local containerText = { } local containTab = UI.Tab { - tabTitle = 'Contain', + title = 'Contain', button = UI.Button { x = 2, y = 2, text = 'Set corner', @@ -321,7 +321,7 @@ local containTab = UI.Tab { } local blocksTab = UI.Tab { - tabTitle = 'Blocks', + title = 'Blocks', grid = UI.ScrollingGrid { y = 1, columns = { @@ -333,7 +333,7 @@ local blocksTab = UI.Tab { } local turtlesTab = UI.Tab { - tabTitle = 'Turtles', + title = 'Turtles', grid = UI.ScrollingGrid { y = 1, values = pool, diff --git a/lzwfs/system/lzwfs.lua b/lzwfs/system/lzwfs.lua index 3f0a7a2..69bccee 100644 --- a/lzwfs/system/lzwfs.lua +++ b/lzwfs/system/lzwfs.lua @@ -14,7 +14,7 @@ local config = Config.load('lzwfs', { }) local tab = UI.Tab { - tabTitle = 'Compression', + title = 'Compression', description = 'Disk compression', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 6, diff --git a/milo/core/learnWizard.lua b/milo/core/learnWizard.lua index 0f8917f..5fbcc05 100644 --- a/milo/core/learnWizard.lua +++ b/milo/core/learnWizard.lua @@ -1,33 +1,29 @@ local Milo = require('milo') local UI = require('opus.ui') -local turtle = _G.turtle - local learnPage = UI.Page { titleBar = UI.TitleBar { title = 'Learn Recipe' }, wizard = UI.Wizard { y = 2, ey = -2, - pages = { - general = UI.WizardPage { - index = 1, - grid = UI.ScrollingGrid { - x = 2, ex = -2, y = 2, ey = -2, - disableHeader = true, - columns = { - { heading = 'Name', key = 'name'}, - }, - sortColumn = 'name', - }, - accelerators = { - grid_select = 'nextView', + general = UI.WizardPage { + index = 1, + grid = UI.ScrollingGrid { + x = 2, ex = -2, y = 2, ey = -2, + disableHeader = true, + columns = { + { heading = 'Name', key = 'name'}, }, + sortColumn = 'name', + }, + accelerators = { + grid_select = 'nextView', }, }, }, notification = UI.Notification { }, } -local general = learnPage.wizard.pages.general +local general = learnPage.wizard.general function general:validate() Milo:setState('learnType', self.grid:getSelected().value) @@ -37,7 +33,7 @@ end function learnPage:enable() local t = { } - for _, page in pairs(self.wizard.pages) do + for _, page in pairs(self.wizard:getPages()) do if page.validFor then t[page.validFor] = { name = page.validFor, @@ -63,7 +59,7 @@ function learnPage.wizard:getPage(index) local pages = { } table.insert(pages, general) local selected = general.grid:getSelected() - for _, page in pairs(self.pages) do + for _, page in pairs(self:getPages()) do if page.validFor and (not selected or selected.value == page.validFor) then table.insert(pages, page) end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index b3779db..a336d37 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -35,6 +35,36 @@ local networkPage = UI.Page { }, sortColumn = 'displayName', help = 'Select Node', + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + local t = { row.name:match(':(.+)_(%d+)$') } + if #t ~= 2 then + t = { row.name:match('(.+)_(%d+)$') } + end + if t and #t == 2 then + row.name, row.suffix = table.unpack(t) + row.name = row.name .. '_' .. row.suffix + end + row.displayName = row.displayName or row.name + return row + end, + getRowTextColor = function(self, row, selected) + if not device[row.name] then + return colors.red + end + if row.mtype == 'ignore' then + return colors.lightGray + end + return UI.Grid.getRowTextColor(self, row, selected) + end, + sortCompare = function(self, 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, }, remove = UI.Button { y = -2, x = -4, @@ -55,39 +85,6 @@ local networkPage = UI.Page { } } -function networkPage.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - local t = { row.name:match(':(.+)_(%d+)$') } - if #t ~= 2 then - t = { row.name:match('(.+)_(%d+)$') } - end - if t and #t == 2 then - row.name, row.suffix = table.unpack(t) - row.name = row.name .. '_' .. row.suffix - end - row.displayName = row.displayName or row.name - return row -end - -function networkPage.grid:getRowTextColor(row, selected) - if not device[row.name] then - return colors.red - end - if row.mtype == 'ignore' then - return colors.lightGray - end - return UI.Grid:getRowTextColor(row, selected) -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() for _, v in pairs(device) do if not context.storage.nodes[v.name] then @@ -96,7 +93,7 @@ function networkPage:getList() mtype = 'ignore', category = 'ignore', } - for _, page in pairs(nodeWizard.wizard.pages) do + for _, page in pairs(nodeWizard.wizard:getPages()) do if page.isValidType and page:isValidType(node) then context.storage.nodes[v.name] = node break @@ -191,46 +188,91 @@ nodeWizard = UI.Page { titleBar = UI.TitleBar { title = 'Configure' }, wizard = UI.Wizard { y = 2, ey = -2, - pages = { - general = UI.WizardPage { - index = 1, - form = UI.Form { - x = 2, ex = -2, y = 1, ey = 3, - manualControls = true, - [1] = UI.TextEntry { - formLabel = 'Name', formKey = 'displayName', - help = 'Set a friendly name', - limit = 64, - }, - [2] = UI.Chooser { - width = 25, - formLabel = 'Type', formKey = 'mtype', - --nochoice = 'Storage', - help = 'Select type', - }, + general = UI.WizardPage { + index = 1, + form = UI.Form { + x = 2, ex = -2, y = 1, ey = 3, + manualControls = true, + [1] = UI.TextEntry { + formLabel = 'Name', formKey = 'displayName', + help = 'Set a friendly name', + limit = 64, }, - grid = UI.ScrollingGrid { - y = 5, ey = -2, x = 2, ex = -2, - columns = { - { heading = 'Slot', key = 'slot', width = 4 }, - { heading = 'Name', key = 'displayName', }, - { heading = 'Qty', key = 'count' , width = 3 }, - }, - sortColumn = 'slot', - help = 'Contents of inventory', + [2] = UI.Chooser { + width = 25, + formLabel = 'Type', formKey = 'mtype', + --nochoice = 'Storage', + help = 'Select type', }, }, - confirmation = UI.WizardPage { - title = 'Confirm changes', - index = 2, - notice = UI.TextArea { - x = 2, ex = -2, y = 2, ey = -2, - value = + grid = UI.ScrollingGrid { + y = 5, ey = -2, x = 2, ex = -2, + columns = { + { heading = 'Slot', key = 'slot', width = 4 }, + { heading = 'Name', key = 'displayName', }, + { heading = 'Qty', key = 'count' , width = 3 }, + }, + sortColumn = 'slot', + help = 'Contents of inventory', + }, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row) + return row + end, + enable = function(self) + UI.WizardPage.enable(self) + self:focusFirst() + end, + isValidFor = function() + return false + end, + showInventory = function(self, node) + local inventory + + if device[node.name] and device[node.name].list then + pcall(function() + inventory = device[node.name].list() + for k,v in pairs(inventory) do + v.slot = k + end + end) + end + + self.grid:setValues(inventory or { }) + end, + validate = function(self) + if self.form:save() then + nodeWizard.node.category = Util.find(nodeWizard.choices, 'value', nodeWizard.node.mtype).category + + nodeWizard.nodePages = { } + table.insert(nodeWizard.nodePages, nodeWizard.wizard.general) + for _, page in pairs(nodeWizard.wizard:getPages()) do + if not page.isValidFor or page:isValidFor(nodeWizard.node) then + table.insert(nodeWizard.nodePages, page) + if page.setNode then + page:setNode(nodeWizard.node) + end + end + end + table.insert(nodeWizard.nodePages, nodeWizard.wizard.confirmation) + return true + end + end, + }, + confirmation = UI.WizardPage { + title = 'Confirm changes', + index = 2, + notice = UI.TextArea { + x = 2, ex = -2, y = 2, ey = -2, + value = [[Press accept to save the changes. The settings will take effect immediately!]], - }, }, + isValidFor = function() + return false + end, }, }, statusBar = UI.StatusBar { @@ -255,6 +297,11 @@ The settings will take effect immediately!]], accelerators = { delete = 'remove_entry', }, + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row) + return row + end, }, remove = UI.Button { x = -4, y = 4, @@ -291,143 +338,76 @@ The settings will take effect immediately!]], statusBar = UI.StatusBar { backgroundColor = 'primary', }, + show = function(self, entry, callback, whitelistOnly) + self.entry = entry + self.callback = callback + + if not self.entry.filter then + self.entry.filter = { } + end + + self.form:setValues(entry) + self:resetGrid() + + self.form[3].inactive = whitelistOnly + + UI.SlideOut.show(self) + self:setFocus(self.form.scan) + + Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) + end, + hide = function(self) + UI.SlideOut.hide(self) + Milo:resumeCrafting({ key = 'gridInUse' }) + end, + resetGrid = function(self) + local t = { } + for k in pairs(self.entry.filter) do + table.insert(t, itemDB:splitKey(k)) + end + self.grid:setValues(t) + end, + eventHandler = function(self, event) + if event.type == 'focus_change' then + self.statusBar:setStatus(event.focused.help) + + elseif event.type == 'scan_turtle' then + local inventory = Milo:getTurtleInventory() + for _,item in pairs(inventory) do + self.entry.filter[itemDB:makeKey(item)] = true + end + self:resetGrid() + self.grid:update() + self.grid:draw() + Milo:emptyInventory() + + elseif event.type == 'remove_entry' then + local row = self.grid:getSelected() + if row then + Util.removeByValue(self.grid.values, row) + self.grid:update() + self.grid:draw() + end + + elseif event.type == 'save' then + self.form:save() + self.entry.filter = { } + for _,v in pairs(self.grid.values) do + self.entry.filter[itemDB:makeKey(v)] = true + end + self:hide() + self.callback() + + elseif event.type == 'cancel' then + self:hide() + else + return UI.SlideOut.eventHandler(self, event) + end + return true + end, }, } ---[[ Filter slide out ]] -- -function nodeWizard.filter:show(entry, callback, whitelistOnly) - self.entry = entry - self.callback = callback - - if not self.entry.filter then - self.entry.filter = { } - end - - self.form:setValues(entry) - self:resetGrid() - - self.form[3].inactive = whitelistOnly - - UI.SlideOut.show(self) - self:setFocus(self.form.scan) - - Milo:pauseCrafting({ key = 'gridInUse', msg = 'Crafting paused' }) -end - -function nodeWizard.filter:hide() - UI.SlideOut.hide(self) - Milo:resumeCrafting({ key = 'gridInUse' }) -end - -function nodeWizard.filter:resetGrid() - local t = { } - for k in pairs(self.entry.filter) do - table.insert(t, itemDB:splitKey(k)) - end - self.grid:setValues(t) -end - -function nodeWizard.filter.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = itemDB:getName(row) - return row -end - -function nodeWizard.filter:eventHandler(event) - if event.type == 'focus_change' then - self.statusBar:setStatus(event.focused.help) - - elseif event.type == 'scan_turtle' then - local inventory = Milo:getTurtleInventory() - for _,item in pairs(inventory) do - self.entry.filter[itemDB:makeKey(item)] = true - end - self:resetGrid() - self.grid:update() - self.grid:draw() - Milo:emptyInventory() - - elseif event.type == 'remove_entry' then - local row = self.grid:getSelected() - if row then - Util.removeByValue(self.grid.values, row) - self.grid:update() - self.grid:draw() - end - - elseif event.type == 'save' then - self.form:save() - self.entry.filter = { } - for _,v in pairs(self.grid.values) do - self.entry.filter[itemDB:makeKey(v)] = true - end - self:hide() - self.callback() - - elseif event.type == 'cancel' then - self:hide() - - else - return UI.SlideOut.eventHandler(self, event) - end - return true -end - ---[[ General Page ]] -- -function nodeWizard.wizard.pages.general:enable() - UI.WizardPage.enable(self) - self:focusFirst() -end - -function nodeWizard.wizard.pages.general:isValidFor() - return false -end - -function nodeWizard.wizard.pages.general:showInventory(node) - local inventory - - if device[node.name] and device[node.name].list then - pcall(function() - inventory = device[node.name].list() - for k,v in pairs(inventory) do - v.slot = k - end - end) - end - - self.grid:setValues(inventory or { }) -end - -function nodeWizard.wizard.pages.general.grid:getDisplayValues(row) - row = Util.shallowCopy(row) - row.displayName = itemDB:getName(row) - return row -end - -function nodeWizard.wizard.pages.general:validate() - if self.form:save() then - nodeWizard.node.category = Util.find(nodeWizard.choices, 'value', nodeWizard.node.mtype).category - - nodeWizard.nodePages = { } - table.insert(nodeWizard.nodePages, nodeWizard.wizard.pages.general) - for _, page in pairs(nodeWizard.wizard.pages) do - if not page.isValidFor or page:isValidFor(nodeWizard.node) then - table.insert(nodeWizard.nodePages, page) - if page.setNode then - page:setNode(nodeWizard.node) - end - end - end - table.insert(nodeWizard.nodePages, nodeWizard.wizard.pages.confirmation) - return true - end -end - ---[[ Confirmation ]]-- -function nodeWizard.wizard.pages.confirmation:isValidFor() - return false -end - --[[ Wizard ]] -- function nodeWizard:enable(node) local adapter = node.adapter @@ -440,7 +420,7 @@ function nodeWizard:enable(node) { name = 'Ignore', value = 'ignore', category = 'ignore' }, { 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:getPages()) do if page.isValidType then local choice = page:isValidType(self.node) if choice and not Util.find(self.choices, 'value', choice.value) then @@ -448,15 +428,15 @@ function nodeWizard:enable(node) end end end - self.wizard.pages.general.form[1].shadowText = self.node.name - self.wizard.pages.general.form[2].choices = self.choices - self.wizard.pages.general.form:setValues(self.node) + self.wizard.general.form[1].shadowText = self.node.name + self.wizard.general.form[2].choices = self.choices + self.wizard.general.form:setValues(self.node) - self.wizard.pages.general:showInventory(self.node) + self.wizard.general:showInventory(self.node) self.nodePages = { } - table.insert(self.nodePages, self.wizard.pages.general) - table.insert(self.nodePages, self.wizard.pages.confirmation) + table.insert(self.nodePages, self.wizard.general) + table.insert(self.nodePages, self.wizard.confirmation) UI.Page.enable(self) end diff --git a/milo/plugins/item/infoTab.lua b/milo/plugins/item/infoTab.lua index fb96fc1..6634948 100644 --- a/milo/plugins/item/infoTab.lua +++ b/milo/plugins/item/infoTab.lua @@ -2,7 +2,7 @@ local Ansi = require('opus.ansi') local UI = require('opus.ui') local infoTab = UI.Tab { - tabTitle = 'Info', + title = 'Info', index = 4, textArea = UI.TextArea { x = 2, ex = -2, y = 2, ey = -2, diff --git a/milo/plugins/item/machinesTab.lua b/milo/plugins/item/machinesTab.lua index 8f52595..4f4e7a7 100644 --- a/milo/plugins/item/machinesTab.lua +++ b/milo/plugins/item/machinesTab.lua @@ -7,7 +7,7 @@ local colors = _G.colors local context = Milo:getContext() local machinesTab = UI.Tab { - tabTitle = 'Machine', + title = 'Machine', index = 3, grid = UI.ScrollingGrid { x = 2, ex = -2, y = 2, ey = -2, diff --git a/milo/plugins/item/manageTab.lua b/milo/plugins/item/manageTab.lua index 2df0a13..e1f49fe 100644 --- a/milo/plugins/item/manageTab.lua +++ b/milo/plugins/item/manageTab.lua @@ -7,7 +7,7 @@ local Util = require('opus.util') local context = Milo:getContext() local manageTab = UI.Tab { - tabTitle = 'Manage', + title = 'Manage', index = 1, form = UI.Form { x = 1, ex = -1, ey = -1, diff --git a/milo/plugins/item/recipeTab.lua b/milo/plugins/item/recipeTab.lua index 93e321f..591715f 100644 --- a/milo/plugins/item/recipeTab.lua +++ b/milo/plugins/item/recipeTab.lua @@ -4,7 +4,7 @@ local Milo = require('milo') local UI = require('opus.ui') local recipeTab = UI.Tab { - tabTitle = 'Recipe', + title = 'Recipe', index = 2, grid = UI.ScrollingGrid { x = 2, ex = -2, y = 2, ey = -4, diff --git a/milo/plugins/item/resetTab.lua b/milo/plugins/item/resetTab.lua index 0d9d34d..217124b 100644 --- a/milo/plugins/item/resetTab.lua +++ b/milo/plugins/item/resetTab.lua @@ -7,7 +7,7 @@ local colors = _G.colors local context = Milo:getContext() local resetTab = UI.Tab { - tabTitle = 'Reset', + title = 'Reset', index = 5, noFill = true, textArea = UI.TextArea { diff --git a/milo/plugins/remote/autostore.lua b/milo/plugins/remote/autostore.lua index 0c7272b..e7315f2 100644 --- a/milo/plugins/remote/autostore.lua +++ b/milo/plugins/remote/autostore.lua @@ -23,7 +23,7 @@ local page = UI.Page { tabs = UI.Tabs { y = 2, ey = -2, inventory = UI.Tab { - tabTitle = 'Inventory', + title = 'Inventory', grid = UI.ScrollingGrid { y = 2, ey = -2, columns = { @@ -33,7 +33,7 @@ local page = UI.Page { }, }, autostore = UI.Tab { - tabTitle = 'Deposit', + title = 'Deposit', grid = UI.ScrollingGrid { y = 2, ey = -2, columns = { diff --git a/milo/plugins/statsView.lua b/milo/plugins/statsView.lua index b6ab90c..799c1a2 100644 --- a/milo/plugins/statsView.lua +++ b/milo/plugins/statsView.lua @@ -83,7 +83,7 @@ local function createPage(node) parent = monitor, tabs = UI.Tabs { [1] = UI.Tab { - tabTitle = 'Overview', + title = 'Overview', backgroundColor = colors.black, noFill = true, onlineLabel = UI.Text { @@ -135,14 +135,14 @@ local function createPage(node) }, }, [2] = UI.Tab { - tabTitle = 'Stats', + title = 'Stats', noFill = true, textArea = UI.TextArea { y = 3, }, }, [3] = UI.Tab { - tabTitle = 'Storage', + title = 'Storage', noFill = true, grid = UI.ScrollingGrid { y = 2, @@ -157,7 +157,7 @@ local function createPage(node) }, }, [4] = UI.Tab { - tabTitle = 'Offline', + title = 'Offline', noFill = true, grid = UI.ScrollingGrid { y = 2, @@ -168,14 +168,14 @@ local function createPage(node) }, }, [5] = UI.Tab { - tabTitle = 'Activity', + title = 'Activity', noFill = true, term = UI.Embedded { --visible = true, }, }, [6] = UI.Tab { - tabTitle = 'Tasks', + title = 'Tasks', noFill = true, grid = UI.ScrollingGrid { y = 2, @@ -449,7 +449,7 @@ Unlocked Slots : %d of %d (%d%%) function page:eventHandler(event) if event.type == 'tab_activate' then local state = Milo:getState('statusState') or { } - state[node.name] = event.activated.tabTitle + state[node.name] = event.activated.title Milo:setState('statusState', state) end return UI.Page.eventHandler(self, event) @@ -472,7 +472,7 @@ Unlocked Slots : %d of %d (%d%%) -- restore active tab local tabState = Milo:getState('statusState') or { } if tabState[node.name] then - page.tabs:selectTab(Util.find(page.tabs, 'tabTitle', tabState[node.name])) + page.tabs:selectTab(Util.find(page.tabs, 'title', tabState[node.name])) end return page diff --git a/neural/Sensor.lua b/neural/Sensor.lua index 42d0b9a..dbd3c98 100644 --- a/neural/Sensor.lua +++ b/neural/Sensor.lua @@ -23,7 +23,7 @@ local config = Config.load('Sensor') local page = UI.Page { tabs = UI.Tabs { listing = UI.Tab { - tabTitle = 'Listing', + title = 'Listing', grid = UI.ScrollingGrid { columns = { { heading = 'Name', key = 'displayName' }, @@ -35,7 +35,7 @@ local page = UI.Page { }, }, summary = UI.Tab { - tabTitle = 'Summary', + title = 'Summary', grid = UI.ScrollingGrid { columns = { { heading = 'Name', key = 'displayName' }, @@ -256,7 +256,7 @@ function page:eventHandler(event) UI:quit() elseif event.type == 'tab_activate' then - config.activeTab = event.activated.tabTitle + config.activeTab = event.activated.title Config.update('Sensor', config) end @@ -264,7 +264,7 @@ function page:eventHandler(event) end if config.activeTab then - page.tabs:selectTab(Util.find(page.tabs.children, 'tabTitle', config.activeTab)) + page.tabs:selectTab(Util.find(page.tabs.children, 'title', config.activeTab)) end UI:setPage(page) diff --git a/screenSaver/system/saver.lua b/screenSaver/system/saver.lua index 7391f2d..6bf0408 100644 --- a/screenSaver/system/saver.lua +++ b/screenSaver/system/saver.lua @@ -7,7 +7,7 @@ local config = Config.load('saver', { }) return UI.Tab { - tabTitle = 'Screen Saver', + title = 'Screen Saver', description = 'Screen saver', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 5, diff --git a/secure/system/secure.lua b/secure/system/secure.lua index 9fd0137..05f1e18 100644 --- a/secure/system/secure.lua +++ b/secure/system/secure.lua @@ -7,7 +7,7 @@ local config = Config.load('secure', { }) local tab = UI.Tab { - tabTitle = 'Secure', + title = 'Secure', description = 'Secure options', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 5, diff --git a/shellex/apis/transfer.lua b/shellex/apis/transfer.lua index aeb781a..d29b730 100644 --- a/shellex/apis/transfer.lua +++ b/shellex/apis/transfer.lua @@ -172,7 +172,7 @@ function lib.recurse(fromPath, toPath, options, origin, top) end end if mv then - if fs.list(toReal)() then -- to is NOT empty + if fs.exists(toPath) then -- to is NOT empty return nil, "cannot move '" .. fromPath .. "' to '" .. toPath .. "': Directory not empty" end status(verbose, fromPath, toPath) diff --git a/swshop/shopTab.lua b/swshop/shopTab.lua index f731ebf..f098574 100644 --- a/swshop/shopTab.lua +++ b/swshop/shopTab.lua @@ -7,7 +7,7 @@ local os = _G.os local config = Config.load('shop') local shopTab = UI.Tab { - tabTitle = 'Store', + title = 'Store', index = 2, form = UI.Form { x = 2, ex = -2, y = 2, ey = -2, diff --git a/turtle/system/turtle.lua b/turtle/system/turtle.lua index a144205..172d32d 100644 --- a/turtle/system/turtle.lua +++ b/turtle/system/turtle.lua @@ -9,7 +9,7 @@ if turtle then local config = Config.load('gps') local gpsTab = UI.Tab { - tabTitle = 'Home', + title = 'Home', description = 'Turtle home location', labelText = UI.Text { x = 3, ex = -3, y = 2, -- 2.49.1 From 8a9878b8e52f9ed5177dc239bbdfe86d135cd804 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 27 Apr 2020 22:31:57 -0600 Subject: [PATCH 10/24] minor bug fixes --- ccemux/autorun/startup.lua | 13 +++++++++++++ milo/core/machines.lua | 10 +++++----- miners/etc/apps.db | 4 ++-- monitor/mwm.lua | 3 +++ swshop/etc/apps.db | 2 +- turtle/system/turtle.lua | 4 ++-- 6 files changed, 26 insertions(+), 10 deletions(-) diff --git a/ccemux/autorun/startup.lua b/ccemux/autorun/startup.lua index ca21d16..cb3d466 100644 --- a/ccemux/autorun/startup.lua +++ b/ccemux/autorun/startup.lua @@ -1,6 +1,7 @@ local ccemux = _G.ccemux local fs = _G.fs local peripheral = _G.peripheral +local textutils = _G.textutils if ccemux then -- add a System setup tab @@ -13,4 +14,16 @@ if ccemux then ccemux.attach(k, v.type, v.args) end end + + _G.kernel.hook('clipboard_copy', function(_, args) + local data = args[1] + if type(data) == 'table' then + local s, m = pcall(textutils.serialize, data) + data = s and m or tostring(data) + end + + if data then + ccemux.setClipboard(data) + end + end) end diff --git a/milo/core/machines.lua b/milo/core/machines.lua index a336d37..049c5a1 100644 --- a/milo/core/machines.lua +++ b/milo/core/machines.lua @@ -214,12 +214,12 @@ nodeWizard = UI.Page { }, sortColumn = 'slot', help = 'Contents of inventory', + getDisplayValues = function(_, row) + row = Util.shallowCopy(row) + row.displayName = itemDB:getName(row) + return row + end, }, - getDisplayValues = function(_, row) - row = Util.shallowCopy(row) - row.displayName = itemDB:getName(row) - return row - end, enable = function(self) UI.WizardPage.enable(self) self:focusFirst() diff --git a/miners/etc/apps.db b/miners/etc/apps.db index 4d0341f..df03753 100644 --- a/miners/etc/apps.db +++ b/miners/etc/apps.db @@ -1,14 +1,14 @@ { [ "4486006f811b88cacd5f211fd579717e29b600cd" ] = { title = "Simple", - category = "Mining", + category = "Turtle", icon = "\030 \0315\\\030 \031 \010\030 \0304\031f _ \030 \031c/\0315\\\010\030 \0304 ", run = "simpleMiner.lua", requires = 'turtle', }, [ "da39a3ee5e6b4b0d3255bfef95601890afd80709" ] = { title = "Scanning", - category = "Mining", + category = "Turtle", icon = "\030 \0315\\\030 \031 \010\030 \0304\031f _ \030 \031c/\0315\\\010\030 \0304 ", run = "scanningMiner.lua", requires = 'turtle', diff --git a/monitor/mwm.lua b/monitor/mwm.lua index a397606..eef8753 100644 --- a/monitor/mwm.lua +++ b/monitor/mwm.lua @@ -120,6 +120,9 @@ function Process:new(args) self.window = window.create(self.container, 2, 3, args.width, args.height, true) self.terminal = self.window + self.container.setBackgroundColor(colors.black) + self.container.clear() + self.container.canvas.parent = monitor.canvas if not monitor.canvas.children then monitor.canvas.children = { } diff --git a/swshop/etc/apps.db b/swshop/etc/apps.db index b0dc5c9..68115b0 100644 --- a/swshop/etc/apps.db +++ b/swshop/etc/apps.db @@ -1,7 +1,7 @@ { [ "377aaf9a802a23873738c0c3916282c8884c11fb" ] = { title = "ShopLogs", - category = "Shop", + category = "Apps", run = "Shoplogs /usr/swshop.log", iconExt = "\0300\031f\136\140\132\0308\031 \130\030 \0318\144\010\0300\0315/\0305\031d\\\030f\0310\142\143\030 \149\010\0305\031d\\\030d\0315/\0300\031f\132\140\030 \0310\149", }, diff --git a/turtle/system/turtle.lua b/turtle/system/turtle.lua index 172d32d..b4bda06 100644 --- a/turtle/system/turtle.lua +++ b/turtle/system/turtle.lua @@ -1,7 +1,6 @@ local Config = require('opus.config') local UI = require('opus.ui') -local colors = _G.colors local fs = _G.fs local turtle = _G.turtle @@ -11,9 +10,10 @@ if turtle then local gpsTab = UI.Tab { title = 'Home', description = 'Turtle home location', + noFill = true, labelText = UI.Text { x = 3, ex = -3, y = 2, - textColor = colors.yellow, + textColor = 'yellow', value = 'On restart, return to this location' }, grid = UI.Grid { -- 2.49.1 From 9bf017fe2718098ea9190aa82e2e6fd1b1794703 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 2 May 2020 22:22:30 -0600 Subject: [PATCH 11/24] rename help files with .txt - overlay.lua creates a terminal compatible window on the overlay glasses - you can now run any program on the glasses overlay --- builder/help/{builder => builder.txt} | 0 ccemux/system/ccemux.lua | 193 +++++++++--------- common/.package | 3 + common/Devices.lua | 95 ++++----- common/debugMonitor.lua | 3 +- common/edit.lua | 2 +- farms/help/{farmer => farmer.txt} | 0 .../help/{superTreefarm => superTreefarm.txt} | 0 .../help/{scanningMiner => scanningMiner.txt} | 0 miners/help/{simpleMiner => simpleMiner.txt} | 0 monitor/help/{mirror => mirror.txt} | 0 neural/overlay.lua | 108 ++++++++++ swshop/help/{swshop => swshop.txt} | 0 13 files changed, 258 insertions(+), 146 deletions(-) rename builder/help/{builder => builder.txt} (100%) rename farms/help/{farmer => farmer.txt} (100%) rename farms/help/{superTreefarm => superTreefarm.txt} (100%) rename miners/help/{scanningMiner => scanningMiner.txt} (100%) rename miners/help/{simpleMiner => simpleMiner.txt} (100%) rename monitor/help/{mirror => mirror.txt} (100%) create mode 100644 neural/overlay.lua rename swshop/help/{swshop => swshop.txt} (100%) diff --git a/builder/help/builder b/builder/help/builder.txt similarity index 100% rename from builder/help/builder rename to builder/help/builder.txt diff --git a/ccemux/system/ccemux.lua b/ccemux/system/ccemux.lua index d066bab..2c60daf 100644 --- a/ccemux/system/ccemux.lua +++ b/ccemux/system/ccemux.lua @@ -6,121 +6,120 @@ local ccemux = _G.ccemux local sides = { 'bottom', 'top', 'back', 'front', 'right', 'left' } local tab = UI.Tab { - title = 'CCEmuX', - description = 'CCEmuX peripherals', - form = UI.Form { - x = 2, ex = -2, y = 2, ey = 5, - values = { - side = 'bottom', - type = 'wireless_modem', - }, - manualControls = true, - side = UI.Chooser { - formLabel = 'Side', formKey = 'side', - width = 10, - }, - ptype = UI.Chooser { - formLabel = 'Type', formKey = 'type', - width = 10, - choices = { - { name = 'Modem', value = 'wireless_modem' }, - { name = 'Drive', value = 'disk_drive' }, - }, - }, - drive_id = UI.TextEntry { - x = 19, y = 3, - formKey = 'drive_id', - shadowText = 'id', - width = 5, - limit = 3, - transform = 'number', - }, - add = UI.Button { - x = -6, y = 3, width = 5, - text = 'Add', event = 'form_ok', - help = 'Add items to turtle to add to filter', - }, - }, - grid = UI.Grid { - x = 2, ex = -2, y = 7, ey = -2, - columns = { - { heading = 'Side', key = 'side', width = 8 }, - { heading = 'Type', key = 'type' }, - { heading = 'ID', key = 'args', width = 4 }, - }, - }, + title = 'CCEmuX', + description = 'CCEmuX peripherals', + form = UI.Form { + x = 2, ex = -2, y = 2, ey = 5, + values = { + side = 'bottom', + type = 'wireless_modem', + }, + manualControls = true, + side = UI.Chooser { + formLabel = 'Side', formKey = 'side', + width = 10, + }, + ptype = UI.Chooser { + formLabel = 'Type', formKey = 'type', + width = 10, + choices = { + { name = 'Modem', value = 'wireless_modem' }, + { name = 'Drive', value = 'disk_drive' }, + }, + }, + drive_id = UI.TextEntry { + x = 19, y = 3, + formKey = 'drive_id', + shadowText = 'id', + width = 5, + limit = 3, + transform = 'number', + }, + add = UI.Button { + x = -6, y = 3, width = 5, + text = 'Add', event = 'form_ok', + }, + }, + grid = UI.Grid { + x = 2, ex = -2, y = 7, ey = -2, + columns = { + { heading = 'Side', key = 'side', width = 8 }, + { heading = 'Type', key = 'type' }, + { heading = 'ID', key = 'args', width = 4 }, + }, + }, } function tab:updatePeripherals(config) - self.grid.values = { } - for k,v in pairs(config) do - table.insert(self.grid.values, { - side = k, - type = v.type, - args = v.args and v.args.id, - }) - end - self.grid:update() + self.grid.values = { } + for k,v in pairs(config) do + table.insert(self.grid.values, { + side = k, + type = v.type, + args = v.args and v.args.id, + }) + end + self.grid:update() end function tab:enable() - local config = Config.load('ccemux') + local config = Config.load('ccemux') - local choices = { } - for _,k in pairs(sides) do - table.insert(choices, { name = k, value = k }) - end - self.form.side.choices = choices + local choices = { } + for _,k in pairs(sides) do + table.insert(choices, { name = k, value = k }) + end + self.form.side.choices = choices - self:updatePeripherals(config) - UI.Tab.enable(self) + self:updatePeripherals(config) + UI.Tab.enable(self) - self.form.drive_id.enabled = false + self.form.drive_id.enabled = false end function tab:eventHandler(event) - if event.type == 'form_complete' then - if event.values.type == 'disk_drive' and not event.values.drive_id then - self:emit({ type = 'error_message', message = 'Invalid drive ID' }) - else - ccemux.detach(event.values.side) + if event.type == 'form_complete' then + if event.values.type == 'disk_drive' and not event.values.drive_id then + self:emit({ type = 'error_message', message = 'Invalid drive ID' }) + else + ccemux.detach(event.values.side) - local config = Config.load('ccemux') - config[event.values.side] = { - type = event.values.type - } - if event.values.type == 'disk_drive' then - config[event.values.side].args = { - id = event.values.drive_id - } - ccemux.attach(event.values.side, event.values.type, { id = event.values.drive_id }) - else - ccemux.attach(event.values.side, event.values.type) - end - Config.update('ccemux', config) - self:updatePeripherals(config) - self.grid:draw() + local config = Config.load('ccemux') + config[event.values.side] = { + type = event.values.type + } + if event.values.type == 'disk_drive' then + config[event.values.side].args = { + id = event.values.drive_id + } + ccemux.attach(event.values.side, event.values.type, { id = event.values.drive_id }) + else + ccemux.attach(event.values.side, event.values.type) + end + Config.update('ccemux', config) + self:updatePeripherals(config) + self.grid:draw() - self:emit({ type = 'success_message', message = 'Attached' }) - end + self:emit({ type = 'success_message', message = 'Attached' }) + end - elseif event.type == 'choice_change' then - if event.element == self.form.ptype then - self.form.drive_id.enabled = event.value == 'disk_drive' - self.form:draw() - end + elseif event.type == 'choice_change' then + if event.element == self.form.ptype then + self.form.drive_id.enabled = event.value == 'disk_drive' + self.form:draw() + end - elseif event.type == 'grid_select' then - local config = Config.load('ccemux') - config[event.selected.side] = nil - Config.update('ccemux', config) - self:updatePeripherals(config) - self.grid:draw() + elseif event.type == 'grid_select' then + local config = Config.load('ccemux') + config[event.selected.side] = nil + Config.update('ccemux', config) + self:updatePeripherals(config) + self.grid:draw() - self:emit({ type = 'success_message', message = 'Detached' }) + self:emit({ type = 'success_message', message = 'Detached' }) - return true - end + return true + end end return tab diff --git a/common/.package b/common/.package index 939bc39..4ffb026 100644 --- a/common/.package +++ b/common/.package @@ -13,4 +13,7 @@ * and more... ]], license = 'MIT', + required = { + 'core', + }, } diff --git a/common/Devices.lua b/common/Devices.lua index fc2cecd..7710bd5 100644 --- a/common/Devices.lua +++ b/common/Devices.lua @@ -3,26 +3,26 @@ local Event = require('opus.event') local UI = require('opus.ui') local Util = require('opus.util') -local peripheral = _G.peripheral +local device = _G.device --[[ -- PeripheralsPage -- ]] -- local peripheralsPage = UI.Page { grid = UI.ScrollingGrid { ey = -2, columns = { + --{ heading = 'Name', key = 'name' }, { heading = 'Type', key = 'type' }, { heading = 'Side', key = 'side' }, }, sortColumn = 'type', autospace = true, enable = function(self) - local sides = peripheral.getNames() - Util.clear(self.values) - for _,side in pairs(sides) do + for _,v in pairs(device) do table.insert(self.values, { - type = peripheral.getType(side), - side = side + type = v.type, + side = v.side, + name = v.name, }) end self:update() @@ -75,49 +75,50 @@ local methodsPage = UI.Page { [ 'control-q' ] = 'back', backspace = 'back', }, + enable = function(self, p) + self.peripheral = p or self.peripheral + + p = device[self.peripheral.name] + if p.getDocs then + -- plethora + self.grid.values = { } + for k,v in pairs(p.getDocs()) do + table.insert(self.grid.values, { + name = k, + doc = v, + }) + end + elseif not p.getAdvancedMethodsData then + -- computercraft + self.grid.values = { } + for k,v in pairs(p) do + if type(v) == 'function' then + table.insert(self.grid.values, { + name = k, + noext = true, + }) + end + end + else + -- open peripherals + self.grid.values = p.getAdvancedMethodsData() + for name,f in pairs(self.grid.values) do + f.name = name + end + end + + self.grid:update() + self.grid:setIndex(1) + + self.doc:setText(self:getDocumentation()) + + self.statusBar:setStatus(self.peripheral.type) + UI.Page.enable(self) + + self:setFocus(self.grid) + end, } -function methodsPage:enable(p) - self.peripheral = p or self.peripheral - - p = peripheral.wrap(self.peripheral.side) - if p.getDocs then - -- plethora - self.grid.values = { } - for k,v in pairs(p.getDocs()) do - table.insert(self.grid.values, { - name = k, - doc = v, - }) - end - elseif not p.getAdvancedMethodsData then - -- computercraft - self.grid.values = { } - for name in pairs(p) do - table.insert(self.grid.values, { - name = name, - noext = true, - }) - end - else - -- open peripherals - self.grid.values = p.getAdvancedMethodsData() - for name,f in pairs(self.grid.values) do - f.name = name - end - end - - self.grid:update() - self.grid:setIndex(1) - - self.doc:setText(self:getDocumentation()) - - self.statusBar:setStatus(self.peripheral.type) - UI.Page.enable(self) - - self:setFocus(self.grid) -end - function methodsPage:eventHandler(event) if event.type == 'back' then UI:setPage(peripheralsPage) diff --git a/common/debugMonitor.lua b/common/debugMonitor.lua index 2d963b5..74e8c84 100644 --- a/common/debugMonitor.lua +++ b/common/debugMonitor.lua @@ -1,11 +1,12 @@ local Util = require('opus.util') +local device = _G.device local os = _G.os local peripheral = _G.peripheral local term = _G.term local args = { ... } -local mon = args[1] and peripheral.wrap(args[1]) or +local mon = args[1] and device[args[1]] or peripheral.wrap(args[1]) or peripheral.find('monitor') or error('Syntax: debug ') diff --git a/common/edit.lua b/common/edit.lua index c2097e3..f0c7be3 100644 --- a/common/edit.lua +++ b/common/edit.lua @@ -306,7 +306,7 @@ local page = UI.Page { x = 2, y = 2, ey = -4, ex = -2, columns = { { key = 'name', heading = 'Name' }, - { key = 'dir', heading = 'Directory' }, + { key = 'dir', heading = 'Directory', textColor = 'lightGray' }, }, accelerators = { backspace = 'slide_hide', diff --git a/farms/help/farmer b/farms/help/farmer.txt similarity index 100% rename from farms/help/farmer rename to farms/help/farmer.txt diff --git a/farms/help/superTreefarm b/farms/help/superTreefarm.txt similarity index 100% rename from farms/help/superTreefarm rename to farms/help/superTreefarm.txt diff --git a/miners/help/scanningMiner b/miners/help/scanningMiner.txt similarity index 100% rename from miners/help/scanningMiner rename to miners/help/scanningMiner.txt diff --git a/miners/help/simpleMiner b/miners/help/simpleMiner.txt similarity index 100% rename from miners/help/simpleMiner rename to miners/help/simpleMiner.txt diff --git a/monitor/help/mirror b/monitor/help/mirror.txt similarity index 100% rename from monitor/help/mirror rename to monitor/help/mirror.txt diff --git a/neural/overlay.lua b/neural/overlay.lua new file mode 100644 index 0000000..598d209 --- /dev/null +++ b/neural/overlay.lua @@ -0,0 +1,108 @@ +local Terminal = require('opus.terminal') + +local colors = _G.colors +local device = _G.device + +--[[ + Create a device for glasses + Usable as a redirect or UI target + + Example usage: + Files --display=glasses + debugMonitor glasses + + In a program: + local prev = term.redirect(device.glasses) + shell.run('shell') + term.redirect(prev) + + Glasses do not use the CC font - so extended chars + do not display correctly. +]] + +-- configurable +local w, h = 46, 19 +local scale = .5 + +local glasses = device['plethora:glasses'] +local canvas = glasses.canvas() +local _, cy = 1, 1 +local _, gh = canvas:getSize() +local lines = { } +local map = { + ['0'] = 0xF0F0F0FF, + ['1'] = 0xF2B233FF, + ['2'] = 0xE57FD8FF, + ['3'] = 0x99B2F2FF, + ['4'] = 0xDEDE6CFF, + ['5'] = 0x7FCC19FF, + ['6'] = 0xF2B2CCFF, + ['7'] = 0x4C4C4CFF, + ['8'] = 0x999999FF, + ['9'] = 0x4C99B2FF, + ['a'] = 0xB266E5FF, + ['b'] = 0x3366CCFF, + ['c'] = 0x7F664CFF, + ['d'] = 0x57A64EFF, + ['e'] = 0xCC4C4CFF, + ['f'] = 0x191919FF, -- transparent +} + +local xs, ys = 6 * scale, 9 * scale + +-- Position bottom left +local group = canvas.addGroup({ x = 1, y = gh - (h * ys) - 10 }) + +for y = 1, h do + lines[y] = { + text = { }, + bg = { } + } + for x = 1, w do + lines[y].bg[x] = group.addRectangle(x * xs, y * ys, xs, ys, 0xF0F0F04F) + lines[y].text[x] = group.addText({ x * xs, y * ys }, '', 0x7FCC19FF) + lines[y].text[x].setScale(scale) + end +end + +device.glasses = Terminal.window({ + getSize = function() + return w, h + end, + isColor = function() + return true + end, + clear = function() + --canvas.clear() + end, + blit = function(text, fg, bg) + for x = 1, #text do + local ln = lines[cy] + ln.bg[x].setColor(map[bg:sub(x, x)]) + ln.text[x].setColor(map[fg:sub(x, x)]) + ln.text[x].setText(text:sub(x, x)) + end + end, + setCursorPos = function(_, y) + -- full lines are always blit + cy = y + end, + setBackgroundColor = function() + end, + setTextColor = function() + end, + setCursorBlink = function() + end, + getBackgroundColor = function() + return colors.black + end, + getTextColor = function() + return colors.white + end, +}, 1, 1, w, h, true) + +function device.glasses.setTextScale() end + +device.glasses.side = 'glasses' +device.glasses.type = 'glasses' +device.glasses.name = 'glasses' diff --git a/swshop/help/swshop b/swshop/help/swshop.txt similarity index 100% rename from swshop/help/swshop rename to swshop/help/swshop.txt -- 2.49.1 From 38b5c4a5ed34bbd17830e7233af7761f0e4230c7 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Sat, 2 May 2020 23:45:58 -0600 Subject: [PATCH 12/24] support mouse clicks when using plethora keyboard on overlay --- neural/overlay.lua | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/neural/overlay.lua b/neural/overlay.lua index 598d209..83984c5 100644 --- a/neural/overlay.lua +++ b/neural/overlay.lua @@ -2,6 +2,7 @@ local Terminal = require('opus.terminal') local colors = _G.colors local device = _G.device +local kernel = _G.kernel --[[ Create a device for glasses @@ -51,7 +52,8 @@ local map = { local xs, ys = 6 * scale, 9 * scale -- Position bottom left -local group = canvas.addGroup({ x = 1, y = gh - (h * ys) - 10 }) +local pos = { x = 1, y = gh - (h * ys) - 10 } +local group = canvas.addGroup(pos) for y = 1, h do lines[y] = { @@ -106,3 +108,9 @@ function device.glasses.setTextScale() end device.glasses.side = 'glasses' device.glasses.type = 'glasses' device.glasses.name = 'glasses' + +kernel.hook('glasses_click', function(_, eventData) + os.queueEvent('monitor_touch', 'glasses', + math.floor((eventData[2] - pos.x) / xs), + math.floor((eventData[3] - pos.y) / ys)) +end) -- 2.49.1 From 39fb43d6a3e002b89af2d6b18169b5f47ec899f3 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 4 May 2020 02:53:17 -0400 Subject: [PATCH 13/24] Update storageGen to a more user-friendly UI - Able to merge into current Milo configs - Moved it to the Milo package --- milo/apps/storageGen.lua | 143 +++++++++++++++++++++++++++++++++++ miloApps/apps/storageGen.lua | 29 ------- 2 files changed, 143 insertions(+), 29 deletions(-) create mode 100644 milo/apps/storageGen.lua delete mode 100644 miloApps/apps/storageGen.lua diff --git a/milo/apps/storageGen.lua b/milo/apps/storageGen.lua new file mode 100644 index 0000000..4dd4e03 --- /dev/null +++ b/milo/apps/storageGen.lua @@ -0,0 +1,143 @@ +--[[ + For initially setting up large amounts of storage chests. +]] + +local UI = require('opus.ui') +local Util = require('opus.util') +local Peripheral = require('opus.peripheral') + +local defaultStoragePath = "/usr/config/storage" + +local page = UI.Page { + notification = UI.Notification {}, + + infoText = UI.TextArea { + x = 2, y = 2, + height = 2, + textColor = colors.yellow, + value = "Select storage types to merge into your Milo storage config.", + }, + + typeGrid = UI.CheckboxGrid { + x = 2, y = 4, + ex = -2, ey = -4, + sortColumn = "amount", + inverseSort = true, + columns = { + {heading = 'Type', key = 'type'}, + {heading = 'Amount', key = 'amount', align = 'right', width = 6}, + }, + }, + + rescanButton = UI.Button { + x = 2, y = -2, + text = 'Rescan', + event = 'rescan', + }, + + doneButton = UI.Button { + x = -7, y = -2, + text = 'Save', + event = 'save', + }, + + confirm = UI.Question { + x = -38, y = -2, + label = 'Overwrite Milo settings?', + }, + + fileSelect = UI.FileSelect { + modal = true, + enable = function() end, + transitionHint = 'expandUp', + show = function(self) + UI.FileSelect.enable(self) + self:focusFirst() + self:draw() + end, + disable = function(self) + UI.FileSelect.disable(self) + self.parent:focusFirst() + self.parent:capture(self.parent) + end, + eventHandler = function(self, event) + if event.type == 'select_cancel' then + self:disable() + elseif event.type == 'select_file' then + self:disable() + end + return UI.FileSelect.eventHandler(self, event) + end, + }, +} + +function page:scan() + _syslog("Scanned") + + self.storages = Util.filter(Peripheral.getList(), function(dev) + return not not dev.pushItems + end) + + local types = {} + Util.each(self.storages, function(dev, name) + if not types[dev.type] then types[dev.type] = {amount = 0, type = dev.type} end + types[dev.type].amount = types[dev.type].amount + 1 + end) + + self.typeGrid:setValues(types) + self:draw() + self:sync() +end + +function page:saveConfig(path) + _syslog("Saving to "..path) + + local config = Util.readTable(path) or {} + Util.each(self.storages, function(dev, name) + if self.typeGrid.values[dev.type] and self.typeGrid.values[dev.type].checked then + config[name] = { + name = name, + category = 'storage', + mtype = 'storage', + } + end + end) + Util.writeTable(path, config) + self.notification:success("Config saved to "..path) +end + +function page:enable() + self:scan() + UI.Page.enable(self) +end + +function page.typeGrid:getRowTextColor(row, selected) + return row.checked and colors.yellow or UI.Grid.getRowTextColor(self, row, selected) +end + +function page:eventHandler(event) + _syslog(event) + if event.type == "rescan" then + self:scan() + + elseif event.type == "save" then + self.confirm:show() + + elseif event.type == "question_yes" then + self:saveConfig(defaultStoragePath) + self.confirm:hide() + + elseif event.type == "question_no" then + self.confirm:hide() + self.fileSelect:show() + + elseif event.type == "select_file" then + self:saveConfig(event.file) + + else return UI.Page.eventHandler(self, event) + end + return true +end + +UI:setPage(page) +UI:start() diff --git a/miloApps/apps/storageGen.lua b/miloApps/apps/storageGen.lua deleted file mode 100644 index e2676a5..0000000 --- a/miloApps/apps/storageGen.lua +++ /dev/null @@ -1,29 +0,0 @@ ---[[ - For initially setting up large amounts of storage chests. -]] - -local Util = require('opus.util') - -local peripheral = _G.peripheral - -local args = { ... } -local st = args[1] or error('Specify a storage type (ie. minecraft:chest)') - -local config = { } -peripheral.find(st, function(n) - config[n] = { - name = n, - category = 'storage', - mtype = 'storage', - } -end) - -print('Found ' .. Util.size(config)) - -if Util.size(config) == 0 then - error('Invalid peripheral type') -end - -Util.writeTable('usr/config/storageGen', config) -print('storageGen file created in usr/config') -print('update /usr/config/storage with contents (or rename to storage)') -- 2.49.1 From caa525e31d045c5456e93eb386540d8aa29b9aa6 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 4 May 2020 04:09:13 -0400 Subject: [PATCH 14/24] Swshop: Add option to show out of stock items - Make ShopView use colors from ui.theme --- swshop/shopConfig.lua | 13 ++++++++----- swshop/shopView.lua | 23 +++++++++++++---------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/swshop/shopConfig.lua b/swshop/shopConfig.lua index 857d0c1..c5958ea 100644 --- a/swshop/shopConfig.lua +++ b/swshop/shopConfig.lua @@ -29,10 +29,13 @@ local wizardPage = UI.WizardPage { [3] = UI.Checkbox { formLabel = 'Single shop', formKey = 'refundInvalid', help = 'Only this shop uses this domain', - limit = 64, }, - [4] = UI.Chooser { - formLabel = 'RS Signal', formKey = 'rsSide', formIndex = 5, + [4] = UI.Checkbox { + formLabel = 'Show out of stock', formKey = 'showOutOfStock', + help = 'Show out of stock items in red', + }, + [5] = UI.Chooser { + formLabel = 'RS Signal', formKey = 'rsSide', formIndex = 6, width = 10, nochoice = 'Top', choices = { @@ -45,9 +48,9 @@ local wizardPage = UI.WizardPage { }, required = true, }, - [5] = UI.Chooser { + [6] = UI.Chooser { width = 9, - formIndex = 6, + formIndex = 7, formLabel = 'Font Size', formKey = 'textScale', nochoice = 'Small', choices = { diff --git a/swshop/shopView.lua b/swshop/shopView.lua index 223ff6c..4a51b17 100644 --- a/swshop/shopView.lua +++ b/swshop/shopView.lua @@ -49,15 +49,15 @@ local function createPage(node) local page = UI.Page { parent = monitor, header = UI.Window { - backgroundColor = colors.cyan, + backgroundColor = 'primary', ey = 3, }, grid = UI.Grid { y = 4, ey = -7, headerHeight = 3, - headerBackgroundColor = colors.gray, + headerBackgroundColor = 'tertiary', backgroundSelectedColor = colors.black, - unfocusedBackgroundSelectedColor = colors.gray, + unfocusedBackgroundSelectedColor = 'tertiary', columns = { { heading = 'Stock', key = 'count', width = 6, align = 'right' }, { heading = 'Name', key = 'displayName' }, @@ -68,17 +68,17 @@ local function createPage(node) }, footer = UI.Window { y = -6, - backgroundColor = colors.gray, + backgroundColor = 'tertiary', prevButton = UI.Button { x = 2, y = 3, height = 3, width = 5, event = 'previous', - backgroundColor = colors.lightGray, + backgroundColor = 'secondary', text = ' \017 ', }, nextButton = UI.Button { x = -6, y = 3, height = 3, width = 5, event = 'next', - backgroundColor = colors.lightGray, + backgroundColor = 'secondary', text = ' \016 ', }, info = UI.Window { @@ -94,7 +94,7 @@ local function createPage(node) if node.header then self:centeredWrite(2, node.header, nil, colors.white) end - self:write(self.width - 15, 3, 'powered by Milo', nil, colors.gray) + self:write(self.width - 15, 3, 'powered by Milo', nil, 'tertiary') end function page.footer.info:draw() @@ -112,6 +112,9 @@ local function createPage(node) end function page.grid:getRowTextColor(row, selected) + if row.count < 1 then + return colors.red + end if selected then return colors.yellow end @@ -151,11 +154,11 @@ local function createPage(node) local list = Milo:listItems() self.grid.values = { } for k,v in pairs(config) do - local item = list[k] - if item and item.count > 0 then + local item = list[k] or itemDB:get(k) + if item and (node.showOutOfStock or item.count > 0) then table.insert(self.grid.values, { displayName = item.displayName, - count = item.count, + count = item.count or 0, name = v.name, price = v.price, info = v.info, -- 2.49.1 From db3c43f8c31f6579108c673fefcf7bee71d1121f Mon Sep 17 00:00:00 2001 From: Luca_S Date: Mon, 4 May 2020 22:22:21 +0200 Subject: [PATCH 15/24] Wrap pushItems in pcall --- milo/apis/storage.lua | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 01aaf18..34c0d34 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -363,9 +363,26 @@ function Storage:defrag(throttle) end local toMove = math.min(to.item.maxCount - to.item.count, from.item.count) - from.device.pushItems(to.device.name, from.slot, toMove, to.slot) - to.item.count = to.item.count + toMove - from.item.count = from.item.count - toMove + local s, m = pcall(function() + from.device.pushItems(to.device.name, from.slot, toMove, to.slot) + end) + + if not s and m then + _G._syslog(m) + end + + if s then + to.item.count = to.item.count + toMove + from.item.count = from.item.count - toMove + else + -- Do not try to send to the target again after it failed + for i = 2, #providers do + if to == providers[i] then + table.remove(providers, i) + break + end + end + end if from.item.count <= 0 then table.remove(providers, 1) -- 2.49.1 From f6d67a61f35eacfc578116cc41312d90291e6bd2 Mon Sep 17 00:00:00 2001 From: Luca_S Date: Mon, 4 May 2020 23:23:40 +0200 Subject: [PATCH 16/24] Handle locked chests --- milo/apis/storage.lua | 37 +++++++++++++++++++++++++++++-------- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 34c0d34..143b801 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -327,7 +327,7 @@ function Storage:listProviders(throttle) if not res[key] then res[key] = {} end - table.insert(res[key], {item = item, device = device[chest], slot = slot}) + table.insert(res[key], {item = item, device = device[chest], lockedToThis = Util.contains(Util.keys(self.nodes[chest].lock or {}), key) and true or false, slot = slot}) end end return res @@ -338,10 +338,29 @@ function Storage:defrag(throttle) local items = self:listProviders(throttle) local slotsSaved = 0 - for _, providers in pairs(items) do - table.sort(providers, function(a, b) + -- This will make sure the table is sorted in the following order: + -- Unlocked stacks with less than maxCount items | Locked stacks with less than maxCount items | stacks with more than maxCount items + -- This way the locked stacks will be filled first + local function sortFunction(a, b) + local preferenceA, preferenceB + preferenceA = (a.item.count == a.item.maxCount and 3) + or (a.lockedToThis and 2) + or 1 + preferenceB = (b.item.count == b.item.maxCount and 3) + or (b.lockedToThis and 2) + or 1 + + if preferenceA < preferenceB then + return true + elseif preferenceB > preferenceA then + return false + else return a.item.count < b.item.count - end) + end + end + + for _, providers in pairs(items) do + table.sort(providers, sortFunction) -- We're done when we either compressed the stacks so far, that there's only one left (#providers == 1) -- Or when we've compressed so far, that the there's only one stack which has a lower count than the maxCount @@ -354,10 +373,14 @@ function Storage:defrag(throttle) -- This loop is guarenteed to assign a value to "to", as the only cases where it wouldn't (#providers == 1 or no provider with less than maxCount) -- are ruled out by the condition of the outer while loop for i = 2, #providers do - if providers[i].item.count < providers[i].item.maxCount then + -- Give preference to locked chests + if not to.lockedToThis and providers[i].lockedToThis then + to = providers[i] + elseif (to.lockedToThis == providers[i].lockedToThis) and providers[i].item.count < providers[i].item.maxCount then to = providers[i] else -- As this slot is already at maxCount, all the remaining ones will also be due to sorting + -- If any of the remaining providers is locked that doesn't matter. We wouldn't have been able to push there anyways break end end @@ -389,9 +412,7 @@ function Storage:defrag(throttle) slotsSaved = slotsSaved + 1 end - table.sort(providers, function(a, b) - return a.item.count < b.item.count - end) + table.sort(providers, sortFunction) end end -- 2.49.1 From 94477e5d980dd8f6dd660becc5edb35a360b6727 Mon Sep 17 00:00:00 2001 From: Luca_S Date: Mon, 4 May 2020 23:26:05 +0200 Subject: [PATCH 17/24] Add check that the slot is actually full --- milo/apis/storage.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 143b801..aaa0811 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -378,7 +378,7 @@ function Storage:defrag(throttle) to = providers[i] elseif (to.lockedToThis == providers[i].lockedToThis) and providers[i].item.count < providers[i].item.maxCount then to = providers[i] - else + elseif providers[i].item.count == providers[i].item.maxCount then -- As this slot is already at maxCount, all the remaining ones will also be due to sorting -- If any of the remaining providers is locked that doesn't matter. We wouldn't have been able to push there anyways break -- 2.49.1 From 8f13a0932e0e2e480d087b8ae63919cb833fcaa9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Mon, 4 May 2020 16:45:31 -0600 Subject: [PATCH 18/24] neural window manager --- ccemux/system/ccemux.lua | 192 +++++++++++++++++++-------------------- neural/apis/glasses.lua | 121 ++++++++++++++++++++++++ neural/nwm.lua | 160 ++++++++++++++++++++++++++++++++ neural/overlay.lua | 116 ----------------------- 4 files changed, 377 insertions(+), 212 deletions(-) create mode 100644 neural/apis/glasses.lua create mode 100644 neural/nwm.lua delete mode 100644 neural/overlay.lua diff --git a/ccemux/system/ccemux.lua b/ccemux/system/ccemux.lua index 2c60daf..e4360ea 100644 --- a/ccemux/system/ccemux.lua +++ b/ccemux/system/ccemux.lua @@ -6,120 +6,120 @@ local ccemux = _G.ccemux local sides = { 'bottom', 'top', 'back', 'front', 'right', 'left' } local tab = UI.Tab { - title = 'CCEmuX', - description = 'CCEmuX peripherals', - form = UI.Form { - x = 2, ex = -2, y = 2, ey = 5, - values = { - side = 'bottom', - type = 'wireless_modem', - }, - manualControls = true, - side = UI.Chooser { - formLabel = 'Side', formKey = 'side', - width = 10, - }, - ptype = UI.Chooser { - formLabel = 'Type', formKey = 'type', - width = 10, - choices = { - { name = 'Modem', value = 'wireless_modem' }, - { name = 'Drive', value = 'disk_drive' }, - }, - }, - drive_id = UI.TextEntry { - x = 19, y = 3, - formKey = 'drive_id', - shadowText = 'id', - width = 5, - limit = 3, - transform = 'number', - }, - add = UI.Button { - x = -6, y = 3, width = 5, - text = 'Add', event = 'form_ok', - }, - }, - grid = UI.Grid { - x = 2, ex = -2, y = 7, ey = -2, - columns = { - { heading = 'Side', key = 'side', width = 8 }, - { heading = 'Type', key = 'type' }, - { heading = 'ID', key = 'args', width = 4 }, - }, - }, + title = 'CCEmuX', + description = 'CCEmuX peripherals', + form = UI.Form { + x = 2, ex = -2, y = 2, ey = 5, + values = { + side = 'bottom', + type = 'wireless_modem', + }, + manualControls = true, + side = UI.Chooser { + formLabel = 'Side', formKey = 'side', + width = 10, + }, + ptype = UI.Chooser { + formLabel = 'Type', formKey = 'type', + width = 10, + choices = { + { name = 'Modem', value = 'wireless_modem' }, + { name = 'Drive', value = 'disk_drive' }, + }, + }, + drive_id = UI.TextEntry { + x = 19, y = 3, + formKey = 'drive_id', + shadowText = 'id', + width = 5, + limit = 3, + transform = 'number', + }, + add = UI.Button { + x = -6, y = 3, width = 5, + text = 'Add', event = 'form_ok', + }, + }, + grid = UI.Grid { + x = 2, ex = -2, y = 7, ey = -2, + columns = { + { heading = 'Side', key = 'side', width = 8 }, + { heading = 'Type', key = 'type' }, + { heading = 'ID', key = 'args', width = 4 }, + }, + }, } function tab:updatePeripherals(config) - self.grid.values = { } - for k,v in pairs(config) do - table.insert(self.grid.values, { - side = k, - type = v.type, - args = v.args and v.args.id, - }) - end - self.grid:update() + self.grid.values = { } + for k,v in pairs(config) do + table.insert(self.grid.values, { + side = k, + type = v.type, + args = v.args and v.args.id, + }) + end + self.grid:update() end function tab:enable() - local config = Config.load('ccemux') + local config = Config.load('ccemux') - local choices = { } - for _,k in pairs(sides) do - table.insert(choices, { name = k, value = k }) - end - self.form.side.choices = choices + local choices = { } + for _,k in pairs(sides) do + table.insert(choices, { name = k, value = k }) + end + self.form.side.choices = choices - self:updatePeripherals(config) - UI.Tab.enable(self) + self:updatePeripherals(config) + UI.Tab.enable(self) - self.form.drive_id.enabled = false + self.form.drive_id.enabled = false end function tab:eventHandler(event) - if event.type == 'form_complete' then - if event.values.type == 'disk_drive' and not event.values.drive_id then - self:emit({ type = 'error_message', message = 'Invalid drive ID' }) - else - ccemux.detach(event.values.side) + if event.type == 'form_complete' then + if event.values.type == 'disk_drive' and not event.values.drive_id then + self:emit({ type = 'error_message', message = 'Invalid drive ID' }) + else + ccemux.detach(event.values.side) - local config = Config.load('ccemux') - config[event.values.side] = { - type = event.values.type - } - if event.values.type == 'disk_drive' then - config[event.values.side].args = { - id = event.values.drive_id - } - ccemux.attach(event.values.side, event.values.type, { id = event.values.drive_id }) - else - ccemux.attach(event.values.side, event.values.type) - end - Config.update('ccemux', config) - self:updatePeripherals(config) - self.grid:draw() + local config = Config.load('ccemux') + config[event.values.side] = { + type = event.values.type + } + if event.values.type == 'disk_drive' then + config[event.values.side].args = { + id = event.values.drive_id + } + ccemux.attach(event.values.side, event.values.type, { id = event.values.drive_id }) + else + ccemux.attach(event.values.side, event.values.type) + end + Config.update('ccemux', config) + self:updatePeripherals(config) + self.grid:draw() - self:emit({ type = 'success_message', message = 'Attached' }) - end + self:emit({ type = 'success_message', message = 'Attached' }) + end - elseif event.type == 'choice_change' then - if event.element == self.form.ptype then - self.form.drive_id.enabled = event.value == 'disk_drive' - self.form:draw() - end + elseif event.type == 'choice_change' then + if event.element == self.form.ptype then + self.form.drive_id.enabled = event.value == 'disk_drive' + self.form:draw() + end - elseif event.type == 'grid_select' then - local config = Config.load('ccemux') - config[event.selected.side] = nil - Config.update('ccemux', config) - self:updatePeripherals(config) - self.grid:draw() + elseif event.type == 'grid_select' then + local config = Config.load('ccemux') + config[event.selected.side] = nil + Config.update('ccemux', config) + self:updatePeripherals(config) + self.grid:draw() - self:emit({ type = 'success_message', message = 'Detached' }) + self:emit({ type = 'success_message', message = 'Detached' }) - return true - end + return true + end end return tab diff --git a/neural/apis/glasses.lua b/neural/apis/glasses.lua new file mode 100644 index 0000000..f6b62a1 --- /dev/null +++ b/neural/apis/glasses.lua @@ -0,0 +1,121 @@ +--[[ + Create a terminal compatible window for glasses canvas. +]] + +local Terminal = require('opus.terminal') + +local colors = _G.colors +local device = _G.device + +local scale = .5 +local xs, ys = 6 * scale, 9 * scale + +local Glasses = { } + +function Glasses.create(name, sx, sy, w, h) + w, h = w or 46, h or 19 + sx, sy = sx or 1, sy or 20 + + local glasses = device['plethora:glasses'] + local canvas = glasses.canvas() + local _, cy = 1, 1 + local lines = { } + local map = { + ['0'] = 0xF0F0F0FF, + ['1'] = 0xF2B233FF, + ['2'] = 0xE57FD8FF, + ['3'] = 0x99B2F2FF, + ['4'] = 0xDEDE6CFF, + ['5'] = 0x7FCC19FF, + ['6'] = 0xF2B2CCFF, + ['7'] = 0x4C4C4CFF, + ['8'] = 0x999999FF, + ['9'] = 0x4C99B2FF, + ['a'] = 0xB266E5FF, + ['b'] = 0x3366CCFF, + ['c'] = 0x7F664CFF, + ['d'] = 0x57A64EFF, + ['e'] = 0xCC4C4CFF, + ['f'] = 0x191919FF, + } + + -- Position bottom left + local pos = { x = sx * xs, y = sy * ys } + + local function init(group) + for y = 1, h do + lines[y] = { + text = { }, + bg = { } + } + for x = 1, w do + lines[y].bg[x] = group.addRectangle(x * xs, y * ys, xs, ys, 0xF0F0F04F) + lines[y].text[x] = group.addText({ x * xs, y * ys }, '', 0x7FCC19FF) + lines[y].text[x].setScale(scale) + end + end + end + + local group = canvas.addGroup(pos) + init(group) + + local gterm = Terminal.window({ + getSize = function() + return w, h + end, + isColor = function() + return true + end, + clear = function() + for y = 1, h do + for x = 1, w do + local ln = lines[y] + ln.bg[x].setColor(0xF0F0F04F) + ln.text[x].setText('') + end + end + end, + blit = function(text, fg, bg) + for x = 1, #text do + local ln = lines[cy] + ln.bg[x].setColor(map[bg:sub(x, x)]) + ln.text[x].setColor(map[fg:sub(x, x)]) + ln.text[x].setText(text:sub(x, x)) + end + end, + setCursorPos = function(_, y) + cy = y -- full lines are always blit + end, + getTextColor = function() + return colors.white + end, + setTextColor = function() end, + getBackgroundColor = function() + return colors.black + end, + setBackgroundColor = function() end, + setCursorBlink = function() end, + }, 1, 1, w, h, true) + + function gterm.setTextScale() end + function gterm.getPosition() return sx, sy end + function gterm.setVisible() end + function gterm.raise() + local g = canvas.addGroup(pos) + init(g) + gterm.redraw() + group.remove() + group = g + end + function gterm.destroy() + group.remove() + end + + gterm.name = name + gterm.side = name + gterm.type = 'glasses' + + return gterm +end + +return Glasses diff --git a/neural/nwm.lua b/neural/nwm.lua new file mode 100644 index 0000000..b956e62 --- /dev/null +++ b/neural/nwm.lua @@ -0,0 +1,160 @@ +--[[ + A simplistic window manager for glasses. + TODO: support moving windows via mouse drag. +]] + +local Config = require('opus.config') +local Glasses = require('neural.glasses') +local UI = require('opus.ui') +local Util = require('opus.util') + +local kernel = _G.kernel +local multishell = _ENV.multishell +local shell = _ENV.shell + +local sandbox = Util.shallowCopy(_ENV) + +-- TODO: figure out how to better define scaling +local scale = .5 +local xs, ys = 6 * scale, 9 * scale + +local events = { + glasses_click = 'mouse_click', + glasses_up = 'mouse_up', + glasses_drag = 'mouse_drag', + glasses_scroll = 'mouse_scroll', +} + +local hookEvents = { 'glasses_click', 'glasses_up', 'glasses_drag', 'glasses_scroll' } + +local function hook(e, eventData) + local currentTab = kernel.getFocused() + local x = math.floor(eventData[2] / xs) + local y = math.floor(eventData[3] / ys) + local clickedTab + + for _,tab in ipairs(kernel.routines) do + if tab.window.type == 'glasses' then + local wx, wy = tab.window.getPosition() + local ww, wh = tab.window.getSize() + + if x >= wx and x <= wx + ww and y >= wy and y <= wy + wh then + clickedTab = tab + x = x - wx + y = y - wy + break + end + end + end + + if clickedTab then + if clickedTab ~= currentTab then + clickedTab.window.raise() + multishell.setFocus(clickedTab.uid) + end + + kernel.event(events[e], { + eventData[1], x, y, clickedTab.window.side, + }) + + end + return true +end + +local config = Config.load('nwm', { session = { } }) + +local function run(args) + local window = Glasses.create('glasses', args.x, args.y, args.w, args.h) + + local env = Util.shallowCopy(sandbox) + _G.requireInjector(env) + + multishell.openTab({ + path = args.path, + args = args.args, + env = env, + focused = false, + hidden = true, + onDestroy = function() + Util.removeByValue(config.session, args) + Config.update('nwm', config) + window.destroy() + end, + window = window, + }) +end + +kernel.hook(hookEvents, hook) + +UI:setPage(UI.Page { + form = UI.Form { + values = { + x = 1, y = 25, w = 51, h = 19, + }, + path = UI.TextEntry { + y = 5, + formKey = 'path', formLabel = 'Run', required = true, + }, + args = UI.TextEntry { + y = 7, + formKey = 'args', formLabel = 'Args', + }, + UI.Text { + x = 7, y = 5, + textColor = 'yellow', + value = ' x y' + }, + wx = UI.TextEntry { + x = 7, y = 6, width = 7, limit = 3, + transform = 'number', + formKey = 'x', required = true, + }, + wy = UI.TextEntry { + x = 15, y = 6, width = 7, limit = 4, + transform = 'number', + formKey = 'y', required = true, + }, + UI.Text { + x = 7, y = 8, + textColor = 'yellow', + value = ' width height' + }, + ww = UI.TextEntry { + x = 7, y = 9, width = 7, limit = 4, + transform = 'number', + formKey = 'w', required = true, + }, + wh = UI.TextEntry { + x = 15, y = 9, width = 7, limit = 4, + transform = 'number', + formKey = 'h', required = true, + }, + }, + notification = UI.Notification { }, + eventHandler = function(self, event) + if event.type == 'form_complete' then + local args = Util.shallowCopy(event.values) + args.path = shell.resolveProgram(args.path) + if not args.path then + self.notification:error('Invalid program') + else + if args.args then + args.args = Util.split(args.args, '(.-) ') + end + table.insert(config.session, args) + Config.update('nwm', config) + run(args) + self.notification:success('Started program') + end + end + return UI.Page.eventHandler(self, event) + end, +}) + +for _,v in pairs(config.session) do + run(v) +end + +UI:start() + +kernel.unhook(hookEvents, hook) diff --git a/neural/overlay.lua b/neural/overlay.lua deleted file mode 100644 index 83984c5..0000000 --- a/neural/overlay.lua +++ /dev/null @@ -1,116 +0,0 @@ -local Terminal = require('opus.terminal') - -local colors = _G.colors -local device = _G.device -local kernel = _G.kernel - ---[[ - Create a device for glasses - Usable as a redirect or UI target - - Example usage: - Files --display=glasses - debugMonitor glasses - - In a program: - local prev = term.redirect(device.glasses) - shell.run('shell') - term.redirect(prev) - - Glasses do not use the CC font - so extended chars - do not display correctly. -]] - --- configurable -local w, h = 46, 19 -local scale = .5 - -local glasses = device['plethora:glasses'] -local canvas = glasses.canvas() -local _, cy = 1, 1 -local _, gh = canvas:getSize() -local lines = { } -local map = { - ['0'] = 0xF0F0F0FF, - ['1'] = 0xF2B233FF, - ['2'] = 0xE57FD8FF, - ['3'] = 0x99B2F2FF, - ['4'] = 0xDEDE6CFF, - ['5'] = 0x7FCC19FF, - ['6'] = 0xF2B2CCFF, - ['7'] = 0x4C4C4CFF, - ['8'] = 0x999999FF, - ['9'] = 0x4C99B2FF, - ['a'] = 0xB266E5FF, - ['b'] = 0x3366CCFF, - ['c'] = 0x7F664CFF, - ['d'] = 0x57A64EFF, - ['e'] = 0xCC4C4CFF, - ['f'] = 0x191919FF, -- transparent -} - -local xs, ys = 6 * scale, 9 * scale - --- Position bottom left -local pos = { x = 1, y = gh - (h * ys) - 10 } -local group = canvas.addGroup(pos) - -for y = 1, h do - lines[y] = { - text = { }, - bg = { } - } - for x = 1, w do - lines[y].bg[x] = group.addRectangle(x * xs, y * ys, xs, ys, 0xF0F0F04F) - lines[y].text[x] = group.addText({ x * xs, y * ys }, '', 0x7FCC19FF) - lines[y].text[x].setScale(scale) - end -end - -device.glasses = Terminal.window({ - getSize = function() - return w, h - end, - isColor = function() - return true - end, - clear = function() - --canvas.clear() - end, - blit = function(text, fg, bg) - for x = 1, #text do - local ln = lines[cy] - ln.bg[x].setColor(map[bg:sub(x, x)]) - ln.text[x].setColor(map[fg:sub(x, x)]) - ln.text[x].setText(text:sub(x, x)) - end - end, - setCursorPos = function(_, y) - -- full lines are always blit - cy = y - end, - setBackgroundColor = function() - end, - setTextColor = function() - end, - setCursorBlink = function() - end, - getBackgroundColor = function() - return colors.black - end, - getTextColor = function() - return colors.white - end, -}, 1, 1, w, h, true) - -function device.glasses.setTextScale() end - -device.glasses.side = 'glasses' -device.glasses.type = 'glasses' -device.glasses.name = 'glasses' - -kernel.hook('glasses_click', function(_, eventData) - os.queueEvent('monitor_touch', 'glasses', - math.floor((eventData[2] - pos.x) / xs), - math.floor((eventData[3] - pos.y) / ys)) -end) -- 2.49.1 From 87a3f9fa965ae4d037719092ded5405428616ae8 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Mon, 4 May 2020 21:21:43 -0400 Subject: [PATCH 19/24] Cleanup --- milo/apps/storageGen.lua | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/milo/apps/storageGen.lua b/milo/apps/storageGen.lua index 4dd4e03..c4ea024 100644 --- a/milo/apps/storageGen.lua +++ b/milo/apps/storageGen.lua @@ -1,7 +1,3 @@ ---[[ - For initially setting up large amounts of storage chests. -]] - local UI = require('opus.ui') local Util = require('opus.util') local Peripheral = require('opus.peripheral') @@ -72,10 +68,8 @@ local page = UI.Page { } function page:scan() - _syslog("Scanned") - self.storages = Util.filter(Peripheral.getList(), function(dev) - return not not dev.pushItems + return dev.pushItems end) local types = {} @@ -90,11 +84,9 @@ function page:scan() end function page:saveConfig(path) - _syslog("Saving to "..path) - local config = Util.readTable(path) or {} Util.each(self.storages, function(dev, name) - if self.typeGrid.values[dev.type] and self.typeGrid.values[dev.type].checked then + if self.typeGrid.values[dev.type] and self.typeGrid.values[dev.type].checked and not config[name] then config[name] = { name = name, category = 'storage', @@ -103,12 +95,13 @@ function page:saveConfig(path) end end) Util.writeTable(path, config) + self.notification:success("Config saved to "..path) end function page:enable() - self:scan() UI.Page.enable(self) + self:scan() end function page.typeGrid:getRowTextColor(row, selected) @@ -116,7 +109,6 @@ function page.typeGrid:getRowTextColor(row, selected) end function page:eventHandler(event) - _syslog(event) if event.type == "rescan" then self:scan() -- 2.49.1 From 8db9a89f680cc7304ed478d95fa6d40a4d34ffe9 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 5 May 2020 17:26:44 -0600 Subject: [PATCH 20/24] nwm opacity / improvements - env cleanup --- common/Appstore.lua | 9 ++-- neural/apis/glasses.lua | 92 +++++++++++++++++++---------------------- neural/nwm.lua | 69 ++++++++++++++----------------- secure/autorun/lock.lua | 4 -- 4 files changed, 75 insertions(+), 99 deletions(-) diff --git a/common/Appstore.lua b/common/Appstore.lua index 7576f5f..7e0ad85 100644 --- a/common/Appstore.lua +++ b/common/Appstore.lua @@ -27,9 +27,6 @@ local function unregisterApp(key) end end -local sandboxEnv = Util.shallowCopy(_ENV) -setmetatable(sandboxEnv, { __index = _G }) - multishell.setTitle(multishell.getCurrent(), 'App Store') UI:configure('Appstore', ...) @@ -60,7 +57,7 @@ local function downloadApp(app) end local function runApp(app, checkExists, ...) - + local env = shell.makeEnv() local path, fn local args = { ... } @@ -81,14 +78,14 @@ local function runApp(app, checkExists, ...) error('Failed to download') end - _G.setfenv(fn, sandboxEnv) + _G.setfenv(fn, env) fn(table.unpack(args)) end end multishell.openTab({ title = app.name, - env = sandboxEnv, + env = env, path = path, fn = fn, focused = true, diff --git a/neural/apis/glasses.lua b/neural/apis/glasses.lua index f6b62a1..1d8a1d4 100644 --- a/neural/apis/glasses.lua +++ b/neural/apis/glasses.lua @@ -3,55 +3,64 @@ ]] local Terminal = require('opus.terminal') +local Util = require('opus.util') local colors = _G.colors local device = _G.device -local scale = .5 -local xs, ys = 6 * scale, 9 * scale - local Glasses = { } -function Glasses.create(name, sx, sy, w, h) - w, h = w or 46, h or 19 - sx, sy = sx or 1, sy or 20 +function Glasses.create(args) + local opts = { + x = 1, y = 20, + width = 51, height = 19, + scale = .5, + name = 'glasses', + opacity = 0xff, + } + Util.merge(opts, args) + local xs, ys = 6 * opts.scale, 9 * opts.scale local glasses = device['plethora:glasses'] local canvas = glasses.canvas() local _, cy = 1, 1 local lines = { } local map = { - ['0'] = 0xF0F0F0FF, - ['1'] = 0xF2B233FF, - ['2'] = 0xE57FD8FF, - ['3'] = 0x99B2F2FF, - ['4'] = 0xDEDE6CFF, - ['5'] = 0x7FCC19FF, - ['6'] = 0xF2B2CCFF, - ['7'] = 0x4C4C4CFF, - ['8'] = 0x999999FF, - ['9'] = 0x4C99B2FF, - ['a'] = 0xB266E5FF, - ['b'] = 0x3366CCFF, - ['c'] = 0x7F664CFF, - ['d'] = 0x57A64EFF, - ['e'] = 0xCC4C4CFF, - ['f'] = 0x191919FF, + ['0'] = 0xF0F0F000, + ['1'] = 0xF2B23300, + ['2'] = 0xE57FD800, + ['3'] = 0x99B2F200, + ['4'] = 0xDEDE6C00, + ['5'] = 0x7FCC1900, + ['6'] = 0xF2B2CC00, + ['7'] = 0x4C4C4C00, + ['8'] = 0x99999900, + ['9'] = 0x4C99B200, + ['a'] = 0xB266E500, + ['b'] = 0x3366CC00, + ['c'] = 0x7F664C00, + ['d'] = 0x57A64E00, + ['e'] = 0xCC4C4C00, + ['f'] = 0x19191900, } + for k,v in pairs(map) do + map[k] = v + opts.opacity + end + -- Position bottom left - local pos = { x = sx * xs, y = sy * ys } + local pos = { x = opts.x * xs, y = opts.y * ys } local function init(group) - for y = 1, h do + for y = 1, opts.height do lines[y] = { text = { }, bg = { } } - for x = 1, w do + for x = 1, opts.width do lines[y].bg[x] = group.addRectangle(x * xs, y * ys, xs, ys, 0xF0F0F04F) lines[y].text[x] = group.addText({ x * xs, y * ys }, '', 0x7FCC19FF) - lines[y].text[x].setScale(scale) + lines[y].text[x].setScale(opts.scale) end end end @@ -60,21 +69,9 @@ function Glasses.create(name, sx, sy, w, h) init(group) local gterm = Terminal.window({ - getSize = function() - return w, h - end, isColor = function() return true end, - clear = function() - for y = 1, h do - for x = 1, w do - local ln = lines[y] - ln.bg[x].setColor(0xF0F0F04F) - ln.text[x].setText('') - end - end - end, blit = function(text, fg, bg) for x = 1, #text do local ln = lines[cy] @@ -86,19 +83,11 @@ function Glasses.create(name, sx, sy, w, h) setCursorPos = function(_, y) cy = y -- full lines are always blit end, - getTextColor = function() - return colors.white - end, - setTextColor = function() end, - getBackgroundColor = function() - return colors.black - end, - setBackgroundColor = function() end, setCursorBlink = function() end, - }, 1, 1, w, h, true) + }, 1, 1, opts.width, opts.height, true) function gterm.setTextScale() end - function gterm.getPosition() return sx, sy end + function gterm.getPosition() return opts.x, opts.y end function gterm.setVisible() end function gterm.raise() local g = canvas.addGroup(pos) @@ -110,9 +99,12 @@ function Glasses.create(name, sx, sy, w, h) function gterm.destroy() group.remove() end + function gterm.getTextScale() + return opts.scale + end - gterm.name = name - gterm.side = name + gterm.name = opts.name + gterm.side = opts.name gterm.type = 'glasses' return gterm diff --git a/neural/nwm.lua b/neural/nwm.lua index b956e62..7e54db0 100644 --- a/neural/nwm.lua +++ b/neural/nwm.lua @@ -12,7 +12,7 @@ local kernel = _G.kernel local multishell = _ENV.multishell local shell = _ENV.shell -local sandbox = Util.shallowCopy(_ENV) +local config = Config.load('nwm', { session = { } }) -- TODO: figure out how to better define scaling local scale = .5 @@ -25,8 +25,6 @@ local events = { glasses_scroll = 'mouse_scroll', } -local hookEvents = { 'glasses_click', 'glasses_up', 'glasses_drag', 'glasses_scroll' } - local function hook(e, eventData) local currentTab = kernel.getFocused() local x = math.floor(eventData[2] / xs) @@ -61,19 +59,15 @@ local function hook(e, eventData) return true end -local config = Config.load('nwm', { session = { } }) +local hookEvents = Util.keys(events) +kernel.hook(hookEvents, hook) local function run(args) - local window = Glasses.create('glasses', args.x, args.y, args.w, args.h) - - local env = Util.shallowCopy(sandbox) - _G.requireInjector(env) + local window = Glasses.create(args) multishell.openTab({ path = args.path, args = args.args, - env = env, - focused = false, hidden = true, onDestroy = function() Util.removeByValue(config.session, args) @@ -84,66 +78,63 @@ local function run(args) }) end -kernel.hook(hookEvents, hook) - UI:setPage(UI.Page { form = UI.Form { values = { - x = 1, y = 25, w = 51, h = 19, + x = 1, y = 25, width = 51, height = 19, + opacity = 255, }, - path = UI.TextEntry { - y = 5, - formKey = 'path', formLabel = 'Run', required = true, + UI.TextEntry { + formKey = 'run', formLabel = 'Run', required = true, }, - args = UI.TextEntry { - y = 7, - formKey = 'args', formLabel = 'Args', + UI.Slider { + min = 0, max = 255, + formLabel = 'Opacity', formKey = 'opacity', formIndex = 3, }, UI.Text { - x = 7, y = 5, + x = 10, y = 5, textColor = 'yellow', value = ' x y' }, - wx = UI.TextEntry { - x = 7, y = 6, width = 7, limit = 3, + UI.TextEntry { + x = 10, y = 6, width = 7, limit = 3, transform = 'number', formKey = 'x', required = true, }, - wy = UI.TextEntry { - x = 15, y = 6, width = 7, limit = 4, + UI.TextEntry { + x = 18, y = 6, width = 7, limit = 4, transform = 'number', formKey = 'y', required = true, }, UI.Text { - x = 7, y = 8, + x = 10, y = 8, textColor = 'yellow', value = ' width height' }, - ww = UI.TextEntry { - x = 7, y = 9, width = 7, limit = 4, + UI.TextEntry { + x = 10, y = 9, width = 7, limit = 4, transform = 'number', - formKey = 'w', required = true, + formKey = 'width', required = true, }, - wh = UI.TextEntry { - x = 15, y = 9, width = 7, limit = 4, + UI.TextEntry { + x = 18, y = 9, width = 7, limit = 4, transform = 'number', - formKey = 'h', required = true, + formKey = 'height', required = true, }, }, notification = UI.Notification { }, eventHandler = function(self, event) if event.type == 'form_complete' then - local args = Util.shallowCopy(event.values) - args.path = shell.resolveProgram(args.path) - if not args.path then + local opts = Util.shallowCopy(event.values) + local words = Util.split(opts.run, '(.-) ') + opts.path = shell.resolveProgram(table.remove(words, 1)) + if not opts.path then self.notification:error('Invalid program') else - if args.args then - args.args = Util.split(args.args, '(.-) ') - end - table.insert(config.session, args) + opts.args = #words > 0 and words + table.insert(config.session, opts) Config.update('nwm', config) - run(args) + run(opts) self.notification:success('Started program') end end diff --git a/secure/autorun/lock.lua b/secure/autorun/lock.lua index a8965d7..eadff4c 100644 --- a/secure/autorun/lock.lua +++ b/secure/autorun/lock.lua @@ -17,9 +17,6 @@ local config = Config.load('secure', { local timer = config.enabled and os.startTimer(config.timeout) -local sandboxEnv = Util.shallowCopy(_ENV) -setmetatable(sandboxEnv, { __index = _G }) - local function buildLockScreen() _G.requireInjector(_ENV) @@ -81,7 +78,6 @@ local function showLockScreen() pinned = true, focused = true, title = 'Lock', - env = sandboxEnv, }) end -- 2.49.1 From 59de8e7f63d544351e13cd62c7f8d89967976754 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Wed, 6 May 2020 02:54:15 -0400 Subject: [PATCH 21/24] Update k.lua and others --- swshop/apis/jua.lua | 2 +- swshop/apis/k.lua | 16 +++++++++++++--- swshop/apis/r.lua | 19 +++++++++++-------- swshop/apis/w.lua | 28 ++++++++++++++-------------- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/swshop/apis/jua.lua b/swshop/apis/jua.lua index 273b9ec..4ee5b73 100644 --- a/swshop/apis/jua.lua +++ b/swshop/apis/jua.lua @@ -119,4 +119,4 @@ return { go = go, stop = stop, await = await -} \ No newline at end of file +} diff --git a/swshop/apis/k.lua b/swshop/apis/k.lua index 47ad499..4adf49e 100644 --- a/swshop/apis/k.lua +++ b/swshop/apis/k.lua @@ -101,6 +101,15 @@ function addresses(cb, limit, offset) end, "/addresses?limit="..(limit or 50).."&offset="..(offset or 0)) end +function name(cb, name) + asserttype(cb, "callback", "function") + asserttype(name, "name", "string") + + api_request(function(success, data) + cb(success and data and data.ok, data.name or data) + end, "/names/"..name) +end + function rich(cb, limit, offset) asserttype(cb, "callback", "function") asserttype(limit, "limit", "number", true) @@ -165,7 +174,8 @@ local wsEventNameLookup = { names = "name", ownNames = "name", ownWebhooks = "webhook", - motd = "motd" + motd = "motd", + keepalive = "keepalive", } local wsEvents = {} @@ -362,6 +372,7 @@ return { addressTransactions = addressTransactions, addressNames = addressNames, addresses = addresses, + name = name, rich = rich, transactions = transactions, latestTransactions = latestTransactions, @@ -369,5 +380,4 @@ return { makeTransaction = makeTransaction, connect = connect, parseMeta = parseMeta, - sha256 = sha256, -} \ No newline at end of file +} diff --git a/swshop/apis/r.lua b/swshop/apis/r.lua index 3982634..f967776 100644 --- a/swshop/apis/r.lua +++ b/swshop/apis/r.lua @@ -18,13 +18,16 @@ end local function findID(url) local found = gfind(url, idPatt) - if found then - return tonumber(found[#found]:sub(found[#found]:find("%d+"))) - end + if not found then return nil end + return tonumber(found[#found]:sub(found[#found]:find("%d+"))) end local function newID() - return #callbackRegistry + 1 + for i = 1, math.huge do + if not callbackRegistry[i] then + return i + end + end end local function trimID(url) @@ -36,8 +39,8 @@ end function request(callback, url, headers, postData) local id = newID() local newUrl = url .. "#R" .. id - http.request(newUrl, postData, headers) callbackRegistry[id] = callback + http.request(newUrl, postData, headers) end function init(jua) @@ -46,7 +49,7 @@ function init(jua) local id = findID(url) if id and callbackRegistry[id] then callbackRegistry[id](true, trimID(url), handle) - table.remove(callbackRegistry, id) + callbackRegistry[id] = nil end end) @@ -54,7 +57,7 @@ function init(jua) local id = findID(url) if id and callbackRegistry[id] then callbackRegistry[id](false, trimID(url), handle) - table.remove(callbackRegistry, id) + callbackRegistry[id] = nil end end) end @@ -62,4 +65,4 @@ end return { request = request, init = init -} \ No newline at end of file +} diff --git a/swshop/apis/w.lua b/swshop/apis/w.lua index 85fadf7..7fe1e3e 100644 --- a/swshop/apis/w.lua +++ b/swshop/apis/w.lua @@ -31,8 +31,8 @@ end local function findID(url) local found = gfind(url, idPatt) - local id = tonumber(found[#found]:sub(found[#found]:find("%d+"))) - return id + if not found then return nil end + return tonumber(found[#found]:sub(found[#found]:find("%d+"))) end local function newID() @@ -71,57 +71,57 @@ function init(jua) jua = jua if async then jua.on("websocket_success", function(event, url, handle) - local success, id = pcall(findID,url) - if success and id and callbackRegistry[id] and callbackRegistry[id].success then + local id = findID(url) + if id and callbackRegistry[id].success then callbackRegistry[id].success(id, handle) end end) jua.on("websocket_failure", function(event, url) - local success, id = pcall(findID,url) - if success and id and callbackRegistry[id] and callbackRegistry[id].failure then + local id = findID(url) + if id and callbackRegistry[id].failure then callbackRegistry[id].failure(id) end table.remove(callbackRegistry, id) end) jua.on("websocket_message", function(event, url, data) - local success, id = pcall(findID,url) - if success and id and callbackRegistry[id] and callbackRegistry[id].message then + local id = findID(url) + if id and callbackRegistry[id].message then callbackRegistry[id].message(id, data) end end) jua.on("websocket_closed", function(event, url) - local success, id = pcall(findID,url) - if success and id and callbackRegistry[id] and callbackRegistry[id].closed then + local id = findID(url) + if id and callbackRegistry[id].closed then callbackRegistry[id].closed(id) end table.remove(callbackRegistry, id) end) else jua.on("socket_connect", function(event, id) - if id and callbackRegistry[id] and callbackRegistry[id].success then + if id and callbackRegistry[id].success then callbackRegistry[id].success(id, wsRegistry[id]) end end) jua.on("socket_error", function(event, id, msg) - if id and callbackRegistry[id] and callbackRegistry[id].failure then + if id and callbackRegistry[id].failure then callbackRegistry[id].failure(id, msg) end table.remove(callbackRegistry, id) end) jua.on("socket_message", function(event, id) - if id and callbackRegistry[id] and callbackRegistry[id].message then + if id and callbackRegistry[id].message then local data = wsRegistry[id].read() callbackRegistry[id].message(id, data) end end) jua.on("socket_closed", function(event, id) - if id and callbackRegistry[id] and callbackRegistry[id].closed then + if id and callbackRegistry[id].closed then callbackRegistry[id].closed(id) end table.remove(callbackRegistry, id) -- 2.49.1 From bc3a48f30f0526e2b6b937bf52e2b749de17dec9 Mon Sep 17 00:00:00 2001 From: Anavrins Date: Wed, 6 May 2020 03:03:49 -0400 Subject: [PATCH 22/24] Swshop update - Toggle lamp based on keepalive packets from the node - Add functionality to split profits of a bought item (Will document later) --- swshop/swshop.lua | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/swshop/swshop.lua b/swshop/swshop.lua index a90c20a..eaf1dc9 100644 --- a/swshop/swshop.lua +++ b/swshop/swshop.lua @@ -45,7 +45,7 @@ local function getItemDetails(item) t = textutils.unserialize(t) for key, v in pairs(t) do if v.name == item then - return key, tonumber(v.price) + return key, v end end end @@ -89,7 +89,9 @@ local function handleTransaction(transaction) logTransaction(t, { refund = amount, reason = reason }) end - t.itemId, t.price = getItemDetails(metadata.name) + local id, item = getItemDetails(metadata.name) + t.itemId, t.price = id, tonumber(item.price) + if not t.itemId or not t.price then print('invalid item') logTransaction(t, { reason = 'invalid item' }) @@ -104,6 +106,15 @@ local function handleTransaction(transaction) return refundTransaction(value, "error=Please pay the price listed on-screen.") end + if item.shares then + local amount = t.value - (t.value % t.price) + for _, share in pairs(item.shares) do + local cut = math.floor(amount * share.rate) -- Don't include fractional Krist + print("Sent shares of "..cut.." to "..share.address) + await(k.makeTransaction, privatekey, share.address, cut, share.meta) + end + end + local count = math.floor(value / t.price) local uid = math.random() print(string.format('requesting %d of %s', count, t.itemId)) @@ -151,6 +162,12 @@ local function connect() local transaction = data.transaction handleTransaction(transaction) end) + + await(ws.subscribe, "keepalive", function(data) + local state = rs.getOutput(rsSide) + rs.setOutput(rsSide, not state) + end) + assert(success, "Failed to subscribe to event") end -- 2.49.1 From 21b4d7a78c52d3e7ac7a48fa956ce916db9ee738 Mon Sep 17 00:00:00 2001 From: Luca_S Date: Sat, 9 May 2020 20:23:45 +0200 Subject: [PATCH 23/24] Set a default value for to.lockedToThis in the first iteration of the loop --- milo/apis/storage.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index aaa0811..1855096 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -374,9 +374,9 @@ function Storage:defrag(throttle) -- are ruled out by the condition of the outer while loop for i = 2, #providers do -- Give preference to locked chests - if not to.lockedToThis and providers[i].lockedToThis then + if not (to and to.lockedToThis or false) and providers[i].lockedToThis then to = providers[i] - elseif (to.lockedToThis == providers[i].lockedToThis) and providers[i].item.count < providers[i].item.maxCount then + elseif ((to and to.lockedToThis or false) == providers[i].lockedToThis) and providers[i].item.count < providers[i].item.maxCount then to = providers[i] elseif providers[i].item.count == providers[i].item.maxCount then -- As this slot is already at maxCount, all the remaining ones will also be due to sorting -- 2.49.1 From ca808cdd7d8c60b99bad4b6593a8611c6bf3e97f Mon Sep 17 00:00:00 2001 From: Luca_S Date: Sat, 9 May 2020 20:42:44 +0200 Subject: [PATCH 24/24] No need to check if a key is contained, just index it doh --- milo/apis/storage.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/milo/apis/storage.lua b/milo/apis/storage.lua index 1855096..a4d19c4 100644 --- a/milo/apis/storage.lua +++ b/milo/apis/storage.lua @@ -327,7 +327,7 @@ function Storage:listProviders(throttle) if not res[key] then res[key] = {} end - table.insert(res[key], {item = item, device = device[chest], lockedToThis = Util.contains(Util.keys(self.nodes[chest].lock or {}), key) and true or false, slot = slot}) + table.insert(res[key], {item = item, device = device[chest], lockedToThis = (self.nodes[chest].lock or {})[key] or false, slot = slot}) end end return res -- 2.49.1