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