From 8e381d3ebfeaf6083825d83680ddd7f64b0dac64 Mon Sep 17 00:00:00 2001 From: "kepler155c@gmail.com" Date: Tue, 3 Oct 2017 00:50:54 -0400 Subject: [PATCH] ui improvements --- sys/apis/ansi.lua | 55 ++++++++ sys/apis/history.lua | 69 ++++----- sys/apis/ui.lua | 308 +++++++++++++++++++++++++++-------------- sys/apis/ui/fileui.lua | 4 +- sys/apis/util.lua | 24 ++-- sys/apps/Files.lua | 2 +- sys/apps/Lua.lua | 38 ++--- sys/apps/Overview.lua | 59 ++++---- sys/apps/shell | 46 +++--- sys/network/snmp.lua | 16 ++- 10 files changed, 393 insertions(+), 228 deletions(-) create mode 100644 sys/apis/ansi.lua diff --git a/sys/apis/ansi.lua b/sys/apis/ansi.lua new file mode 100644 index 0000000..54b8227 --- /dev/null +++ b/sys/apis/ansi.lua @@ -0,0 +1,55 @@ +local Ansi = setmetatable({ }, { + __call = function(self, ...) + local str = '\027[' + for k,v in ipairs({ ...}) do + if k == 1 then + str = str .. v + else + str = str .. ';' .. v + end + end + return str .. 'm' + end +}) + +Ansi.codes = { + reset = 0, + white = 1, + orange = 2, + magenta = 3, + lightBlue = 4, + yellow = 5, + lime = 6, + pink = 7, + gray = 8, + lightGray = 9, + cyan = 10, + purple = 11, + blue = 12, + brown = 13, + green = 14, + red = 15, + black = 16, + onwhite = 21, + onorange = 22, + onmagenta = 23, + onlightBlue = 24, + onyellow = 25, + onlime = 26, + onpink = 27, + ongray = 28, + onlightGray = 29, + oncyan = 30, + onpurple = 31, + onblue = 32, + onbrown = 33, + ongreen = 34, + onred = 35, + onblack = 36, +} + +for k,v in pairs(Ansi.codes) do + Ansi[k] = Ansi(v) +end + +return Ansi diff --git a/sys/apis/history.lua b/sys/apis/history.lua index 4f90376..3bef67e 100644 --- a/sys/apis/history.lua +++ b/sys/apis/history.lua @@ -1,47 +1,50 @@ local Util = require('util') -local History = { } +local History = { } +local History_mt = { __index = History } function History.load(filename, limit) - local entries = Util.readLines(filename) or { } - local pos = #entries + 1 + local self = setmetatable({ + limit = limit, + filename = filename, + }, History_mt) - return { - entries = entries, + self.entries = Util.readLines(filename) or { } + self.pos = #self.entries + 1 - add = function(line) - local last = entries[pos] or entries[pos - 1] - if not last or line ~= last then - table.insert(entries, line) - if limit then - while #entries > limit do - table.remove(entries, 1) - end - end - Util.writeLines(filename, entries) - pos = #entries + 1 + return self +end + +function History:add(line) + if line ~= self.entries[#self.entries] then + table.insert(self.entries, line) + if self.limit then + while #self.entries > self.limit do + table.remove(self.entries, 1) end - end, + end + Util.writeLines(self.filename, self.entries) + self.pos = #self.entries + 1 + end +end - setPosition = function(p) - pos = p - end, +function History:reset() + self.pos = #self.entries + 1 +end - back = function() - if pos > 1 then - pos = pos - 1 - return entries[pos] - end - end, +function History:back() + if self.pos > 1 then + self.pos = self.pos - 1 + return self.entries[self.pos] + end +end - forward = function() - if pos <= #entries then - pos = pos + 1 - return entries[pos] - end - end, - } +function History:forward() + if self.pos <= #self.entries then + self.pos = self.pos + 1 + return self.entries[self.pos] + end end return History diff --git a/sys/apis/ui.lua b/sys/apis/ui.lua index ba7cc97..48bbd7f 100644 --- a/sys/apis/ui.lua +++ b/sys/apis/ui.lua @@ -1,8 +1,9 @@ -local Util = require('util') +local Ansi = require('ansi') local class = require('class') local Event = require('event') -local Tween = require('ui.tween') local Region = require('ui.region') +local Tween = require('ui.tween') +local Util = require('util') local mapColorToGray = { [ colors.white ] = colors.white, @@ -725,15 +726,6 @@ function UI.Window:centeredWrite(y, text, bg, fg) end end --- deprecated - use print instead -function UI.Window:wrappedWrite(x, y, text, len, bg, fg) - for _,v in pairs(Util.wordWrap(text, len)) do - self:write(x, y, v, bg, fg) - y = y + 1 - end - return y -end - function UI.Window:print(text, bg, fg, indent) indent = indent or 1 @@ -752,40 +744,73 @@ function UI.Window:print(text, bg, fg, indent) end end - --[[ - TODO - local test = "\027[0;1;33mYou tell foo, \"// Test string.\"\027[0;37mbar" - for sequence, text in string.gmatch (test, "\027%[([0-9;]+)m([^\027]+)") do - for ansi in string.gmatch (sequence, "%d+") do - print ("ANSI code: ", ansi) - end -- for - print ("Text: ", text) + local function pieces(f, bg, fg) + local pos = 1 + local t = { } + while true do + local s = f:find('\027', pos) + if not s then + break + end + if pos < s then + table.insert(t, f:sub(pos, s - 1)) + end + local seq = f:sub(s) + seq = seq:match("\027%[([%d;]+)m") + local e = { } + for color in string.gmatch(seq, "%d+") do + color = tonumber(color) + if color == 0 then + e.fg = fg + e.bg = bg + elseif color > 20 then + e.bg = 2 ^ (color - 21) + else + e.fg = 2 ^ (color - 1) + end + end + table.insert(t, e) + pos = s + #seq + 3 + end + if pos < #f then + table.insert(t, f:sub(pos)) + end + return t end - --]] local lines = Util.split(text) for k,line in pairs(lines) do - local lx = 1 - while true do - local word = nextWord(line, lx) - if not word then - if lines[k + 1] then - self.cursorX = indent - self.cursorY = self.cursorY + 1 + local fragments = pieces(line, bg, fg) + for l, fragment in ipairs(fragments) do + local lx = 1 + if type(fragment) == 'table' then -- ansi sequence + fg = fragment.fg + bg = fragment.bg + else + while true do + local word = nextWord(fragment, lx) + if not word then + break + end + local w = word + if self.cursorX + #word > self.width then + self.cursorX = indent + self.cursorY = self.cursorY + 1 + w = word:gsub(' ', '') + end + self:write(self.cursorX, self.cursorY, w, bg, fg) + self.cursorX = self.cursorX + #word + lx = lx + #word end - break end - local w = word - if self.cursorX + #word > self.width then - self.cursorX = indent - self.cursorY = self.cursorY + 1 - w = word:gsub(' ', '') - end - self:write(self.cursorX, self.cursorY, w, bg, fg) - self.cursorX = self.cursorX + #word - lx = lx + #word + end + if lines[k + 1] then + self.cursorX = indent + self.cursorY = self.cursorY + 1 end end + + return self.cursorX, self.cursorY end function UI.Window:setFocus(focus) @@ -852,22 +877,24 @@ function UI.Window:scrollIntoView() end end -function UI.Window:addTransition(effect, x, y, width, height) +function UI.Window:addTransition(effect, args) if self.parent then - x = x or 1 - y = y or 1 - width = width or self.width - height = height or self.height - self.parent:addTransition(effect, x + self.x - 1, y + self.y - 1, width, height) + args = args or { } + if not args.x then -- not good + args.x, args.y = getPosition(self) + args.width = self.width + args.height = self.height + end + + args.canvas = args.canvas or self.canvas + self.parent:addTransition(effect, args) end end function UI.Window:emit(event) local parent = self - --debug(self.UIElement .. ' emitting ' .. event.type) while parent do if parent.eventHandler then - --debug('calling ' .. parent.UIElement) if parent:eventHandler(event) then return true end @@ -1089,13 +1116,16 @@ function Canvas:blit(device, src, tgt) for i = 0, src.ey - src.y do local line = self.lines[src.y + i] - if line.dirty then + if line and line.dirty then local t, fg, bg = line.text, line.fg, line.bg if src.x > 1 or src.ex < self.ex then t = t:sub(src.x, src.ex) fg = fg:sub(src.x, src.ex) bg = bg:sub(src.x, src.ex) end + --if tgt.y + i > self.ey then -- wrong place to do clipping ?? + -- break + --end device.setCursorPos(tgt.x, tgt.y + i) device.blit(t, fg, bg) end @@ -1106,7 +1136,7 @@ end UI.TransitionSlideLeft = class() UI.TransitionSlideLeft.defaults = { UIElement = 'TransitionSlideLeft', - ticks = 4, + ticks = 6, easing = 'outQuint', } function UI.TransitionSlideLeft:init(args) @@ -1146,7 +1176,7 @@ end UI.TransitionSlideRight = class() UI.TransitionSlideRight.defaults = { UIElement = 'TransitionSlideRight', - ticks = 4, + ticks = 6, easing = 'outQuint', } function UI.TransitionSlideRight:init(args) @@ -1201,6 +1231,31 @@ function UI.TransitionExpandUp:update(device) return self.pos.y ~= self.y end +--[[-- TransitionGrow --]]-- +UI.TransitionGrow = class() +UI.TransitionGrow.defaults = { + UIElement = 'TransitionGrow', + ticks = 3, + easing = 'linear', +} +function UI.TransitionGrow:init(args) + local defaults = UI:getDefaults(UI.TransitionGrow, args) + UI.setProperties(self, defaults) + self.tween = Tween.new(self.ticks, + { x = self.width / 2 - 1, y = self.height / 2 - 1, w = 1, h = 1 }, + { x = 1, y = 1, w = self.width, h = self.height }, self.easing) +end + +function UI.TransitionGrow:update(device) + local finished = self.tween:update(1) + local subj = self.tween.subject + local rect = { x = math.floor(subj.x), y = math.floor(subj.y) } + rect.ex = math.floor(rect.x + subj.w - 1) + rect.ey = math.floor(rect.y + subj.h - 1) + self.canvas:blit(device, rect, { x = self.x + rect.x - 1, y = self.y + rect.y - 1}) + return not finished +end + --[[-- Terminal for computer / advanced computer / monitor --]]-- UI.Device = class(UI.Window) UI.Device.defaults = { @@ -1271,26 +1326,33 @@ function UI.Device:reset() self.device.setCursorPos(1, 1) end -function UI.Device:addTransition(effect, x, y, width, height) +-- refactor into canvas... +function UI.Device:addTransition(effect, args) if not self.transitions then self.transitions = { } end + args = args or { } + args.ex = args.x + args.width - 1 + args.ey = args.y + args.height - 1 + args.canvas = args.canvas or self.canvas + if type(effect) == 'string' then - local c - if effect == 'slideLeft' then - c = UI.TransitionSlideLeft - else - c = UI.TransitionSlideRight - end - effect = c { - x = x, - y = y, - ex = x + width - 1, - ey = y + height - 1, - canvas = self.canvas, + local transitions = { + slideLeft = UI.TransitionSlideLeft, + slideRight = UI.TransitionSlideRight, + expandUp = UI.TransitionExpandUp, + grow = UI.TransitionGrow, } + local c = transitions[effect] + if not c then + error('Invalid transition: ' .. effect) + end + effect = c(args) + else + Util.merge(effect, args) end + table.insert(self.transitions, effect) end @@ -1335,8 +1397,8 @@ function UI.Device:sync() end if self:getCursorBlink() then - self.device.setCursorBlink(true) self.device.setCursorPos(self.cursorX, self.cursorY) + self.device.setCursorBlink(true) end end @@ -1954,10 +2016,10 @@ UI.ScrollingGrid.defaults = { sliderChar = '#', upArrowChar = '^', downArrowChar = 'v', + scrollbarColor = colors.lightGray, } function UI.ScrollingGrid:init(args) - local defaults = UI:getDefaults(UI.ScrollingGrid, args) - UI.Grid.init(self, defaults) + UI.Grid.init(self, UI:getDefaults(UI.ScrollingGrid, args)) end function UI.ScrollingGrid:drawRows() @@ -1968,44 +2030,36 @@ end function UI.ScrollingGrid:drawScrollbar() local ts = Util.size(self.values) if ts > self.pageSize then - local sbSize = self.pageSize - 2 - local sa = ts - sa = self.pageSize / sa - sa = math.floor(sbSize * sa) - if sa < 1 then - sa = 1 + local maxScroll = ts - self.pageSize + local percent = (self.scrollOffset - 1) / maxScroll + local sliderSize = self.pageSize / ts * (self.pageSize - 2) + local row = 2 + + if self.disableHeader then + row = 1 end - if sa > sbSize then - sa = sbSize - end - local sp = ts-self.pageSize - sp = self.scrollOffset / sp - sp = math.floor(sp * (sbSize-sa + 0.5)) local x = self.width + for i = 1, self.pageSize - 2 do + self:write(x, row + i, self.lineChar, nil, self.scrollbarColor) + end + + local y = Util.round((self.pageSize - 2 - sliderSize) * percent) + for i = 1, Util.round(sliderSize) do + self:write(x, row + y + i, self.sliderChar, nil, self.scrollbarColor) + end + + local color = self.scrollbarColor if self.scrollOffset > 1 then - self:write(x, 2, self.upArrowChar) - else - self:write(x, 2, ' ') - end - local row = 0 - for i = 1, sp - 1 do - self:write(x, row+3, self.lineChar) - row = row + 1 - end - for i = 1, sa do - self:write(x, row+3, self.sliderChar) - row = row + 1 - end - for i = row, sbSize do - self:write(x, row+3, self.lineChar) - row = row + 1 + color = colors.white end + self:write(x, 2, self.upArrowChar, nil, color) + + color = self.scrollbarColor if self.scrollOffset + self.pageSize - 1 < Util.size(self.values) then - self:write(x, self.pageSize + 1, self.downArrowChar) - else - self:write(x, self.pageSize + 1, ' ') + color = colors.white end + self:write(x, self.pageSize + 1, self.downArrowChar, nil, color) end end @@ -2035,6 +2089,31 @@ function UI.ScrollingGrid:setIndex(index) UI.Grid.setIndex(self, index) end +function UI.ScrollingGrid:eventHandler(event) + + if event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then + if event.x == self.width then + local ts = Util.size(self.values) + if ts > self.pageSize then + local row = 2 + if self.disableHeader then + row = 1 + end + if event.y == row then + self:setIndex(self.scrollOffset - 1) + elseif event.y == self.height then + self:setIndex(self.scrollOffset + self.pageSize) + else + -- ... percentage ... + end + return true + end + end + end + + return UI.Grid.eventHandler(self, event) +end + --[[-- Menu --]]-- UI.Menu = class(UI.Grid) UI.Menu.defaults = { @@ -2591,14 +2670,7 @@ function UI.Notification:display(value, timeout) -- need to get the current canvas - not ui.term.canvas self.canvas = UI.term.canvas:addLayer(self, self.backgroundColor, self.textColor or colors.white) - self:addTransition(UI.TransitionExpandUp { - x = self.x, - y = self.y, - ex = self.x + self.width - 1, - ey = self.y + self.height - 1, - canvas = self.canvas, - ticks = self.height, - }) + self:addTransition('expandUp', { ticks = self.height }) self.canvas:setVisible(true) self:clear() for k,v in pairs(lines) do @@ -3209,6 +3281,29 @@ function UI.Text:draw() self:write(1, 1, Util.widthify(value, self.width), self.backgroundColor) end +--[[-- Text --]]-- +UI.TextArea = class(UI.Window) +UI.TextArea.defaults = { + UIElement = 'TextArea', + value = '', +} +function UI.TextArea:init(args) + local defaults = UI:getDefaults(UI.TextArea, args) + UI.Window.init(self, defaults) +end + +function UI.TextArea:setText(text) + self.value = text + self:draw() +end + +function UI.TextArea:draw() + local value = self.value or '' + self:clear() + self:setCursorPos(1, 1) + self:print(self.value) +end + --[[-- Form --]]-- UI.Form = class(UI.Window) UI.Form.defaults = { @@ -3347,8 +3442,6 @@ function UI.Dialog:init(args) defaults.width = UI.term.width-11 end defaults.titleBar = UI.TitleBar({ previousPage = true, title = defaults.title }) - - --UI.setProperties(defaults, args) UI.Page.init(self, defaults) end @@ -3358,6 +3451,11 @@ function UI.Dialog:setParent() self.y = math.floor((self.parent.height - self.height) / 2) + 1 end +function UI.Dialog:enable(...) + self:addTransition('grow') + UI.Page.enable(self, ...) +end + function UI.Dialog:eventHandler(event) if event.type == 'cancel' then UI:setPreviousPage() diff --git a/sys/apis/ui/fileui.lua b/sys/apis/ui/fileui.lua index f0f2647..acb8040 100644 --- a/sys/apis/ui/fileui.lua +++ b/sys/apis/ui/fileui.lua @@ -53,7 +53,7 @@ return function(args) function selectFile:enable(path, fn) self:setPath(path) self.fn = fn - UI.Page.enable(self) + UI.Dialog.enable(self) end function selectFile:setPath(path) @@ -133,7 +133,7 @@ return function(args) UI:setPreviousPage() self.fn() else - return UI.Page.eventHandler(self, event) + return UI.Dialog.eventHandler(self, event) end return true end diff --git a/sys/apis/util.lua b/sys/apis/util.lua index 99aa33b..1093a53 100644 --- a/sys/apis/util.lua +++ b/sys/apis/util.lua @@ -473,27 +473,25 @@ end -- word wrapping based on: -- https://www.rosettacode.org/wiki/Word_wrap#Lua and -- http://lua-users.org/wiki/StringRecipes -local function splittokens(s) - local res = {} - for w in s:gmatch("%S+") do - res[#res+1] = w - end - return res -end - local function paragraphwrap(text, linewidth, res) linewidth = linewidth or 75 local spaceleft = linewidth - local line = {} + local line = { } - for _, word in ipairs(splittokens(text)) do - if #word + 1 > spaceleft then + for word in text:gmatch("%S+") do + local len = #word + 1 + + --if colorMode then + -- word:gsub('()@([@%d])', function(pos, c) len = len - 2 end) + --end + + if len > spaceleft then table.insert(res, table.concat(line, ' ')) line = { word } - spaceleft = linewidth - #word + spaceleft = linewidth - len - 1 else table.insert(line, word) - spaceleft = spaceleft - (#word + 1) + spaceleft = spaceleft - len end end diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index bdab236..be79e88 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -143,7 +143,7 @@ function Browser.grid:eventHandler(event) if event.type == 'copy' then -- let copy be handled by parent return false end - return UI.Grid.eventHandler(self, event) + return UI.ScrollingGrid.eventHandler(self, event) end function Browser.statusBar:draw() diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 09df02f..1c789ad 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -8,6 +8,7 @@ local Util = require('util') local sandboxEnv = setmetatable(Util.shallowCopy(getfenv(1)), { __index = _G }) sandboxEnv.exit = function() Event.exitPullEvents() end +sandboxEnv._echo = function( ... ) return ... end requireInjector(sandboxEnv) multishell.setTitle(multishell.getCurrent(), 'Lua') @@ -16,15 +17,15 @@ UI:configure('Lua', ...) local command = '' local history = History.load('usr/.lua_history', 25) -local page = UI.Page({ - menuBar = UI.MenuBar({ +local page = UI.Page { + menuBar = UI.MenuBar { buttons = { { text = 'Local', event = 'local' }, { text = 'Global', event = 'global' }, { text = 'Device', event = 'device', name = 'Device' }, }, - }), - prompt = UI.TextEntry({ + }, + prompt = UI.TextEntry { y = 2, shadowText = 'enter command', limit = 256, @@ -35,8 +36,8 @@ local page = UI.Page({ mouse_rightclick = 'clear_prompt', -- [ 'control-space' ] = 'autocomplete', }, - }), - grid = UI.ScrollingGrid({ + }, + grid = UI.ScrollingGrid { y = 3, columns = { { heading = 'Key', key = 'name' }, @@ -44,9 +45,9 @@ local page = UI.Page({ }, sortColumn = 'name', autospace = true, - }), + }, notification = UI.Notification(), -}) +} function page:setPrompt(value, focus) self.prompt:setValue(value) @@ -138,23 +139,24 @@ function page:eventHandler(event) self:executeStatement('device') elseif event.type == 'history_back' then - local value = history.back() + local value = history:back() if value then self:setPrompt(value) end elseif event.type == 'history_forward' then - self:setPrompt(history.forward() or '') + self:setPrompt(history:forward() or '') elseif event.type == 'clear_prompt' then self:setPrompt('') - history.setPosition(#history.entries + 1) + history:reset() elseif event.type == 'command_enter' then local s = tostring(self.prompt.value) if #s > 0 then - history.add(s) + history:add(s) + history:back() self:executeStatement(s) else local t = { } @@ -166,7 +168,7 @@ function page:eventHandler(event) pos = k, }) end - history.setPosition(#history.entries + 1) + history:reset() command = nil self.grid:setValues(t) self.grid:setIndex(1) @@ -228,7 +230,7 @@ function page.grid:eventHandler(event) local function commandAppend() if entry.isHistory then - history.setPosition(entry.pos) + --history.setPosition(entry.pos) return entry.value end if type(entry.rawValue) == 'function' then @@ -262,15 +264,14 @@ function page.grid:eventHandler(event) clipboard.setData(entry.rawValue) end else - return UI.Grid.eventHandler(self, event) + return UI.ScrollingGrid.eventHandler(self, event) end return true end function page:rawExecute(s) - local fn, m = loadstring('return (' .. s .. ')', 'lua') + local fn, m = load('return _echo(' ..s.. ');', 'lua', nil, sandboxEnv) if fn then - setfenv(fn, sandboxEnv) m = { pcall(fn) } fn = table.remove(m, 1) if #m == 1 then @@ -279,9 +280,8 @@ function page:rawExecute(s) return fn, m end - fn, m = loadstring(s, 'lua') + fn, m = load(s, 'lua', nil, sandboxEnv) if fn then - setfenv(fn, sandboxEnv) fn, m = pcall(fn) end diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 72dc4d8..bfa7d05 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -270,11 +270,23 @@ function page.container:setCategory(categoryName) local col, row = gutter, 2 local count = #self.children + local r = math.random(1, 4) -- reposition all children for k,child in ipairs(self.children) do - child.x = self.width - child.y = math.floor(self.height) - child.tween = Tween.new(6, child, { x = col, y = row }, 'outSine') + if r == 1 then + child.x = math.random(1, self.width) + child.y = math.random(1, self.height) + elseif r == 2 then + child.x = self.width + child.y = self.height + elseif r == 3 then + child.x = math.floor(self.width / 2) + child.y = math.floor(self.height / 2) + elseif r == 4 then + child.x = self.width - col + child.y = row + end + child.tween = Tween.new(6, child, { x = col, y = row }, 'linear') if k < count then col = col + child.width @@ -286,25 +298,25 @@ function page.container:setCategory(categoryName) end self:initChildren() - self.animate = true + + local transition = { i = 1, parent = self, children = self.children } + function transition:update(device) + self.parent:clear() + for _,child in ipairs(self.children) do + child.tween:update(1) + child.x = math.floor(child.x) + child.y = math.floor(child.y) + child:draw() + end + self.canvas:blit(device, self, self) + self.i = self.i + 1 + return self.i < 7 + end + self:addTransition(transition) end function page.container:draw() - if self.animate then - self.animate = false - for i = 1, 6 do - for _,child in ipairs(self.children) do - child.tween:update(1) - child.x = math.floor(child.x) - child.y = math.floor(child.y) - end - UI.ViewportWindow.draw(self) - self:sync() - os.sleep() - end - else - UI.ViewportWindow.draw(self) - end + UI.ViewportWindow.draw(self) end function page:refresh() @@ -442,10 +454,7 @@ local editor = UI.Dialog { text = 'Icon', event = 'loadIcon', help = 'Select icon' }, image = UI.NftImage { - y = 6, - x = 2, - height = 3, - width = 8, + y = 6, x = 2, height = 3, width = 8, }, }, statusBar = UI.StatusBar(), @@ -462,7 +471,7 @@ function editor:enable(app) end self.form.image:setImage(icon) end - UI.Page.enable(self) + UI.Dialog.enable(self) self:focusFirst() end @@ -531,7 +540,7 @@ function editor:eventHandler(event) page:refresh() page:draw() else - return UI.Page.eventHandler(self, event) + return UI.Dialog.eventHandler(self, event) end return true end diff --git a/sys/apps/shell b/sys/apps/shell index 47dcabd..0eaaf54 100644 --- a/sys/apps/shell +++ b/sys/apps/shell @@ -489,17 +489,18 @@ local function autocomplete(line, suggestions) end end -local function shellRead(_tHistory ) +local function shellRead(history) term.setCursorBlink( true ) local sLine = "" - local nHistoryPos local nPos = 0 local lastPattern local w = term.getSize() local sx = term.getCursorPos() + history:reset() + local function redraw( sReplace ) local nScroll = 0 if sx + nPos >= w then @@ -563,32 +564,19 @@ local function shellRead(_tHistory ) redraw() end elseif param == keys.up or param == keys.down then - if _tHistory then - redraw(" ") - if param == keys.up then - if nHistoryPos == nil then - if #_tHistory > 0 then - nHistoryPos = #_tHistory - end - elseif nHistoryPos > 1 then - nHistoryPos = nHistoryPos - 1 - end - else - if nHistoryPos == #_tHistory then - nHistoryPos = nil - elseif nHistoryPos ~= nil then - nHistoryPos = nHistoryPos + 1 - end - end - if nHistoryPos then - sLine = _tHistory[nHistoryPos] - nPos = string.len( sLine ) - else - sLine = "" - nPos = 0 - end - redraw() + redraw(" ") + if param == keys.up then + sLine = history:back() + else + sLine = history:forward() end + if sLine then + nPos = string.len(sLine) + else + sLine = "" + nPos = 0 + end + redraw() elseif param == keys.backspace then if nPos > 0 then redraw(" ") @@ -637,13 +625,13 @@ while not bExit do write("$ " ) term.setTextColour(_colors.commandTextColor) term.setBackgroundColor(colors.black) - local sLine = shellRead(history.entries) + local sLine = shellRead(history) if bExit then -- terminated break end sLine = Util.trim(sLine) if #sLine > 0 and sLine ~= 'exit' then - history.add(sLine) + history:add(sLine) end term.setTextColour(_colors.textColor) if #sLine > 0 then diff --git a/sys/network/snmp.lua b/sys/network/snmp.lua index 5e6cc96..bf38d8b 100644 --- a/sys/network/snmp.lua +++ b/sys/network/snmp.lua @@ -26,7 +26,6 @@ local function snmpConnection(socket) socket:write('pong') elseif msg.type == 'script' then - local fn, msg = loadstring(msg.args, 'script') if fn then multishell.openTab({ @@ -38,6 +37,21 @@ local function snmpConnection(socket) printError(msg) end + elseif msg.type == 'scriptEx' then + local s, m = pcall(function() + local env = setmetatable(Util.shallowCopy(getfenv(1)), { __index = _G }) + local fn, m = load(msg.args, 'script', nil, env) + if not fn then + error(m) + end + return { fn() } + end) + if s then + socket:write(m) + else + socket:write({ s, m }) + end + elseif msg.type == 'gps' then if gpsRequested then repeat