embedded windows

This commit is contained in:
kepler155c@gmail.com 2018-01-21 22:08:30 -05:00
parent 8451c94abe
commit 1eea0d7cd8
4 changed files with 357 additions and 247 deletions

View File

@ -3,6 +3,7 @@ local class = require('class')
local Event = require('event')
local Input = require('input')
local Peripheral = require('peripheral')
local Terminal = require('terminal')
local Transition = require('ui.transition')
local Util = require('util')
@ -13,6 +14,7 @@ local device = _G.device
local fs = _G.fs
local os = _G.os
local term = _G.term
local window = _G.window
--[[
Using the shorthand window definition, elements are created from
@ -2405,6 +2407,63 @@ function UI.SlideOut:eventHandler(event)
end
end
--[[-- Embedded --]]--
UI.Embedded = class(UI.Window)
UI.Embedded.defaults = {
UIElement = 'Embedded',
backgroundColor = colors.black,
textColor = colors.white,
accelerators = {
up = 'scroll_up',
down = 'scroll_down',
}
}
function UI.Embedded:setParent()
UI.Window.setParent(self)
self.win = window.create(UI.term.device, 1, 1, self.width, self.height, false)
Canvas.convertWindow(self.win, UI.term.device, self.x, self.y)
Terminal.scrollable(self.win, 100)
local canvas = self:getCanvas()
self.win.canvas.parent = canvas
table.insert(canvas.layers, self.win.canvas)
self.canvas = self.win.canvas
self.win.setCursorPos(1, 1)
self.win.setBackgroundColor(self.backgroundColor)
self.win.setTextColor(self.textColor)
self.win.clear()
end
function UI.Embedded:draw()
self.canvas:dirty()
end
function UI.Embedded:enable()
self.canvas:setVisible(true)
UI.Window.enable(self)
end
function UI.Embedded:disable()
self.canvas:setVisible(false)
UI.Window.disable(self)
end
function UI.Embedded:eventHandler(event)
if event.type == 'scroll_up' then
self.win.scrollUp()
return true
elseif event.type == 'scroll_down' then
self.win.scrollDown()
return true
end
end
function UI.Embedded:focus()
-- allow scrolling
end
--[[-- Notification --]]--
UI.Notification = class(UI.Window)
UI.Notification.defaults = {

View File

@ -129,7 +129,6 @@ end
function Canvas:writeBlit(x, y, text, bg, fg)
if y > 0 and y <= self.height and x <= self.width then
local width = #text
-- fix ffs
@ -342,6 +341,7 @@ function Canvas.convertWindow(win, parent, wx, wy)
str,
win.getBackgroundColor(),
win.getTextColor())
win.setCursorPos(x + #str, y)
end
function win.blit(text, fg, bg)
@ -353,8 +353,10 @@ function Canvas.convertWindow(win, parent, wx, wy)
win.canvas:redraw(parent)
end
function win.scroll()
error('scroll: not implemented')
function win.scroll(n)
table.insert(win.canvas.lines, table.remove(win.canvas.lines, 1))
win.canvas.lines[#win.canvas.lines].text = _rep(' ', win.canvas.width)
win.canvas:dirty()
end
function win.reposition(x, y, width, height)

View File

@ -55,11 +55,11 @@ function Util.tostring(pattern, ...)
end
str = str .. string.format(' %s: %s\n', k, value)
end
if #str < width then
str = str:gsub('\n', '') .. ' }'
else
--if #str < width then
--str = str:gsub('\n', '') .. ' }'
--else
str = str .. '}'
end
--end
return str
end

View File

@ -8,6 +8,7 @@ local Util = require('util')
local colors = _G.colors
local os = _G.os
local textutils = _G.textutils
local term = _G.term
local sandboxEnv = setmetatable(Util.shallowCopy(_ENV), { __index = _G })
sandboxEnv.exit = function() Event.exitPullEvents() end
@ -21,304 +22,352 @@ local history = History.load('usr/.lua_history', 25)
local extChars = Util.getVersion() > 1.76
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 {
y = 2,
shadowText = 'enter command',
limit = 256,
accelerators = {
enter = 'command_enter',
up = 'history_back',
down = 'history_forward',
mouse_rightclick = 'clear_prompt',
[ 'control-space' ] = 'autocomplete',
},
},
indicator = UI.Text {
backgroundColor = colors.black,
y = 2, x = -1, width = 1,
},
grid = UI.ScrollingGrid {
y = 3,
columns = {
{ heading = 'Key', key = 'name' },
{ heading = 'Value', key = 'value' },
},
sortColumn = 'name',
autospace = true,
},
notification = UI.Notification(),
menuBar = UI.MenuBar {
buttons = {
{ text = 'Local', event = 'local' },
{ text = 'Global', event = 'global' },
{ text = 'Device', event = 'device', name = 'Device' },
},
},
prompt = UI.TextEntry {
y = 2,
shadowText = 'enter command',
limit = 256,
accelerators = {
enter = 'command_enter',
up = 'history_back',
down = 'history_forward',
mouse_rightclick = 'clear_prompt',
[ 'control-space' ] = 'autocomplete',
},
},
indicator = UI.Text {
backgroundColor = colors.black,
y = 2, x = -1, width = 1,
},
grid = UI.ScrollingGrid {
y = 3, ey = -2,
columns = {
{ heading = 'Key', key = 'name' },
{ heading = 'Value', key = 'value' },
},
sortColumn = 'name',
autospace = true,
},
titleBar = UI.TitleBar {
title = 'Output',
y = -1,
event = 'show_output',
closeInd = '^'
},
output = UI.Embedded {
y = -6,
},
--notification = UI.Notification(),
}
function page.indicator:showResult(s)
local values = {
[ true ] = { c = colors.green, i = (extChars and '\003') or '+' },
[ false ] = { c = colors.red, i = 'x' }
}
local values = {
[ true ] = { c = colors.green, i = (extChars and '\003') or '+' },
[ false ] = { c = colors.red, i = 'x' }
}
self.textColor = values[s].c
self.value = values[s].i
self:draw()
self.textColor = values[s].c
self.value = values[s].i
self:draw()
end
function page:setPrompt(value, focus)
self.prompt:setValue(value)
self.prompt.scroll = 0
self.prompt:setPosition(#value)
self.prompt:updateScroll()
self.prompt:setValue(value)
self.prompt.scroll = 0
self.prompt:setPosition(#value)
self.prompt:updateScroll()
if value:sub(-1) == ')' then
self.prompt:setPosition(#value - 1)
end
if value:sub(-1) == ')' then
self.prompt:setPosition(#value - 1)
end
self.prompt:draw()
if focus then
page:setFocus(self.prompt)
end
self.prompt:draw()
if focus then
page:setFocus(self.prompt)
end
end
function page:enable()
self:setFocus(self.prompt)
UI.Page.enable(self)
self:setFocus(self.prompt)
UI.Page.enable(self)
self.output:disable()
end
local function autocomplete(env, oLine, x)
local sLine = oLine:sub(1, x)
local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$")
if nStartPos then
sLine = sLine:sub(nStartPos)
end
local sLine = oLine:sub(1, x)
local nStartPos = sLine:find("[a-zA-Z0-9_%.]+$")
if nStartPos then
sLine = sLine:sub(nStartPos)
end
if #sLine > 0 then
local results = textutils.complete(sLine, env)
if #sLine > 0 then
local results = textutils.complete(sLine, env)
if #results == 1 then
return Util.insertString(oLine, results[1], x + 1)
if #results == 1 then
return Util.insertString(oLine, results[1], x + 1)
elseif #results > 1 then
local prefix = results[1]
for n = 1, #results do
local result = 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
return Util.insertString(oLine, prefix, x + 1)
end
end
end
return oLine
elseif #results > 1 then
local prefix = results[1]
for n = 1, #results do
local result = 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
return Util.insertString(oLine, prefix, x + 1)
end
end
end
return oLine
end
function page:eventHandler(event)
if event.type == 'global' then
self:setPrompt('', true)
self:executeStatement('_G')
command = nil
if event.type == 'global' then
self:setPrompt('', true)
self:executeStatement('_G')
command = nil
elseif event.type == 'local' then
self:setPrompt('', true)
self:executeStatement('_ENV')
command = nil
elseif event.type == 'local' then
self:setPrompt('', true)
self:executeStatement('_ENV')
command = nil
elseif event.type == 'autocomplete' then
local sz = #self.prompt.value
local pos = self.prompt.pos
self:setPrompt(autocomplete(sandboxEnv, self.prompt.value, self.prompt.pos))
self.prompt:setPosition(pos + #self.prompt.value - sz)
self.prompt:updateCursor()
elseif event.type == 'hide_output' then
self.output:disable()
elseif event.type == 'device' then
self:setPrompt('device', true)
self:executeStatement('device')
self.titleBar.oy = -1
self.titleBar.event = 'show_output'
self.titleBar.closeInd = '^'
self.titleBar:resize()
elseif event.type == 'history_back' then
local value = history:back()
if value then
self:setPrompt(value)
end
self.grid.ey = -2
self.grid:resize()
elseif event.type == 'history_forward' then
self:setPrompt(history:forward() or '')
self:draw()
elseif event.type == 'clear_prompt' then
self:setPrompt('')
history:reset()
elseif event.type == 'show_output' then
self.output:enable()
elseif event.type == 'command_enter' then
local s = tostring(self.prompt.value)
self.titleBar.oy = -7
self.titleBar.event = 'hide_output'
self.titleBar.closeInd = 'v'
self.titleBar:resize()
if #s > 0 then
history:add(s)
history:back()
self:executeStatement(s)
else
local t = { }
for k = #history.entries, 1, -1 do
table.insert(t, {
name = #t + 1,
value = history.entries[k],
isHistory = true,
pos = k,
})
end
history:reset()
command = nil
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
return true
self.grid.ey = -8
self.grid:resize()
else
return UI.Page.eventHandler(self, event)
end
return true
self:draw()
elseif event.type == 'autocomplete' then
local sz = #self.prompt.value
local pos = self.prompt.pos
self:setPrompt(autocomplete(sandboxEnv, self.prompt.value, self.prompt.pos))
self.prompt:setPosition(pos + #self.prompt.value - sz)
self.prompt:updateCursor()
elseif event.type == 'device' then
self:setPrompt('device', true)
self:executeStatement('device')
elseif event.type == 'history_back' then
local value = history:back()
if value then
self:setPrompt(value)
end
elseif event.type == 'history_forward' then
self:setPrompt(history:forward() or '')
elseif event.type == 'clear_prompt' then
self:setPrompt('')
history:reset()
elseif event.type == 'command_enter' then
local s = tostring(self.prompt.value)
if #s > 0 then
history:add(s)
history:back()
self:executeStatement(s)
else
local t = { }
for k = #history.entries, 1, -1 do
table.insert(t, {
name = #t + 1,
value = history.entries[k],
isHistory = true,
pos = k,
})
end
history:reset()
command = nil
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
return true
else
return UI.Page.eventHandler(self, event)
end
return true
end
function page:setResult(result)
local t = { }
local t = { }
local function safeValue(v)
if type(v) == 'string' or type(v) == 'number' then
return v
end
return tostring(v)
end
local oterm = term.redirect(self.output.win)
Util.print(result)
term.redirect(oterm)
if type(result) == 'table' then
for k,v in pairs(result) do
local entry = {
name = safeValue(k),
rawName = k,
value = safeValue(v),
rawValue = v,
}
if type(v) == 'table' then
if Util.size(v) == 0 then
entry.value = 'table: (empty)'
else
entry.value = tostring(v)
end
end
table.insert(t, entry)
end
else
table.insert(t, {
name = type(result),
value = tostring(result),
rawValue = result,
})
end
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
local function safeValue(v)
if type(v) == 'string' or type(v) == 'number' then
return v
end
return tostring(v)
end
if type(result) == 'table' then
for k,v in pairs(result) do
local entry = {
name = safeValue(k),
rawName = k,
value = safeValue(v),
rawValue = v,
}
if type(v) == 'table' then
if Util.size(v) == 0 then
entry.value = 'table: (empty)'
else
entry.value = tostring(v)
end
end
table.insert(t, entry)
end
else
table.insert(t, {
name = type(result),
value = tostring(result),
rawValue = result,
})
end
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
function page.grid:eventHandler(event)
local entry = self:getSelected()
local entry = self:getSelected()
local function commandAppend()
if entry.isHistory then
--history.setPosition(entry.pos)
return entry.value
end
if type(entry.rawValue) == 'function' then
if command then
return command .. '.' .. entry.name .. '()'
end
return entry.name .. '()'
end
if command then
if type(entry.rawName) == 'number' then
return command .. '[' .. entry.name .. ']'
end
if entry.name:match("%W") or
entry.name:sub(1, 1):match("%d") then
return command .. "['" .. tostring(entry.name) .. "']"
end
return command .. '.' .. entry.name
end
return entry.name
end
local function commandAppend()
if entry.isHistory then
--history.setPosition(entry.pos)
return entry.value
end
if type(entry.rawValue) == 'function' then
if command then
return command .. '.' .. entry.name .. '()'
end
return entry.name .. '()'
end
if command then
if type(entry.rawName) == 'number' then
return command .. '[' .. entry.name .. ']'
end
if entry.name:match("%W") or
entry.name:sub(1, 1):match("%d") then
return command .. "['" .. tostring(entry.name) .. "']"
end
return command .. '.' .. entry.name
end
return entry.name
end
if event.type == 'grid_focus_row' then
if self.focused then
page:setPrompt(commandAppend())
end
elseif event.type == 'grid_select' then
page:setPrompt(commandAppend(), true)
page:executeStatement(commandAppend())
if event.type == 'grid_focus_row' then
if self.focused then
page:setPrompt(commandAppend())
end
elseif event.type == 'grid_select' then
page:setPrompt(commandAppend(), true)
page:executeStatement(commandAppend())
elseif event.type == 'copy' then
if entry then
os.queueEvent('clipboard_copy', entry.rawValue)
end
else
return UI.ScrollingGrid.eventHandler(self, event)
end
return true
elseif event.type == 'copy' then
if entry then
os.queueEvent('clipboard_copy', entry.rawValue)
end
else
return UI.ScrollingGrid.eventHandler(self, event)
end
return true
end
function page:rawExecute(s)
local fn, m
local fn, m
fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv)
fn = load('return (' ..s.. ')', 'lua', nil, sandboxEnv)
if fn then
fn = load('return {' ..s.. '}', 'lua', nil, sandboxEnv)
end
if fn then
fn = load('return {' ..s.. '}', 'lua', nil, sandboxEnv)
end
if fn then
fn, m = pcall(fn)
if #m == 1 then
m = m[1]
end
return fn, m
end
if fn then
fn, m = pcall(fn)
if #m == 1 then
m = m[1]
end
return fn, m
end
fn, m = load(s, 'lua', nil, sandboxEnv)
if fn then
fn, m = pcall(fn)
end
fn, m = load(s, 'lua', nil, sandboxEnv)
if fn then
fn, m = pcall(fn)
end
return fn, m
return fn, m
end
function page:executeStatement(statement)
command = statement
command = statement
local s, m = self:rawExecute(command)
local oterm = term.redirect(self.output.win)
local s, m = self:rawExecute(command)
if not s then
_G.printError(m)
end
term.redirect(oterm)
if s and m then
self:setResult(m)
else
self.grid:setValues({ })
self.grid:draw()
if m then
self.notification:error(m, 5)
end
end
self.indicator:showResult(not not s)
if s and m then
self:setResult(m)
else
self.grid:setValues({ })
self.grid:draw()
if m then
if not self.output.enabled then
self:emit({ type = 'show_output' })
end
--self.notification:error(m, 5)
end
end
self.indicator:showResult(not not s)
end
local args = { ... }
if args[1] then
command = 'args[1]'
sandboxEnv.args = args
page:setResult(args[1])
command = 'args[1]'
sandboxEnv.args = args
page:setResult(args[1])
end
UI:setPage(page)