Files
Opus/sys/apps/PackageManager.lua

303 lines
7.0 KiB
Lua

local Ansi = require('opus.ansi')
local Config = require('opus.config')
local Packages = require('opus.packages')
local UI = require('opus.ui')
local Util = require('opus.util')
local colors = _G.colors
local term = _G.term
UI:configure('PackageManager', ...)
local config = Config.load('package')
local page = UI.Page {
grid = UI.ScrollingGrid {
x = 2, ex = 14, y = 2, ey = -6,
values = { },
columns = {
{ heading = ' Package', key = 'displayName' },
},
sortColumn = 'name',
autospace = true,
help = 'Space to select, Enter to toggle',
},
installSelected = UI.Button {
x = 2, y = -3,
text = ' + ',
event = 'batch_action',
operation = 'install',
operationText = 'Install',
help = 'Install or update selected',
},
removeSelected = UI.Button {
x = 8, y = -3,
text = ' - ',
event = 'batch_action',
operation = 'uninstall',
operationText = 'Remove',
help = 'Remove selected',
},
updateall = UI.Button {
ex = -2, y = -3, width = 12,
text = 'Update All',
event = 'updateall',
help = 'Update all installed packages',
},
description = UI.TextArea {
x = 16, y = 3, ey = -5,
marginRight = 2, marginLeft = 0,
},
UI.Checkbox {
x = 3, y = -5,
label = 'Compress',
textColor = 'yellow',
backgroundColor = 'primary',
value = config.compression,
help = 'Compress packages (experimental)',
},
action = UI.SlideOut {
titleBar = UI.TitleBar {
event = 'hide-action',
},
button = UI.Button {
x = -10, y = 3,
text = ' Begin ', event = 'begin',
},
output = UI.Embedded {
y = 5, ey = -2, x = 2, ex = -2,
visible = true,
},
},
statusBar = UI.StatusBar { },
accelerators = {
[ 'control-q' ] = 'quit',
},
}
function page:loadPackages()
self.grid.values = { }
self.statusBar:setStatus('Downloading...')
self:sync()
for k in pairs(Packages:list()) do
local manifest = Packages:getManifest(k)
if not manifest then
manifest = {
invalid = true,
description = 'Unable to download manifest',
title = '',
}
end
table.insert(self.grid.values, {
installed = not not Packages:isInstalled(k),
selected = false,
name = k,
displayName = k,
manifest = manifest,
})
end
self.grid:update()
self.grid:setIndex(1)
self.grid:emit({
type = 'grid_focus_row',
selected = self.grid:getSelected(),
element = self.grid,
})
self.statusBar:setStatus('Updated packages')
end
function page.grid:getRowTextColor(row, selected)
if row.selected then
return colors.cyan
end
if row.installed then
return colors.yellow
end
return UI.Grid.getRowTextColor(self, row, selected)
end
function page:getSelectedPackages()
local selected = { }
for _, row in pairs(self.grid.values) do
if row.selected then
table.insert(selected, row)
end
end
return selected
end
function page:getSelectedCount()
local count = 0
for _, row in pairs(self.grid.values) do
if row.selected then
count = count + 1
end
end
return count
end
function page:toggleSelection(row)
row.selected = not row.selected
row.displayName = row.selected and ('\187 ' .. row.name) or row.name
self.grid:draw()
self:updateStatus()
end
function page:clearSelection()
for _, row in pairs(self.grid.values) do
row.selected = false
row.displayName = row.name
end
self.grid:draw()
end
function page:updateStatus()
local count = self:getSelectedCount()
if count > 0 then
self.statusBar:setStatus(count .. ' package(s) selected')
else
local focused = self.grid:getSelected()
if focused then
self.statusBar:setStatus(focused.installed and 'Installed' or '')
end
end
end
function page.action:show()
self.output.win:clear()
UI.SlideOut.show(self)
end
function page:run(operation, name)
local oterm = term.redirect(self.action.output.win)
self.action.output:clear()
local cmd = string.format('package %s %s', operation, name)
term.setCursorPos(1, 1)
term.clear()
term.setTextColor(colors.yellow)
print(cmd .. '\n')
term.setTextColor(colors.white)
local s, m = Util.run(_ENV, '/sys/apps/package.lua', operation, name)
if not s and m then
_G.printError(m)
end
term.redirect(oterm)
self.action.output:draw()
end
function page:updateSelection(selected)
-- no-op: buttons are always active for batch operations
end
function page:eventHandler(event)
if event.type == 'focus_change' then
self.statusBar:setStatus(event.focused.help)
elseif event.type == 'grid_focus_row' then
local manifest = event.selected.manifest
self.description:setValue(string.format('%s%s\n\n%s%s',
Ansi.yellow, manifest.title,
Ansi.white, manifest.description))
self.description:draw()
self:updateStatus()
elseif event.type == 'grid_select' then
-- Space or Enter toggles selection
local row = self.grid:getSelected()
if row then
self:toggleSelection(row)
end
elseif event.type == 'checkbox_change' then
config.compression = not config.compression
Config.update('package', config)
elseif event.type == 'updateall' then
self.operation = 'updateall'
self.operationTargets = { }
self.action.button.text = ' Begin '
self.action.button.event = 'begin'
self.action.titleBar.title = 'Update All'
self.action:show()
elseif event.type == 'batch_action' then
local targets = self:getSelectedPackages()
local operation = event.button.operation
-- fall back to focused row if nothing selected
if #targets == 0 then
local focused = self.grid:getSelected()
if focused then
targets = { focused }
end
end
if #targets == 0 then return end
-- for install: update already-installed packages, install new ones
if operation == 'install' then
self.operation = 'install'
else
-- filter to only installed packages for uninstall
local installed = { }
for _, t in ipairs(targets) do
if t.installed then
table.insert(installed, t)
end
end
targets = installed
if #targets == 0 then return end
self.operation = 'uninstall'
end
self.operationTargets = targets
local title = #targets == 1
and targets[1].manifest.title
or (#targets .. ' packages')
self.action.button.text = ' Begin '
self.action.button.event = 'begin'
self.action.titleBar.title = string.format('%s %s',
operation == 'install' and 'Install/Update' or 'Remove', title)
self.action:show()
elseif event.type == 'hide-action' then
self.action:hide()
elseif event.type == 'begin' then
if self.operation == 'updateall' then
self:run('updateall', '')
else
for _, target in ipairs(self.operationTargets) do
local op = self.operation
if op == 'install' and target.installed then
op = 'update'
end
self:run(op, target.name)
target.installed = Packages:isInstalled(target.name)
end
self:clearSelection()
self:updateStatus()
end
self.action.button.text = ' Done '
self.action.button.event = 'hide-action'
self.action.button:draw()
elseif event.type == 'quit' then
UI:quit()
end
UI.Page.eventHandler(self, event)
end
UI:setPage(page)
page.statusBar:setStatus('Downloading...')
page:sync()
Packages:downloadList()
page:loadPackages()
page:sync()
UI:start()