1
0
mirror of https://github.com/kepler155c/opus synced 2025-10-26 13:17:39 +00:00

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 {
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 ''

View File

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

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 {
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,

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 {

View File

@@ -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 {

View File

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

View File

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

View File

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