canvas overhaul

This commit is contained in:
kepler155c@gmail.com 2020-03-31 09:57:23 -06:00
parent 369070e19c
commit 5a874c1944
69 changed files with 1134 additions and 786 deletions

View File

@ -91,7 +91,6 @@ local Browser = UI.Page {
}, },
notification = UI.Notification { }, notification = UI.Notification { },
associations = UI.SlideOut { associations = UI.SlideOut {
backgroundColor = colors.cyan,
menuBar = UI.MenuBar { menuBar = UI.MenuBar {
buttons = { buttons = {
{ text = 'Save', event = 'save' }, { text = 'Save', event = 'save' },
@ -99,7 +98,7 @@ local Browser = UI.Page {
}, },
}, },
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
x = 2, ex = -6, y = 3, ey = -5, x = 2, ex = -6, y = 3, ey = -8,
columns = { columns = {
{ heading = 'Extension', key = 'name' }, { heading = 'Extension', key = 'name' },
{ heading = 'Program', key = 'value' }, { heading = 'Program', key = 'value' },
@ -114,8 +113,11 @@ local Browser = UI.Page {
x = -4, y = 6, x = -4, y = 6,
text = '-', event = 'remove_entry', help = 'Remove', text = '-', event = 'remove_entry', help = 'Remove',
}, },
[1] = UI.Window {
x = 2, y = -6, ex = -6, ey = -3,
},
form = UI.Form { form = UI.Form {
x = 3, y = -3, ey = -2, x = 3, y = -5, ex = -7, ey = -3,
margin = 1, margin = 1,
manualControls = true, manualControls = true,
[1] = UI.TextEntry { [1] = UI.TextEntry {

View File

@ -59,7 +59,6 @@ local page = UI.Page {
[2] = UI.Tab { [2] = UI.Tab {
tabTitle = 'Output', tabTitle = 'Output',
output = UI.Embedded { output = UI.Embedded {
visible = true,
maxScroll = 1000, maxScroll = 1000,
backgroundColor = colors.black, backgroundColor = colors.black,
}, },

View File

@ -74,7 +74,6 @@ local page = UI.Page {
}, },
}, },
help = UI.SlideOut { help = UI.SlideOut {
backgroundColor = colors.cyan,
x = 5, ex = -5, height = 8, y = -8, x = 5, ex = -5, height = 8, y = -8,
titleBar = UI.TitleBar { titleBar = UI.TitleBar {
title = 'Network Help', title = 'Network Help',
@ -82,7 +81,6 @@ local page = UI.Page {
}, },
text = UI.TextArea { text = UI.TextArea {
x = 2, y = 2, x = 2, y = 2,
backgroundColor = colors.cyan,
value = [[ value = [[
In order to connect to another computer: In order to connect to another computer:

View File

@ -26,6 +26,9 @@ local REGISTRY_DIR = 'usr/.registry'
local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\ local DEFAULT_ICON = NFT.parse("\0308\0317\153\153\153\153\153\
\0307\0318\153\153\153\153\153\ \0307\0318\153\153\153\153\153\
\0308\0317\153\153\153\153\153") \0308\0317\153\153\153\153\153")
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")
-- overview -- overview
local uid = _ENV.multishell.getCurrent() local uid = _ENV.multishell.getCurrent()
@ -65,6 +68,7 @@ local function parseIcon(iconText)
if icon.height > 3 or icon.width > 8 then if icon.height > 3 or icon.width > 8 then
error('Must be an NFT image - 3 rows, 8 cols max') error('Must be an NFT image - 3 rows, 8 cols max')
end end
NFT.transparency(icon)
end end
return icon return icon
end) end)
@ -89,6 +93,7 @@ function UI.VerticalTabBar:setParent()
c.ox, c.oy = c.x, c.y c.ox, c.oy = c.x, c.y
c.ow = 8 c.ow = 8
c.width = 8 c.width = 8
c:reposition(c.x, c.y, c.width, c.height)
end end
end end
@ -114,7 +119,6 @@ local page = UI.Page {
}, },
editor = UI.SlideOut { editor = UI.SlideOut {
y = -12, height = 12, y = -12, height = 12,
backgroundColor = colors.cyan,
titleBar = UI.TitleBar { titleBar = UI.TitleBar {
title = 'Edit Application', title = 'Edit Application',
event = 'slide_hide', event = 'slide_hide',
@ -122,7 +126,7 @@ local page = UI.Page {
form = UI.Form { form = UI.Form {
y = 2, ey = -2, y = 2, ey = -2,
[1] = UI.TextEntry { [1] = UI.TextEntry {
formLabel = 'Title', formKey = 'title', limit = 11, help = 'Application title', formLabel = 'Title', formKey = 'title', limit = 11, width = 13, help = 'Application title',
required = true, required = true,
}, },
[2] = UI.TextEntry { [2] = UI.TextEntry {
@ -130,21 +134,24 @@ local page = UI.Page {
required = true, required = true,
}, },
[3] = UI.TextEntry { [3] = UI.TextEntry {
formLabel = 'Category', formKey = 'category', limit = 11, help = 'Category of application', formLabel = 'Category', formKey = 'category', limit = 6, width = 8, help = 'Category of application',
required = true, required = true,
}, },
iconFile = UI.TextEntry { editIcon = UI.Button {
x = 11, ex = -12, y = 7, x = 11, y = 9,
limit = 128, help = 'Path to icon file', text = 'Edit', event = 'editIcon', help = 'Edit icon file',
shadowText = 'Path to icon file',
}, },
loadIcon = UI.Button { loadIcon = UI.Button {
x = 11, y = 9, x = 18, y = 9,
text = 'Load', event = 'loadIcon', help = 'Load icon file', text = 'Load', event = 'loadIcon', help = 'Load icon file',
}, },
info = UI.TextArea {
x = 11, y = 6, height = 2,
value = 'magenta is transparent\n3 high - max width is 8'
},
image = UI.NftImage { image = UI.NftImage {
backgroundColor = colors.black, backgroundColor = colors.black,
y = 7, x = 2, height = 3, width = 8, y = 6, x = 2, height = 3, width = 8,
}, },
}, },
notification = UI.Notification(), notification = UI.Notification(),
@ -226,6 +233,7 @@ local function loadApplications()
page:add { page:add {
tabBar = UI.VerticalTabBar { tabBar = UI.VerticalTabBar {
buttons = buttons, buttons = buttons,
selectedBackgroundColor = UI.colors.primary,
}, },
} }
@ -308,14 +316,12 @@ function page.container:setCategory(categoryName, animate)
image = UI.NftImage({ image = UI.NftImage({
x = math.floor((width - icon.width) / 2) + 1, x = math.floor((width - icon.width) / 2) + 1,
image = icon, image = icon,
width = 5,
height = 3,
}), }),
button = UI.Button({ button = UI.Button({
x = math.floor((width - #title - 2) / 2) + 1, x = math.floor((width - #title - 2) / 2) + 1,
y = 4, y = 4,
text = title, text = title,
backgroundColor = self.backgroundColor, backgroundColor = self:getProperty('backgroundColor'),
backgroundFocusColor = colors.gray, backgroundFocusColor = colors.gray,
textColor = colors.white, textColor = colors.white,
textFocusColor = colors.white, textFocusColor = colors.white,
@ -333,7 +339,8 @@ function page.container:setCategory(categoryName, animate)
local col, row = gutter, 2 local col, row = gutter, 2
local count = #self.children local count = #self.children
local r = math.random(1, 5) local r = math.random(1, 7)
local frames = 5
-- reposition all children -- reposition all children
for k,child in ipairs(self.children) do for k,child in ipairs(self.children) do
if r == 1 then if r == 1 then
@ -355,14 +362,22 @@ function page.container:setCategory(categoryName, animate)
child.x = self.width child.x = self.width
child.y = self.height - 3 child.y = self.height - 3
end end
elseif r == 6 then
child.x = col
child.y = 1
elseif r == 7 then
child.x = 1
child.y = self.height - 3
end end
child.tween = Tween.new(6, child, { x = col, y = row }, 'linear') child.tween = Tween.new(frames, child, { x = col, y = row }, 'inQuad')
if not animate then if not animate then
child.x = col child.x = col
child.y = row child.y = row
end end
self:setViewHeight(row + 3)
if k < count then if k < count then
col = col + child.width col = col + child.width
if col + self.children[k + 1].width + gutter - 2 > self.width then if col + self.children[k + 1].width + gutter - 2 > self.width then
@ -377,15 +392,13 @@ function page.container:setCategory(categoryName, animate)
local function transition() local function transition()
local i = 1 local i = 1
return function() return function()
self:clear()
for _,child in pairs(self.children) do for _,child in pairs(self.children) do
child.tween:update(1) child.tween:update(1)
child.x = math.floor(child.x) child:move(math.floor(child.x), math.floor(child.y))
child.y = math.floor(child.y)
child:draw()
end end
--os.sleep(.5)
i = i + 1 i = i + 1
return i < 7 return i <= frames
end end
end end
self:addTransition(transition) self:addTransition(transition)
@ -512,6 +525,30 @@ function page.editor:updateApplications(app)
loadApplications() loadApplications()
end end
function page.editor:loadImage(filename)
local s, m = pcall(function()
local iconLines = Util.readFile(filename)
if not iconLines then
error('Must be an NFT image - 3 rows, 8 cols max')
end
local icon, m = parseIcon(iconLines)
if not icon then
error(m)
end
if extSupport then
self.form.values.iconExt = iconLines
else
self.form.values.icon = iconLines
end
self.form.image:setImage(icon)
self.form.image:draw()
end)
if not s and m then
local msg = m:gsub('.*: (.*)', '%1')
self.notification:error(msg)
end
end
function page.editor:eventHandler(event) function page.editor:eventHandler(event)
if event.type == 'form_cancel' or event.type == 'cancel' then if event.type == 'form_cancel' or event.type == 'cancel' then
self:hide() self:hide()
@ -520,27 +557,20 @@ function page.editor:eventHandler(event)
self.statusBar:setStatus(event.focused.help or '') self.statusBar:setStatus(event.focused.help or '')
self.statusBar:draw() self.statusBar:draw()
elseif event.type == 'editIcon' then
local filename = '/tmp/editing.nft'
NFT.save(self.form.image.image or TRANS_ICON, filename)
local success = shell.run('pain.lua ' .. filename)
self.parent:dirty(true)
if success then
self:loadImage(filename)
end
elseif event.type == 'loadIcon' then elseif event.type == 'loadIcon' then
local s, m = pcall(function() local success, filename = shell.run('fileui.lua')
local iconLines = Util.readFile(self.form.iconFile.value) self.parent:dirty(true)
if not iconLines then if success and filename then
error('Must be an NFT image - 3 rows, 8 cols max') self:loadImage(filename)
end
local icon, m = parseIcon(iconLines)
if not icon then
error(m)
end
if extSupport then
self.form.values.iconExt = iconLines
else
self.form.values.icon = iconLines
end
self.form.image:setImage(icon)
self.form.image:draw()
end)
if not s and m then
local msg = m:gsub('.*: (.*)', '%1')
self.notification:error(msg)
end end
elseif event.type == 'form_invalid' then elseif event.type == 'form_invalid' then
@ -590,5 +620,4 @@ end)
loadApplications() loadApplications()
UI:setPage(page) UI:setPage(page)
UI:start()
UI:pullEvents()

View File

@ -44,7 +44,6 @@ local page = UI.Page {
marginRight = 0, marginLeft = 0, marginRight = 0, marginLeft = 0,
}, },
action = UI.SlideOut { action = UI.SlideOut {
backgroundColor = colors.cyan,
titleBar = UI.TitleBar { titleBar = UI.TitleBar {
event = 'hide-action', event = 'hide-action',
}, },

View File

@ -48,6 +48,7 @@ local page = UI.Page {
y = 2, y = 2,
filterTab = UI.Tab { filterTab = UI.Tab {
tabTitle = 'Filter', tabTitle = 'Filter',
noFill = true,
filterGridText = UI.Text { filterGridText = UI.Text {
x = 2, y = 2, x = 2, y = 2,
value = 'ID filter', value = 'ID filter',
@ -130,7 +131,6 @@ local page = UI.Page {
title = 'Packet Information', title = 'Packet Information',
event = 'packet_close', event = 'packet_close',
}, },
backgroundColor = colors.cyan,
accelerators = { accelerators = {
['backspace'] = 'packet_close', ['backspace'] = 'packet_close',
['left'] = 'prev_packet', ['left'] = 'prev_packet',

View File

@ -11,7 +11,7 @@ local systemPage = UI.Page {
settings = UI.Tab { settings = UI.Tab {
tabTitle = 'Category', tabTitle = 'Category',
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 2, x = 2, y = 2, ex = -2, ey = -2,
columns = { columns = {
{ heading = 'Name', key = 'name' }, { heading = 'Name', key = 'name' },
{ heading = 'Description', key = 'description' }, { heading = 'Description', key = 'description' },
@ -35,7 +35,7 @@ function systemPage.tabs.settings:eventHandler(event)
tab:disable() tab:disable()
end end
systemPage.tabs:selectTab(tab) systemPage.tabs:selectTab(tab)
self.parent:draw() --self.parent:draw()
return true return true
end end
end end

View File

@ -3,6 +3,7 @@ local UI = require('opus.ui')
local kernel = _G.kernel local kernel = _G.kernel
local multishell = _ENV.multishell local multishell = _ENV.multishell
local tasks = multishell and multishell.getTabs and multishell.getTabs() or kernel.routines
UI:configure('Tasks', ...) UI:configure('Tasks', ...)
@ -21,7 +22,7 @@ local page = UI.Page {
{ heading = 'Status', key = 'status' }, { heading = 'Status', key = 'status' },
{ heading = 'Time', key = 'timestamp' }, { heading = 'Time', key = 'timestamp' },
}, },
values = kernel.routines, values = tasks,
sortColumn = 'uid', sortColumn = 'uid',
autospace = true, autospace = true,
getDisplayValues = function (_, row) getDisplayValues = function (_, row)

138
sys/apps/fileui.lua Normal file
View File

@ -0,0 +1,138 @@
local UI = require('opus.ui')
local Util = require('opus.util')
local colors = _G.colors
local fs = _G.fs
local shell = _ENV.shell
local selected
-- fileui [--path=path] [--exec=filename]
local page = UI.Page {
title = 'Select File',
-- x = 3, ex = -3, y = 2, ey = -2,
grid = UI.ScrollingGrid {
x = 2, y = 2, ex = -2, ey = -4,
path = '',
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,
},
path = UI.TextEntry {
x = 2,
y = -2,
ex = -11,
limit = 256,
accelerators = {
enter = 'path_enter',
}
},
cancel = UI.Button {
text = 'Cancel',
x = -9,
y = -2,
event = 'cancel',
},
draw = function(self)
self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray)
self:drawChildren()
end,
}
function page:enable(path)
self:setPath(path or shell.dir())
UI.Page.enable(self)
end
function page:setPath(path)
self.grid.dir = path
while not fs.isDir(self.grid.dir) do
self.grid.dir = fs.getDir(self.grid.dir)
end
self.path.value = self.grid.dir
end
function page:eventHandler(event)
if event.type == 'grid_select' then
self.grid.dir = fs.combine(self.grid.dir, event.selected.name)
self.path.value = self.grid.dir
if event.selected.isDir then
self.grid:draw()
self.path:draw()
else
selected = self.path.value
UI:quit()
end
elseif event.type == 'path_enter' then
if fs.isDir(self.path.value) then
self:setPath(self.path.value)
self.grid:draw()
self.path:draw()
else
selected = self.path.value
UI:quit()
end
elseif event.type == 'cancel' then
UI:quit()
else
return UI.Page.eventHandler(self, event)
end
return true
end
local _, args = Util.parse(...)
UI:setPage(page, args.path)
UI:start()
UI.term:setCursorBlink(false)
if args.exec and selected then
shell.openForegroundTab(string.format('%s %s', args.exec, selected))
return
end
--print('selected: ' .. tostring(selected))
return selected

View File

@ -1,5 +1,3 @@
_G.requireInjector(_ENV)
local Event = require('opus.event') local Event = require('opus.event')
local Util = require('opus.util') local Util = require('opus.util')

View File

@ -66,11 +66,11 @@ local function run(env, ...)
_ENV.multishell.setTitle(_ENV.multishell.getCurrent(), fs.getName(path):match('([^%.]+)')) _ENV.multishell.setTitle(_ENV.multishell.getCurrent(), fs.getName(path):match('([^%.]+)'))
end end
if isUrl then tProgramStack[#tProgramStack + 1] = {
tProgramStack[#tProgramStack + 1] = path -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$") path = path, -- path:match("^https?://([^/:]+:?[0-9]*/?.*)$")
else env = env,
tProgramStack[#tProgramStack + 1] = path args = args,
end }
env[ "arg" ] = { [0] = path, table.unpack(args) } env[ "arg" ] = { [0] = path, table.unpack(args) }
local r = { fn(table.unpack(args)) } local r = { fn(table.unpack(args)) }
@ -278,6 +278,10 @@ function shell.getCompletionInfo()
end end
function shell.getRunningProgram() function shell.getRunningProgram()
return tProgramStack[#tProgramStack] and tProgramStack[#tProgramStack].path
end
function shell.getRunningInfo()
return tProgramStack[#tProgramStack] return tProgramStack[#tProgramStack]
end end

View File

@ -20,7 +20,7 @@ local aliasTab = UI.Tab {
}, },
}, },
grid = UI.Grid { grid = UI.Grid {
y = 5, x = 2, y = 5, ex = -2, ey = -2,
sortColumn = 'alias', sortColumn = 'alias',
columns = { columns = {
{ heading = 'Alias', key = 'alias' }, { heading = 'Alias', key = 'alias' },

View File

@ -22,20 +22,19 @@ local tab = UI.Tab {
disableHeader = true, disableHeader = true,
columns = { columns = {
{ key = 'file' }, { key = 'file' },
} },
getRowTextColor = function(self, row)
if row == self.values[1] then
return colors.yellow
end
return UI.Grid.getRowTextColor(self, row)
end,
}, },
statusBar = UI.StatusBar { statusBar = UI.StatusBar {
values = 'Double-click to set as preferred' values = 'Double-click to set as preferred'
}, },
} }
function tab.choices:getRowTextColor(row)
if row == self.values[1] then
return colors.yellow
end
return UI.Grid.getRowTextColor(self, row)
end
function tab:updateChoices() function tab:updateChoices()
local app = self.apps:getSelected().name local app = self.apps:getSelected().name
local choices = { } local choices = { }

View File

@ -4,16 +4,17 @@ local UI = require('opus.ui')
local colors = _G.colors local colors = _G.colors
-- -t80x30
if _G.http.websocket then 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', tabTitle = 'Cloud',
description = 'Cloud Catcher options', description = 'Cloud Catcher options',
[1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4,
},
key = UI.TextEntry { key = UI.TextEntry {
x = 3, ex = -3, y = 2, x = 3, ex = -3, y = 3,
limit = 32, limit = 32,
value = config.key, value = config.key,
shadowText = 'Cloud key', shadowText = 'Cloud key',
@ -22,13 +23,14 @@ if _G.http.websocket then
}, },
}, },
button = UI.Button { button = UI.Button {
x = 3, y = 4, x = -8, ex = -2, y = -2,
text = 'Update', text = 'Apply',
event = 'update_key', event = 'update_key',
}, },
labelText = UI.TextArea { labelText = UI.TextArea {
x = 3, ex = -3, y = 6, x = 2, ex = -2, y = 6, ey = -4,
textColor = colors.yellow, textColor = colors.yellow,
backgroundColor = colors.black,
marginLeft = 0, marginRight = 0, marginLeft = 0, marginRight = 0,
value = string.format( value = string.format(
[[Use a non-changing cloud key. Note that only a single computer can use this session at one time. [[Use a non-changing cloud key. Note that only a single computer can use this session at one time.

View File

@ -20,8 +20,8 @@ local tab = UI.Tab {
description = 'Visualise HDD and disks usage', description = 'Visualise HDD and disks usage',
drives = UI.ScrollingGrid { drives = UI.ScrollingGrid {
x = 2, y = 1, x = 2, y = 2,
ex = '47%', ey = -7, ex = '47%', ey = -8,
columns = { columns = {
{ heading = 'Drive', key = 'name' }, { heading = 'Drive', key = 'name' },
{ heading = 'Side' ,key = 'side', textColor = colors.yellow } { heading = 'Side' ,key = 'side', textColor = colors.yellow }
@ -30,7 +30,7 @@ local tab = UI.Tab {
}, },
infos = UI.Grid { infos = UI.Grid {
x = '52%', y = 2, x = '52%', y = 2,
ex = -2, ey = -4, ex = -2, ey = -8,
disableHeader = true, disableHeader = true,
unfocusedBackgroundSelectedColor = colors.black, unfocusedBackgroundSelectedColor = colors.black,
inactive = true, inactive = true,
@ -40,18 +40,23 @@ local tab = UI.Tab {
{ key = 'value', align = 'right', textColor = colors.yellow }, { key = 'value', align = 'right', textColor = colors.yellow },
} }
}, },
[1] = UI.Window {
x = 2, y = -6, ex = -2, ey = -2,
backgroundColor = colors.black,
},
progress = UI.ProgressBar { progress = UI.ProgressBar {
x = 11, y = -2, x = 11, y = -3,
ex = -2, ex = -3,
}, },
percentage = UI.Text { percentage = UI.Text {
x = 11, y = -3, y = -4, width = 5,
ex = '47%', x = 12,
align = 'center', --align = 'center',
backgroundColor = colors.black,
}, },
icon = UI.NftImage { icon = UI.NftImage {
x = 2, y = -5, x = 2, y = -6, ey = -2,
backgroundColor = colors.black,
image = NFT.parse(NftImages.blank) image = NFT.parse(NftImages.blank)
}, },
} }

View File

@ -8,7 +8,7 @@ local tab = UI.Tab {
tabTitle = 'Kiosk', tabTitle = 'Kiosk',
description = 'Kiosk options', description = 'Kiosk options',
form = UI.Form { form = UI.Form {
x = 2, ex = -2, x = 2, y = 2, ex = -2, ey = 5,
manualControls = true, manualControls = true,
monitor = UI.Chooser { monitor = UI.Chooser {
formLabel = 'Monitor', formKey = 'monitor', formLabel = 'Monitor', formKey = 'monitor',
@ -22,11 +22,12 @@ local tab = UI.Tab {
}, },
help = 'Adjust text scaling', help = 'Adjust text scaling',
}, },
labelText = UI.TextArea { },
x = 2, ex = -2, y = 5, labelText = UI.TextArea {
textColor = colors.yellow, x = 2, ex = -2, y = 7, ey = -2,
value = 'Settings apply to kiosk mode selected during startup' textColor = colors.yellow,
}, backgroundColor = colors.black,
value = 'Settings apply to kiosk mode selected during startup'
}, },
} }

View File

@ -8,19 +8,22 @@ local labelTab = UI.Tab {
tabTitle = 'Label', tabTitle = 'Label',
description = 'Set the computer label', description = 'Set the computer label',
labelText = UI.Text { labelText = UI.Text {
x = 3, y = 2, x = 3, y = 3,
value = 'Label' value = 'Label'
}, },
label = UI.TextEntry { label = UI.TextEntry {
x = 9, y = 2, ex = -4, x = 9, y = 3, ex = -4,
limit = 32, limit = 32,
value = os.getComputerLabel(), value = os.getComputerLabel(),
accelerators = { accelerators = {
enter = 'update_label', enter = 'update_label',
}, },
}, },
[1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4,
},
grid = UI.ScrollingGrid { grid = UI.ScrollingGrid {
y = 3, x = 2, y = 6, ex = -2, ey = -2,
values = { values = {
{ name = '', value = '' }, { name = '', value = '' },
{ name = 'CC version', value = Util.getVersion() }, { name = 'CC version', value = Util.getVersion() },
@ -30,10 +33,11 @@ local labelTab = UI.Tab {
{ name = 'Computer ID', value = tostring(os.getComputerID()) }, { name = 'Computer ID', value = tostring(os.getComputerID()) },
{ name = 'Day', value = tostring(os.day()) }, { name = 'Day', value = tostring(os.day()) },
}, },
disableHeader = true,
inactive = true, inactive = true,
columns = { columns = {
{ key = 'name', width = 12 }, { key = 'name', width = 12 },
{ key = 'value' }, { key = 'value', textColor = colors.yellow },
}, },
}, },
} }

View File

@ -9,12 +9,15 @@ local config = Config.load('multishell')
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Launcher', tabTitle = 'Launcher',
description = 'Set the application launcher', description = 'Set the application launcher',
[1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 5,
},
launcherLabel = UI.Text { launcherLabel = UI.Text {
x = 3, y = 2, x = 3, y = 3,
value = 'Launcher', value = 'Launcher',
}, },
launcher = UI.Chooser { launcher = UI.Chooser {
x = 13, y = 2, width = 12, x = 13, y = 3, width = 12,
choices = { choices = {
{ name = 'Overview', value = 'sys/apps/Overview.lua' }, { name = 'Overview', value = 'sys/apps/Overview.lua' },
{ name = 'Shell', value = 'sys/apps/ShellLauncher.lua' }, { name = 'Shell', value = 'sys/apps/ShellLauncher.lua' },
@ -22,17 +25,18 @@ local tab = UI.Tab {
}, },
}, },
custom = UI.TextEntry { custom = UI.TextEntry {
x = 13, ex = -3, y = 3, x = 13, ex = -3, y = 4,
limit = 128, limit = 128,
shadowText = 'File name', shadowText = 'File name',
}, },
button = UI.Button { button = UI.Button {
x = 3, y = 5, x = -8, ex = -2, y = -2,
text = 'Update', text = 'Apply',
event = 'update', event = 'update',
}, },
labelText = UI.TextArea { labelText = UI.TextArea {
x = 3, ex = -3, y = 7, x = 2, ex = -2, y = 7, ey = -4,
backgroundColor = colors.black,
textColor = colors.yellow, textColor = colors.yellow,
value = 'Choose an application launcher', value = 'Choose an application launcher',
}, },

View File

@ -2,24 +2,29 @@ local Ansi = require('opus.ansi')
local Config = require('opus.config') local Config = require('opus.config')
local UI = require('opus.ui') local UI = require('opus.ui')
local colors = _G.colors
local device = _G.device local device = _G.device
local tab = UI.Tab { local tab = UI.Tab {
tabTitle = 'Network', tabTitle = 'Network',
description = 'Networking options', description = 'Networking options',
info = UI.TextArea { info = UI.TextArea {
x = 3, y = 4, x = 2, y = 6, ex = -2, ey = -2,
backgroundColor = colors.black,
value = string.format( value = string.format(
[[%sSet the primary modem used for wireless communications.%s [[%sSet the primary modem used for wireless communications.%s
Reboot to take effect.]], Ansi.yellow, Ansi.reset) Reboot to take effect.]], Ansi.yellow, Ansi.reset)
}, },
[1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4,
},
label = UI.Text { label = UI.Text {
x = 3, y = 2, x = 3, y = 3,
value = 'Modem', value = 'Modem',
}, },
modem = UI.Chooser { modem = UI.Chooser {
x = 10, ex = -3, y = 2, x = 10, ex = -3, y = 3,
nochoice = 'auto', nochoice = 'auto',
}, },
} }

View File

@ -7,6 +7,9 @@ local colors = _G.colors
local passwordTab = UI.Tab { local passwordTab = UI.Tab {
tabTitle = 'Password', tabTitle = 'Password',
description = 'Wireless network password', description = 'Wireless network password',
[1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4,
},
newPass = UI.TextEntry { newPass = UI.TextEntry {
x = 3, ex = -3, y = 3, x = 3, ex = -3, y = 3,
limit = 32, limit = 32,
@ -17,12 +20,13 @@ local passwordTab = UI.Tab {
}, },
}, },
button = UI.Button { button = UI.Button {
x = 3, y = 5, x = -8, ex = -2, y = -2,
text = 'Update', text = 'Apply',
event = 'update_password', event = 'update_password',
}, },
info = UI.TextArea { info = UI.TextArea {
x = 3, ex = -3, y = 7, x = 2, ex = -2, y = 6, ey = -4,
backgroundColor = colors.black,
textColor = colors.yellow, textColor = colors.yellow,
inactive = true, inactive = true,
value = 'Add a password to enable other computers to connect to this one.', value = 'Add a password to enable other computers to connect to this one.',

View File

@ -6,8 +6,11 @@ local tab = UI.Tab {
tabTitle = 'Path', tabTitle = 'Path',
description = 'Set the shell path', description = 'Set the shell path',
tabClose = true, tabClose = true,
[1] = UI.Window {
x = 2, y = 2, ex = -2, ey = 4,
},
entry = UI.TextEntry { entry = UI.TextEntry {
x = 2, y = 2, ex = -2, x = 3, y = 3, ex = -3,
limit = 256, limit = 256,
shadowText = 'enter new path', shadowText = 'enter new path',
accelerators = { accelerators = {
@ -16,7 +19,7 @@ local tab = UI.Tab {
help = 'add a new path', help = 'add a new path',
}, },
grid = UI.Grid { grid = UI.Grid {
y = 4, ey = -3, x = 2, y = 6, ex = -2, ey = -3,
disableHeader = true, disableHeader = true,
columns = { { key = 'value' } }, columns = { { key = 'value' } },
autospace = true, autospace = true,

View File

@ -7,7 +7,7 @@ if settings then
tabTitle = 'Settings', tabTitle = 'Settings',
description = 'Computercraft configurable settings', description = 'Computercraft configurable settings',
grid = UI.Grid { grid = UI.Grid {
y = 2, x = 2, y = 2, ex = -2, ey = -2,
autospace = true, autospace = true,
sortColumn = 'name', sortColumn = 'name',
columns = { columns = {

View File

@ -42,25 +42,23 @@ local tab = UI.Tab {
tabTitle = 'Shell', tabTitle = 'Shell',
description = 'Shell options', description = 'Shell options',
grid1 = UI.ScrollingGrid { grid1 = UI.ScrollingGrid {
y = 2, ey = -10, x = 3, ex = -16, y = 2, ey = -10, x = 2, ex = -17,
disableHeader = true, disableHeader = true,
columns = { { key = 'name' } }, columns = { { key = 'name' } },
values = allSettings, values = allSettings,
sortColumn = 'name', sortColumn = 'name',
}, },
grid2 = UI.ScrollingGrid { grid2 = UI.ScrollingGrid {
y = 2, ey = -10, x = -14, ex = -3, y = 2, ey = -10, x = -14, ex = -2,
disableHeader = true, disableHeader = true,
columns = { { key = 'name' } }, columns = { { key = 'name' } },
values = allColors, values = allColors,
sortColumn = 'name', sortColumn = 'name',
}, },
directoryLabel = UI.Text {
x = 2, y = -2,
value = 'Display directory',
},
directory = UI.Checkbox { directory = UI.Checkbox {
x = 20, y = -2, x = 2, y = -2,
labelBackgroundColor = colors.black,
label = 'Directory',
value = config.displayDirectory value = config.displayDirectory
}, },
reset = UI.Button { reset = UI.Button {
@ -74,7 +72,7 @@ local tab = UI.Tab {
event = 'update', event = 'update',
}, },
display = UI.Window { display = UI.Window {
x = 3, ex = -3, y = -8, height = 5, x = 2, ex = -2, y = -8, height = 5,
}, },
} }

View File

@ -21,4 +21,4 @@ deleteIfExists('sys/autorun/apps.lua')
deleteIfExists('sys/init/6.tl3.lua') deleteIfExists('sys/init/6.tl3.lua')
-- remove this file -- remove this file
deleteIfExists('sys/autorun/upgraded.lua') --deleteIfExists('sys/autorun/upgraded.lua')

View File

@ -1,5 +1,3 @@
_G.requireInjector(_ENV)
local Peripheral = require('opus.peripheral') local Peripheral = require('opus.peripheral')
_G.device = Peripheral.getList() _G.device = Peripheral.getList()

View File

@ -4,11 +4,8 @@ if fs.native then
return return
end end
_G.requireInjector(_ENV)
local Util = require('opus.util') local Util = require('opus.util')
-- TODO: support getDrive for virtual nodes
fs.native = Util.shallowCopy(fs) fs.native = Util.shallowCopy(fs)
local fstypes = { } local fstypes = { }
@ -23,7 +20,6 @@ for k,fn in pairs(fs) do
end end
function nativefs.list(node, dir) function nativefs.list(node, dir)
local files local files
if fs.native.isDir(dir) then if fs.native.isDir(dir) then
files = fs.native.list(dir) files = fs.native.list(dir)
@ -265,7 +261,6 @@ local function getfstype(fstype)
end end
function fs.mount(path, fstype, ...) function fs.mount(path, fstype, ...)
local vfs = getfstype(fstype) local vfs = getfstype(fstype)
if not vfs then if not vfs then
error('Invalid file system type') error('Invalid file system type')

View File

@ -1,5 +1,3 @@
_G.requireInjector(_ENV)
local Config = require('opus.config') local Config = require('opus.config')
local device = _G.device local device = _G.device

View File

@ -1,5 +1,3 @@
_G.requireInjector(_ENV)
local Config = require('opus.config') local Config = require('opus.config')
local trace = require('opus.trace') local trace = require('opus.trace')
local Util = require('opus.util') local Util = require('opus.util')
@ -334,16 +332,12 @@ kernel.hook('mouse_scroll', function(_, eventData)
end) end)
kernel.hook('kernel_ready', function() kernel.hook('kernel_ready', function()
local env = Util.shallowCopy(shell.getEnv())
_G.requireInjector(env)
overviewId = multishell.openTab({ overviewId = multishell.openTab({
path = config.launcher or 'sys/apps/Overview.lua', path = config.launcher or 'sys/apps/Overview.lua',
isOverview = true, isOverview = true,
noTerminate = true, noTerminate = true,
focused = true, focused = true,
title = '+', title = '+',
env = env,
}) })
multishell.openTab({ multishell.openTab({

View File

@ -1,5 +1,3 @@
_G.requireInjector(_ENV)
local Array = require('opus.array') local Array = require('opus.array')
local Terminal = require('opus.terminal') local Terminal = require('opus.terminal')
local Util = require('opus.util') local Util = require('opus.util')

View File

@ -14,7 +14,7 @@ function Array.removeByValue(t, e)
for k,v in pairs(t) do for k,v in pairs(t) do
if v == e then if v == e then
table.remove(t, k) table.remove(t, k)
break return e
end end
end end
end end

View File

@ -50,18 +50,20 @@ function input:toCode(ch, code)
table.insert(result, 'alt') table.insert(result, 'alt')
end end
if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or if ch then -- some weird things happen with control/command on mac
code == keys.leftShift or code == keys.rightShift then if keyboard.state[keys.leftShift] or keyboard.state[keys.rightShift] or
if code and modifiers[code] then code == keys.leftShift or code == keys.rightShift then
table.insert(result, 'shift') if code and modifiers[code] then
elseif #ch == 1 then table.insert(result, 'shift')
table.insert(result, ch:upper()) elseif #ch == 1 then
else table.insert(result, ch:upper())
table.insert(result, 'shift') else
table.insert(result, 'shift')
table.insert(result, ch)
end
elseif not code or not modifiers[code] then
table.insert(result, ch) table.insert(result, ch)
end end
elseif not code or not modifiers[code] then
table.insert(result, ch)
end end
return table.concat(result, '-') return table.concat(result, '-')
@ -118,6 +120,7 @@ function input:translate(event, code, p1, p2)
local buttons = { 'mouse_click', 'mouse_rightclick' } local buttons = { 'mouse_click', 'mouse_rightclick' }
self.mch = buttons[code] self.mch = buttons[code]
self.mfired = nil self.mfired = nil
self.anchor = { x = p1, y = p2 }
return { return {
code = input:toCode('mouse_down', 255), code = input:toCode('mouse_down', 255),
button = code, button = code,
@ -132,6 +135,8 @@ function input:translate(event, code, p1, p2)
button = code, button = code,
x = p1, x = p1,
y = p2, y = p2,
dx = p1 - self.anchor.x,
dy = p2 - self.anchor.y,
} }
elseif event == 'mouse_up' then elseif event == 'mouse_up' then

View File

@ -1,16 +1,19 @@
local Util = require('opus.util') local Util = require('opus.util')
local colors = _G.colors
local NFT = { } local NFT = { }
-- largely copied from http://www.computercraft.info/forums2/index.php?/topic/5029-145-npaintpro/ -- largely copied from http://www.computercraft.info/forums2/index.php?/topic/5029-145-npaintpro/
local tColourLookup = { } local hexToColor = { }
for n = 1, 16 do for n = 1, 16 do
tColourLookup[string.byte("0123456789abcdef", n, n)] = 2 ^ (n - 1) hexToColor[string.sub("0123456789abcdef", n, n)] = 2 ^ (n - 1)
end end
local colorToHex = Util.transpose(hexToColor)
local function getColourOf(hex) local function getColourOf(hex)
return tColourLookup[hex:byte()] return hexToColor[hex]
end end
function NFT.parse(imageText) function NFT.parse(imageText)
@ -62,8 +65,22 @@ function NFT.parse(imageText)
return image return image
end end
function NFT.load(path) function NFT.transparency(image)
for y = 1, image.height do
for _,key in pairs(Util.keys(image.fg[y])) do
if image.fg[y][key] == colors.magenta then
image.fg[y][key] = nil
end
end
for _,key in pairs(Util.keys(image.bg[y])) do
if image.bg[y][key] == colors.magenta then
image.bg[y][key] = nil
end
end
end
end
function NFT.load(path)
local imageText = Util.readFile(path) local imageText = Util.readFile(path)
if not imageText then if not imageText then
error('Unable to read image file') error('Unable to read image file')
@ -71,4 +88,35 @@ function NFT.load(path)
return NFT.parse(imageText) return NFT.parse(imageText)
end end
function NFT.save(image, filename)
local bgcode, txcode = '\30', '\31'
local output = { }
for y = 1, image.height do
local lastBG, lastFG
if image.text[y] then
for x = 1, #image.text[y] do
local bg = image.bg[y][x] or colors.magenta
if bg ~= lastBG then
lastBG = bg
table.insert(output, bgcode .. colorToHex[bg])
end
local fg = image.fg[y][x] or colors.magenta
if fg ~= lastFG then
lastFG = fg
table.insert(output, txcode .. colorToHex[fg])
end
table.insert(output, image.text[y][x])
end
end
if y < image.height then
table.insert(output, '\n')
end
end
Util.writeFile(filename, table.concat(output))
end
return NFT return NFT

View File

@ -38,7 +38,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
local blink = false local blink = false
local bg, fg = parent.getBackgroundColor(), parent.getTextColor() local bg, fg = parent.getBackgroundColor(), parent.getTextColor()
local canvas = Canvas({ win.canvas = Canvas({
x = sx, x = sx,
y = sy, y = sy,
width = w, width = w,
@ -47,50 +47,53 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
offy = 0, offy = 0,
}) })
win.canvas = canvas
local function update() local function update()
if isVisible then if isVisible then
canvas:render(parent) win.canvas:render(parent)
win.setCursorPos(cx, cy) win.setCursorPos(cx, cy)
end end
end end
local function scrollTo(y) local function scrollTo(y)
y = math.max(0, y) y = math.max(0, y)
y = math.min(#canvas.lines - canvas.height, y) y = math.min(#win.canvas.lines - win.canvas.height, y)
if y ~= canvas.offy then if y ~= win.canvas.offy then
canvas.offy = y win.canvas.offy = y
canvas:dirty() win.canvas:dirty()
update() update()
end end
end end
function win.write(str) function win.write(str)
str = tostring(str) or '' str = tostring(str) or ''
canvas:write(cx, cy + canvas.offy, str, bg, fg) win.canvas:write(cx, cy + win.canvas.offy, str, bg, fg)
win.setCursorPos(cx + #str, cy) win.setCursorPos(cx + #str, cy)
update() update()
end end
function win.blit(str, fg, bg) function win.blit(str, fg, bg)
canvas:blit(cx, cy + canvas.offy, str, bg, fg) win.canvas:blit(cx, cy + win.canvas.offy, str, bg, fg)
win.setCursorPos(cx + #str, cy) win.setCursorPos(cx + #str, cy)
update() update()
end end
function win.clear() function win.clear()
canvas.offy = 0 win.canvas.offy = 0
for i = #canvas.lines, canvas.height + 1, -1 do for i = #win.canvas.lines, win.canvas.height + 1, -1 do
canvas.lines[i] = nil win.canvas.lines[i] = nil
end end
canvas:clear(bg, fg) win.canvas:clear(bg, fg)
update() update()
end end
function win.getLine(n)
local line = win.canvas.lines[n]
return line.text, line.fg, line.bg
end
function win.clearLine() function win.clearLine()
canvas:clearLine(cy + canvas.offy, bg, fg) win.canvas:clearLine(cy + win.canvas.offy, bg, fg)
win.setCursorPos(cx, cy) win.setCursorPos(cx, cy)
update() update()
end end
@ -102,10 +105,14 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
function win.setCursorPos(x, y) function win.setCursorPos(x, y)
cx, cy = math.floor(x), math.floor(y) cx, cy = math.floor(x), math.floor(y)
if isVisible then if isVisible then
parent.setCursorPos(cx + canvas.x - 1, cy + canvas.y - 1) parent.setCursorPos(cx + win.canvas.x - 1, cy + win.canvas.y - 1)
end end
end end
function win.getCursorBlink()
return blink
end
function win.setCursorBlink(b) function win.setCursorBlink(b)
blink = b blink = b
if isVisible then if isVisible then
@ -114,7 +121,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
end end
function win.isColor() function win.isColor()
return canvas.isColor return win.canvas.isColor
end end
win.isColour = win.isColor win.isColour = win.isColor
@ -144,22 +151,22 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
win.setBackgroundColour = win.setBackgroundColor win.setBackgroundColour = win.setBackgroundColor
function win.getSize() function win.getSize()
return canvas.width, canvas.height return win.canvas.width, win.canvas.height
end end
function win.scroll(n) function win.scroll(n)
n = n or 1 n = n or 1
if n > 0 then if n > 0 then
local lines = #canvas.lines local lines = #win.canvas.lines
for i = 1, n do for i = 1, n do
canvas.lines[lines + i] = { } win.canvas.lines[lines + i] = { }
canvas:clearLine(lines + i, bg, fg) win.canvas:clearLine(lines + i, bg, fg)
end end
while #canvas.lines > maxScroll do while #win.canvas.lines > maxScroll do
table.remove(canvas.lines, 1) table.remove(win.canvas.lines, 1)
end end
scrollTo(#canvas.lines) scrollTo(#win.canvas.lines)
canvas:dirty() win.canvas:dirty()
update() update()
end end
end end
@ -178,7 +185,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
if visible ~= isVisible then if visible ~= isVisible then
isVisible = visible isVisible = visible
if isVisible then if isVisible then
canvas:dirty() win.canvas:dirty()
update() update()
end end
end end
@ -186,7 +193,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
function win.redraw() function win.redraw()
if isVisible then if isVisible then
canvas:dirty() win.canvas:dirty()
update() update()
end end
end end
@ -200,21 +207,21 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
end end
function win.getPosition() function win.getPosition()
return canvas.x, canvas.y return win.canvas.x, win.canvas.y
end end
function win.reposition(x, y, width, height) function win.reposition(x, y, width, height)
canvas.x, canvas.y = x, y win.canvas.x, win.canvas.y = x, y
canvas:resize(width or canvas.width, height or canvas.height) win.canvas:resize(width or win.canvas.width, height or win.canvas.height)
end end
--[[ Additional methods ]]-- --[[ Additional methods ]]--
function win.scrollDown() function win.scrollDown()
scrollTo(canvas.offy + 1) scrollTo(win.canvas.offy + 1)
end end
function win.scrollUp() function win.scrollUp()
scrollTo(canvas.offy - 1) scrollTo(win.canvas.offy - 1)
end end
function win.scrollTop() function win.scrollTop()
@ -222,7 +229,7 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
end end
function win.scrollBottom() function win.scrollBottom()
scrollTo(#canvas.lines) scrollTo(#win.canvas.lines)
end end
function win.setMaxScroll(ms) function win.setMaxScroll(ms)
@ -230,37 +237,35 @@ function Terminal.window(parent, sx, sy, w, h, isVisible)
end end
function win.getCanvas() function win.getCanvas()
return canvas return win.canvas
end end
function win.getParent() function win.getParent()
return parent return parent
end end
canvas:clear() win.canvas:clear()
return win return win
end end
-- get windows contents -- get windows contents
function Terminal.getContents(win, parent) function Terminal.getContents(win)
local oblit, oscp = parent.blit, parent.setCursorPos if not win.getLine then
local lines = { } error('window is required')
end
parent.blit = function(text, fg, bg) local lines = { }
lines[#lines + 1] = { local _, h = win.getSize()
for i = 1, h do
local text, fg, bg = win.getLine(i)
lines[i] = {
text = text, text = text,
fg = fg, fg = fg,
bg = bg, bg = bg,
} }
end end
parent.setCursorPos = function() end
win.setVisible(true)
win.redraw()
parent.blit = oblit
parent.setCursorPos = oscp
return lines return lines
end end

View File

@ -1,8 +1,10 @@
local Array = require('opus.array')
local class = require('opus.class') local class = require('opus.class')
local Event = require('opus.event') local Event = require('opus.event')
local Input = require('opus.input') local Input = require('opus.input')
local Transition = require('opus.ui.transition') local Transition = require('opus.ui.transition')
local Util = require('opus.util') local Util = require('opus.util')
local Canvas = require('opus.ui.canvas')
local _rep = string.rep local _rep = string.rep
local _sub = string.sub local _sub = string.sub
@ -44,8 +46,7 @@ function Manager:init()
local currentPage = self:getActivePage() local currentPage = self:getActivePage()
if ie and currentPage then if ie and currentPage then
local target = currentPage.focused or currentPage local target = currentPage.focused or currentPage
self:inputEvent(target, target:emit({ type = 'key', key = ie.code == 'char' and ie.ch or ie.code, element = target, ie = ie })
{ type = 'key', key = ie.code == 'char' and ie.ch or ie.code, element = target, ie = ie })
currentPage:sync() currentPage:sync()
end end
end end
@ -81,8 +82,7 @@ function Manager:init()
} }
-- revisit - should send out scroll_up and scroll_down events -- revisit - should send out scroll_up and scroll_down events
-- let the element convert them to up / down -- let the element convert them to up / down
self:inputEvent(event.element, event.element:emit({ type = 'key', key = directions[direction] })
{ type = 'key', key = directions[direction] })
currentPage:sync() currentPage:sync()
end end
end, end,
@ -92,7 +92,7 @@ function Manager:init()
if dev and dev.currentPage then if dev and dev.currentPage then
Input:translate('mouse_click', 1, x, y) Input:translate('mouse_click', 1, x, y)
local ie = Input:translate('mouse_up', 1, x, y) local ie = Input:translate('mouse_up', 1, x, y)
self:click(dev.currentPage, ie.code, 1, x, y) self:click(dev.currentPage, ie)
end end
end, end,
@ -107,7 +107,7 @@ function Manager:init()
currentPage:setFocus(event.element) currentPage:setFocus(event.element)
currentPage:sync() currentPage:sync()
end end
self:click(currentPage, ie.code, button, x, y) self:click(currentPage, ie)
end end
end end
end, end,
@ -125,7 +125,7 @@ function Manager:init()
elseif ie and currentPage then elseif ie and currentPage then
if not currentPage.parent.device.side then if not currentPage.parent.device.side then
self:click(currentPage, ie.code, button, x, y) self:click(currentPage, ie)
end end
end end
end, end,
@ -135,7 +135,7 @@ function Manager:init()
local currentPage = self:getActivePage() local currentPage = self:getActivePage()
if ie and currentPage then if ie and currentPage then
self:click(currentPage, ie.code, button, x, y) self:click(currentPage, ie)
end end
end, end,
@ -247,30 +247,44 @@ function Manager:emitEvent(event)
end end
end end
function Manager:inputEvent(parent, event) -- deprecate ? function Manager:click(target, ie)
return parent and parent:emit(event) local clickEvent
end
function Manager:click(target, code, button, x, y) if ie.code == 'mouse_drag' then
local clickEvent = target:pointToChild(x, y) clickEvent = {
element = self.lastClicked,
x = ie.x,
y = ie.y, -- this is not correct (should be relative to element)
dx = ie.dx,
dy = ie.dy,
}
else
clickEvent = target:pointToChild(ie.x, ie.y)
end
if code == 'mouse_doubleclick' then -- hack for dropdown menus
if self.doubleClickElement ~= clickEvent.element then if ie.code == 'mouse_click' and not clickEvent.element.focus then
self:emitEvent({ type = 'mouse_out' })
end
if ie.code == 'mouse_doubleclick' then
if self.lastClicked ~= clickEvent.element then
return return
end end
else else
self.doubleClickElement = clickEvent.element self.lastClicked = clickEvent.element
end end
clickEvent.button = button clickEvent.button = ie.button
clickEvent.type = code clickEvent.type = ie.code
clickEvent.key = code clickEvent.key = ie.code
clickEvent.ie = { code = code, x = clickEvent.x, y = clickEvent.y } clickEvent.ie = { code = ie.code, x = clickEvent.x, y = clickEvent.y }
clickEvent.raw = ie
if clickEvent.element.focus then if clickEvent.element.focus then
target:setFocus(clickEvent.element) target:setFocus(clickEvent.element)
end end
self:inputEvent(clickEvent.element, clickEvent) clickEvent.element:emit(clickEvent)
target:sync() target:sync()
end end
@ -310,7 +324,6 @@ end
function Manager:setActivePage(page) function Manager:setActivePage(page)
page.parent.currentPage = page page.parent.currentPage = page
page.parent.canvas = page.canvas
end end
function Manager:setPage(pageOrName, ...) function Manager:setPage(pageOrName, ...)
@ -388,6 +401,12 @@ function Manager:pullEvents(...)
end end
end end
Manager.colors = {
primary = colors.cyan,
secondary = colors.blue,
tertiary = colors.blue,
}
Manager.exitPullEvents = Event.exitPullEvents Manager.exitPullEvents = Event.exitPullEvents
Manager.quit = Event.exitPullEvents Manager.quit = Event.exitPullEvents
Manager.start = Manager.pullEvents Manager.start = Manager.pullEvents
@ -395,7 +414,7 @@ Manager.start = Manager.pullEvents
local UI = Manager() local UI = Manager()
--[[-- Basic drawable area --]]-- --[[-- Basic drawable area --]]--
UI.Window = class() UI.Window = class(Canvas)
UI.Window.uid = 1 UI.Window.uid = 1
UI.Window.docs = { } UI.Window.docs = { }
UI.Window.defaults = { UI.Window.defaults = {
@ -413,7 +432,9 @@ function UI.Window:init(args)
local defaults = args local defaults = args
local m = getmetatable(self) -- get the class for this instance local m = getmetatable(self) -- get the class for this instance
repeat repeat
defaults = UI:getDefaults(m, defaults) if m.disable then
defaults = UI:getDefaults(m, defaults)
end
m = m._base m = m._base
until not m until not m
UI:mergeProperties(self, defaults) UI:mergeProperties(self, defaults)
@ -531,6 +552,8 @@ function UI.Window:layout()
if not self.height then if not self.height then
self.height = self.parent.height - self.y + 1 self.height = self.parent.height - self.y + 1
end end
self:reposition(self.x, self.y, self.width, self.height)
end end
-- Called when the window's parent has be assigned -- Called when the window's parent has be assigned
@ -539,23 +562,6 @@ function UI.Window:setParent()
self.ox, self.oy = self.x, self.y self.ox, self.oy = self.x, self.y
self:layout() self:layout()
-- Experimental
-- Inherit properties from the parent container
-- does this need to be in reverse order ?
local m = getmetatable(self) -- get the class for this instance
repeat
if m.inherits then
for k, v in pairs(m.inherits) do
local value = self.parent:getProperty(v)
if value then
self[k] = value
end
end
end
m = m._base
until not m
self:initChildren() self:initChildren()
end end
@ -566,12 +572,33 @@ function UI.Window:resize()
self:layout() self:layout()
if self.children then if self.children then
for _,child in ipairs(self.children) do for child in self:eachChild() do
child:resize() child:resize()
end end
end end
end end
function UI.Window:reposition(x, y, w, h)
if not self.lines then
Canvas.init(self, {
x = x,
y = y,
width = w,
height = h,
isColor = self.parent.isColor,
})
else
self:move(x, y)
Canvas.resize(self, w, h)
end
end
function UI.Window:raise()
Array.removeByValue(self.parent.children, self)
table.insert(self.parent.children, self)
self:dirty(true)
end
UI.Window.docs.add = [[add(TABLE) UI.Window.docs.add = [[add(TABLE)
Add element(s) to a window. Example: Add element(s) to a window. Example:
page:add({ page:add({
@ -584,6 +611,20 @@ function UI.Window:add(children)
self:initChildren() self:initChildren()
end end
function UI.Window:eachChild()
local c = self.children and Util.shallowCopy(self.children)
local i = 0
return function()
i = i + 1
return c and c[i]
end
end
function UI.Window:remove()
Array.removeByValue(self.parent.children, self)
self.parent:dirty(true)
end
function UI.Window:getCursorPos() function UI.Window:getCursorPos()
return self.cursorX, self.cursorY return self.cursorX, self.cursorY
end end
@ -601,12 +642,14 @@ end
UI.Window.docs.draw = [[draw(VOID) UI.Window.docs.draw = [[draw(VOID)
Redraws the window in the internal buffer.]] Redraws the window in the internal buffer.]]
function UI.Window:draw() function UI.Window:draw()
self:clear(self.backgroundColor) self:clear()
if self.children then self:drawChildren()
for _,child in pairs(self.children) do end
if child.enabled then
child:draw() function UI.Window:drawChildren()
end for child in self:eachChild() do
if child.enabled then
child:draw()
end end
end end
end end
@ -634,19 +677,30 @@ end
function UI.Window:enable(...) function UI.Window:enable(...)
self.enabled = true self.enabled = true
if self.children then if self.transitionHint then
for _,child in pairs(self.children) do self:addTransition(self.transitionHint)
child:enable(...) end
end
if self.modal then
self:raise()
self:capture(self)
end
for child in self:eachChild() do
child:enable(...)
end end
end end
function UI.Window:disable() function UI.Window:disable()
self.enabled = false self.enabled = false
if self.children then self.parent:dirty(true)
for _,child in pairs(self.children) do
child:disable() if self.modal then
end self:release(self)
end
for child in self:eachChild() do
child:disable()
end end
end end
@ -656,13 +710,9 @@ function UI.Window:setTextScale(textScale)
end end
UI.Window.docs.clear = [[clear(opt COLOR bg, opt COLOR fg) UI.Window.docs.clear = [[clear(opt COLOR bg, opt COLOR fg)
Clears the window using the either the passed values or the defaults for that window.]] Clears the window using either the passed values or the defaults for that window.]]
function UI.Window:clear(bg, fg) function UI.Window:clear(bg, fg)
if self.canvas then Canvas.clear(self, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
self.canvas:clear(bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
else
self:clearArea(1 + self.offx, 1 + self.offy, self.width, self.height, bg)
end
end end
function UI.Window:clearLine(y, bg) function UI.Window:clearLine(y, bg)
@ -670,28 +720,20 @@ function UI.Window:clearLine(y, bg)
end end
function UI.Window:clearArea(x, y, width, height, bg) function UI.Window:clearArea(x, y, width, height, bg)
self:fillArea(x, y, width, height, ' ', bg)
end
function UI.Window:fillArea(x, y, width, height, fillChar, bg, fg)
if width > 0 then if width > 0 then
local filler = _rep(' ', width) local filler = _rep(fillChar, width)
for i = 0, height - 1 do for i = 0, height - 1 do
self:write(x, y + i, filler, bg) self:write(x, y + i, filler, bg, fg)
end end
end end
end end
function UI.Window:write(x, y, text, bg, fg) function UI.Window:write(x, y, text, bg, fg)
bg = bg or self.backgroundColor Canvas.write(self, x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
fg = fg or self.textColor
if self.canvas then
self.canvas:write(x, y, text, bg or self:getProperty('backgroundColor'), fg or self:getProperty('textColor'))
else
x = x - self.offx
y = y - self.offy
if y <= self.height and y > 0 then
self.parent:write(
self.x + x - 1, self.y + y - 1, tostring(text), bg, fg)
end
end
end end
function UI.Window:centeredWrite(y, text, bg, fg) function UI.Window:centeredWrite(y, text, bg, fg)
@ -826,7 +868,8 @@ function UI.Window:pointToChild(x, y)
x = x + self.offx - self.x + 1 x = x + self.offx - self.x + 1
y = y + self.offy - self.y + 1 y = y + self.offy - self.y + 1
if self.children then if self.children then
for _,child in pairs(self.children) do for i = #self.children, 1, -1 do
local child = self.children[i]
if child.enabled and not child.inactive and if child.enabled and not child.inactive and
x >= child.x and x < child.x + child.width and x >= child.x and x < child.x + child.width and
y >= child.y and y < child.y + child.height then y >= child.y and y < child.y + child.height then
@ -882,36 +925,28 @@ function UI.Window:focusFirst()
end end
end end
function UI.Window:refocus()
local el = self
while el do
local focusables = el:getFocusables()
if focusables[1] then
self:setFocus(focusables[1])
break
end
el = el.parent
end
end
function UI.Window:scrollIntoView() function UI.Window:scrollIntoView()
local parent = self.parent local parent = self.parent
local offx, offy = parent.offx, parent.offy
if self.x <= parent.offx then if self.x <= parent.offx then
parent.offx = math.max(0, self.x - 1) parent.offx = math.max(0, self.x - 1)
parent:draw() if offx ~= parent.offx then
parent:draw()
end
elseif self.x + self.width > parent.width + parent.offx then elseif self.x + self.width > parent.width + parent.offx then
parent.offx = self.x + self.width - parent.width - 1 parent.offx = self.x + self.width - parent.width - 1
parent:draw() if offx ~= parent.offx then
parent:draw()
end
end end
-- TODO: fix -- TODO: fix
local function setOffset(y) local function setOffset(y)
parent.offy = y parent.offy = y
if parent.canvas then if offy ~= parent.offy then
parent.canvas.offy = parent.offy parent:draw()
end end
parent:draw()
end end
if self.y <= parent.offy then if self.y <= parent.offy then
@ -921,43 +956,16 @@ function UI.Window:scrollIntoView()
end end
end end
function UI.Window:getCanvas()
local el = self
repeat
if el.canvas then
return el.canvas
end
el = el.parent
until not el
end
function UI.Window:addLayer(bg, fg)
local canvas = self:getCanvas()
local x, y = self.x, self.y
local parent = self.parent
while parent and not parent.canvas do
x = x + parent.x - 1
y = y + parent.y - 1
parent = parent.parent
end
canvas = canvas:addLayer({
x = x, y = y, height = self.height, width = self.width
}, bg, fg)
canvas:clear(bg or self.backgroundColor, fg or self.textColor)
return canvas
end
function UI.Window:addTransition(effect, args) function UI.Window:addTransition(effect, args)
if self.parent then if self.parent then
args = args or { } args = args or { }
if not args.x then -- not good if not args.x then -- not good
args.x, args.y = self.x, self.y -- getPosition(self) args.x, args.y = self.x, self.y
args.width = self.width args.width = self.width
args.height = self.height args.height = self.height
args.canvas = self
end end
args.canvas = args.canvas or self.canvas
self.parent:addTransition(effect, args) self.parent:addTransition(effect, args)
end end
end end
@ -991,7 +999,16 @@ function UI.Window:getProperty(property)
end end
function UI.Window:find(uid) function UI.Window:find(uid)
return self.children and Util.find(self.children, 'uid', uid) local el = self.children and Util.find(self.children, 'uid', uid)
if not el then
for child in self:eachChild() do
el = child:find(uid)
if el then
break
end
end
end
return el
end end
function UI.Window:eventHandler() function UI.Window:eventHandler()
@ -1010,18 +1027,14 @@ UI.Device.defaults = {
function UI.Device:postInit() function UI.Device:postInit()
self.device = self.device or term.current() self.device = self.device or term.current()
--if self.deviceType then
-- self.device = device[self.deviceType]
--end
if not self.device.setTextScale then if not self.device.setTextScale then
self.device.setTextScale = function() end self.device.setTextScale = function() end
end end
self.device.setTextScale(self.textScale) self.device.setTextScale(self.textScale)
self.width, self.height = self.device.getSize() self.width, self.height = self.device.getSize()
self.isColor = self.device.isColor() self.isColor = self.device.isColor()
Canvas.init(self, { isColor = self.isColor })
UI.devices[self.device.side or 'terminal'] = self UI.devices[self.device.side or 'terminal'] = self
end end
@ -1031,8 +1044,8 @@ function UI.Device:resize()
self.width, self.height = self.device.getSize() self.width, self.height = self.device.getSize()
self.lines = { } self.lines = { }
-- TODO: resize all pages added to this device -- TODO: resize all pages added to this device
self.canvas:resize(self.width, self.height) Canvas.resize(self, self.width, self.height)
self.canvas:clear(self.backgroundColor, self.textColor) Canvas.clear(self, self.backgroundColor, self.textColor)
end end
function UI.Device:setCursorPos(x, y) function UI.Device:setCursorPos(x, y)
@ -1069,7 +1082,7 @@ function UI.Device:addTransition(effect, args)
args = args or { } args = args or { }
args.ex = args.x + args.width - 1 args.ex = args.x + args.width - 1
args.ey = args.y + args.height - 1 args.ey = args.y + args.height - 1
args.canvas = args.canvas or self.canvas args.canvas = args.canvas or self
if type(effect) == 'string' then if type(effect) == 'string' then
effect = Transition[effect] effect = Transition[effect]
@ -1078,10 +1091,13 @@ function UI.Device:addTransition(effect, args)
end end
end end
table.insert(self.transitions, { update = effect(args), args = args }) table.insert(self.transitions, { effect = effect, args = args })
end end
function UI.Device:runTransitions(transitions, canvas) function UI.Device:runTransitions(transitions)
for _,k in pairs(transitions) do
k.update = k.effect(k.args)
end
while true do while true do
for _,k in ipairs(Util.keys(transitions)) do for _,k in ipairs(Util.keys(transitions)) do
local transition = transitions[k] local transition = transitions[k]
@ -1089,7 +1105,7 @@ function UI.Device:runTransitions(transitions, canvas)
transitions[k] = nil transitions[k] = nil
end end
end end
canvas:render(self.device) self.currentPage:render(self.device)
if Util.empty(transitions) then if Util.empty(transitions) then
break break
end end
@ -1105,9 +1121,9 @@ function UI.Device:sync()
self.device.setCursorBlink(false) self.device.setCursorBlink(false)
end end
self.canvas:render(self.device) self.currentPage:render(self.device)
if transitions then if transitions then
self:runTransitions(transitions, self.canvas) self:runTransitions(transitions)
end end
if self:getCursorBlink() then if self:getCursorBlink() then

View File

@ -9,7 +9,6 @@ local colors = _G.colors
local Canvas = class() local Canvas = class()
Canvas.__visualize = false
Canvas.colorPalette = { } Canvas.colorPalette = { }
Canvas.darkPalette = { } Canvas.darkPalette = { }
Canvas.grayscalePalette = { } Canvas.grayscalePalette = { }
@ -22,15 +21,17 @@ end
--[[ --[[
A canvas can have more lines than canvas.height in order to scroll A canvas can have more lines than canvas.height in order to scroll
]]
TODO: finish vertical scrolling
]]
function Canvas:init(args) function Canvas:init(args)
self.x = 1 self.bg = colors.black
self.y = 1 self.fg = colors.white
self.layers = { }
Util.merge(self, args) Util.merge(self, args)
self.x = self.x or 1
self.y = self.y or 1
self.ex = self.x + self.width - 1 self.ex = self.x + self.width - 1
self.ey = self.y + self.height - 1 self.ey = self.y + self.height - 1
@ -46,15 +47,30 @@ function Canvas:init(args)
for i = 1, self.height do for i = 1, self.height do
self.lines[i] = { } self.lines[i] = { }
end end
self:clear()
end end
function Canvas:move(x, y) function Canvas:move(x, y)
self.x, self.y = x, y self.x, self.y = x, y
self.ex = self.x + self.width - 1 self.ex = self.x + self.width - 1
self.ey = self.y + self.height - 1 self.ey = self.y + self.height - 1
if self.parent then
self.parent:dirty(true)
end
end end
function Canvas:resize(w, h) function Canvas:resize(w, h)
self:resizeBuffer(w, h)
self.ex = self.x + w - 1
self.ey = self.y + h - 1
self.width = w
self.height = h
end
-- resize the canvas buffer - not the canvas itself
function Canvas:resizeBuffer(w, h)
for i = #self.lines, h do for i = #self.lines, h do
self.lines[i] = { } self.lines[i] = { }
self:clearLine(i) self:clearLine(i)
@ -66,26 +82,24 @@ function Canvas:resize(w, h)
if w < self.width then if w < self.width then
for i = 1, h do for i = 1, h do
self.lines[i].text = _sub(self.lines[i].text, 1, w) local ln = self.lines[i]
self.lines[i].fg = _sub(self.lines[i].fg, 1, w) ln.text = _sub(ln.text, 1, w)
self.lines[i].bg = _sub(self.lines[i].bg, 1, w) ln.fg = _sub(ln.fg, 1, w)
ln.bg = _sub(ln.bg, 1, w)
end end
elseif w > self.width then elseif w > self.width then
local d = w - self.width local d = w - self.width
local text = _rep(' ', d) local text = _rep(' ', d)
local fg = _rep(self.palette[self.fg or colors.white], d) local fg = _rep(self.palette[self.fg], d)
local bg = _rep(self.palette[self.bg or colors.black], d) local bg = _rep(self.palette[self.bg], d)
for i = 1, h do for i = 1, h do
self.lines[i].text = self.lines[i].text .. text local ln = self.lines[i]
self.lines[i].fg = self.lines[i].fg .. fg ln.text = ln.text .. text
self.lines[i].bg = self.lines[i].bg .. bg ln.fg = ln.fg .. fg
ln.bg = ln.bg .. bg
ln.dirty = true
end end
end end
self.ex = self.x + w - 1
self.ey = self.y + h - 1
self.width = w
self.height = h
end end
function Canvas:copy() function Canvas:copy()
@ -113,22 +127,25 @@ function Canvas:addLayer(layer)
isColor = self.isColor, isColor = self.isColor,
}) })
canvas.parent = self canvas.parent = self
table.insert(self.layers, canvas) if not self.children then
self.children = { }
end
table.insert(self.children, canvas)
return canvas return canvas
end end
function Canvas:removeLayer() function Canvas:removeLayer()
for k, layer in pairs(self.parent.layers) do for k, layer in pairs(self.parent.children) do
if layer == self then if layer == self then
self:setVisible(false) self:setVisible(false)
table.remove(self.parent.layers, k) table.remove(self.parent.children, k)
break break
end end
end end
end end
function Canvas:setVisible(visible) function Canvas:setVisible(visible)
self.visible = visible self.visible = visible -- TODO: use self.active = visible
if not visible and self.parent then if not visible and self.parent then
self.parent:dirty() self.parent:dirty()
-- TODO: set parent's lines to dirty for each line in self -- TODO: set parent's lines to dirty for each line in self
@ -137,11 +154,10 @@ end
-- Push a layer to the top -- Push a layer to the top
function Canvas:raise() function Canvas:raise()
if self.parent then if self.parent and self.parent.children then
local layers = self.parent.layers or { } for k, v in pairs(self.parent.children) do
for k, v in pairs(layers) do
if v == self then if v == self then
table.insert(layers, table.remove(layers, k)) table.insert(self.parent.children, table.remove(self.parent.children, k))
break break
end end
end end
@ -224,15 +240,15 @@ function Canvas:writeLine(y, text, fg, bg)
end end
function Canvas:clearLine(y, bg, fg) function Canvas:clearLine(y, bg, fg)
fg = _rep(self.palette[fg or colors.white], self.width) fg = _rep(self.palette[fg or self.fg], self.width)
bg = _rep(self.palette[bg or colors.black], self.width) bg = _rep(self.palette[bg or self.bg], self.width)
self:writeLine(y, _rep(' ', self.width), fg, bg) self:writeLine(y, _rep(' ', self.width), fg, bg)
end end
function Canvas:clear(bg, fg) function Canvas:clear(bg, fg)
local text = _rep(' ', self.width) local text = _rep(' ', self.width)
fg = _rep(self.palette[fg or colors.white], self.width) fg = _rep(self.palette[fg or self.fg], self.width)
bg = _rep(self.palette[bg or colors.black], self.width) bg = _rep(self.palette[bg or self.bg], self.width)
for i = 1, #self.lines do for i = 1, #self.lines do
self:writeLine(i, text, fg, bg) self:writeLine(i, text, fg, bg)
end end
@ -246,13 +262,16 @@ function Canvas:isDirty()
end end
end end
function Canvas:dirty() function Canvas:dirty(includingChildren)
for i = 1, #self.lines do if self.lines then
self.lines[i].dirty = true for i = 1, #self.lines do
end self.lines[i].dirty = true
if self.layers then end
for _, canvas in pairs(self.layers) do
canvas:dirty() if includingChildren and self.children then
for _, child in pairs(self.children) do
child:dirty(true)
end
end end
end end
end end
@ -280,104 +299,89 @@ end
function Canvas:render(device) function Canvas:render(device)
local offset = { x = 0, y = 0 } local offset = { x = 0, y = 0 }
-- WIP
local function getRegion(canvas)
local region
if canvas.parent then
region = getRegion(canvas.parent)
else
region = Region.new(self.x, self.y, self.ex, self.ey)
end
offset.x = offset.x + canvas.x - 1
offset.y = offset.y + canvas.y - 1
-- clip against parent
return region
end
-- this code works - but is all kinds of wrong
-- adding a margin to UI.Page will cause issues
-- and could be clipping issues
offset = { x = self.x - 1, y = self.y - 1 }
local parent = self.parent local parent = self.parent
while parent do while parent do
offset.x = offset.x + parent.x - 1 offset.x = offset.x + parent.x - 1
offset.y = offset.y + parent.y - 1 offset.y = offset.y + parent.y - 1
parent = parent.parent parent = parent.parent
end end
if #self.layers > 0 then
self:__renderLayers(device, offset) -- TODO: need to clip if there is a parent
else --self.regions = Region.new(self.x + offset.x, self.y + offset.y, self.ex + offset.x, self.ey + offset.y)
self:__blitRect(device, nil, { --self:__renderLayers(device, offset)
x = self.x + offset.x,
y = self.y + offset.y self.regions = Region.new(self.x, self.y, self.ex, self.ey)
}) self:__renderLayers(device, { x = self.x - 1, y = self.y - 1 })
self:clean()
end
end end
-- regions are comprised of absolute values that coorespond to the output device. -- regions are comprised of absolute values that correspond to the output device.
-- canvases have coordinates relative to their parent. -- canvases have coordinates relative to their parent.
-- canvas layer's stacking order is determined by the position within the array. -- canvas layer's stacking order is determined by the position within the array.
-- layers in the beginning of the array are overlayed by layers further down in -- layers in the beginning of the array are overlayed by layers further down in
-- the array. -- the array.
function Canvas:__renderLayers(device, offset) function Canvas:__renderLayers(device, offset)
if #self.layers > 0 then if self.children then
self.regions = self.regions or Region.new(self.x + offset.x, self.y + offset.y, self.ex + offset.x, self.ey + offset.y) for i = #self.children, 1, -1 do
local canvas = self.children[i]
for i = 1, #self.layers do if canvas.visible or canvas.enabled then
local canvas = self.layers[i]
if canvas.visible then
-- punch out this area from the parent's canvas
self:__punch(canvas, offset)
-- get the area to render for this layer -- get the area to render for this layer
canvas.regions = Region.new( canvas.regions = Region.new(
canvas.x + offset.x, canvas.x + offset.x - (self.offx or 0),
canvas.y + offset.y, canvas.y + offset.y - (self.offy or 0),
canvas.ex + offset.x, canvas.ex + offset.x - (self.offx or 0),
canvas.ey + offset.y) canvas.ey + offset.y - (self.offy or 0))
-- contain within parent
canvas.regions:andRegion(self.regions)
-- punch out this area from the parent's canvas
self.regions:subRect(
canvas.x + offset.x - (self.offx or 0),
canvas.y + offset.y - (self.offy or 0),
canvas.ex + offset.x - (self.offx or 0),
canvas.ey + offset.y - (self.offy or 0))
-- punch out any layers that overlap this one
for j = i + 1, #self.layers do
if self.layers[j].visible then
canvas:__punch(self.layers[j], offset)
end
end
if #canvas.regions.region > 0 then if #canvas.regions.region > 0 then
canvas:__renderLayers(device, { canvas:__renderLayers(device, {
x = canvas.x + offset.x - 1, x = canvas.x + offset.x - 1 - (self.offx or 0),
y = canvas.y + offset.y - 1, y = canvas.y + offset.y - 1 - (self.offy or 0),
}) })
end end
canvas.regions = nil canvas.regions = nil
end end
end end
self:__blitClipped(device, offset)
self.regions = nil
elseif self.regions and #self.regions.region > 0 then
self:__blitClipped(device, offset)
self.regions = nil
else
self:__blitRect(device, nil, {
x = self.x + offset.x,
y = self.y + offset.y
})
self.regions = nil
end
self:clean()
end
function Canvas:__blitClipped(device, offset)
if self.parent then
-- contain the rendered region in the parent's region
local p = Region.new(1, 1,
self.parent.width + offset.x - self.x + 1,
self.parent.height + offset.y - self.y + 1)
self.regions:andRegion(p)
end end
for _,region in ipairs(self.regions.region) do for _,region in ipairs(self.regions.region) do
self:__blitRect(device, self:__blitRect(device,
{ x = region[1] - offset.x, { x = region[1] - offset.x,
y = region[2] - offset.y, y = region[2] - offset.y,
ex = region[3] - offset.x, ex = region[3] - offset.x,
ey = region[4] - offset.y}, ey = region[4] - offset.y },
{ x = region[1], y = region[2] }) { x = region[1], y = region[2] })
end end
end self.regions = nil
function Canvas:__punch(rect, offset) self:clean()
self.regions:subRect(
rect.x + offset.x,
rect.y + offset.y,
rect.ex + offset.x,
rect.ey + offset.y)
end end
-- performance can probably be improved by using one more buffer tied to the device -- performance can probably be improved by using one more buffer tied to the device
@ -386,7 +390,7 @@ function Canvas:__blitRect(device, src, tgt)
tgt = tgt or self tgt = tgt or self
-- for visualizing updates on the screen -- for visualizing updates on the screen
if Canvas.__visualize then if Canvas.__visualize or self.visualize then
local drew local drew
local t = _rep(' ', src.ex-src.x + 1) local t = _rep(' ', src.ex-src.x + 1)
local bg = _rep(2, src.ex-src.x + 1) local bg = _rep(2, src.ex-src.x + 1)
@ -399,8 +403,8 @@ function Canvas:__blitRect(device, src, tgt)
end end
end end
if drew then if drew then
local t = os.clock() local c = os.clock()
repeat until os.clock()-t > .2 repeat until os.clock()-c > .03
end end
end end
for i = 0, src.ey - src.y do for i = 0, src.ey - src.y do
@ -418,4 +422,16 @@ function Canvas:__blitRect(device, src, tgt)
end end
end end
if not ({ ... })[1] then
local UI = require('opus.ui')
UI:setPage(UI.Page {
button = UI.Button {
x = 5, y = 5,
text = 'abc'
}
})
UI:start()
end
return Canvas return Canvas

View File

@ -1,32 +0,0 @@
local class = require('opus.class')
local UI = require('opus.ui')
UI.ActiveLayer = class(UI.Window)
UI.ActiveLayer.defaults = {
UIElement = 'ActiveLayer',
}
function UI.ActiveLayer:layout()
UI.Window.layout(self)
if not self.canvas then
self.canvas = self:addLayer()
else
self.canvas:resize(self.width, self.height)
end
end
function UI.ActiveLayer:enable(...)
self.canvas:raise()
self.canvas:setVisible(true)
UI.Window.enable(self, ...)
if self.parent.transitionHint then
self:addTransition(self.parent.transitionHint)
end
self:focusFirst()
end
function UI.ActiveLayer:disable()
if self.canvas then
self.canvas:setVisible(false)
end
UI.Window.disable(self)
end

View File

@ -35,11 +35,11 @@ function UI.Button:draw()
local bg = self.backgroundColor local bg = self.backgroundColor
local ind = ' ' local ind = ' '
if self.focused then if self.focused then
bg = self.backgroundFocusColor bg = self:getProperty('backgroundFocusColor')
fg = self.textFocusColor fg = self:getProperty('textFocusColor')
ind = self.focusIndicator ind = self.focusIndicator
elseif self.inactive then elseif self.inactive then
fg = self.textInactiveColor fg = self:getProperty('textInactiveColor')
end end
local text = ind .. self.text .. ' ' local text = ind .. self.text .. ' '
if self.centered then if self.centered then

View File

@ -21,9 +21,6 @@ UI.Checkbox.defaults = {
mouse_click = 'checkbox_toggle', mouse_click = 'checkbox_toggle',
} }
} }
UI.Checkbox.inherits = {
labelBackgroundColor = 'backgroundColor',
}
function UI.Checkbox:postInit() function UI.Checkbox:postInit()
self.width = self.label and #self.label + 4 or 3 self.width = self.label and #self.label + 4 or 3
end end

View File

@ -11,8 +11,8 @@ UI.Chooser.defaults = {
nochoice = 'Select', nochoice = 'Select',
backgroundFocusColor = colors.lightGray, backgroundFocusColor = colors.lightGray,
textInactiveColor = colors.gray, textInactiveColor = colors.gray,
leftIndicator = UI.extChars and '\17' or '<', leftIndicator = UI.extChars and '\171' or '<',
rightIndicator = UI.extChars and '\16' or '>', rightIndicator = UI.extChars and '\187' or '>',
height = 1, height = 1,
accelerators = { accelerators = {
space = 'choice_next', space = 'choice_next',

View File

@ -1,4 +1,3 @@
local Canvas = require('opus.ui.canvas')
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
@ -19,14 +18,14 @@ function UI.Dialog:postInit()
end end
function UI.Dialog:show(...) function UI.Dialog:show(...)
local canvas = self.parent:getCanvas() local canvas = self.parent
self.oldPalette = canvas.palette self.oldPalette = canvas.palette
canvas:applyPalette(Canvas.darkPalette) canvas:applyPalette(self.darkPalette)
UI.SlideOut.show(self, ...) UI.SlideOut.show(self, ...)
end end
function UI.Dialog:hide(...) function UI.Dialog:hide(...)
self.parent:getCanvas().palette = self.oldPalette self.parent.palette = self.oldPalette
UI.SlideOut.hide(self, ...) UI.SlideOut.hide(self, ...)
self.parent:draw() self.parent:draw()
end end
@ -37,3 +36,30 @@ function UI.Dialog:eventHandler(event)
end end
return UI.SlideOut.eventHandler(self, event) return UI.SlideOut.eventHandler(self, event)
end end
function UI.Dialog.example()
return UI.Dialog {
title = 'Enter Starting Level',
height = 7,
form = UI.Form {
y = 3, x = 2, height = 4,
event = 'setStartLevel',
cancelEvent = 'slide_hide',
text = UI.Text {
x = 5, y = 1, width = 20,
textColor = colors.gray,
},
textEntry = UI.TextEntry {
formKey = 'level',
x = 15, y = 1, width = 7,
},
},
statusBar = UI.StatusBar(),
enable = function(self)
require('opus.event').onTimeout(0, function()
self:show()
self:sync()
end)
end,
}
end

View File

@ -32,42 +32,38 @@ function UI.DropMenu:layout()
self.height = #self.children + 1 self.height = #self.children + 1
self.width = maxWidth + 2 self.width = maxWidth + 2
if not self.canvas then if self.x + self.width > self.parent.width then
self.canvas = self:addLayer() self.x = self.parent.width - self.width + 1
else
self.canvas:resize(self.width, self.height)
end end
self:reposition(self.x, self.y, self.width, self.height)
end end
function UI.DropMenu:enable() function UI.DropMenu:enable()
end for _,c in pairs(self.children) do
if not c.spacer then
function UI.DropMenu:show(x, y) c.inactive = not self:getActive(c)
self.x, self.y = x, y end
self.canvas:move(x, y) end
self.canvas:setVisible(true)
UI.Window.enable(self) UI.Window.enable(self)
self:draw()
self:capture(self)
self:focusFirst() self:focusFirst()
self:draw()
end end
function UI.DropMenu:hide() function UI.DropMenu:disable()
self:disable() UI.Window.disable(self)
self.canvas:setVisible(false) self:remove()
self:release(self)
end end
function UI.DropMenu:eventHandler(event) function UI.DropMenu:eventHandler(event)
if event.type == 'focus_lost' and self.enabled then if event.type == 'focus_lost' and self.enabled then
if not Util.contains(self.children, event.focused) then if not Util.contains(self.children, event.focused) then
self:hide() self:disable()
end end
elseif event.type == 'mouse_out' and self.enabled then elseif event.type == 'mouse_out' and self.enabled then
self:hide() self:disable()
self:refocus() self:setFocus(self.parent:find(self.lastFocus))
else else
return UI.MenuBar.eventHandler(self, event) return UI.MenuBar.eventHandler(self, event)
end end
@ -83,6 +79,15 @@ function UI.DropMenu.example()
{ spacer = true }, { spacer = true },
{ text = 'Quit ^q', event = 'quit' }, { text = 'Quit ^q', event = 'quit' },
} }, } },
{ text = 'Edit', dropdown = {
{ text = 'Copy', event = 'run' },
{ text = 'Paste s', event = 'shell' },
} },
{ text = '\187',
x = -3,
dropdown = {
{ text = 'Associations', event = 'associate' },
} },
} }
} }
end end

View File

@ -14,7 +14,7 @@ UI.DropMenuItem.defaults = {
} }
function UI.DropMenuItem:eventHandler(event) function UI.DropMenuItem:eventHandler(event)
if event.type == 'button_activate' then if event.type == 'button_activate' then
self.parent:hide() self.parent:disable()
end end
return UI.Button.eventHandler(self, event) return UI.Button.eventHandler(self, event)
end end

View File

@ -18,45 +18,33 @@ UI.Embedded.defaults = {
function UI.Embedded:setParent() function UI.Embedded:setParent()
UI.Window.setParent(self) UI.Window.setParent(self)
function self.render()
self:sync()
end
self.win = Terminal.window(UI.term.device, self.x, self.y, self.width, self.height, false) self.win = Terminal.window(UI.term.device, self.x, self.y, self.width, self.height, false)
self.win.canvas = self
self.win.setMaxScroll(self.maxScroll) self.win.setMaxScroll(self.maxScroll)
local canvas = self:getCanvas()
self.win.getCanvas().parent = canvas
table.insert(canvas.layers, self.win.getCanvas())
self.canvas = self.win.getCanvas()
self.win.setCursorPos(1, 1) self.win.setCursorPos(1, 1)
self.win.setBackgroundColor(self.backgroundColor) self.win.setBackgroundColor(self.backgroundColor)
self.win.setTextColor(self.textColor) self.win.setTextColor(self.textColor)
self.win.clear() self.win.clear()
end end
function UI.Embedded:layout() function UI.Embedded:draw()
UI.Window.layout(self) self:dirty()
if self.win then
self.win.reposition(self.x, self.y, self.width, self.height)
end
end end
function UI.Embedded:draw() function UI.Embedded:focus()
self.canvas:dirty() -- allow scrolling
end end
function UI.Embedded:enable() function UI.Embedded:enable()
self.canvas:setVisible(true)
self.canvas:raise()
if self.visible then
-- the window will automatically update on changes
-- the canvas does not need to be rendereed
self.win.setVisible(true)
end
UI.Window.enable(self) UI.Window.enable(self)
self.canvas:dirty() self.win.setVisible(true)
self:dirty()
end end
function UI.Embedded:disable() function UI.Embedded:disable()
self.canvas:setVisible(false)
self.win.setVisible(false) self.win.setVisible(false)
UI.Window.disable(self) UI.Window.disable(self)
end end
@ -71,17 +59,12 @@ function UI.Embedded:eventHandler(event)
end end
end end
function UI.Embedded:focus()
-- allow scrolling
end
function UI.Embedded.example() function UI.Embedded.example()
local Event = require('opus.event') local Event = require('opus.event')
local Util = require('opus.util') local Util = require('opus.util')
local term = _G.term local term = _G.term
return UI.Embedded { return UI.Embedded {
visible = true,
enable = function (self) enable = function (self)
UI.Embedded.enable(self) UI.Embedded.enable(self)
Event.addRoutine(function() Event.addRoutine(function()

View File

@ -60,7 +60,7 @@ UI.Grid.defaults = {
textSelectedColor = colors.white, textSelectedColor = colors.white,
backgroundColor = colors.black, backgroundColor = colors.black,
backgroundSelectedColor = colors.gray, backgroundSelectedColor = colors.gray,
headerBackgroundColor = colors.cyan, headerBackgroundColor = UI.colors.tertiary,
headerTextColor = colors.white, headerTextColor = colors.white,
headerSortColor = colors.yellow, headerSortColor = colors.yellow,
unfocusedTextSelectedColor = colors.white, unfocusedTextSelectedColor = colors.white,

View File

@ -1,19 +1,28 @@
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 lookup = '0123456789abcdef'
-- handle files produced by Paint
UI.Image = class(UI.Window) UI.Image = class(UI.Window)
UI.Image.defaults = { UI.Image.defaults = {
UIElement = 'Image', UIElement = 'Image',
event = 'button_press', event = 'button_press',
} }
function UI.Image:setParent() function UI.Image:postInit()
if self.image then if self.filename then
self.image = Util.readLines(self.filename)
end
if self.image and not (self.height or self.ey) then
self.height = #self.image self.height = #self.image
end end
if self.image and not self.width then if self.image and not (self.width or self.ex) then
self.width = #self.image[1] for i = 1, self.height do
self.width = math.max(self.width or 0, #self.image[i])
end
end end
UI.Window.setParent(self)
end end
function UI.Image:draw() function UI.Image:draw()
@ -22,19 +31,22 @@ function UI.Image:draw()
for y = 1, #self.image do for y = 1, #self.image do
local line = self.image[y] local line = self.image[y]
for x = 1, #line do for x = 1, #line do
local ch = line[x] local ch = lookup:find(line:sub(x, x))
if type(ch) == 'number' then if ch then
if ch > 0 then self:write(x, y, ' ', 2 ^ (ch -1))
self:write(x, y, ' ', ch)
end
else
self:write(x, y, ch)
end end
end end
end end
end end
self:drawChildren()
end end
function UI.Image:setImage(image) function UI.Image:setImage(image)
self.image = image self.image = image
end end
function UI.Image.example()
return UI.Image {
filename = 'test.paint',
}
end

View File

@ -3,16 +3,6 @@ local UI = require('opus.ui')
local colors = _G.colors local colors = _G.colors
local function getPosition(element)
local x, y = 1, 1
repeat
x = element.x + x - 1
y = element.y + y - 1
element = element.parent
until not element
return x, y
end
UI.MenuBar = class(UI.Window) UI.MenuBar = class(UI.Window)
UI.MenuBar.defaults = { UI.MenuBar.defaults = {
UIElement = 'MenuBar', UIElement = 'MenuBar',
@ -22,7 +12,6 @@ UI.MenuBar.defaults = {
textColor = colors.black, textColor = colors.black,
spacing = 2, spacing = 2,
lastx = 1, lastx = 1,
showBackButton = false,
buttonClass = 'MenuItem', buttonClass = 'MenuItem',
} }
function UI.MenuBar:postInit() function UI.MenuBar:postInit()
@ -62,10 +51,6 @@ function UI.MenuBar:addButtons(buttons)
else else
table.insert(self.children, button) table.insert(self.children, button)
end end
if button.dropdown then
button.dropmenu = UI.DropMenu { buttons = button.dropdown }
end
end end
end end
if self.parent then if self.parent then
@ -78,23 +63,27 @@ function UI.MenuBar:getActive(menuItem)
end end
function UI.MenuBar:eventHandler(event) function UI.MenuBar:eventHandler(event)
if event.type == 'button_press' and event.button.dropmenu then if event.type == 'button_press' and event.button.dropdown then
if event.button.dropmenu.enabled then local function getPosition(element)
event.button.dropmenu:hide() local x, y = 1, 1
self:refocus() repeat
return true x = element.x + x - 1
else y = element.y + y - 1
local x, y = getPosition(event.button) element = element.parent
if x + event.button.dropmenu.width > self.width then until not element
x = self.width - event.button.dropmenu.width + 1 return x, y
end
for _,c in pairs(event.button.dropmenu.children) do
if not c.spacer then
c.inactive = not self:getActive(c)
end
end
event.button.dropmenu:show(x, y + 1)
end end
local x, y = getPosition(event.button)
local menu = UI.DropMenu {
buttons = event.button.dropdown,
x = x,
y = y + 1,
lastFocus = event.button.uid,
}
self.parent:add({ dropmenu = menu })
return true return true
end end
end end

View File

@ -6,8 +6,6 @@ local colors = _G.colors
UI.MenuItem = class(UI.Button) UI.MenuItem = class(UI.Button)
UI.MenuItem.defaults = { UI.MenuItem.defaults = {
UIElement = 'MenuItem', UIElement = 'MenuItem',
textColor = colors.black,
backgroundColor = colors.lightGray,
textFocusColor = colors.white, textFocusColor = colors.white,
backgroundFocusColor = colors.lightGray, backgroundFocusColor = colors.lightGray,
} }

View File

@ -5,17 +5,18 @@ UI.NftImage = class(UI.Window)
UI.NftImage.defaults = { UI.NftImage.defaults = {
UIElement = 'NftImage', UIElement = 'NftImage',
} }
function UI.NftImage:setParent() function UI.NftImage:postInit()
if self.image then if self.image and not (self.ey or self.height) then
self.height = self.image.height self.height = self.image.height
end end
if self.image and not self.width then if self.image and not (self.ex or self.width) then
self.width = self.image.width self.width = self.image.width
end end
UI.Window.setParent(self)
end end
function UI.NftImage:draw() function UI.NftImage:draw()
self:clear()
if self.image then if self.image then
-- due to blittle, the background and foreground transparent -- due to blittle, the background and foreground transparent
-- color is the same as the background color -- color is the same as the background color
@ -25,8 +26,6 @@ function UI.NftImage:draw()
self:write(x, y, self.image.text[y][x], self.image.bg[y][x], self.image.fg[y][x] or bg) self:write(x, y, self.image.text[y][x], self.image.bg[y][x], self.image.fg[y][x] or bg)
end end
end end
else
self:clear()
end end
end end

View File

@ -43,32 +43,34 @@ function UI.Notification:cancel()
self.timer = nil self.timer = nil
end end
if self.canvas then self:disable()
self.enabled = false
self.canvas:removeLayer()
self.canvas = nil
end
end end
function UI.Notification:display(value, timeout) function UI.Notification:display(value, timeout)
self:cancel()
self.enabled = true
local lines = Util.wordWrap(value, self.width - 3) local lines = Util.wordWrap(value, self.width - 3)
self.enabled = true
self.height = #lines self.height = #lines
if self.anchor == 'bottom' then if self.anchor == 'bottom' then
self.y = self.parent.height - self.height + 1 self.y = self.parent.height - self.height + 1
self.canvas = self:addLayer(self.backgroundColor, self.textColor)
self:addTransition('expandUp', { ticks = self.height }) self:addTransition('expandUp', { ticks = self.height })
else else
self.canvas = self:addLayer(self.backgroundColor, self.textColor)
self.y = 1 self.y = 1
end end
self.canvas:setVisible(true)
self:reposition(self.x, self.y, self.width, self.height)
self:raise()
self:clear() self:clear()
for k,v in pairs(lines) do for k,v in pairs(lines) do
self:write(2, k, v) self:write(2, k, v)
end end
self:write(self.width, 1, self.closeInd)
if self.timer then
Event.off(self.timer)
self.timer = nil
end
timeout = timeout or self.timeout timeout = timeout or self.timeout
if timeout > 0 then if timeout > 0 then
@ -77,7 +79,6 @@ function UI.Notification:display(value, timeout)
self:sync() self:sync()
end) end)
else else
self:write(self.width, 1, self.closeInd)
self:sync() self:sync()
end end
end end
@ -92,7 +93,7 @@ function UI.Notification:eventHandler(event)
end end
function UI.Notification.example() function UI.Notification.example()
return UI.ActiveLayer { return UI.Window {
notify1 = UI.Notification { notify1 = UI.Notification {
anchor = 'top', anchor = 'top',
}, },
@ -111,7 +112,9 @@ function UI.Notification.example()
if event.type == 'test_success' then if event.type == 'test_success' then
self.notify1:success('Example text') self.notify1:success('Example text')
elseif event.type == 'test_error' then elseif event.type == 'test_error' then
self.notify2:error('Example text', 0) self.notify2:error([[Example text test test
test test test test test
test test test]], 0)
end end
end, end,
} }

View File

@ -1,21 +1,9 @@
local Canvas = require('opus.ui.canvas')
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 colors = _G.colors
-- need to add offsets to this test
local function getPosition(element)
local x, y = 1, 1
repeat
x = element.x + x - 1
y = element.y + y - 1
element = element.parent
until not element
return x, y
end
UI.Page = class(UI.Window) UI.Page = class(UI.Window)
UI.Page.defaults = { UI.Page.defaults = {
UIElement = 'Page', UIElement = 'Page',
@ -26,21 +14,15 @@ UI.Page.defaults = {
['shift-tab' ] = 'focus_prev', ['shift-tab' ] = 'focus_prev',
up = 'focus_prev', up = 'focus_prev',
}, },
backgroundColor = colors.cyan, backgroundColor = UI.colors.primary,
textColor = colors.white, textColor = colors.white,
} }
function UI.Page:postInit() function UI.Page:postInit()
self.parent = self.parent or UI.defaultDevice self.parent = self.parent or UI.defaultDevice
self.__target = self self.__target = self
self.canvas = Canvas({
x = 1, y = 1, width = self.parent.width, height = self.parent.height,
isColor = self.parent.isColor,
})
self.canvas:clear(self.backgroundColor, self.textColor)
end end
function UI.Page:enable() function UI.Page:enable()
self.canvas.visible = true
UI.Window.enable(self) UI.Window.enable(self)
if not self.focused or not self.focused.enabled then if not self.focused or not self.focused.enabled then
@ -49,12 +31,12 @@ function UI.Page:enable()
end end
function UI.Page:disable() function UI.Page:disable()
self.canvas.visible = false
UI.Window.disable(self) UI.Window.disable(self)
end end
function UI.Page:sync() function UI.Page:sync()
if self.enabled then if self.enabled then
self:checkFocus()
self.parent:sync() self.parent:sync()
end end
end end
@ -73,22 +55,24 @@ function UI.Page:pointToChild(x, y)
if self.__target == self then if self.__target == self then
return UI.Window.pointToChild(self, x, y) return UI.Window.pointToChild(self, x, y)
end end
x = x + self.offx - self.x + 1
y = y + self.offy - self.y + 1 -- need to add offsets to this test
--[[ local function getPosition(element)
-- this is supposed to fix when there are multiple sub canvases local x, y = 1, 1
local absX, absY = getPosition(self.__target) repeat
if self.__target.canvas then x = element.x + x - 1
x = x - (self.__target.canvas.x - self.__target.x) y = element.y + y - 1
y = y - (self.__target.canvas.y - self.__target.y) element = element.parent
_syslog({'raw', self.__target.canvas.y, self.__target.y}) until not element
return x, y
end end
]]
return self.__target:pointToChild(x, y) local absX, absY = getPosition(self.__target)
return self.__target:pointToChild(x - absX + self.__target.x, y - absY + self.__target.y)
end end
function UI.Page:getFocusables() function UI.Page:getFocusables()
if self.__target == self or self.__target.pageType ~= 'modal' then if self.__target == self or not self.__target.modal then
return UI.Window.getFocusables(self) return UI.Window.getFocusables(self)
end end
return self.__target:getFocusables() return self.__target:getFocusables()
@ -149,12 +133,25 @@ function UI.Page:setFocus(child)
if not child.focused then if not child.focused then
child.focused = true child.focused = true
child:emit({ type = 'focus_change', focused = child }) child:emit({ type = 'focus_change', focused = child })
--self:emit({ type = 'focus_change', focused = child })
end end
child:focus() child:focus()
end end
function UI.Page:checkFocus()
if not self.focused or not self.focused.enabled then
local el = self.__target
while el do
local focusables = el:getFocusables()
if focusables[1] then
self:setFocus(focusables[1])
break
end
el = el.parent
end
end
end
function UI.Page:eventHandler(event) function UI.Page:eventHandler(event)
if self.focused then if self.focused then
if event.type == 'focus_next' then if event.type == 'focus_next' then

View File

@ -28,12 +28,11 @@ function UI.ProgressBar:draw()
end end
function UI.ProgressBar.example() function UI.ProgressBar.example()
local Event = require('opus.event')
return UI.ProgressBar { return UI.ProgressBar {
x = 2, ex = -2, y = 2, x = 2, ex = -2, y = 2,
focus = function() end, focus = function() end,
enable = function(self) enable = function(self)
Event.onInterval(.25, function() require('opus.event').onInterval(.25, function()
self.value = self.value == 100 and 0 or self.value + 5 self.value = self.value == 100 and 0 or self.value + 5
self:draw() self:draw()
self:sync() self:sync()

View File

@ -17,7 +17,10 @@ UI.ScrollBar.defaults = {
ey = -1, ey = -1,
} }
function UI.ScrollBar:draw() function UI.ScrollBar:draw()
local view = self.parent:getViewArea() local parent = self.target or self.parent --self:find(self.target)
local view = parent:getViewArea()
self:clear()
if view.totalHeight > view.height then if view.totalHeight > view.height then
local maxScroll = view.totalHeight - view.height local maxScroll = view.totalHeight - view.height
@ -27,7 +30,7 @@ function UI.ScrollBar:draw()
local row = view.y local row = view.y
if not view.static then -- does the container scroll ? if not view.static then -- does the container scroll ?
self.height = view.totalHeight self:reposition(self.x, self.y, self.width, view.totalHeight)
end end
for i = 1, view.height - 2 do for i = 1, view.height - 2 do
@ -56,16 +59,17 @@ end
function UI.ScrollBar:eventHandler(event) function UI.ScrollBar:eventHandler(event)
if event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then if event.type == 'mouse_click' or event.type == 'mouse_doubleclick' then
if event.x == 1 then if event.x == 1 then
local view = self.parent:getViewArea() local parent = self.target or self.parent --self:find(self.target)
local view = parent:getViewArea()
if view.totalHeight > view.height then if view.totalHeight > view.height then
if event.y == view.y then if event.y == view.y then
self:emit({ type = 'scroll_up'}) parent:emit({ type = 'scroll_up'})
elseif event.y == view.y + view.height - 1 then elseif event.y == view.y + view.height - 1 then
self:emit({ type = 'scroll_down'}) parent:emit({ type = 'scroll_down'})
else else
local percent = (event.y - view.y) / (view.height - 2) local percent = (event.y - view.y) / (view.height - 2)
local y = math.floor((view.totalHeight - view.height) * percent) local y = math.floor((view.totalHeight - view.height) * percent)
self:emit({ type = 'scroll_to', offset = y }) parent :emit({ type = 'scroll_to', offset = y })
end end
end end
return true return true

View File

@ -57,3 +57,21 @@ function UI.ScrollingGrid:setIndex(index)
end end
UI.Grid.setIndex(self, index) UI.Grid.setIndex(self, index)
end end
function UI.ScrollingGrid.example()
local values = { }
for i = 1, 20 do
table.insert(values, { key = 'key' .. i, value = 'value' .. i })
end
return UI.ScrollingGrid {
values = values,
sortColumn = 'key',
columns = {
{ heading = 'key', key = 'key' },
{ heading = 'value', key = 'value' },
},
accelerators = {
grid_select = 'custom_select',
}
}
end

View File

@ -4,17 +4,9 @@ local UI = require('opus.ui')
UI.SlideOut = class(UI.Window) UI.SlideOut = class(UI.Window)
UI.SlideOut.defaults = { UI.SlideOut.defaults = {
UIElement = 'SlideOut', UIElement = 'SlideOut',
pageType = 'modal', transitionHint = 'expandUp',
modal = true,
} }
function UI.SlideOut:layout()
UI.Window.layout(self)
if not self.canvas then
self.canvas = self:addLayer()
else
self.canvas:resize(self.width, self.height)
end
end
function UI.SlideOut:enable() function UI.SlideOut:enable()
end end
@ -27,24 +19,20 @@ function UI.SlideOut:toggle()
end end
function UI.SlideOut:show(...) function UI.SlideOut:show(...)
self:addTransition('expandUp')
self.canvas:raise()
self.canvas:setVisible(true)
UI.Window.enable(self, ...) UI.Window.enable(self, ...)
self:draw() self:draw()
self:capture(self)
self:focusFirst() self:focusFirst()
end end
function UI.SlideOut:disable()
self.canvas:setVisible(false)
UI.Window.disable(self)
end
function UI.SlideOut:hide() function UI.SlideOut:hide()
self:disable() self:disable()
self:release(self) end
self:refocus()
function UI.SlideOut:draw()
if not self.noFill then
self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray)
end
self:drawChildren()
end end
function UI.SlideOut:eventHandler(event) function UI.SlideOut:eventHandler(event)
@ -59,24 +47,27 @@ function UI.SlideOut:eventHandler(event)
end end
function UI.SlideOut.example() function UI.SlideOut.example()
-- for the transistion to work properly, the parent must have a canvas return UI.Window {
return UI.ActiveLayer { y = 3,
y = 2, backgroundColor = 2048,
button = UI.Button { button = UI.Button {
x = 2, y = 5, x = 2, y = 5,
text = 'show', text = 'show',
}, },
slideOut = UI.SlideOut { slideOut = UI.SlideOut {
backgroundColor = _G.colors.yellow, backgroundColor = 16,
y = -4, height = 4, x = 3, ex = -3, y = -7, height = 4, x = 3, ex = -3,
titleBar = UI.TitleBar {
title = 'test',
},
button = UI.Button { button = UI.Button {
x = 2, y = 2, x = 2, y = 2,
text = 'hide', text = 'hide',
--visualize = true,
}, },
}, },
eventHandler = function (self, event) eventHandler = function (self, event)
if event.type == 'button_press' then if event.type == 'button_press' then
self.slideOut.canvas.xxx = true
self.slideOut:toggle() self.slideOut:toggle()
end end
end, end,

View File

@ -57,8 +57,16 @@ end
function UI.Slider:eventHandler(event) function UI.Slider:eventHandler(event)
if event.type == "mouse_down" or event.type == "mouse_drag" then if event.type == "mouse_down" or event.type == "mouse_drag" then
local pos = event.x - 1
if event.type == 'mouse_down' then
self.anchor = event.x - 1
else
pos = self.anchor + event.dx
end
local range = self.max - self.min local range = self.max - self.min
local i = (event.x - 1) / (self.width - 1) local i = pos / (self.width - 1)
self.value = self.min + (i * range) self.value = self.min + (i * range)
self:emit({ type = self.event, value = self.value, element = self }) self:emit({ type = self.event, value = self.value, element = self })
self:draw() self:draw()

View File

@ -63,7 +63,7 @@ end
function UI.StatusBar:timedStatus(status, timeout) function UI.StatusBar:timedStatus(status, timeout)
self:write(2, 1, Util.widthify(status, self.width-2), self.backgroundColor) self:write(2, 1, Util.widthify(status, self.width-2), self.backgroundColor)
Event.on(timeout or 3, function() Event.onTimeout(timeout or 3, function()
if self.enabled then if self.enabled then
self:draw() self:draw()
self:sync() self:sync()

View File

@ -1,9 +1,16 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
UI.Tab = class(UI.ActiveLayer) UI.Tab = class(UI.Window)
UI.Tab.defaults = { UI.Tab.defaults = {
UIElement = 'Tab', UIElement = 'Tab',
tabTitle = 'tab', tabTitle = 'tab',
y = 2, y = 2,
} }
function UI.Tab:draw()
if not self.noFill then
self:fillArea(1, 1, self.width, self.height, string.rep('\127', self.width), colors.black, colors.gray)
end
self:drawChildren()
end

View File

@ -2,13 +2,14 @@ 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
UI.TabBar = class(UI.MenuBar) UI.TabBar = class(UI.MenuBar)
UI.TabBar.defaults = { UI.TabBar.defaults = {
UIElement = 'TabBar', UIElement = 'TabBar',
buttonClass = 'TabBarMenuItem', buttonClass = 'TabBarMenuItem',
} selectedBackgroundColor = UI.colors.secondary,
UI.TabBar.inherits = { unselectedBackgroundColor = colors.lightGray,
selectedBackgroundColor = 'backgroundColor',
} }
function UI.TabBar:enable() function UI.TabBar:enable()
UI.MenuBar.enable(self) UI.MenuBar.enable(self)

View File

@ -1,27 +1,18 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local colors = _G.colors
UI.TabBarMenuItem = class(UI.Button) UI.TabBarMenuItem = class(UI.Button)
UI.TabBarMenuItem.defaults = { UI.TabBarMenuItem.defaults = {
UIElement = 'TabBarMenuItem', UIElement = 'TabBarMenuItem',
event = 'tab_select', event = 'tab_select',
textColor = colors.black,
selectedBackgroundColor = colors.cyan,
unselectedBackgroundColor = colors.lightGray,
backgroundColor = colors.lightGray,
}
UI.TabBarMenuItem.inherits = {
selectedBackgroundColor = 'selectedBackgroundColor',
} }
function UI.TabBarMenuItem:draw() function UI.TabBarMenuItem:draw()
if self.selected then if self.selected then
self.backgroundColor = self.selectedBackgroundColor self.backgroundColor = self:getProperty('selectedBackgroundColor')
self.backgroundFocusColor = self.selectedBackgroundColor self.backgroundFocusColor = self.backgroundColor
else else
self.backgroundColor = self.unselectedBackgroundColor self.backgroundColor = self:getProperty('unselectedBackgroundColor')
self.backgroundFocusColor = self.unselectedBackgroundColor self.backgroundFocusColor = self.backgroundColor
end end
UI.Button.draw(self) UI.Button.draw(self)
end end

View File

@ -56,12 +56,12 @@ end
function UI.Tabs:enable() function UI.Tabs:enable()
self.enabled = true self.enabled = true
self.transitionHint = nil
self.tabBar:enable() self.tabBar:enable()
local menuItem = Util.find(self.tabBar.children, 'selected', true) local menuItem = Util.find(self.tabBar.children, 'selected', true)
for _,child in pairs(self.children or { }) do for child in self:eachChild() do
child.transitionHint = nil
if child.uid == menuItem.tabUid then if child.uid == menuItem.tabUid then
child:enable() child:enable()
self:emit({ type = 'tab_activate', activated = child }) self:emit({ type = 'tab_activate', activated = child })
@ -74,14 +74,11 @@ end
function UI.Tabs:eventHandler(event) function UI.Tabs:eventHandler(event)
if event.type == 'tab_change' then if event.type == 'tab_change' then
local tab = self:find(event.tab.tabUid) local tab = self:find(event.tab.tabUid)
if event.current > event.last then local hint = event.current > event.last and 'slideLeft' or 'slideRight'
self.transitionHint = 'slideLeft'
else
self.transitionHint = 'slideRight'
end
for _,child in pairs(self.children) do for child in self:eachChild() do
if child.uid == event.tab.tabUid then if child.uid == event.tab.tabUid then
child.transitionHint = hint
child:enable() child:enable()
elseif child.tabTitle then elseif child.tabTitle then
child:disable() child:disable()
@ -89,6 +86,7 @@ function UI.Tabs:eventHandler(event)
end end
self:emit({ type = 'tab_activate', activated = tab }) self:emit({ type = 'tab_activate', activated = tab })
tab:draw() tab:draw()
return true
end end
end end
@ -102,7 +100,18 @@ function UI.Tabs.example()
tab2 = UI.Tab { tab2 = UI.Tab {
index = 2, index = 2,
tabTitle = 'tab2', tabTitle = 'tab2',
button = UI.Button { y = 3 }, subtabs = UI.Tabs {
x = 3, y = 2, ex = -3, ey = -2,
tab1 = UI.Tab {
index = 1,
tabTitle = 'tab4',
entry = UI.TextEntry { y = 3, shadowText = 'text' },
},
tab3 = UI.Tab {
index = 2,
tabTitle = 'tab5',
},
},
}, },
tab3 = UI.Tab { tab3 = UI.Tab {
index = 3, index = 3,

View File

@ -6,11 +6,8 @@ UI.TextArea.defaults = {
UIElement = 'TextArea', UIElement = 'TextArea',
marginRight = 2, marginRight = 2,
value = '', value = '',
showScrollBar = true,
} }
function UI.TextArea:postInit()
self.scrollBar = UI.ScrollBar()
end
function UI.TextArea:setText(text) function UI.TextArea:setText(text)
self:reset() self:reset()
self.value = text self.value = text
@ -23,19 +20,28 @@ end
function UI.TextArea:draw() function UI.TextArea:draw()
self:clear() self:clear()
-- self:setCursorPos(1, 1)
self.cursorX, self.cursorY = 1, 1 self.cursorX, self.cursorY = 1, 1
self:print(self.value) self:print(self.value)
self:drawChildren()
for _,child in pairs(self.children) do
if child.enabled then
child:draw()
end
end
end end
function UI.TextArea.example() function UI.TextArea.example()
return UI.TextArea { return UI.Window {
value = 'sample text\nabc' backgroundColor = 2048,
t1 = UI.TextArea {
ey = 3,
value = 'sample text\nabc'
},
t2 = UI.TextArea {
y = 5,
value = [[1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
3
4
5
6
7
8]]
}
} }
end end

View File

@ -19,7 +19,6 @@ UI.TextEntry = class(UI.Window)
UI.TextEntry.docs = { } UI.TextEntry.docs = { }
UI.TextEntry.defaults = { UI.TextEntry.defaults = {
UIElement = 'TextEntry', UIElement = 'TextEntry',
--value = '',
shadowText = '', shadowText = '',
focused = false, focused = false,
textColor = colors.white, textColor = colors.white,

View File

@ -20,24 +20,15 @@ UI.Throttle.defaults = {
' //) (O ). @ \\-d ) (@ ' ' //) (O ). @ \\-d ) (@ '
} }
} }
function UI.Throttle:setParent() function UI.Throttle:layout()
self.x = math.ceil((self.parent.width - self.width) / 2) self.x = math.ceil((self.parent.width - self.width) / 2)
self.y = math.ceil((self.parent.height - self.height) / 2) self.y = math.ceil((self.parent.height - self.height) / 2)
UI.Window.setParent(self) self:reposition(self.x, self.y, self.width, self.height)
end end
function UI.Throttle:enable() function UI.Throttle:enable()
self.c = os.clock() self.c = os.clock()
self.enabled = false self.ctr = 0
end
function UI.Throttle:disable()
if self.canvas then
self.enabled = false
self.canvas:removeLayer()
self.canvas = nil
self.ctr = 0
end
end end
function UI.Throttle:update() function UI.Throttle:update()
@ -46,11 +37,7 @@ function UI.Throttle:update()
os.sleep(0) os.sleep(0)
self.c = os.clock() self.c = os.clock()
self.enabled = true self.enabled = true
if not self.canvas then self:clear(self.borderColor)
self.canvas = self:addLayer(self.backgroundColor, self.borderColor)
self.canvas:setVisible(true)
self:clear(self.borderColor)
end
local image = self.image[self.ctr + 1] local image = self.image[self.ctr + 1]
local width = self.width - 2 local width = self.width - 2
for i = 0, #self.image do for i = 0, #self.image do
@ -63,3 +50,25 @@ function UI.Throttle:update()
self:sync() self:sync()
end end
end end
function UI.Throttle.example()
return UI.Window {
button1 = UI.Button {
x = 2, y = 2,
text = 'Test',
},
throttle = UI.Throttle {
textColor = colors.yellow,
borderColor = colors.green,
},
eventHandler = function (self, event)
if event.type == 'button_press' then
for _ = 1, 40 do
self.throttle:update()
os.sleep(.05)
end
self.throttle:disable()
end
end,
}
end

View File

@ -38,8 +38,6 @@ UI.TitleBar = class(UI.Window)
UI.TitleBar.defaults = { UI.TitleBar.defaults = {
UIElement = 'TitleBar', UIElement = 'TitleBar',
height = 1, height = 1,
textColor = colors.white,
backgroundColor = colors.cyan,
title = '', title = '',
frameChar = UI.extChars and '\140' or '-', frameChar = UI.extChars and '\140' or '-',
closeInd = UI.extChars and '\215' or '*', closeInd = UI.extChars and '\215' or '*',
@ -69,5 +67,73 @@ function UI.TitleBar:eventHandler(event)
end end
return true return true
end end
elseif event.type == 'mouse_down' then
self.anchor = { x = event.x, y = event.y, ox = self.parent.x, oy = self.parent.y, h = self.parent.height }
elseif event.type == 'mouse_drag' then
if self.expand == 'height' then
local d = event.dy
if self.anchor.h - d > 0 and self.anchor.oy + d > 0 then
self.parent:reposition(self.parent.x, self.anchor.oy + event.dy, self.width, self.anchor.h - d)
end
else --if self.moveable then
local d = event.dy
if self.anchor.oy + d > 0 and self.anchor.oy + d <= self.parent.parent.height then
self.parent:move(self.anchor.ox + event.dx, self.anchor.oy + event.dy)
end
end
end end
end end
function UI.TitleBar.example()
return UI.Window {
win1 = UI.Window {
x = 9, y = 2, ex = -7, ey = -3,
backgroundColor = colors.green,
titleBar = UI.TitleBar {
title = 'test', moveable = true,
},
button1 = UI.Button {
x = 2, y = 3,
text = 'Press',
},
focus = function (self)
self:raise()
end,
},
win2 = UI.Window {
x = 7, y = 3, ex = -9, ey = -2,
backgroundColor = colors.orange,
titleBar = UI.TitleBar {
title = 'test', moveable = true,
},
button1 = UI.Button {
x = 2, y = 3,
text = 'Press',
},
focus = function (self)
self:raise()
end,
},
draw = function(self, isBG)
for i = 1, self.height do
self:write(1, i, self.filler or '')
end
if not isBG then
for _,v in pairs(self.children) do
v:draw()
end
end
end,
enable = function (self)
require('opus.event').onInterval(.5, function()
self.filler = string.rep(string.char(math.random(33, 126)), self.width)
self:draw(true)
self:sync()
end)
UI.Window.enable(self)
end
}
end

View File

@ -18,12 +18,11 @@ function UI.VerticalMeter:draw()
end end
function UI.VerticalMeter.example() function UI.VerticalMeter.example()
local Event = require('opus.event')
return UI.VerticalMeter { return UI.VerticalMeter {
x = 2, width = 3, y = 2, ey = -2, x = 2, width = 3, y = 2, ey = -2,
focus = function() end, focus = function() end,
enable = function(self) enable = function(self)
Event.onInterval(.25, function() require('opus.event').onInterval(.25, function()
self.value = self.value == 100 and 0 or self.value + 5 self.value = self.value == 100 and 0 or self.value + 5
self:draw() self:draw()
self:sync() self:sync()

View File

@ -1,16 +1,15 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local colors = _G.colors
UI.Viewport = class(UI.Window) UI.Viewport = class(UI.Window)
UI.Viewport.defaults = { UI.Viewport.defaults = {
UIElement = 'Viewport', UIElement = 'Viewport',
backgroundColor = colors.cyan,
accelerators = { accelerators = {
down = 'scroll_down', down = 'scroll_down',
up = 'scroll_up', up = 'scroll_up',
home = 'scroll_top', home = 'scroll_top',
left = 'scroll_left',
right = 'scroll_right',
[ 'end' ] = 'scroll_bottom', [ 'end' ] = 'scroll_bottom',
pageUp = 'scroll_pageUp', pageUp = 'scroll_pageUp',
[ 'control-b' ] = 'scroll_pageUp', [ 'control-b' ] = 'scroll_pageUp',
@ -18,53 +17,53 @@ UI.Viewport.defaults = {
[ 'control-f' ] = 'scroll_pageDown', [ 'control-f' ] = 'scroll_pageDown',
}, },
} }
function UI.Viewport:layout() function UI.Viewport:postInit()
UI.Window.layout(self) if self.showScrollBar then
if not self.canvas then self.scrollBar = UI.ScrollBar()
self.canvas = self:addLayer()
else
self.canvas:resize(self.width, self.height)
end end
end end
function UI.Viewport:enable() function UI.Viewport:setScrollPosition(offy, offx) -- argh - reverse
UI.Window.enable(self) local oldOffy = self.offy
self.canvas:setVisible(true) self.offy = math.max(offy, 0)
end self.offy = math.min(self.offy, math.max(#self.lines, self.height) - self.height)
if self.offy ~= oldOffy then
function UI.Viewport:disable()
UI.Window.disable(self)
self.canvas:setVisible(false)
end
function UI.Viewport:setScrollPosition(offset)
local oldOffset = self.offy
self.offy = math.max(offset, 0)
self.offy = math.min(self.offy, math.max(#self.canvas.lines, self.height) - self.height)
if self.offy ~= oldOffset then
if self.scrollBar then if self.scrollBar then
self.scrollBar:draw() self.scrollBar:draw()
end end
self.canvas.offy = offset self.offy = offy
self.canvas:dirty() self:dirty(true)
end
local oldOffx = self.offx
self.offx = math.max(offx or 0, 0)
self.offx = math.min(self.offx, math.max(#self.lines[1], self.width) - self.width)
if self.offx ~= oldOffx then
if self.scrollBar then
--self.scrollBar:draw()
end
self.offx = offx or 0
self:dirty(true)
end end
end end
function UI.Viewport:write(x, y, text, bg, tc) function UI.Viewport:write(x, y, text, bg, tc)
if y > #self.canvas.lines then if y > #self.lines then
for i = #self.canvas.lines, y do self:resizeBuffer(self.width, y)
self.canvas.lines[i + 1] = { }
self.canvas:clearLine(i + 1, self.backgroundColor, self.textColor)
end
end end
return UI.Window.write(self, x, y, text, bg, tc) return UI.Window.write(self, x, y, text, bg, tc)
end end
function UI.Viewport:setViewHeight(h)
if h > #self.lines then
self:resizeBuffer(self.width, h)
end
end
function UI.Viewport:reset() function UI.Viewport:reset()
self.offy = 0 self.offy = 0
self.canvas.offy = 0 for i = self.height + 1, #self.lines do
for i = self.height + 1, #self.canvas.lines do self.lines[i] = nil
self.canvas.lines[i] = nil
end end
end end
@ -72,26 +71,30 @@ function UI.Viewport:getViewArea()
return { return {
y = (self.offy or 0) + 1, y = (self.offy or 0) + 1,
height = self.height, height = self.height,
totalHeight = #self.canvas.lines, totalHeight = #self.lines,
offsetY = self.offy or 0, offsetY = self.offy or 0,
} }
end end
function UI.Viewport:eventHandler(event) function UI.Viewport:eventHandler(event)
if event.type == 'scroll_down' then if event.type == 'scroll_down' then
self:setScrollPosition(self.offy + 1) self:setScrollPosition(self.offy + 1, self.offx)
elseif event.type == 'scroll_up' then elseif event.type == 'scroll_up' then
self:setScrollPosition(self.offy - 1) self:setScrollPosition(self.offy - 1, self.offx)
elseif event.type == 'scroll_left' then
self:setScrollPosition(self.offy, self.offx - 1)
elseif event.type == 'scroll_right' then
self:setScrollPosition(self.offy, self.offx + 1)
elseif event.type == 'scroll_top' then elseif event.type == 'scroll_top' then
self:setScrollPosition(0) self:setScrollPosition(0, 0)
elseif event.type == 'scroll_bottom' then elseif event.type == 'scroll_bottom' then
self:setScrollPosition(10000000) self:setScrollPosition(10000000, 0)
elseif event.type == 'scroll_pageUp' then elseif event.type == 'scroll_pageUp' then
self:setScrollPosition(self.offy - self.height) self:setScrollPosition(self.offy - self.height, self.offx)
elseif event.type == 'scroll_pageDown' then elseif event.type == 'scroll_pageDown' then
self:setScrollPosition(self.offy + self.height) self:setScrollPosition(self.offy + self.height, self.offx)
elseif event.type == 'scroll_to' then elseif event.type == 'scroll_to' then
self:setScrollPosition(event.offset) self:setScrollPosition(event.offset, 0)
else else
return false return false
end end

View File

@ -25,9 +25,6 @@ function UI.Wizard:postInit()
} }
Util.merge(self, self.pages) Util.merge(self, self.pages)
--for _, child in pairs(self.pages) do
-- child.ey = -2
--end
end end
function UI.Wizard:add(pages) function UI.Wizard:add(pages)
@ -50,9 +47,8 @@ end
function UI.Wizard:enable(...) function UI.Wizard:enable(...)
self.enabled = true self.enabled = true
self.index = 1 self.index = 1
self.transitionHint = nil
local initial = self:getPage(1) local initial = self:getPage(1)
for _,child in pairs(self.children) do for child in self:eachChild() do
if child == initial or not child.index then if child == initial or not child.index then
child:enable(...) child:enable(...)
else else
@ -93,12 +89,13 @@ function UI.Wizard:eventHandler(event)
elseif event.type == 'enable_view' then elseif event.type == 'enable_view' then
local current = event.next or event.prev local current = event.next or event.prev
if not current then error('property "index" is required on wizard pages') end if not current then error('property "index" is required on wizard pages') end
local hint
if event.current then if event.current then
if event.next then if event.next then
self.transitionHint = 'slideLeft' hint = 'slideLeft'
elseif event.prev then elseif event.prev then
self.transitionHint = 'slideRight' hint = 'slideRight'
end end
event.current:disable() event.current:disable()
end end
@ -117,6 +114,7 @@ function UI.Wizard:eventHandler(event)
self.nextButton.event = 'wizard_complete' self.nextButton.event = 'wizard_complete'
end end
-- a new current view -- a new current view
current.transitionHint = hint
current:enable() current:enable()
current:emit({ type = 'view_enabled', view = current }) current:emit({ type = 'view_enabled', view = current })
self:draw() self:draw()

View File

@ -1,11 +1,8 @@
local class = require('opus.class') local class = require('opus.class')
local UI = require('opus.ui') local UI = require('opus.ui')
local colors = _G.colors UI.WizardPage = class(UI.Window)
UI.WizardPage = class(UI.ActiveLayer)
UI.WizardPage.defaults = { UI.WizardPage.defaults = {
UIElement = 'WizardPage', UIElement = 'WizardPage',
backgroundColor = colors.cyan,
ey = -2, ey = -2,
} }

View File

@ -13,7 +13,7 @@ function Transition.slideLeft(args)
return function() return function()
local finished = tween:update(1) local finished = tween:update(1)
args.canvas:move(math.floor(pos.x), args.canvas.y) args.canvas:move(math.floor(pos.x), args.canvas.y)
args.canvas:dirty() args.canvas:dirty(true)
return not finished return not finished
end end
end end
@ -29,7 +29,7 @@ function Transition.slideRight(args)
return function() return function()
local finished = tween:update(1) local finished = tween:update(1)
args.canvas:move(math.floor(pos.x), args.canvas.y) args.canvas:move(math.floor(pos.x), args.canvas.y)
args.canvas:dirty() args.canvas:dirty(true)
return not finished return not finished
end end
end end
@ -45,7 +45,7 @@ function Transition.expandUp(args)
return function() return function()
local finished = tween:update(1) local finished = tween:update(1)
args.canvas:move(args.x, math.floor(pos.y)) args.canvas:move(args.x, math.floor(pos.y))
args.canvas:dirty() args.canvas.parent:dirty(true)
return not finished return not finished
end end
end end