partition manager + tab/wizard rework

This commit is contained in:
kepler155c@gmail.com 2020-04-26 19:39:58 -06:00
parent b0d2ce0199
commit ef9f0e09b6
35 changed files with 588 additions and 334 deletions

View File

@ -45,8 +45,9 @@ local page = UI.Page {
}, },
tabs = UI.Tabs { tabs = UI.Tabs {
y = 3, y = 3,
[1] = UI.Tab { formatted = UI.Tab {
tabTitle = 'Formatted', title = 'Formatted',
index = 1,
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
columns = { columns = {
{ heading = 'Key', key = 'name' }, { heading = 'Key', key = 'name' },
@ -56,8 +57,9 @@ local page = UI.Page {
autospace = true, autospace = true,
}, },
}, },
[2] = UI.Tab { output = UI.Tab {
tabTitle = 'Output', title = 'Output',
index = 2,
backgroundColor = 'black', backgroundColor = 'black',
output = UI.Embedded { output = UI.Embedded {
y = 2, y = 2,
@ -72,8 +74,8 @@ local page = UI.Page {
}, },
} }
page.grid = page.tabs[1].grid page.grid = page.tabs.formatted.grid
page.output = page.tabs[2].output page.output = page.tabs.output.output
function page:setPrompt(value, focus) function page:setPrompt(value, focus)
self.prompt:setValue(value) self.prompt:setValue(value)
@ -142,7 +144,7 @@ function page:eventHandler(event)
self:setFocus(self.prompt) self:setFocus(self.prompt)
elseif event.type == 'show_output' then elseif event.type == 'show_output' then
self.tabs:selectTab(self.tabs[2]) self.tabs:selectTab(self.tabs.output)
elseif event.type == 'autocomplete' then elseif event.type == 'autocomplete' then
local value = self.prompt.value or '' local value = self.prompt.value or ''

View File

@ -29,12 +29,14 @@ if not _ENV.multishell then
end end
local REGISTRY_DIR = 'usr/.registry' local REGISTRY_DIR = 'usr/.registry'
local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\
\0307\0318\153\153\153\153\153\ -- icon:gsub('.', function(b) return '\\' .. b:byte() end)
\0308\0317\153\153\153\153\153") local DEFAULT_ICON = NFT.parse('\30\55\31\48\136\140\140\140\132\
local TRANS_ICON = NFT.parse("\0302\0312\32\32\32\32\32\ \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") \0302\0312\32\32\32\32\32')
-- overview -- overview
local uid = _ENV.multishell.getCurrent() local uid = _ENV.multishell.getCurrent()

238
sys/apps/Partition.lua Normal file
View File

@ -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()

View File

@ -46,7 +46,7 @@ local page = UI.Page {
configTabs = UI.Tabs { configTabs = UI.Tabs {
y = 2, y = 2,
filterTab = UI.Tab { filterTab = UI.Tab {
tabTitle = 'Filter', title = 'Filter',
noFill = true, noFill = true,
filterGridText = UI.Text { filterGridText = UI.Text {
x = 2, y = 2, x = 2, y = 2,
@ -93,7 +93,7 @@ local page = UI.Page {
}, },
}, },
modemTab = UI.Tab { modemTab = UI.Tab {
tabTitle = 'Modem', title = 'Modem',
channelGrid = UI.ScrollingGrid { channelGrid = UI.ScrollingGrid {
x = 2, y = 2, x = 2, y = 2,
width = 12, height = 5, width = 12, height = 5,

View File

@ -6,62 +6,6 @@ local shell = _ENV.shell
UI:configure('System', ...) 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 function loadDirectory(dir)
local plugins = { } local plugins = { }
for _, file in pairs(fs.list(dir)) do for _, file in pairs(fs.list(dir)) do
@ -70,7 +14,7 @@ local function loadDirectory(dir)
_G.printError('Error loading: ' .. file) _G.printError('Error loading: ' .. file)
error(m or 'Unknown error') error(m or 'Unknown error')
elseif s and m then 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
end end
return plugins return plugins
@ -79,7 +23,60 @@ end
local programDir = fs.getDir(shell.getRunningProgram()) local programDir = fs.getDir(shell.getRunningProgram())
local plugins = loadDirectory(fs.combine(programDir, 'system'), { }) 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() UI:start()

View File

@ -33,87 +33,85 @@ https://github.com/kepler155c/opus]]
local page = UI.Page { local page = UI.Page {
wizard = UI.Wizard { wizard = UI.Wizard {
ey = -2, ey = -2,
pages = { splash = UI.WizardPage {
splash = UI.WizardPage { index = 1,
index = 1, intro = UI.TextArea {
intro = UI.TextArea { textColor = colors.yellow,
textColor = colors.yellow, inactive = true,
inactive = true, x = 3, ex = -3, y = 2, ey = -2,
x = 3, ex = -3, y = 2, ey = -2, value = string.format(splashIntro, Ansi.white),
value = string.format(splashIntro, Ansi.white),
},
}, },
label = UI.WizardPage { },
index = 2, label = UI.WizardPage {
labelText = UI.Text { index = 2,
x = 3, y = 2, labelText = UI.Text {
value = 'Label' 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,
}, },
password = UI.WizardPage { label = UI.TextEntry {
index = 3, x = 9, y = 2, ex = -3,
passwordLabel = UI.Text { limit = 32,
x = 3, y = 2, value = os.getComputerLabel(),
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 { intro = UI.TextArea {
index = 4, textColor = colors.yellow,
button = UI.Button { inactive = true,
x = 3, y = -3, x = 3, ex = -3, y = 4, ey = -3,
text = 'Open Package Manager', value = string.format(labelIntro, Ansi.white),
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 { validate = function (self)
index = 5, if self.label.value then
intro = UI.TextArea { os.setComputerLabel(self.label.value)
textColor = colors.yellow, end
inactive = true, return true
x = 3, ex = -3, y = 2, ey = -2, end,
value = string.format(contributorsIntro, Ansi.white, Ansi.yellow, Ansi.white), },
}, 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),
}, },
}, },
}, },

View File

@ -49,7 +49,7 @@ page = UI.Page {
backgroundColor = colors.red, backgroundColor = colors.red,
y = '50%', y = '50%',
properties = UI.Tab { properties = UI.Tab {
tabTitle = 'Properties', title = 'Properties',
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
headerBackgroundColor = colors.red, headerBackgroundColor = colors.red,
sortColumn = 'key', sortColumn = 'key',
@ -64,7 +64,7 @@ page = UI.Page {
}, },
methodsTab = UI.Tab { methodsTab = UI.Tab {
index = 2, index = 2,
tabTitle = 'Methods', title = 'Methods',
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
ex = '50%', ex = '50%',
headerBackgroundColor = colors.red, headerBackgroundColor = colors.red,
@ -85,7 +85,7 @@ page = UI.Page {
}, },
events = UI.Tab { events = UI.Tab {
index = 1, index = 1,
tabTitle = 'Events', title = 'Events',
UI.MenuBar { UI.MenuBar {
y = -1, y = -1,
backgroundColor = colors.red, backgroundColor = colors.red,

View File

@ -4,7 +4,7 @@ local UI = require('opus.ui')
local kernel = _G.kernel local kernel = _G.kernel
local aliasTab = UI.Tab { local aliasTab = UI.Tab {
tabTitle = 'Aliases', title = 'Aliases',
description = 'Shell aliases', description = 'Shell aliases',
alias = UI.TextEntry { alias = UI.TextEntry {
x = 2, y = 2, ex = -2, x = 2, y = 2, ex = -2,

View File

@ -3,7 +3,7 @@ local Config = require('opus.config')
local UI = require('opus.ui') local UI = require('opus.ui')
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Preferred', title = 'Preferred',
description = 'Select preferred applications', description = 'Select preferred applications',
apps = UI.ScrollingGrid { apps = UI.ScrollingGrid {
x = 2, y = 2, x = 2, y = 2,

View File

@ -6,7 +6,7 @@ if _G.http.websocket then
local config = Config.load('cloud') local config = Config.load('cloud')
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Cloud', title = 'Cloud',
description = 'Cloud Catcher options', description = 'Cloud Catcher options',
[1] = UI.Window { [1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4, x = 2, y = 2, ex = -2, ey = 4,

View File

@ -16,7 +16,7 @@ local NftImages = {
} }
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Disks Usage', title = 'Disks Usage',
description = 'Visualise HDD and disks usage', description = 'Visualise HDD and disks usage',
drives = UI.ScrollingGrid { drives = UI.ScrollingGrid {
@ -138,11 +138,9 @@ function tab:enable()
UI.Tab.enable(self) UI.Tab.enable(self)
self.handler = Event.on({ 'disk', 'disk_eject' }, function() self.handler = Event.on({ 'disk', 'disk_eject' }, function()
os.sleep(1) os.sleep(1)
if tab.enabled then tab:updateDrives()
tab:updateDrives() tab:updateInfo()
tab:updateInfo() tab:sync()
tab:sync()
end
end) end)
end end

View File

@ -5,7 +5,7 @@ local peripheral = _G.peripheral
local settings = _G.settings local settings = _G.settings
return peripheral.find('monitor') and UI.Tab { return peripheral.find('monitor') and UI.Tab {
tabTitle = 'Kiosk', title = 'Kiosk',
description = 'Kiosk options', description = 'Kiosk options',
form = UI.Form { form = UI.Form {
x = 2, y = 2, ex = -2, ey = 5, x = 2, y = 2, ex = -2, ey = 5,

View File

@ -5,7 +5,7 @@ local fs = _G.fs
local os = _G.os local os = _G.os
return UI.Tab { return UI.Tab {
tabTitle = 'Label', title = 'Label',
description = 'Set the computer label', description = 'Set the computer label',
labelText = UI.Text { labelText = UI.Text {
x = 3, y = 3, x = 3, y = 3,

View File

@ -7,7 +7,7 @@ local fs = _G.fs
local config = Config.load('multishell') local config = Config.load('multishell')
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Launcher', title = 'Launcher',
description = 'Set the application launcher', description = 'Set the application launcher',
[1] = UI.Window { [1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 5, x = 2, y = 2, ex = -2, ey = 5,

View File

@ -6,7 +6,7 @@ local colors = _G.colors
local device = _G.device local device = _G.device
return UI.Tab { return UI.Tab {
tabTitle = 'Network', title = 'Network',
description = 'Networking options', description = 'Networking options',
info = UI.TextArea { info = UI.TextArea {
x = 2, y = 5, ex = -2, ey = -2, x = 2, y = 5, ex = -2, ey = -2,

View File

@ -3,7 +3,7 @@ local SHA = require('opus.crypto.sha2')
local UI = require('opus.ui') local UI = require('opus.ui')
return UI.Tab { return UI.Tab {
tabTitle = 'Password', title = 'Password',
description = 'Wireless network password', description = 'Wireless network password',
[1] = UI.Window { [1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4, x = 2, y = 2, ex = -2, ey = 4,

View File

@ -3,7 +3,7 @@ local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Path', title = 'Path',
description = 'Set the shell path', description = 'Set the shell path',
tabClose = true, tabClose = true,
[1] = UI.Window { [1] = UI.Window {

View File

@ -3,7 +3,7 @@ local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Requires', title = 'Requires',
description = 'Require path', description = 'Require path',
tabClose = true, tabClose = true,
entry = UI.TextEntry { entry = UI.TextEntry {

View File

@ -8,7 +8,7 @@ local transform = {
} }
return settings and UI.Tab { return settings and UI.Tab {
tabTitle = 'Settings', title = 'Settings',
description = 'Computercraft settings', description = 'Computercraft settings',
grid = UI.Grid { grid = UI.Grid {
x = 2, y = 2, ex = -2, ey = -2, x = 2, y = 2, ex = -2, ey = -2,

View File

@ -39,7 +39,7 @@ if not _colors.backgroundColor then
end end
return UI.Tab { return UI.Tab {
tabTitle = 'Shell', title = 'Shell',
description = 'Shell options', description = 'Shell options',
grid1 = UI.ScrollingGrid { grid1 = UI.ScrollingGrid {
y = 2, ey = -10, x = 2, ex = -17, y = 2, ey = -10, x = 2, ex = -17,

View File

@ -17,7 +17,7 @@ for k,v in pairs(UI.colors) do
end end
return UI.Tab { return UI.Tab {
tabTitle = 'Theme', title = 'Theme',
description = 'Theme colors', description = 'Theme colors',
grid1 = UI.ScrollingGrid { grid1 = UI.ScrollingGrid {
y = 2, ey = -10, x = 2, ex = -17, y = 2, ey = -10, x = 2, ex = -17,

View File

@ -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", 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", 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",
}
} }

View File

@ -134,6 +134,8 @@ local function getNode(dir)
return node return node
end end
fs.getNode = getNode
local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize', local methods = { 'delete', 'getFreeSpace', 'exists', 'isDir', 'getSize',
'isReadOnly', 'makeDir', 'getDrive', 'list', 'open' } 'isReadOnly', 'makeDir', 'getDrive', 'list', 'open' }

View File

@ -10,6 +10,13 @@ if not fs.exists('usr/autorun') then
fs.makeDir('usr/autorun') fs.makeDir('usr/autorun')
end 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 -- TODO: Temporary
local upgrade = Util.readTable('usr/config/shell') local upgrade = Util.readTable('usr/config/shell')
if upgrade and (not upgrade.upgraded or upgrade.upgraded ~= 1) then 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.LUA_PATH = config.lua_path
--_G.settings.set('mbs.shell.require_path', config.lua_path) --_G.settings.set('mbs.shell.require_path', config.lua_path)
fs.loadTab('usr/config/fstab')

View File

@ -14,7 +14,7 @@ for name in pairs(Packages:installed()) do
local packageDir = fs.combine('packages', name) local packageDir = fs.combine('packages', name)
table.insert(appPaths, 1, '/' .. packageDir) 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 if fs.exists(apiPath) then
fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath) fs.mount(fs.combine('rom/modules/main', name), 'linkfs', apiPath)
end end

View File

@ -675,7 +675,7 @@ function UI.Window:drawChildren()
end end
UI.Window.docs.getDoc = [[getDoc(STRING method) UI.Window.docs.getDoc = [[getDoc(STRING method)
Gets the documentation for a method.]] Get the documentation for a method.]]
function UI.Window:getDoc(method) function UI.Window:getDoc(method)
local m = getmetatable(self) -- get the class for this instance local m = getmetatable(self) -- get the class for this instance
repeat repeat
@ -743,6 +743,8 @@ function UI.Window:clear(bg, fg)
Canvas.clear(self, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor')) Canvas.clear(self, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
end end
UI.Window.docs.clearLine = [[clearLine(NUMBER y, opt COLOR bg)
Clears the specified line.]]
function UI.Window:clearLine(y, bg) function UI.Window:clearLine(y, bg)
self:write(1, y, _rep(' ', self.width), bg) self:write(1, y, _rep(' ', self.width), bg)
end end
@ -760,6 +762,10 @@ function UI.Window:fillArea(x, y, width, height, fillChar, bg, fg)
end end
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) 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')) Canvas.write(self, x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
end end

View File

@ -1,86 +1,75 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local Util = require('opus.util') local Util = require('opus.util')
local colors = _G.colors local fs = _G.fs
local fs = _G.fs
UI.FileSelect = class(UI.Window) UI.FileSelect = class(UI.Window)
UI.FileSelect.defaults = { UI.FileSelect.defaults = {
UIElement = 'FileSelect', UIElement = 'FileSelect',
} }
function UI.FileSelect:postInit() function UI.FileSelect:postInit()
self.grid = UI.ScrollingGrid { self.grid = UI.ScrollingGrid {
x = 2, y = 2, ex = -2, ey = -4, x = 2, y = 2, ex = -2, ey = -4,
dir = '/', dir = '/',
sortColumn = 'name', sortColumn = 'name',
columns = { columns = {
{ heading = 'Name', key = 'name' }, { heading = 'Name', key = 'name' },
{ heading = 'Size', key = 'size', width = 5 } { heading = 'Size', key = 'size', width = 5 }
}, },
getDisplayValues = function(_, row) getDisplayValues = function(_, row)
if row.size then return {
row = Util.shallowCopy(row) name = row.name,
row.size = Util.toBytes(row.size) size = row.size and Util.toBytes(row.size),
end }
return row end,
end, getRowTextColor = function(_, file)
getRowTextColor = function(_, file) return file.isDir and 'cyan' or file.isReadOnly and 'pink' or 'white'
if file.isDir then end,
return colors.cyan sortCompare = function(self, a, b)
end if self.sortColumn == 'size' then
if file.isReadOnly then return a.size < b.size
return colors.pink end
end if a.isDir == b.isDir then
return colors.white return a.name:lower() < b.name:lower()
end, end
sortCompare = function(self, a, b) return a.isDir
if self.sortColumn == 'size' then end,
return a.size < b.size draw = function(self)
end local files = fs.listEx(self.dir)
if a.isDir == b.isDir then if #self.dir > 0 then
return a.name:lower() < b.name:lower() table.insert(files, {
end name = '..',
return a.isDir isDir = true,
end, })
draw = function(self) end
local files = fs.listEx(self.dir) self:setValues(files)
if #self.dir > 0 then self:setIndex(1)
table.insert(files, { UI.Grid.draw(self)
name = '..', end,
isDir = true, }
}) self.path = UI.TextEntry {
end x = 2, y = -2, ex = -11,
self:setValues(files) limit = 256,
self:setIndex(1) accelerators = {
UI.Grid.draw(self) enter = 'path_enter',
end, }
} }
self.path = UI.TextEntry { self.cancel = UI.Button {
x = 2, x = -9, y = -2,
y = -2, text = 'Cancel',
ex = -11, event = 'select_cancel',
limit = 256, }
accelerators = {
enter = 'path_enter',
}
}
self.cancel = UI.Button {
text = 'Cancel',
x = -9,
y = -2,
event = 'select_cancel',
}
end end
function UI.FileSelect:draw() function UI.FileSelect:draw()
self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray) self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), 'black', 'gray')
self:drawChildren() self:drawChildren()
end end
function UI.FileSelect:enable(path) function UI.FileSelect:enable(path)
self:setPath(path or '') self:setPath(path or '')
UI.Window.enable(self) UI.Window.enable(self)
end end
function UI.FileSelect:setPath(path) function UI.FileSelect:setPath(path)
@ -98,21 +87,21 @@ function UI.FileSelect:eventHandler(event)
if event.selected.isDir then if event.selected.isDir then
self.grid:draw() self.grid:draw()
self.path:draw() self.path:draw()
else else
self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self })
end end
return true return true
elseif event.type == 'path_enter' then elseif event.type == 'path_enter' then
if self.path.value then if self.path.value then
if fs.isDir(self.path.value) then if fs.isDir(self.path.value) then
self:setPath(self.path.value) self:setPath(self.path.value)
self.grid:draw() self.grid:draw()
self.path:draw() self.path:draw()
else else
self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self }) self:emit({ type = 'select_file', file = '/' .. self.path.value, element = self })
end end
end end
return true return true
end end
end end

View File

@ -33,7 +33,7 @@ function UI.Image:draw()
for x = 1, #line do for x = 1, #line do
local ch = lookup:find(line:sub(x, x)) local ch = lookup:find(line:sub(x, x))
if ch then if ch then
self:write(x, y, ' ', 2 ^ (ch -1)) self:write(x, y, ' ', 2 ^ (ch - 1))
end end
end end
end end
@ -47,6 +47,7 @@ end
function UI.Image.example() function UI.Image.example()
return UI.Image { return UI.Image {
backgroundColor = 'primary',
filename = 'test.paint', filename = 'test.paint',
} }
end end

View File

@ -7,7 +7,7 @@ local colors = _G.colors
UI.ScrollBar = class(UI.Window) UI.ScrollBar = class(UI.Window)
UI.ScrollBar.defaults = { UI.ScrollBar.defaults = {
UIElement = 'ScrollBar', UIElement = 'ScrollBar',
lineChar = '|', lineChar = '\166',
sliderChar = UI.extChars and '\127' or '#', sliderChar = UI.extChars and '\127' or '#',
upArrowChar = UI.extChars and '\30' or '^', upArrowChar = UI.extChars and '\30' or '^',
downArrowChar = UI.extChars and '\31' or 'v', downArrowChar = UI.extChars and '\31' or 'v',

View File

@ -4,7 +4,7 @@ local UI = require('opus.ui')
UI.Tab = class(UI.Window) UI.Tab = class(UI.Window)
UI.Tab.defaults = { UI.Tab.defaults = {
UIElement = 'Tab', UIElement = 'Tab',
tabTitle = 'tab', title = 'tab',
y = 2, y = 2,
} }
@ -14,3 +14,8 @@ function UI.Tab:draw()
end end
self:drawChildren() self:drawChildren()
end end
function UI.Tab:enable()
UI.Window.enable(self)
self:emit({ type = 'tab_activate', activated = self })
end

View File

@ -37,9 +37,3 @@ function UI.TabBar:eventHandler(event)
return UI.MenuBar.eventHandler(self, event) return UI.MenuBar.eventHandler(self, event)
end end
function UI.TabBar:selectTab(text)
local menuItem = Util.find(self.children, 'text', text)
if menuItem then
menuItem.selected = true
end
end

View File

@ -14,11 +14,11 @@ end
function UI.Tabs:add(children) function UI.Tabs:add(children)
local buttons = { } local buttons = { }
for _,child in pairs(children) do 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 child.y = 2
table.insert(buttons, { table.insert(buttons, {
index = child.index, index = child.index,
text = child.tabTitle, text = child.title,
event = 'tab_select', event = 'tab_select',
tabUid = child.uid, tabUid = child.uid,
}) })
@ -34,7 +34,12 @@ function UI.Tabs:add(children)
end end
if self.parent then if self.parent then
local enabled = self.enabled
-- don't enable children upon add
self.enabled = nil
UI.Window.add(self, children) UI.Window.add(self, children)
self.enabled = enabled
end end
end end
@ -43,7 +48,15 @@ Make to the passed tab active.]]
function UI.Tabs:selectTab(tab) function UI.Tabs:selectTab(tab)
local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid) local menuItem = Util.find(self.tabBar.children, 'tabUid', tab.uid)
if menuItem then 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
end end
@ -59,14 +72,20 @@ function UI.Tabs:enable()
self.tabBar:enable() self.tabBar:enable()
local menuItem = Util.find(self.tabBar.children, 'selected', true) 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 for child in self:eachChild() do
child.transitionHint = nil child.transitionHint = hint
if child.uid == menuItem.tabUid then if child.uid == tabUid then
child:enable() if not child.enabled then
self:emit({ type = 'tab_activate', activated = child }) child:enable()
elseif child.tabTitle then end
child:disable() elseif child.UIElement == 'Tab' then
if child.enabled then
child:disable()
end
end end
end end
end end
@ -76,15 +95,7 @@ function UI.Tabs:eventHandler(event)
local tab = self:find(event.tab.tabUid) local tab = self:find(event.tab.tabUid)
local hint = event.current > event.last and 'slideLeft' or 'slideRight' local hint = event.current > event.last and 'slideLeft' or 'slideRight'
for child in self:eachChild() do self:enableTab(event.tab.tabUid, hint)
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 })
tab:draw() tab:draw()
return true return true
end end
@ -94,28 +105,28 @@ function UI.Tabs.example()
return UI.Tabs { return UI.Tabs {
tab1 = UI.Tab { tab1 = UI.Tab {
index = 1, index = 1,
tabTitle = 'tab1', title = 'tab1',
entry = UI.TextEntry { y = 3, shadowText = 'text' }, entry = UI.TextEntry { y = 3, shadowText = 'text' },
}, },
tab2 = UI.Tab { tab2 = UI.Tab {
index = 2, index = 2,
tabTitle = 'tab2', title = 'tab2',
subtabs = UI.Tabs { subtabs = UI.Tabs {
x = 3, y = 2, ex = -3, ey = -2, x = 3, y = 2, ex = -3, ey = -2,
tab1 = UI.Tab { tab1 = UI.Tab {
index = 1, index = 1,
tabTitle = 'tab4', title = 'tab4',
entry = UI.TextEntry { y = 3, shadowText = 'text' }, entry = UI.TextEntry { y = 3, shadowText = 'text' },
}, },
tab3 = UI.Tab { tab3 = UI.Tab {
index = 2, index = 2,
tabTitle = 'tab5', title = 'tab5',
}, },
}, },
}, },
tab3 = UI.Tab { tab3 = UI.Tab {
index = 3, index = 3,
tabTitle = 'tab3', title = 'tab3',
}, },
enable = function(self) enable = function(self)
UI.Tabs.enable(self) UI.Tabs.enable(self)

View File

@ -8,11 +8,12 @@ UI.TextArea.defaults = {
value = '', value = '',
showScrollBar = true, showScrollBar = true,
} }
function UI.TextArea:setText(text) function UI.TextArea:setValue(text)
self:reset() self:reset()
self.value = text self.value = text
self:draw() self:draw()
end end
UI.TextArea.setText = UI.TextArea.setValue -- deprecate
function UI.TextArea.focus() function UI.TextArea.focus()
-- allow keyboard scrolling -- allow keyboard scrolling

View File

@ -15,52 +15,50 @@ function UI.Wizard:postInit()
} }
self.previousButton = UI.Button { self.previousButton = UI.Button {
x = -18, y = -1, x = -18, y = -1,
text = '< Back', text = '\17 Back',
event = 'previousView', event = 'previousView',
} }
self.nextButton = UI.Button { self.nextButton = UI.Button {
x = -9, y = -1, x = -9, y = -1,
text = 'Next >', text = 'Next \16',
event = 'nextView', event = 'nextView',
} }
Util.merge(self, self.pages) Util.merge(self, self.pages)
end end
function UI.Wizard:add(pages) function UI.Wizard:getPages()
Util.merge(self.pages, pages) local t = { }
Util.merge(self, pages) for child in self:eachChild() do
if type(child) == 'table' and child.UIElement == 'WizardPage' then
for _, child in pairs(self.pages) do table.insert(t, child)
child.ey = child.ey or -2 end
end
if self.parent then
self:initChildren()
end end
return t
end end
function UI.Wizard:getPage(index) function UI.Wizard:getPage(index)
return Util.find(self.pages, 'index', index) local pages = self:getPages()
return Util.find(pages, 'index', index)
end end
function UI.Wizard:enable(...) function UI.Wizard:enable()
self.enabled = true self.enabled = true
self.index = 1 self.index = 1
local initial = self:getPage(1)
for child in self:eachChild() do for child in self:eachChild() do
if child == initial or not child.index then if child.UIElement ~= 'WizardPage' then
child:enable(...) child:enable()
else elseif child.enabled then
child:disable() child:disable()
end end
end end
local initial = self:getPage(1)
self:emit({ type = 'enable_view', next = initial }) self:emit({ type = 'enable_view', next = initial })
end end
function UI.Wizard:isViewValid() function UI.Wizard:isViewValid()
local currentView = self:getPage(self.index) local currentView = self:getPage(self.index)
return not currentView.validate and true or currentView:validate() return currentView:validate()
end end
function UI.Wizard:eventHandler(event) function UI.Wizard:eventHandler(event)
@ -107,7 +105,7 @@ function UI.Wizard:eventHandler(event)
end end
if self:getPage(self.index + 1) then if self:getPage(self.index + 1) then
self.nextButton.text = 'Next >' self.nextButton.text = 'Next \16'
self.nextButton.event = 'nextView' self.nextButton.event = 'nextView'
else else
self.nextButton.text = 'Accept' self.nextButton.text = 'Accept'
@ -124,29 +122,27 @@ end
function UI.Wizard.example() function UI.Wizard.example()
return UI.Wizard { return UI.Wizard {
ey = -2, ey = -2,
pages = { splash = UI.WizardPage {
splash = UI.WizardPage { index = 1,
index = 1, intro = UI.TextArea {
intro = UI.TextArea { inactive = true,
inactive = true, x = 3, ex = -3, y = 2, ey = -2,
x = 3, ex = -3, y = 2, ey = -2, value = 'sample text',
value = 'sample text',
},
}, },
label = UI.WizardPage { },
index = 2, label = UI.WizardPage {
intro = UI.TextArea { index = 2,
inactive = true, intro = UI.TextArea {
x = 3, ex = -3, y = 2, ey = -2, inactive = true,
value = 'sample more text', x = 3, ex = -3, y = 2, ey = -2,
}, value = 'sample more text',
}, },
password = UI.WizardPage { },
index = 3, password = UI.WizardPage {
text = UI.TextEntry { index = 3,
x = 12, ex = -3, y = 2, text = UI.TextEntry {
shadowText = 'tet', x = 12, ex = -3, y = 2,
}, shadowText = 'tet',
}, },
}, },
} }

View File

@ -6,3 +6,6 @@ UI.WizardPage.defaults = {
UIElement = 'WizardPage', UIElement = 'WizardPage',
ey = -2, ey = -2,
} }
function UI.WizardPage.validate()
return true
end