diff --git a/sys/apps/PackageManager.lua b/sys/apps/PackageManager.lua index d0d9598..165989d 100644 --- a/sys/apps/PackageManager.lua +++ b/sys/apps/PackageManager.lua @@ -16,25 +16,27 @@ local page = UI.Page { x = 2, ex = 14, y = 2, ey = -6, values = { }, columns = { - { heading = 'Package', key = 'name' }, + { heading = ' Package', key = 'displayName' }, }, sortColumn = 'name', autospace = true, - help = 'Select a package', + help = 'Space to select, Enter to toggle', }, - add = UI.Button { + installSelected = UI.Button { x = 2, y = -3, text = ' + ', - event = 'action', - help = 'Install or update', + event = 'batch_action', + operation = 'install', + operationText = 'Install', + help = 'Install or update selected', }, - remove = UI.Button { + removeSelected = UI.Button { x = 8, y = -3, text = ' - ', - event = 'action', + event = 'batch_action', operation = 'uninstall', operationText = 'Remove', - help = 'Remove', + help = 'Remove selected', }, updateall = UI.Button { ex = -2, y = -3, width = 12, @@ -89,7 +91,9 @@ function page:loadPackages() end table.insert(self.grid.values, { installed = not not Packages:isInstalled(k), + selected = false, name = k, + displayName = k, manifest = manifest, }) end @@ -104,12 +108,62 @@ function page:loadPackages() 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) @@ -134,11 +188,7 @@ function page:run(operation, name) end function page:updateSelection(selected) - self.add.operation = selected.installed and 'update' or 'install' - self.add.operationText = selected.installed and 'Update' or 'Install' - self.remove.inactive = not selected.installed - self.add:draw() - self.remove:draw() + -- no-op: buttons are always active for batch operations end function page:eventHandler(event) @@ -152,7 +202,14 @@ function page:eventHandler(event) Ansi.yellow, manifest.title, Ansi.white, manifest.description)) self.description:draw() - self:updateSelection(event.selected) + 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 @@ -160,34 +217,69 @@ function page:eventHandler(event) 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 == 'action' then - local selected = self.grid:getSelected() - if selected then - self.operation = event.button.operation - self.action.button.text = event.button.operationText - self.action.titleBar.title = selected.manifest.title - self.action.button.text = ' Begin ' - self.action.button.event = 'begin' - 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(self.operation, '') + self:run('updateall', '') else - local selected = self.grid:getSelected() - self:run(self.operation, selected.name) - selected.installed = Packages:isInstalled(selected.name) - - self:updateSelection(selected) + 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 '