Ui enhancements 2.0 (#31)

* canvas overhaul

* minor tweaks

* list mode for overview

* bugfixes + tweaks for editor 2.0

* minor tweaks

* more editor work

* refactor + new transitions

* use layout() where appropriate and cleanup

* mouse triple click + textEntry scroll ind

* cleanup

* cleanup + theme editor

* color rework + cleanup

* changes for deprecated ui methods

* can now use named colors
pull/33/head
kepler155c 3 years ago committed by GitHub
parent cdd0b6c4d2
commit 7224d441ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

1
.gitignore vendored

@ -1 +1,2 @@
/ignore
.project

@ -82,16 +82,60 @@ local Browser = UI.Page {
},
sortColumn = 'name',
y = 2, ey = -2,
sortCompare = function(self, a, b)
if self.sortColumn == 'fsize' then
return a.size < b.size
elseif self.sortColumn == 'flags' then
return a.flags < b.flags
end
if a.isDir == b.isDir then
return a.name:lower() < b.name:lower()
end
return a.isDir
end,
getRowTextColor = function(_, file)
if file.marked then
return colors.green
end
if file.isDir then
return colors.cyan
end
if file.isReadOnly then
return colors.pink
end
return colors.white
end,
eventHandler = function(self, event)
if event.type == 'copy' then -- let copy be handled by parent
return false
end
return UI.ScrollingGrid.eventHandler(self, event)
end
},
statusBar = UI.StatusBar {
columns = {
{ key = 'status' },
{ key = 'totalSize', width = 6 },
},
draw = function(self)
if self.parent.dir then
local info = '#:' .. Util.size(self.parent.dir.files)
local numMarked = Util.size(marked)
if numMarked > 0 then
info = info .. ' M:' .. numMarked
end
self:setValue('info', info)
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
UI.StatusBar.draw(self)
end
end,
},
question = UI.Question {
y = -2, x = -19,
label = 'Delete',
},
notification = UI.Notification { },
associations = UI.SlideOut {
backgroundColor = colors.cyan,
menuBar = UI.MenuBar {
buttons = {
{ text = 'Save', event = 'save' },
@ -99,7 +143,7 @@ local Browser = UI.Page {
},
},
grid = UI.ScrollingGrid {
x = 2, ex = -6, y = 3, ey = -5,
x = 2, ex = -6, y = 3, ey = -8,
columns = {
{ heading = 'Extension', key = 'name' },
{ heading = 'Program', key = 'value' },
@ -114,8 +158,11 @@ local Browser = UI.Page {
x = -4, y = 6,
text = '-', event = 'remove_entry', help = 'Remove',
},
[1] = UI.Window {
x = 2, y = -6, ex = -6, ey = -3,
},
form = UI.Form {
x = 3, y = -3, ey = -2,
x = 3, y = -5, ex = -7, ey = -3,
margin = 1,
manualControls = true,
[1] = UI.TextEntry {
@ -137,9 +184,7 @@ local Browser = UI.Page {
text = 'Add', event = 'add_association',
},
},
statusBar = UI.StatusBar {
backgroundColor = colors.cyan,
},
statusBar = UI.StatusBar { },
},
accelerators = {
[ 'control-q' ] = 'quit',
@ -175,51 +220,6 @@ function Browser.menuBar:getActive(menuItem)
return true
end
function Browser.grid:sortCompare(a, b)
if self.sortColumn == 'fsize' then
return a.size < b.size
elseif self.sortColumn == 'flags' then
return a.flags < b.flags
end
if a.isDir == b.isDir then
return a.name:lower() < b.name:lower()
end
return a.isDir
end
function Browser.grid:getRowTextColor(file)
if file.marked then
return colors.green
end
if file.isDir then
return colors.cyan
end
if file.isReadOnly then
return colors.pink
end
return colors.white
end
function Browser.grid:eventHandler(event)
if event.type == 'copy' then -- let copy be handled by parent
return false
end
return UI.ScrollingGrid.eventHandler(self, event)
end
function Browser.statusBar:draw()
if self.parent.dir then
local info = '#:' .. Util.size(self.parent.dir.files)
local numMarked = Util.size(marked)
if numMarked > 0 then
info = info .. ' M:' .. numMarked
end
self:setValue('info', info)
self:setValue('totalSize', formatSize(self.parent.dir.totalSize))
UI.StatusBar.draw(self)
end
end
function Browser:setStatus(status, ...)
self.notification:info(string.format(status, ...))
end
@ -255,7 +255,6 @@ function Browser:getDirectory(directory)
end
function Browser:updateDirectory(dir)
dir.size = 0
dir.totalSize = 0
Util.clear(dir.files)
@ -344,7 +343,7 @@ function Browser:eventHandler(event)
local file = self.grid:getSelected()
if event.type == 'quit' then
Event.exitPullEvents()
UI:quit()
elseif event.type == 'edit' and file then
self:run('edit', file.name)
@ -432,28 +431,25 @@ function Browser:eventHandler(event)
elseif event.type == 'delete' then
if self:hasMarked() then
local width = self.statusBar:getColumnWidth('status')
self.statusBar:setColumnWidth('status', UI.term.width)
self.statusBar:setValue('status', 'Delete marked? (y/n)')
self.statusBar:draw()
self.statusBar:sync()
local _, ch = os.pullEvent('char')
if ch == 'y' or ch == 'Y' then
for _,m in pairs(marked) do
pcall(function()
fs.delete(m.fullName)
end)
end
end
marked = { }
self.statusBar:setColumnWidth('status', width)
self.statusBar:setValue('status', '/' .. self.dir.name)
self:updateDirectory(self.dir)
self.question:show()
end
return true
self.statusBar:draw()
self.grid:draw()
self:setFocus(self.grid)
elseif event.type == 'question_yes' then
for _,m in pairs(marked) do
pcall(fs.delete, m.fullName)
end
marked = { }
self:updateDirectory(self.dir)
self.question:hide()
self.statusBar:draw()
self.grid:draw()
self:setFocus(self.grid)
elseif event.type == 'question_no' then
self.question:hide()
self:setFocus(self.grid)
elseif event.type == 'copy' or event.type == 'cut' then
if self:hasMarked() then
@ -549,6 +545,4 @@ local args = Util.parse(...)
Browser:setDir(args[1] or shell.dir())
UI:setPage(Browser)
Event.pullEvents()
UI.term:reset()
UI:start()

@ -1,7 +1,6 @@
local UI = require('opus.ui')
local Util = require('opus.util')
local colors = _G.colors
local help = _G.help
UI:configure('Help', ...)
@ -12,11 +11,11 @@ for _,topic in pairs(help.topics()) do
end
UI:addPage('main', UI.Page {
labelText = UI.Text {
UI.Text {
x = 3, y = 2,
value = 'Search',
},
filter = UI.TextEntry {
UI.TextEntry {
x = 10, y = 2, ex = -3,
limit = 32,
},
@ -38,9 +37,7 @@ UI:addPage('main', UI.Page {
elseif event.type == 'grid_select' then
if self.grid:getSelected() then
local name = self.grid:getSelected().name
UI:setPage('topic', name)
UI:setPage('topic', self.grid:getSelected().name)
end
elseif event.type == 'text_change' then
@ -57,6 +54,7 @@ UI:addPage('main', UI.Page {
self.grid:update()
self.grid:setIndex(1)
self.grid:draw()
else
return UI.Page.eventHandler(self, event)
end
@ -64,13 +62,12 @@ UI:addPage('main', UI.Page {
})
UI:addPage('topic', UI.Page {
backgroundColor = colors.black,
backgroundColor = 'black',
titleBar = UI.TitleBar {
title = 'text',
event = 'back',
},
helpText = UI.TextArea {
backgroundColor = colors.black,
x = 2, ex = -1, y = 3, ey = -2,
},
accelerators = {

@ -58,11 +58,16 @@ local page = UI.Page {
},
[2] = UI.Tab {
tabTitle = 'Output',
backgroundColor = 'black',
output = UI.Embedded {
visible = true,
y = 2,
maxScroll = 1000,
backgroundColor = colors.black,
backgroundColor = 'black',
},
draw = function(self)
self:write(1, 1, string.rep('\131', self.width), 'black', 'primary')
self:drawChildren()
end,
},
},
}
@ -157,7 +162,7 @@ function page:eventHandler(event)
local sz = #value
local pos = self.prompt.entry.pos
self:setPrompt(autocomplete(sandboxEnv, value, self.prompt.entry.pos))
self.prompt:setPosition(pos + #value - sz)
self.prompt:setPosition(pos + #(self.prompt.value or '') - sz)
self.prompt:updateCursor()
elseif event.type == 'device' then
@ -196,7 +201,6 @@ function page:eventHandler(event)
command = nil
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
return true
@ -243,7 +247,6 @@ function page:setResult(result)
end
self.grid:setValues(t)
self.grid:setIndex(1)
self.grid:adjustWidth()
self:draw()
end
@ -373,7 +376,7 @@ function page:executeStatement(statement)
end
if _exit then
UI:exitPullEvents()
UI:quit()
end
end
@ -382,7 +385,8 @@ if args[1] then
command = 'args[1]'
sandboxEnv.args = args
page:setResult(args[1])
page:setPrompt(command)
end
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -4,7 +4,6 @@ local Socket = require('opus.socket')
local UI = require('opus.ui')
local Util = require('opus.util')
local colors = _G.colors
local device = _G.device
local network = _G.network
local os = _G.os
@ -56,6 +55,31 @@ local page = UI.Page {
columns = gridColumns,
sortColumn = 'label',
autospace = true,
getRowTextColor = function(self, row, selected)
if not row.active then
return 'lightGray'
end
return UI.Grid.getRowTextColor(self, row, selected)
end,
getDisplayValues = function(_, row)
row = Util.shallowCopy(row)
if row.uptime then
if row.uptime < 60 then
row.uptime = string.format("%ds", math.floor(row.uptime))
elseif row.uptime < 3600 then
row.uptime = string.format("%sm", math.floor(row.uptime / 60))
else
row.uptime = string.format("%sh", math.floor(row.uptime / 3600))
end
end
if row.fuel then
row.fuel = row.fuel > 0 and Util.toBytes(row.fuel) or ''
end
if row.distance then
row.distance = Util.toBytes(Util.round(row.distance, 1))
end
return row
end,
},
ports = UI.SlideOut {
titleBar = UI.TitleBar {
@ -72,17 +96,22 @@ local page = UI.Page {
sortColumn = 'port',
autospace = true,
},
eventHandler = function(self, event)
if event.type == 'grid_select' then
shell.openForegroundTab('Sniff ' .. event.selected.port)
end
return UI.SlideOut.eventHandler(self, event)
end,
},
help = UI.SlideOut {
backgroundColor = colors.cyan,
x = 5, ex = -5, height = 8, y = -8,
titleBar = UI.TitleBar {
title = 'Network Help',
event = 'slide_hide',
},
text = UI.TextArea {
x = 2, y = 2,
backgroundColor = colors.cyan,
x = 1, y = 2,
marginLeft = 1,
value = [[
In order to connect to another computer:
@ -127,13 +156,6 @@ local function sendCommand(host, command)
end
end
function page.ports:eventHandler(event)
if event.type == 'grid_select' then
shell.openForegroundTab('Sniff ' .. event.selected.port)
end
return UI.SlideOut.eventHandler(self, event)
end
function page.ports.grid:update()
local transport = network:getTransport()
@ -230,7 +252,7 @@ function page:eventHandler(event)
Config.update('network', config)
elseif event.type == 'quit' then
Event.exitPullEvents()
UI:quit()
end
UI.Page.eventHandler(self, event)
end
@ -243,33 +265,6 @@ function page.menuBar:getActive(menuItem)
return menuItem.noCheck or not not t
end
function page.grid:getRowTextColor(row, selected)
if not row.active then
return colors.lightGray
end
return UI.Grid.getRowTextColor(self, row, selected)
end
function page.grid:getDisplayValues(row)
row = Util.shallowCopy(row)
if row.uptime then
if row.uptime < 60 then
row.uptime = string.format("%ds", math.floor(row.uptime))
elseif row.uptime < 3600 then
row.uptime = string.format("%sm", math.floor(row.uptime / 60))
else
row.uptime = string.format("%sh", math.floor(row.uptime / 3600))
end
end
if row.fuel then
row.fuel = row.fuel > 0 and Util.toBytes(row.fuel) or ''
end
if row.distance then
row.distance = Util.toBytes(Util.round(row.distance, 1))
end
return row
end
Event.onInterval(1, function()
page.grid:update()
page.grid:draw()
@ -295,4 +290,4 @@ if not device.wireless_modem then
end
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -1,4 +1,5 @@
local Alt = require('opus.alternate')
local Array = require('opus.array')
local class = require('opus.class')
local Config = require('opus.config')
local Event = require('opus.event')
@ -9,7 +10,6 @@ local Tween = require('opus.ui.tween')
local UI = require('opus.ui')
local Util = require('opus.util')
local colors = _G.colors
local device = _G.device
local fs = _G.fs
local os = _G.os
@ -18,6 +18,12 @@ local shell = _ENV.shell
local term = _G.term
local turtle = _G.turtle
--[[
turtle: 39x13
computer: 51x19
pocket: 26x20
]]
if not _ENV.multishell then
error('multishell is required')
end
@ -26,6 +32,9 @@ local REGISTRY_DIR = 'usr/.registry'
local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\
\0307\0318\153\153\153\153\153\
\0308\0317\153\153\153\153\153")
local TRANS_ICON = NFT.parse("\0302\0312\32\32\32\32\32\
\0302\0312\32\32\32\32\32\
\0302\0312\32\32\32\32\32")
-- overview
local uid = _ENV.multishell.getCurrent()
@ -65,6 +74,7 @@ local function parseIcon(iconText)
if icon.height > 3 or icon.width > 8 then
error('Must be an NFT image - 3 rows, 8 cols max')
end
NFT.transparency(icon)
end
return icon
end)
@ -76,45 +86,38 @@ local function parseIcon(iconText)
return s, m
end
UI.VerticalTabBar = class(UI.TabBar)
function UI.VerticalTabBar:setParent()
self.x = 1
self.width = 8
self.height = nil
self.ey = -2
UI.TabBar.setParent(self)
for k,c in pairs(self.children) do
c.x = 1
c.y = k + 1
c.ox, c.oy = c.x, c.y
c.ow = 8
c.width = 8
end
end
local cx = 9
local cy = 1
local page = UI.Page {
container = UI.Viewport {
x = cx,
y = cy,
x = 9, y = 1,
},
tabBar = UI.TabBar {
ey = -2,
width = 8,
selectedBackgroundColor = 'primary',
backgroundColor = 'tertiary',
layout = function(self)
self.height = nil
UI.TabBar.layout(self)
end,
},
tray = UI.Window {
y = -1, width = 8,
backgroundColor = colors.lightGray,
newApp = UI.Button {
backgroundColor = 'tertiary',
newApp = UI.FlatButton {
x = 2,
text = '+', event = 'new',
},
--[[
volume = UI.Button {
x = 3,
text = '\15', event = 'volume',
},]]
mode = UI.FlatButton {
x = 4,
text = '=', event = 'display_mode',
},
help = UI.FlatButton {
x = 6,
text = '?', event = 'help',
},
},
editor = UI.SlideOut {
y = -12, height = 12,
backgroundColor = colors.cyan,
titleBar = UI.TitleBar {
title = 'Edit Application',
event = 'slide_hide',
@ -122,7 +125,7 @@ local page = UI.Page {
form = UI.Form {
y = 2, ey = -2,
[1] = UI.TextEntry {
formLabel = 'Title', formKey = 'title', limit = 11, help = 'Application title',
formLabel = 'Title', formKey = 'title', limit = 11, width = 13, help = 'Application title',
required = true,
},
[2] = UI.TextEntry {
@ -130,23 +133,50 @@ local page = UI.Page {
required = true,
},
[3] = UI.TextEntry {
formLabel = 'Category', formKey = 'category', limit = 11, help = 'Category of application',
formLabel = 'Category', formKey = 'category', limit = 6, width = 8, help = 'Category of application',
required = true,
},
iconFile = UI.TextEntry {
x = 11, ex = -12, y = 7,
limit = 128, help = 'Path to icon file',
shadowText = 'Path to icon file',
editIcon = UI.Button {
x = 11, y = 6,
text = 'Edit', event = 'editIcon', help = 'Edit icon file',
},
loadIcon = UI.Button {
x = 11, y = 9,
x = 11, y = 8,
text = 'Load', event = 'loadIcon', help = 'Load icon file',
},
helpIcon = UI.Button {
x = 11, y = 8,
text = 'Load', event = 'loadIcon', help = 'Load icon file',
},
image = UI.NftImage {
backgroundColor = colors.black,
y = 7, x = 2, height = 3, width = 8,
backgroundColor = 'black',
y = 6, x = 2, height = 3, width = 8,
},
},
file_open = UI.FileSelect {
modal = true,
enable = function() end,
transitionHint = 'expandUp',
show = function(self)
UI.FileSelect.enable(self)
self:focusFirst()
self:draw()
end,
disable = function(self)
UI.FileSelect.disable(self)
self.parent:focusFirst()
-- need to recapture as we are opening a modal within another modal
self.parent:capture(self.parent)
end,
eventHandler = function(self, event)
if event.type == 'select_cancel' then
self:disable()
elseif event.type == 'select_file' then
self:disable()
end
return UI.FileSelect.eventHandler(self, event)
end,
},
notification = UI.Notification(),
statusBar = UI.StatusBar(),
},
@ -205,7 +235,7 @@ local function loadApplications()
return requirements[a.requires]
end
return true -- Util.startsWith(a.run, 'http') or shell.resolveProgram(a.run)
return true
end)
local categories = { }
@ -215,6 +245,7 @@ local function loadApplications()
categories[f.category] = true
table.insert(buttons, {
text = f.category,
width = 8,
selected = config.currentCategory == f.category
})
end
@ -222,13 +253,13 @@ local function loadApplications()
table.sort(buttons, function(a, b) return a.text < b.text end)
table.insert(buttons, 1, { text = 'Recent' })
Util.removeByValue(page.children, page.tabBar)
for k,v in pairs(buttons) do
v.x = 1
v.y = k + 1
end
page:add {
tabBar = UI.VerticalTabBar {
buttons = buttons,
},
}
page.tabBar.children = { }
page.tabBar:addButtons(buttons)
--page.tabBar:selectTab(config.currentCategory or 'Apps')
page.container:setCategory(config.currentCategory or 'Apps')
@ -243,7 +274,6 @@ UI.Icon.defaults = {
function UI.Icon:eventHandler(event)
if event.type == 'mouse_click' then
self:setFocus(self.button)
--self:emit({ type = self.button.event, button = self.button })
return true
elseif event.type == 'mouse_doubleclick' then
self:emit({ type = self.button.event, button = self.button })
@ -259,37 +289,23 @@ function page.container:setCategory(categoryName, animate)
self.children = { }
self:reset()
local function filter(it, f)
local ot = { }
for _,v in pairs(it) do
if f(v) then
table.insert(ot, v)
end
end
return ot
end
local filtered
local filtered = { }
if categoryName == 'Recent' then
filtered = { }
for _,v in ipairs(config.Recent) do
local app = Util.find(applications, 'key', v)
if app then -- and fs.exists(app.run) then
if app then
table.insert(filtered, app)
end
end
else
filtered = filter(applications, function(a)
return a.category == categoryName -- and fs.exists(a.run)
filtered = Array.filter(applications, function(a)
return a.category == categoryName
end)
table.sort(filtered, function(a, b) return a.title < b.title end)
end
for _,program in ipairs(filtered) do
local icon
if extSupport and program.iconExt then
icon = parseIcon(program.iconExt)
@ -304,27 +320,43 @@ function page.container:setCategory(categoryName, animate)
local title = ellipsis(program.title, 8)
local width = math.max(icon.width + 2, #title + 2)
table.insert(self.children, UI.Icon({
width = width,
image = UI.NftImage({
x = math.floor((width - icon.width) / 2) + 1,
image = icon,
width = 5,
height = 3,
}),
button = UI.Button({
x = math.floor((width - #title - 2) / 2) + 1,
y = 4,
text = title,
backgroundColor = self.backgroundColor,
backgroundFocusColor = colors.gray,
textColor = colors.white,
textFocusColor = colors.white,
width = #title + 2,
event = 'button',
app = program,
}),
}))
if config.listMode then
table.insert(self.children, UI.Icon {
width = self.width - 2,
height = 1,
UI.Button {
x = 1, ex = -1,
text = program.title,
centered = false,
backgroundColor = self:getProperty('backgroundColor'),
backgroundFocusColor = 'gray',
textColor = 'white',
textFocusColor = 'white',
event = 'button',
app = program,
}
})
else
table.insert(self.children, UI.Icon({
width = width,
image = UI.NftImage({
x = math.floor((width - icon.width) / 2) + 1,
image = icon,
}),
button = UI.Button({
x = math.floor((width - #title - 2) / 2) + 1,
y = 4,
text = title,
backgroundColor = self:getProperty('backgroundColor'),
backgroundFocusColor = 'gray',
textColor = 'white',
textFocusColor = 'white',
width = #title + 2,
event = 'button',
app = program,
}),
}))
end
end
local gutter = 2
@ -334,7 +366,8 @@ function page.container:setCategory(categoryName, animate)
local col, row = gutter, 2
local count = #self.children
local r = math.random(1, 5)
local r = math.random(1, 7)
local frames = 5
-- reposition all children
for k,child in ipairs(self.children) do
if r == 1 then
@ -356,19 +389,27 @@ function page.container:setCategory(categoryName, animate)
child.x = self.width
child.y = self.height - 3
end
elseif r == 6 then
child.x = col
child.y = 1
elseif r == 7 then
child.x = 1
child.y = self.height - 3
end
child.tween = Tween.new(6, child, { x = col, y = row }, 'linear')
child.tween = Tween.new(frames, child, { x = col, y = row }, 'inQuad')
if not animate then
child.x = col
child.y = row
end
self:setViewHeight(row + (config.listMode and 1 or 4))
if k < count then
col = col + child.width
if col + self.children[k + 1].width + gutter - 2 > self.width then
col = gutter
row = row + 5
row = row + (config.listMode and 1 or 5)
end
end
end
@ -378,15 +419,12 @@ function page.container:setCategory(categoryName, animate)
local function transition()
local i = 1
return function()
self:clear()
for _,child in pairs(self.children) do
child.tween:update(1)
child.x = math.floor(child.x)
child.y = math.floor(child.y)
child:draw()
child:move(math.floor(child.x), math.floor(child.y))
end
i = i + 1
return i < 7
return i <= frames
end
end
self:addTransition(transition)
@ -439,6 +477,9 @@ function page:eventHandler(event)
elseif event.type == 'network' then
shell.switchTab(shell.openTab('network'))
elseif event.type == 'help' then
shell.switchTab(shell.openTab('Help Overview'))
elseif event.type == 'focus_change' then
if event.focused.parent.UIElement == 'Icon' then
event.focused.parent:scrollIntoView()
@ -473,6 +514,13 @@ function page:eventHandler(event)
end
self.editor:show({ category = category })
elseif event.type == 'display_mode' then
config.listMode = not config.listMode
Config.update('Overview', config)
loadApplications()
self:refresh()
self:draw()
elseif event.type == 'edit' then
local focused = page:getFocused()
if focused.app then
@ -480,7 +528,7 @@ function page:eventHandler(event)
end
else
UI.Page.eventHandler(self, event)
return UI.Page.eventHandler(self, event)
end
return true
end
@ -502,11 +550,6 @@ function page.editor:show(app)
self:focusFirst()
end
function page.editor.form.image:draw()
self:clear()
UI.NftImage.draw(self)
end
function page.editor:updateApplications(app)
if not app.key then
app.key = SHA.compute(app.title)
@ -516,37 +559,52 @@ function page.editor:updateApplications(app)
loadApplications()
end
function page.editor:loadImage(filename)
local s, m = pcall(function()
local iconLines = Util.readFile(filename)
if not iconLines then
error('Must be an NFT image - 3 rows, 8 cols max')
end
local icon, m = parseIcon(iconLines)
if not icon then
error(m)
end
if extSupport then
self.form.values.iconExt = iconLines
else
self.form.values.icon = iconLines
end
self.form.image:setImage(icon)
self.form.image:draw()
end)
if not s and m then
local msg = m:gsub('.*: (.*)', '%1')
self.notification:error(msg)
end
end
function page.editor:eventHandler(event)
if event.type == 'form_cancel' or event.type == 'cancel' then
self:hide()
elseif event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help or '')
self.statusBar:draw()
elseif event.type == 'loadIcon' then
local s, m = pcall(function()
local iconLines = Util.readFile(self.form.iconFile.value)
if not iconLines then
error('Must be an NFT image - 3 rows, 8 cols max')
end
local icon, m = parseIcon(iconLines)
if not icon then
error(m)
end
if extSupport then
self.form.values.iconExt = iconLines
else
self.form.values.icon = iconLines
end
self.form.image:setImage(icon)
self.form.image:draw()
end)
if not s and m then
local msg = m:gsub('.*: (.*)', '%1')
self.notification:error(msg)
elseif event.type == 'editIcon' then
local filename = '/tmp/editing.nft'
NFT.save(self.form.image.image or TRANS_ICON, filename)
local success = shell.run('pain.lua ' .. filename)
self.parent:dirty(true)
if success then
self:loadImage(filename)
end
elseif event.type == 'select_file' then
self:loadImage(event.file)
elseif event.type == 'loadIcon' then
self.file_open:show()
elseif event.type == 'form_invalid' then
self.notification:error(event.message)
@ -554,8 +612,6 @@ function page.editor:eventHandler(event)
local values = self.form.values
self:hide()
self:updateApplications(values)
--page:refresh()
--page:draw()
config.currentCategory = values.category
Config.update('Overview', config)
os.queueEvent('overview_refresh')
@ -565,10 +621,6 @@ function page.editor:eventHandler(event)
return true
end
UI:setPages({
main = page,
})
local function reload()
loadApplications()
page:refresh()
@ -594,5 +646,4 @@ end)
loadApplications()
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -44,7 +44,6 @@ local page = UI.Page {
marginRight = 0, marginLeft = 0,
},
action = UI.SlideOut {
backgroundColor = colors.cyan,
titleBar = UI.TitleBar {
event = 'hide-action',
},
@ -184,7 +183,7 @@ function page:eventHandler(event)
self.action.button:draw()
elseif event.type == 'quit' then
UI:exitPullEvents()
UI:quit()
end
UI.Page.eventHandler(self, event)
end
@ -196,4 +195,4 @@ Packages:downloadList()
page:loadPackages()
page:sync()
UI:pullEvents()
UI:start()

@ -5,14 +5,13 @@ local Util = require('opus.util')
local colors = _G.colors
local device = _G.device
local textutils = _G.textutils
local peripheral = _G.peripheral
local multishell = _ENV.multishell
local gridColumns = {}
table.insert(gridColumns, { heading = '#', key = 'id', width = 5, align = 'right' })
table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' })
table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' })
if UI.defaultDevice.width > 50 then
if UI.term.width > 50 then
table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' })
end
table.insert(gridColumns, { heading = 'Msg', key = 'packetStr' })
@ -42,12 +41,13 @@ local page = UI.Page {
configSlide = UI.SlideOut {
y = -11,
titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close' },
titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close', backgroundColor = colors.black },
accelerators = { ['backspace'] = 'config_close' },
configTabs = UI.Tabs {
y = 2,
filterTab = UI.Tab {
tabTitle = 'Filter',
noFill = true,
filterGridText = UI.Text {
x = 2, y = 2,
value = 'ID filter',
@ -130,7 +130,6 @@ local page = UI.Page {
title = 'Packet Information',
event = 'packet_close',
},
backgroundColor = colors.cyan,
accelerators = {
['backspace'] = 'packet_close',
['left'] = 'prev_packet',
@ -280,7 +279,7 @@ function page.packetSlide:eventHandler(event)
end
function page.packetGrid:getDisplayValues(row)
local row = Util.shallowCopy(row)
row = Util.shallowCopy(row)
row.distance = Util.toBytes(Util.round(row.distance), 2)
return row
end
@ -356,7 +355,7 @@ function page:eventHandler(event)
self.packetSlide:show(event.selected)
elseif event.type == 'quit' then
Event.exitPullEvents()
UI:quit()
else return UI.Page.eventHandler(self, event)
end
@ -386,4 +385,4 @@ if args[1] then
end
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -11,7 +11,7 @@ local systemPage = UI.Page {
settings = UI.Tab {
tabTitle = 'Category',
grid = UI.ScrollingGrid {
y = 2,
x = 2, y = 2, ex = -2, ey = -2,
columns = {
{ heading = 'Name', key = 'name' },
{ heading = 'Description', key = 'description' },
@ -35,14 +35,14 @@ function systemPage.tabs.settings:eventHandler(event)
tab:disable()
end
systemPage.tabs:selectTab(tab)
self.parent:draw()
--self.parent:draw()
return true
end
end
function systemPage:eventHandler(event)
if event.type == 'quit' then
UI:exitPullEvents()
UI:quit()
elseif event.type == 'success_message' then
self.notification:success(event.message)
@ -82,4 +82,4 @@ local plugins = loadDirectory(fs.combine(programDir, 'system'), { })
systemPage.tabs.settings.grid:setValues(plugins)
UI:setPage(systemPage)
UI:pullEvents()
UI:start()

@ -3,6 +3,7 @@ local UI = require('opus.ui')
local kernel = _G.kernel
local multishell = _ENV.multishell
local tasks = multishell and multishell.getTabs and multishell.getTabs() or kernel.routines
UI:configure('Tasks', ...)
@ -21,7 +22,7 @@ local page = UI.Page {
{ heading = 'Status', key = 'status' },
{ heading = 'Time', key = 'timestamp' },
},
values = kernel.routines,
values = tasks,
sortColumn = 'uid',
autospace = true,
getDisplayValues = function (_, row)
@ -51,7 +52,7 @@ local page = UI.Page {
end
end
if event.type == 'quit' then
Event.exitPullEvents()
UI:quit()
end
UI.Page.eventHandler(self, event)
end
@ -64,4 +65,4 @@ Event.onInterval(1, function()
end)
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -131,7 +131,7 @@ function page:eventHandler(event)
shell.openForegroundTab('PackageManager')
elseif event.type == 'wizard_complete' or event.type == 'cancel' then
UI.exitPullEvents()
UI:quit()
else
return UI.Page.eventHandler(self, event)
@ -140,4 +140,4 @@ function page:eventHandler(event)
end
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -0,0 +1,39 @@
local UI = require('opus.ui')
local Util = require('opus.util')
local shell = _ENV.shell
local multishell = _ENV.multishell
-- fileui [--path=path] [--exec=filename] [--title=title]
local page = UI.Page {
fileselect = UI.FileSelect { },
eventHandler = function(self, event)
if event.type == 'select_file' then
self.selected = event.file
UI:quit()
elseif event.type == 'select_cancel' then
UI:quit()
end
return UI.FileSelect.eventHandler(self, event)
end,
}
local _, args = Util.parse(...)
if args.title and multishell then
multishell.setTitle(multishell.getCurrent(), args.title)
end
UI:setPage(page, args.path)
UI:start()
UI.term:setCursorBlink(false)
if args.exec and page.selected then
shell.openForegroundTab(string.format('%s %s', args.exec, page.selected))
return
end
return page.selected

@ -133,6 +133,12 @@ page = UI.Page {
},
},
},
accelerators = {
['shift-right'] = 'size',
['shift-left' ] = 'size',
['shift-up' ] = 'size',
['shift-down' ] = 'size',
},
eventHandler = function (self, event)
if event.type == 'focus_change' and isRelevant(event.focused) then
focused = event.focused
@ -144,7 +150,6 @@ page = UI.Page {
})
end
self.tabs.properties.grid:setValues(t)
self.tabs.properties.grid:update()
self.tabs.properties.grid:draw()
t = { }
@ -156,7 +161,6 @@ page = UI.Page {
end
end
self.tabs.methodsTab.grid:setValues(t)
self.tabs.methodsTab.grid:update()
self.tabs.methodsTab.grid:draw()
elseif event.type == 'edit_property' then
@ -168,6 +172,19 @@ page = UI.Page {
elseif event.type == 'editor_apply' then
self.editor:hide()
elseif event.type == 'size' then
local sizing = {
['shift-right'] = { 1, 0 },
['shift-left' ] = { -1, 0 },
['shift-up' ] = { 0, -1 },
['shift-down' ] = { 0, 1 },
}
self.ox = math.max(self.ox + sizing[event.ie.code][1], 1)
self.oy = math.max(self.oy + sizing[event.ie.code][2], 1)
UI.term:clear()
self:resize()
self:draw()
end
return UI.Page.eventHandler(self, event)
@ -175,4 +192,4 @@ page = UI.Page {
}
UI:setPage(page)
UI:pullEvents()
UI:start()

@ -33,7 +33,7 @@ Event.on('generate_keypair', function()
table.insert(keyPairs, { generateKeyPair() })
_G._syslog('Generated keypair in ' .. timer())
if #keyPairs >= 3 then
break
break
end
end
end)

@ -66,11 +66,11 @@ local function run(env, ...)
_ENV.multishell.setTitle(_ENV.multishell.getCurrent(), fs.getName(path):match('([^%.]+)'))
end
if isUrl then
tProgramStack[#tProgramStack + 1] = path -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$")
else
tProgramStack[#tProgramStack + 1] = path
end
tProgramStack[#tProgramStack + 1] = {
path = path, -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$")
env = env,
args = args,
}
env[ "arg" ] = { [0] = path, table.unpack(args) }
local r = { fn(table.unpack(args)) }
@ -278,6 +278,10 @@ function shell.getCompletionInfo()
end