mirror of
https://github.com/kepler155c/opus
synced 2025-01-12 08:40:26 +00:00
ui improvements
This commit is contained in:
parent
c0baa00668
commit
8e381d3ebf
55
sys/apis/ansi.lua
Normal file
55
sys/apis/ansi.lua
Normal file
@ -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
|
@ -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
|
||||
|
308
sys/apis/ui.lua
308
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()
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
Reference in New Issue
Block a user