diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index d4d7d50..352606e 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -45,8 +45,9 @@ local page = UI.Page { }, tabs = UI.Tabs { y = 3, - [1] = UI.Tab { - tabTitle = 'Formatted', + formatted = UI.Tab { + title = 'Formatted', + index = 1, grid = UI.ScrollingGrid { columns = { { heading = 'Key', key = 'name' }, @@ -56,8 +57,9 @@ local page = UI.Page { autospace = true, }, }, - [2] = UI.Tab { - tabTitle = 'Output', + output = UI.Tab { + title = 'Output', + index = 2, backgroundColor = 'black', output = UI.Embedded { y = 2, @@ -72,8 +74,8 @@ local page = UI.Page { }, } -page.grid = page.tabs[1].grid -page.output = page.tabs[2].output +page.grid = page.tabs.formatted.grid +page.output = page.tabs.output.output function page:setPrompt(value, focus) self.prompt:setValue(value) @@ -142,7 +144,7 @@ function page:eventHandler(event) self:setFocus(self.prompt) elseif event.type == 'show_output' then - self.tabs:selectTab(self.tabs[2]) + self.tabs:selectTab(self.tabs.output) elseif event.type == 'autocomplete' then local value = self.prompt.value or '' diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index b879fd0..1b68f5b 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -29,12 +29,14 @@ if not _ENV.multishell then end 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\ + +-- icon:gsub('.', function(b) return '\\' .. b:byte() end) +local DEFAULT_ICON = NFT.parse('\30\55\31\48\136\140\140\140\132\ +\30\48\31\55\149\31\48\128\128\128\30\55\149\ +\30\55\31\48\138\143\143\143\133') +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") +\0302\0312\32\32\32\32\32') -- overview local uid = _ENV.multishell.getCurrent() diff --git a/sys/apps/Partition.lua b/sys/apps/Partition.lua new file mode 100644 index 0000000..499222e --- /dev/null +++ b/sys/apps/Partition.lua @@ -0,0 +1,238 @@ +local Ansi = require('opus.ansi') +local Event = require('opus.event') +local UI = require('opus.ui') +local Util = require('opus.util') + +local fs = _G.fs +local peripheral = _G.peripheral + +local source, target + +local function getDriveInfo(tgt) + local total = 0 + local throttle = Util.throttle() + + tgt = fs.combine(tgt, '') + local src = fs.getNode(tgt).source or tgt + + local function recurse(path) + throttle() + if fs.isDir(path) then + if path ~= src then + total = total + 500 + end + for _, v in pairs(fs.native.list(path)) do + recurse(fs.combine(path, v)) + end + else + local sz = fs.getSize(path) + total = total + math.max(500, sz) + end + end + + recurse(src) + + local drive = fs.getDrive(src) + return { + path = tgt, + drive = drive, + type = peripheral.getType(drive) or drive, + used = total, + free = fs.getFreeSpace(src), + mountPoint = src, + } +end + +local function getDrives(exclude) + local drives = { } + + for _, path in pairs(fs.native.list('/')) do + local side = fs.getDrive(path) + if side and not drives[side] and not fs.isReadOnly(path) and side ~= exclude then + if side == 'hdd' then + path = '' + end + drives[side] = getDriveInfo(path) + end + end + return drives +end + +local page = UI.Page { + wizard = UI.Wizard { + ey = -2, + partitions = UI.WizardPage { + index = 1, + info = UI.TextArea { + x = 3, y = 2, ex = -3, ey = 5, + value = [[Move the contents of a directory to another disk. A link will be created to point to that location.]] + }, + grid = UI.Grid { + x = 2, y = 7, ex = -2, ey = -2, + columns = { + { heading = 'Path', key = 'path', textColor = 'yellow', width = 10 }, + { heading = 'Mount Point', key = 'mountPoint' }, + { heading = 'Used', key = 'used', width = 6 }, + }, + sortColumn = 'path', + getDisplayValues = function (_, row) + row = Util.shallowCopy(row) + row.used = Util.toBytes(row.used) + return row + end, + enable = function(self) + Event.onTimeout(0, function() + local mounts = { + usr = getDriveInfo('usr/config'), + packages = getDriveInfo('packages'), + } + self:setValues(mounts) + self:draw() + self:sync() + end) + self:setValues({ }) + UI.Grid.enable(self) + end, + }, + validate = function(self) + target = self.grid:getSelected() + return not not target + end, + }, + mounts = UI.WizardPage { + index = 2, + info = UI.TextArea { + x = 3, y = 2, ex = -3, ey = 5, + value = [[Select the target disk. Labeled computers can be inserted into disk drives for larger volumes.]] + }, + grid = UI.Grid { + x = 2, y = 7, ex = -2, ey = -2, + columns = { + { heading = 'Path', key = 'path', textColor = 'yellow', width = 10 }, + { heading = 'Type', key = 'type' }, + { heading = 'Side', key = 'drive' }, + { heading = 'Free', key = 'free', width = 6 }, + }, + sortColumn = 'path', + getDisplayValues = function (_, row) + row = Util.shallowCopy(row) + row.free = Util.toBytes(row.free) + return row + end, + getRowTextColor = function(self, row) + if row.free < target.used then + return 'lightGray' + end + return UI.Grid.getRowTextColor(self, row) + end, + enable = function(self) + Event.on({ 'disk', 'disk_eject', 'partition_update' }, function() + self:setValues(getDrives(target.drive)) + self:draw() + self:sync() + end) + os.queueEvent('partition_update') + self:setValues({ }) + UI.Grid.enable(self) + end, + }, + validate = function(self) + source = self.grid:getSelected() + if not source then + self:emit({ type = 'notify', message = 'No drive selected' }) + elseif source.free < target.used then + self:emit({ type = 'notify', message = 'Insufficient disk space' }) + else + return true + end + end, + }, + confirm = UI.WizardPage { + index = 3, + info = UI.TextArea { + x = 2, y = 2, ex = -2, ey = -2, + marginTop = 1, marginLeft = 1, + backgroundColor = 'black', + }, + enable = function(self) + local fstab = Util.readFile('usr/etc/fstab') + local lines = { } + table.insert(lines, string.format('%sReview changes%s\n', Ansi.yellow, Ansi.reset)) + if fstab then + for _,l in ipairs(Util.split(fstab)) do + l = Util.trim(l) + if #l > 0 and l:sub(1, 1) ~= '#' then + local m = Util.matches(l) + if m and m[1] and m[1] == target.path then + table.insert(lines, string.format('Removed from usr/etc/fstab:\n%s%s%s\n', Ansi.red, l, Ansi.reset)) + end + end + end + end + local t = target.path + local s = fs.combine(source.path .. '/' .. target.path, '') + if t ~= s then + table.insert(lines, string.format('Added to usr/etc/fstab:\n%s%s linkfs %s%s\n', Ansi.green, t, s, Ansi.reset)) + end + + table.insert(lines, string.format('Move directory:\n%s/%s -> /%s', Ansi.green, target.mountPoint, s)) + + self.info:setText(table.concat(lines, '\n')) + UI.WizardPage.enable(self) + end, + validate = function(self) + if self.changesApplied then + return true + end + local fstab = Util.readFile('usr/etc/fstab') + local lines = { } + if fstab then + for _,l in ipairs(Util.split(fstab)) do + table.insert(lines, l) + l = Util.trim(l) + if #l > 0 and l:sub(1, 1) ~= '#' then + local m = Util.matches(l) + if m and m[1] and m[1] == target.path then + fs.unmount(m[1]) + table.remove(lines) + end + end + end + end + + local t = target.path + local s = fs.combine(source.path .. '/' .. target.path, '') + + fs.move('/' .. target.mountPoint, '/' .. s) + + if t ~= s then + table.insert(lines, string.format('%s linkfs %s', t, s)) + fs.mount(t, 'linkfs', s) + end + + Util.writeFile('usr/etc/fstab', table.concat(lines, '\n')) + + self.parent.nextButton.text = 'Exit' + self.parent.cancelButton:disable() + self.parent.previousButton:disable() + + self.changesApplied = true + self.info:setValue('Changes have been applied') + self.parent:draw() + end, + }, + }, + notification = UI.Notification { }, + eventHandler = function(self, event) + if event.type == 'notify' then + self.notification:error(event.message) + elseif event.type == 'accept' or event.type == 'cancel' then + UI:quit() + end + return UI.Page.eventHandler(self, event) + end, +} + +UI:disableEffects() +UI:setPage(page) +UI:start() diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index bec9ac0..5336234 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -46,7 +46,7 @@ local page = UI.Page { configTabs = UI.Tabs { y = 2, filterTab = UI.Tab { - tabTitle = 'Filter', + title = 'Filter', noFill = true, filterGridText = UI.Text { x = 2, y = 2, @@ -93,7 +93,7 @@ local page = UI.Page { }, }, modemTab = UI.Tab { - tabTitle = 'Modem', + title = 'Modem', channelGrid = UI.ScrollingGrid { x = 2, y = 2, width = 12, height = 5, diff --git a/sys/apps/System.lua b/sys/apps/System.lua index 7005d83..cd22812 100644 --- a/sys/apps/System.lua +++ b/sys/apps/System.lua @@ -6,62 +6,6 @@ local shell = _ENV.shell UI:configure('System', ...) -local systemPage = UI.Page { - tabs = UI.Tabs { - settings = UI.Tab { - tabTitle = 'Category', - grid = UI.ScrollingGrid { - x = 2, y = 2, ex = -2, ey = -2, - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Description', key = 'description' }, - }, - sortColumn = 'name', - autospace = true, - }, - }, - }, - notification = UI.Notification(), - accelerators = { - [ 'control-q' ] = 'quit', - }, -} - -function systemPage.tabs.settings:eventHandler(event) - if event.type == 'grid_select' then - local tab = event.selected.tab - if not systemPage.tabs[tab.tabTitle] then - systemPage.tabs:add({ [ tab.tabTitle ] = tab }) - tab:disable() - end - systemPage.tabs:selectTab(tab) - --self.parent:draw() - return true - end -end - -function systemPage:eventHandler(event) - if event.type == 'quit' then - UI:quit() - - elseif event.type == 'success_message' then - self.notification:success(event.message) - - elseif event.type == 'info_message' then - self.notification:info(event.message) - - elseif event.type == 'error_message' then - self.notification:error(event.message) - - elseif event.type == 'tab_activate' then - event.activated:focusFirst() - - else - return UI.Page.eventHandler(self, event) - end - return true -end - local function loadDirectory(dir) local plugins = { } for _, file in pairs(fs.list(dir)) do @@ -70,7 +14,7 @@ local function loadDirectory(dir) _G.printError('Error loading: ' .. file) error(m or 'Unknown error') elseif s and m then - table.insert(plugins, { tab = m, name = m.tabTitle, description = m.description }) + table.insert(plugins, { tab = m, name = m.title, description = m.description }) end end return plugins @@ -79,7 +23,60 @@ end local programDir = fs.getDir(shell.getRunningProgram()) local plugins = loadDirectory(fs.combine(programDir, 'system'), { }) -systemPage.tabs.settings.grid:setValues(plugins) +local page = UI.Page { + tabs = UI.Tabs { + settings = UI.Tab { + title = 'Category', + grid = UI.ScrollingGrid { + x = 2, y = 2, ex = -2, ey = -2, + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Description', key = 'description' }, + }, + sortColumn = 'name', + autospace = true, + values = plugins, + }, + accelerators = { + grid_select = 'category_select', + } + }, + }, + notification = UI.Notification(), + accelerators = { + [ 'control-q' ] = 'quit', + }, + eventHandler = function(self, event) + if event.type == 'quit' then + UI:quit() -UI:setPage(systemPage) + elseif event.type == 'category_select' then + local tab = event.selected.tab + + if not self.tabs[tab.title] then + self.tabs:add({ [ tab.title ] = tab }) + end + self.tabs:selectTab(tab) + return true + + elseif event.type == 'success_message' then + self.notification:success(event.message) + + elseif event.type == 'info_message' then + self.notification:info(event.message) + + elseif event.type == 'error_message' then + self.notification:error(event.message) + + elseif event.type == 'tab_activate' then + event.activated:focusFirst() + + else + return UI.Page.eventHandler(self, event) + end + return true + end, +} + +UI:setPage(page) UI:start() diff --git a/sys/apps/Welcome.lua b/sys/apps/Welcome.lua index 1154fbf..f1a9972 100644 --- a/sys/apps/Welcome.lua +++ b/sys/apps/Welcome.lua @@ -33,87 +33,85 @@ https://github.com/kepler155c/opus]] local page = UI.Page { wizard = UI.Wizard { ey = -2, - pages = { - splash = UI.WizardPage { - index = 1, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = string.format(splashIntro, Ansi.white), - }, + splash = UI.WizardPage { + index = 1, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(splashIntro, Ansi.white), }, - label = UI.WizardPage { - index = 2, - labelText = UI.Text { - x = 3, y = 2, - value = 'Label' - }, - label = UI.TextEntry { - x = 9, y = 2, ex = -3, - limit = 32, - value = os.getComputerLabel(), - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 4, ey = -3, - value = string.format(labelIntro, Ansi.white), - }, - validate = function (self) - if self.label.value then - os.setComputerLabel(self.label.value) - end - return true - end, + }, + label = UI.WizardPage { + index = 2, + labelText = UI.Text { + x = 3, y = 2, + value = 'Label' }, - password = UI.WizardPage { - index = 3, - passwordLabel = UI.Text { - x = 3, y = 2, - value = 'Password' - }, - newPass = UI.TextEntry { - x = 12, ex = -3, y = 2, - limit = 32, - mask = true, - shadowText = 'password', - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 5, ey = -3, - value = string.format(passwordIntro, Ansi.white), - }, - validate = function (self) - if type(self.newPass.value) == "string" and #self.newPass.value > 0 then - Security.updatePassword(SHA.compute(self.newPass.value)) - end - return true - end, + label = UI.TextEntry { + x = 9, y = 2, ex = -3, + limit = 32, + value = os.getComputerLabel(), }, - packages = UI.WizardPage { - index = 4, - button = UI.Button { - x = 3, y = -3, - text = 'Open Package Manager', - event = 'packages', - }, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -4, - value = string.format(packagesIntro, Ansi.white), - }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 4, ey = -3, + value = string.format(labelIntro, Ansi.white), }, - contributors = UI.WizardPage { - index = 5, - intro = UI.TextArea { - textColor = colors.yellow, - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = string.format(contributorsIntro, Ansi.white, Ansi.yellow, Ansi.white), - }, + validate = function (self) + if self.label.value then + os.setComputerLabel(self.label.value) + end + return true + end, + }, + password = UI.WizardPage { + index = 3, + passwordLabel = UI.Text { + x = 3, y = 2, + value = 'Password' + }, + newPass = UI.TextEntry { + x = 12, ex = -3, y = 2, + limit = 32, + mask = true, + shadowText = 'password', + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 5, ey = -3, + value = string.format(passwordIntro, Ansi.white), + }, + validate = function (self) + if type(self.newPass.value) == "string" and #self.newPass.value > 0 then + Security.updatePassword(SHA.compute(self.newPass.value)) + end + return true + end, + }, + packages = UI.WizardPage { + index = 4, + button = UI.Button { + x = 3, y = -3, + text = 'Open Package Manager', + event = 'packages', + }, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -4, + value = string.format(packagesIntro, Ansi.white), + }, + }, + contributors = UI.WizardPage { + index = 5, + intro = UI.TextArea { + textColor = colors.yellow, + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = string.format(contributorsIntro, Ansi.white, Ansi.yellow, Ansi.white), }, }, }, diff --git a/sys/apps/inspect.lua b/sys/apps/inspect.lua index 35a9f5c..0df9526 100644 --- a/sys/apps/inspect.lua +++ b/sys/apps/inspect.lua @@ -49,7 +49,7 @@ page = UI.Page { backgroundColor = colors.red, y = '50%', properties = UI.Tab { - tabTitle = 'Properties', + title = 'Properties', grid = UI.ScrollingGrid { headerBackgroundColor = colors.red, sortColumn = 'key', @@ -64,7 +64,7 @@ page = UI.Page { }, methodsTab = UI.Tab { index = 2, - tabTitle = 'Methods', + title = 'Methods', grid = UI.ScrollingGrid { ex = '50%', headerBackgroundColor = colors.red, @@ -85,7 +85,7 @@ page = UI.Page { }, events = UI.Tab { index = 1, - tabTitle = 'Events', + title = 'Events', UI.MenuBar { y = -1, backgroundColor = colors.red, diff --git a/sys/apps/system/aliases.lua b/sys/apps/system/aliases.lua index 2d122bb..ede9349 100644 --- a/sys/apps/system/aliases.lua +++ b/sys/apps/system/aliases.lua @@ -4,7 +4,7 @@ local UI = require('opus.ui') local kernel = _G.kernel local aliasTab = UI.Tab { - tabTitle = 'Aliases', + title = 'Aliases', description = 'Shell aliases', alias = UI.TextEntry { x = 2, y = 2, ex = -2, diff --git a/sys/apps/system/alternate.lua b/sys/apps/system/alternate.lua index e903074..35bd601 100644 --- a/sys/apps/system/alternate.lua +++ b/sys/apps/system/alternate.lua @@ -3,7 +3,7 @@ local Config = require('opus.config') local UI = require('opus.ui') local tab = UI.Tab { - tabTitle = 'Preferred', + title = 'Preferred', description = 'Select preferred applications', apps = UI.ScrollingGrid { x = 2, y = 2, diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index d837f2e..3cbe740 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -6,7 +6,7 @@ if _G.http.websocket then local config = Config.load('cloud') local tab = UI.Tab { - tabTitle = 'Cloud', + title = 'Cloud', description = 'Cloud Catcher options', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 4, diff --git a/sys/apps/system/diskusage.lua b/sys/apps/system/diskusage.lua index 9a07e17..1042cc4 100644 --- a/sys/apps/system/diskusage.lua +++ b/sys/apps/system/diskusage.lua @@ -16,7 +16,7 @@ local NftImages = { } local tab = UI.Tab { - tabTitle = 'Disks Usage', + title = 'Disks Usage', description = 'Visualise HDD and disks usage', drives = UI.ScrollingGrid { @@ -138,11 +138,9 @@ function tab:enable() UI.Tab.enable(self) self.handler = Event.on({ 'disk', 'disk_eject' }, function() os.sleep(1) - if tab.enabled then - tab:updateDrives() - tab:updateInfo() - tab:sync() - end + tab:updateDrives() + tab:updateInfo() + tab:sync() end) end diff --git a/sys/apps/system/kiosk.lua b/sys/apps/system/kiosk.lua index 2f9510b..be5b18e 100644 --- a/sys/apps/system/kiosk.lua +++ b/sys/apps/system/kiosk.lua @@ -5,7 +5,7 @@ local peripheral = _G.peripheral local settings = _G.settings return peripheral.find('monitor') and UI.Tab { - tabTitle = 'Kiosk', + title = 'Kiosk', description = 'Kiosk options', form = UI.Form { x = 2, y = 2, ex = -2, ey = 5, diff --git a/sys/apps/system/label.lua b/sys/apps/system/label.lua index 8fe3d1e..ba38109 100644 --- a/sys/apps/system/label.lua +++ b/sys/apps/system/label.lua @@ -5,7 +5,7 @@ local fs = _G.fs local os = _G.os return UI.Tab { - tabTitle = 'Label', + title = 'Label', description = 'Set the computer label', labelText = UI.Text { x = 3, y = 3, diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index 9c87fe9..c3f8588 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -7,7 +7,7 @@ local fs = _G.fs local config = Config.load('multishell') local tab = UI.Tab { - tabTitle = 'Launcher', + title = 'Launcher', description = 'Set the application launcher', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 5, diff --git a/sys/apps/system/network.lua b/sys/apps/system/network.lua index b4fd802..b214d12 100644 --- a/sys/apps/system/network.lua +++ b/sys/apps/system/network.lua @@ -6,7 +6,7 @@ local colors = _G.colors local device = _G.device return UI.Tab { - tabTitle = 'Network', + title = 'Network', description = 'Networking options', info = UI.TextArea { x = 2, y = 5, ex = -2, ey = -2, diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 8040c76..a01c610 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -3,7 +3,7 @@ local SHA = require('opus.crypto.sha2') local UI = require('opus.ui') return UI.Tab { - tabTitle = 'Password', + title = 'Password', description = 'Wireless network password', [1] = UI.Window { x = 2, y = 2, ex = -2, ey = 4, diff --git a/sys/apps/system/path.lua b/sys/apps/system/path.lua index eef9f16..9550cb4 100644 --- a/sys/apps/system/path.lua +++ b/sys/apps/system/path.lua @@ -3,7 +3,7 @@ local UI = require('opus.ui') local Util = require('opus.util') local tab = UI.Tab { - tabTitle = 'Path', + title = 'Path', description = 'Set the shell path', tabClose = true, [1] = UI.Window { diff --git a/sys/apps/system/requires.lua b/sys/apps/system/requires.lua index 1e41216..54d78d8 100644 --- a/sys/apps/system/requires.lua +++ b/sys/apps/system/requires.lua @@ -3,7 +3,7 @@ local UI = require('opus.ui') local Util = require('opus.util') local tab = UI.Tab { - tabTitle = 'Requires', + title = 'Requires', description = 'Require path', tabClose = true, entry = UI.TextEntry { diff --git a/sys/apps/system/settings.lua b/sys/apps/system/settings.lua index 27f1f2b..6676910 100644 --- a/sys/apps/system/settings.lua +++ b/sys/apps/system/settings.lua @@ -8,7 +8,7 @@ local transform = { } return settings and UI.Tab { - tabTitle = 'Settings', + title = 'Settings', description = 'Computercraft settings', grid = UI.Grid { x = 2, y = 2, ex = -2, ey = -2, diff --git a/sys/apps/system/shell.lua b/sys/apps/system/shell.lua index 8ca32fe..8fe9807 100644 --- a/sys/apps/system/shell.lua +++ b/sys/apps/system/shell.lua @@ -39,7 +39,7 @@ if not _colors.backgroundColor then end return UI.Tab { - tabTitle = 'Shell', + title = 'Shell', description = 'Shell options', grid1 = UI.ScrollingGrid { y = 2, ey = -10, x = 2, ex = -17, diff --git a/sys/apps/system/theme.lua b/sys/apps/system/theme.lua index b9d4ed4..0df173c 100644 --- a/sys/apps/system/theme.lua +++ b/sys/apps/system/theme.lua @@ -17,7 +17,7 @@ for k,v in pairs(UI.colors) do end return UI.Tab { - tabTitle = 'Theme', + title = 'Theme', description = 'Theme colors', grid1 = UI.ScrollingGrid { y = 2, ey = -10, x = 2, ex = -17, diff --git a/sys/etc/apps.db b/sys/etc/apps.db index f342709..7e96569 100644 --- a/sys/etc/apps.db +++ b/sys/etc/apps.db @@ -136,4 +136,10 @@ iconExt = "\030 \031 \128\0307\143\131\131\131\131\143\030 \128\010\0307\031 \129\0317\128\0319\136\0309\031b\136\132\0307\0319\132\0317\128\031 \130\010\030 \0317\130\143\0307\128\128\128\128\030 \143\129", run = "/rom/programs/fun/dj", }, + [ "4dbdd221e957eff27cc47796f3ed8447290f71c7ad8b95e5bd828b31c1858f15" ] = { + title = "Partition", + category = "System", + iconExt = "\30\55\31\55\128\30\48\135\131\139\30\55\128\128\128\10\30\48\31\55\149\31\48\128\30\55\145\30\48\31\56\140\30\55\157\144\144\10\30\55\31\55\128\31\48\139\143\135\31\55\128\31\56\142\133", + run = "Partition", + } } diff --git a/sys/init/2.vfs.lua b/sys/init/2.vfs.lua index 69eb387..d624810 100644 --- a/sys/init/2.vfs.lua +++ b/sys/init/2.vfs.lua @@ -134,6 +134,8 @@ local function getNode(dir) return node end +fs.getNode = getNode + local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize', 'isReadOnly', 'makeDir', 'getDrive', 'list', 'open' } diff --git a/sys/init/4.user.lua b/sys/init/4.user.lua index 4decc1f..a742b34 100644 --- a/sys/init/4.user.lua +++ b/sys/init/4.user.lua @@ -10,6 +10,13 @@ if not fs.exists('usr/autorun') then fs.makeDir('usr/autorun') end +-- move the fstab out of config so that the config directory +-- can be remapped to another disk (and for consistency) +if fs.exists('usr/config/fstab') and not fs.exists('usr/etc/fstab') then + fs.move('usr/config/fstab', 'usr/etc/fstab') +end +fs.loadTab('usr/etc/fstab') + -- TODO: Temporary local upgrade = Util.readTable('usr/config/shell') if upgrade and (not upgrade.upgraded or upgrade.upgraded ~= 1) then @@ -45,5 +52,3 @@ shell.setPath(table.concat(path, ':')) --_G.LUA_PATH = config.lua_path --_G.settings.set('mbs.shell.require_path', config.lua_path) - -fs.loadTab('usr/config/fstab') diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 49c55bf..3ad43ab 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -14,7 +14,7 @@ for name in pairs(Packages:installed()) do local packageDir = fs.combine('packages', name) table.insert(appPaths, 1, '/' .. packageDir) - local apiPath = fs.combine(packageDir, 'apis') + local apiPath = fs.combine(packageDir, 'apis') -- TODO: rename dir to 'modules' (someday) if fs.exists(apiPath) then fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath) end diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 625128f..f64a77f 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -675,7 +675,7 @@ function UI.Window:drawChildren() end UI.Window.docs.getDoc = [[getDoc(STRING method) -Gets the documentation for a method.]] +Get the documentation for a method.]] function UI.Window:getDoc(method) local m = getmetatable(self) -- get the class for this instance repeat @@ -743,6 +743,8 @@ function UI.Window:clear(bg, fg) Canvas.clear(self, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) end +UI.Window.docs.clearLine = [[clearLine(NUMBER y, opt COLOR bg) +Clears the specified line.]] function UI.Window:clearLine(y, bg) self:write(1, y, _rep(' ', self.width), bg) end @@ -760,6 +762,10 @@ function UI.Window:fillArea(x, y, width, height, fillChar, bg, fg) end end +UI.Window.docs.write = [[write(NUMBER x, NUMBER y, String text, opt COLOR bg, opt COLOR fg) +Write text to the canvas. +If colors are not specified, the colors from the base class will be used. +If the base class does not have colors defined, colors will be inherited from the parent container.]] function UI.Window:write(x, y, text, bg, fg) Canvas.write(self, x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) end diff --git a/sys/modules/opus/ui/components/FileSelect.lua b/sys/modules/opus/ui/components/FileSelect.lua index 55b86b7..dd8747e 100644 --- a/sys/modules/opus/ui/components/FileSelect.lua +++ b/sys/modules/opus/ui/components/FileSelect.lua @@ -1,86 +1,75 @@ local class = require('opus.class') -local UI = require('opus.ui') -local Util = require('opus.util') +local UI = require('opus.ui') +local Util = require('opus.util') -local colors = _G.colors -local fs = _G.fs +local fs = _G.fs UI.FileSelect = class(UI.Window) UI.FileSelect.defaults = { UIElement = 'FileSelect', } function UI.FileSelect:postInit() - self.grid = UI.ScrollingGrid { - x = 2, y = 2, ex = -2, ey = -4, - dir = '/', - sortColumn = 'name', - columns = { - { heading = 'Name', key = 'name' }, - { heading = 'Size', key = 'size', width = 5 } - }, - getDisplayValues = function(_, row) - if row.size then - row = Util.shallowCopy(row) - row.size = Util.toBytes(row.size) - end - return row - end, - getRowTextColor = function(_, file) - if file.isDir then - return colors.cyan - end - if file.isReadOnly then - return colors.pink - end - return colors.white - end, - sortCompare = function(self, a, b) - if self.sortColumn == 'size' then - return a.size < b.size - end - if a.isDir == b.isDir then - return a.name:lower() < b.name:lower() - end - return a.isDir - end, - draw = function(self) - local files = fs.listEx(self.dir) - if #self.dir > 0 then - table.insert(files, { - name = '..', - isDir = true, - }) - end - self:setValues(files) - self:setIndex(1) - UI.Grid.draw(self) - end, - } - self.path = UI.TextEntry { - x = 2, - y = -2, - ex = -11, - limit = 256, - accelerators = { - enter = 'path_enter', - } - } - self.cancel = UI.Button { - text = 'Cancel', - x = -9, - y = -2, - event = 'select_cancel', - } + self.grid = UI.ScrollingGrid { + x = 2, y = 2, ex = -2, ey = -4, + dir = '/', + sortColumn = 'name', + columns = { + { heading = 'Name', key = 'name' }, + { heading = 'Size', key = 'size', width = 5 } + }, + getDisplayValues = function(_, row) + return { + name = row.name, + size = row.size and Util.toBytes(row.size), + } + end, + getRowTextColor = function(_, file) + return file.isDir and 'cyan' or file.isReadOnly and 'pink' or 'white' + end, + sortCompare = function(self, a, b) + if self.sortColumn == 'size' then + return a.size < b.size + end + if a.isDir == b.isDir then + return a.name:lower() < b.name:lower() + end + return a.isDir + end, + draw = function(self) + local files = fs.listEx(self.dir) + if #self.dir > 0 then + table.insert(files, { + name = '..', + isDir = true, + }) + end + self:setValues(files) + self:setIndex(1) + UI.Grid.draw(self) + end, + } + self.path = UI.TextEntry { + x = 2, y = -2, ex = -11, + limit = 256, + accelerators = { + enter = 'path_enter', + } + } + self.cancel = UI.Button { + x = -9, y = -2, + text = 'Cancel', + event = 'select_cancel', + } end function UI.FileSelect:draw() - self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray) - self:drawChildren() + self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), 'black', 'gray') + self:drawChildren() end function UI.FileSelect:enable(path) - self:setPath(path or '') - UI.Window.enable(self) + self:setPath(path or '') + UI.Window.enable(self) end function UI.FileSelect:setPath(path) @@ -98,21 +87,21 @@ function UI.FileSelect:eventHandler(event) if event.selected.isDir then self.grid:draw() self.path:draw() - else - self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) - end - return true + else + self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) + end + return true - elseif event.type == 'path_enter' then - if self.path.value then - if fs.isDir(self.path.value) then - self:setPath(self.path.value) - self.grid:draw() - self.path:draw() - else - self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) - end - end - return true + elseif event.type == 'path_enter' then + if self.path.value then + if fs.isDir(self.path.value) then + self:setPath(self.path.value) + self.grid:draw() + self.path:draw() + else + self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) + end + end + return true end end diff --git a/sys/modules/opus/ui/components/Image.lua b/sys/modules/opus/ui/components/Image.lua index 7d67c35..f24c911 100644 --- a/sys/modules/opus/ui/components/Image.lua +++ b/sys/modules/opus/ui/components/Image.lua @@ -33,7 +33,7 @@ function UI.Image:draw() for x = 1, #line do local ch = lookup:find(line:sub(x, x)) if ch then - self:write(x, y, ' ', 2 ^ (ch -1)) + self:write(x, y, ' ', 2 ^ (ch - 1)) end end end @@ -47,6 +47,7 @@ end function UI.Image.example() return UI.Image { + backgroundColor = 'primary', filename = 'test.paint', } end diff --git a/sys/modules/opus/ui/components/ScrollBar.lua b/sys/modules/opus/ui/components/ScrollBar.lua index 8f8b6ab..715c3cb 100644 --- a/sys/modules/opus/ui/components/ScrollBar.lua +++ b/sys/modules/opus/ui/components/ScrollBar.lua @@ -7,7 +7,7 @@ local colors = _G.colors UI.ScrollBar = class(UI.Window) UI.ScrollBar.defaults = { UIElement = 'ScrollBar', - lineChar = '|', + lineChar = '\166', sliderChar = UI.extChars and '\127' or '#', upArrowChar = UI.extChars and '\30' or '^', downArrowChar = UI.extChars and '\31' or 'v', diff --git a/sys/modules/opus/ui/components/Tab.lua b/sys/modules/opus/ui/components/Tab.lua index d2e591b..ee835d4 100644 --- a/sys/modules/opus/ui/components/Tab.lua +++ b/sys/modules/opus/ui/components/Tab.lua @@ -4,7 +4,7 @@ local UI = require('opus.ui') UI.Tab = class(UI.Window) UI.Tab.defaults = { UIElement = 'Tab', - tabTitle = 'tab', + title = 'tab', y = 2, } @@ -14,3 +14,8 @@ function UI.Tab:draw() end self:drawChildren() end + +function UI.Tab:enable() + UI.Window.enable(self) + self:emit({ type = 'tab_activate', activated = self }) +end diff --git a/sys/modules/opus/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua index eb81b3a..0f75e05 100644 --- a/sys/modules/opus/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -37,9 +37,3 @@ function UI.TabBar:eventHandler(event) return UI.MenuBar.eventHandler(self, event) end -function UI.TabBar:selectTab(text) - local menuItem = Util.find(self.children, 'text', text) - if menuItem then - menuItem.selected = true - end -end diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index 8c2a692..f2b61a8 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -14,11 +14,11 @@ end function UI.Tabs:add(children) local buttons = { } for _,child in pairs(children) do - if type(child) == 'table' and child.UIElement and child.tabTitle then + if type(child) == 'table' and child.UIElement and child.UIElement == 'Tab' then child.y = 2 table.insert(buttons, { index = child.index, - text = child.tabTitle, + text = child.title, event = 'tab_select', tabUid = child.uid, }) @@ -34,7 +34,12 @@ function UI.Tabs:add(children) end if self.parent then + local enabled = self.enabled + + -- don't enable children upon add + self.enabled = nil UI.Window.add(self, children) + self.enabled = enabled end end @@ -43,7 +48,15 @@ Make to the passed tab active.]] function UI.Tabs:selectTab(tab) local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid) if menuItem then - self.tabBar:emit({ type = 'tab_select', button = { uid = menuItem.uid } }) + if self.enabled then + self.tabBar:emit({ type = 'tab_select', button = { uid = menuItem.uid } }) + else + local previous = Util.find(self.tabBar.children, 'selected', true) + if previous then + previous.selected = false + end + menuItem.selected = true + end end end @@ -59,14 +72,20 @@ function UI.Tabs:enable() self.tabBar:enable() local menuItem = Util.find(self.tabBar.children, 'selected', true) + self:enableTab(menuItem.tabUid) +end +function UI.Tabs:enableTab(tabUid, hint) for child in self:eachChild() do - child.transitionHint = nil - if child.uid == menuItem.tabUid then - child:enable() - self:emit({ type = 'tab_activate', activated = child }) - elseif child.tabTitle then - child:disable() + child.transitionHint = hint + if child.uid == tabUid then + if not child.enabled then + child:enable() + end + elseif child.UIElement == 'Tab' then + if child.enabled then + child:disable() + end end end end @@ -76,15 +95,7 @@ function UI.Tabs:eventHandler(event) local tab = self:find(event.tab.tabUid) local hint = event.current > event.last and 'slideLeft' or 'slideRight' - for child in self:eachChild() do - if child.uid == event.tab.tabUid then - child.transitionHint = hint - child:enable() - elseif child.tabTitle then - child:disable() - end - end - self:emit({ type = 'tab_activate', activated = tab }) + self:enableTab(event.tab.tabUid, hint) tab:draw() return true end @@ -94,28 +105,28 @@ function UI.Tabs.example() return UI.Tabs { tab1 = UI.Tab { index = 1, - tabTitle = 'tab1', + title = 'tab1', entry = UI.TextEntry { y = 3, shadowText = 'text' }, }, tab2 = UI.Tab { index = 2, - tabTitle = 'tab2', + title = 'tab2', subtabs = UI.Tabs { x = 3, y = 2, ex = -3, ey = -2, tab1 = UI.Tab { index = 1, - tabTitle = 'tab4', + title = 'tab4', entry = UI.TextEntry { y = 3, shadowText = 'text' }, }, tab3 = UI.Tab { index = 2, - tabTitle = 'tab5', + title = 'tab5', }, }, }, tab3 = UI.Tab { index = 3, - tabTitle = 'tab3', + title = 'tab3', }, enable = function(self) UI.Tabs.enable(self) diff --git a/sys/modules/opus/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua index 3b821c7..8572a59 100644 --- a/sys/modules/opus/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -8,11 +8,12 @@ UI.TextArea.defaults = { value = '', showScrollBar = true, } -function UI.TextArea:setText(text) +function UI.TextArea:setValue(text) self:reset() self.value = text self:draw() end +UI.TextArea.setText = UI.TextArea.setValue -- deprecate function UI.TextArea.focus() -- allow keyboard scrolling diff --git a/sys/modules/opus/ui/components/Wizard.lua b/sys/modules/opus/ui/components/Wizard.lua index 0fcab17..791a8a9 100644 --- a/sys/modules/opus/ui/components/Wizard.lua +++ b/sys/modules/opus/ui/components/Wizard.lua @@ -15,52 +15,50 @@ function UI.Wizard:postInit() } self.previousButton = UI.Button { x = -18, y = -1, - text = '< Back', + text = '\17 Back', event = 'previousView', } self.nextButton = UI.Button { x = -9, y = -1, - text = 'Next >', + text = 'Next \16', event = 'nextView', } Util.merge(self, self.pages) end -function UI.Wizard:add(pages) - Util.merge(self.pages, pages) - Util.merge(self, pages) - - for _, child in pairs(self.pages) do - child.ey = child.ey or -2 - end - - if self.parent then - self:initChildren() +function UI.Wizard:getPages() + local t = { } + for child in self:eachChild() do + if type(child) == 'table' and child.UIElement == 'WizardPage' then + table.insert(t, child) + end end + return t end function UI.Wizard:getPage(index) - return Util.find(self.pages, 'index', index) + local pages = self:getPages() + return Util.find(pages, 'index', index) end -function UI.Wizard:enable(...) +function UI.Wizard:enable() self.enabled = true self.index = 1 - local initial = self:getPage(1) for child in self:eachChild() do - if child == initial or not child.index then - child:enable(...) - else + if child.UIElement ~= 'WizardPage' then + child:enable() + elseif child.enabled then child:disable() end end + local initial = self:getPage(1) self:emit({ type = 'enable_view', next = initial }) end function UI.Wizard:isViewValid() local currentView = self:getPage(self.index) - return not currentView.validate and true or currentView:validate() + return currentView:validate() end function UI.Wizard:eventHandler(event) @@ -107,7 +105,7 @@ function UI.Wizard:eventHandler(event) end if self:getPage(self.index + 1) then - self.nextButton.text = 'Next >' + self.nextButton.text = 'Next \16' self.nextButton.event = 'nextView' else self.nextButton.text = 'Accept' @@ -124,29 +122,27 @@ end function UI.Wizard.example() return UI.Wizard { ey = -2, - pages = { - splash = UI.WizardPage { - index = 1, - intro = UI.TextArea { - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = 'sample text', - }, + splash = UI.WizardPage { + index = 1, + intro = UI.TextArea { + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = 'sample text', }, - label = UI.WizardPage { - index = 2, - intro = UI.TextArea { - inactive = true, - x = 3, ex = -3, y = 2, ey = -2, - value = 'sample more text', - }, + }, + label = UI.WizardPage { + index = 2, + intro = UI.TextArea { + inactive = true, + x = 3, ex = -3, y = 2, ey = -2, + value = 'sample more text', }, - password = UI.WizardPage { - index = 3, - text = UI.TextEntry { - x = 12, ex = -3, y = 2, - shadowText = 'tet', - }, + }, + password = UI.WizardPage { + index = 3, + text = UI.TextEntry { + x = 12, ex = -3, y = 2, + shadowText = 'tet', }, }, } diff --git a/sys/modules/opus/ui/components/WizardPage.lua b/sys/modules/opus/ui/components/WizardPage.lua index da895ef..f23f107 100644 --- a/sys/modules/opus/ui/components/WizardPage.lua +++ b/sys/modules/opus/ui/components/WizardPage.lua @@ -6,3 +6,6 @@ UI.WizardPage.defaults = { UIElement = 'WizardPage', ey = -2, } +function UI.WizardPage.validate() + return true +end