mirror of https://github.com/kepler155c/opus
color rework + cleanup
This commit is contained in:
parent
3e41996b9b
commit
9d2a76f4ea
|
@ -184,9 +184,7 @@ local Browser = UI.Page {
|
|||
text = 'Add', event = 'add_association',
|
||||
},
|
||||
},
|
||||
statusBar = UI.StatusBar {
|
||||
backgroundColor = colors.cyan,
|
||||
},
|
||||
statusBar = UI.StatusBar { },
|
||||
},
|
||||
accelerators = {
|
||||
[ 'control-q' ] = 'quit',
|
||||
|
@ -547,6 +545,4 @@ local args = Util.parse(...)
|
|||
Browser:setDir(args[1] or shell.dir())
|
||||
|
||||
UI:setPage(Browser)
|
||||
|
||||
Event.pullEvents()
|
||||
UI.term:reset()
|
||||
UI:start()
|
||||
|
|
|
@ -382,11 +382,12 @@ function page:executeStatement(statement)
|
|||
end
|
||||
end
|
||||
|
||||
local args = Util.parse(...)
|
||||
local args = { ... }
|
||||
if args[1] then
|
||||
command = 'args[1]'
|
||||
sandboxEnv.args = args
|
||||
page:setResult(args[1])
|
||||
page:setPrompt(command)
|
||||
end
|
||||
|
||||
UI:setPage(page)
|
||||
|
|
|
@ -95,6 +95,7 @@ local page = UI.Page {
|
|||
ey = -2,
|
||||
width = 8,
|
||||
selectedBackgroundColor = UI.colors.primary,
|
||||
backgroundColor = UI.colors.tertiary,
|
||||
layout = function(self)
|
||||
self.height = nil
|
||||
UI.TabBar.layout(self)
|
||||
|
|
|
@ -12,7 +12,7 @@ local gridColumns = {}
|
|||
table.insert(gridColumns, { heading = '#', key = 'id', width = 5, align = 'right' })
|
||||
table.insert(gridColumns, { heading = 'Port', key = 'portid', width = 5, align = 'right' })
|
||||
table.insert(gridColumns, { heading = 'Reply', key = 'replyid', width = 5, align = 'right' })
|
||||
if UI.defaultDevice.width > 50 then
|
||||
if UI.term.width > 50 then
|
||||
table.insert(gridColumns, { heading = 'Dist', key = 'distance', width = 6, align = 'right' })
|
||||
end
|
||||
table.insert(gridColumns, { heading = 'Msg', key = 'packetStr' })
|
||||
|
@ -42,7 +42,7 @@ local page = UI.Page {
|
|||
|
||||
configSlide = UI.SlideOut {
|
||||
y = -11,
|
||||
titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close' },
|
||||
titleBar = UI.TitleBar { title = 'Sniffer Config', event = 'config_close', backgroundColor = colors.black },
|
||||
accelerators = { ['backspace'] = 'config_close' },
|
||||
configTabs = UI.Tabs {
|
||||
y = 2,
|
||||
|
|
|
@ -28,10 +28,10 @@ if _G.http.websocket then
|
|||
event = 'update_key',
|
||||
},
|
||||
labelText = UI.TextArea {
|
||||
x = 2, ex = -2, y = 6, ey = -4,
|
||||
x = 2, ex = -2, y = 5, ey = -4,
|
||||
textColor = colors.yellow,
|
||||
backgroundColor = colors.black,
|
||||
marginLeft = 0, marginRight = 0,
|
||||
marginLeft = 1, marginRight = 1, marginTop = 1,
|
||||
value = string.format(
|
||||
[[Use a non-changing cloud key. Note that only a single computer can use this session at one time.
|
||||
To obtain a key, visit:
|
||||
|
|
|
@ -23,7 +23,7 @@ return UI.Tab {
|
|||
x = 2, y = 2, ex = -2, ey = 4,
|
||||
},
|
||||
grid = UI.ScrollingGrid {
|
||||
x = 2, y = 6, ex = -2, ey = -2,
|
||||
x = 2, y = 5, ex = -2, ey = -2,
|
||||
values = {
|
||||
{ name = '', value = '' },
|
||||
{ name = 'CC version', value = Util.getVersion() },
|
||||
|
|
|
@ -35,9 +35,10 @@ local tab = UI.Tab {
|
|||
event = 'update',
|
||||
},
|
||||
labelText = UI.TextArea {
|
||||
x = 2, ex = -2, y = 7, ey = -4,
|
||||
x = 2, ex = -2, y = 6, ey = -4,
|
||||
backgroundColor = colors.black,
|
||||
textColor = colors.yellow,
|
||||
marginLeft = 1, marginRight = 1, marginTop = 1,
|
||||
value = 'Choose an application launcher',
|
||||
},
|
||||
}
|
||||
|
|
|
@ -9,8 +9,9 @@ return UI.Tab {
|
|||
tabTitle = 'Network',
|
||||
description = 'Networking options',
|
||||
info = UI.TextArea {
|
||||
x = 2, y = 6, ex = -2, ey = -2,
|
||||
x = 2, y = 5, ex = -2, ey = -2,
|
||||
backgroundColor = colors.black,
|
||||
marginLeft = 1, marginRight = 1, marginTop = 1,
|
||||
value = string.format(
|
||||
[[%sSet the primary modem used for wireless communications.%s
|
||||
|
||||
|
|
|
@ -25,10 +25,11 @@ return UI.Tab {
|
|||
event = 'update_password',
|
||||
},
|
||||
info = UI.TextArea {
|
||||
x = 2, ex = -2, y = 6, ey = -4,
|
||||
x = 2, ex = -2, y = 5, ey = -4,
|
||||
backgroundColor = colors.black,
|
||||
textColor = colors.yellow,
|
||||
inactive = true,
|
||||
marginLeft = 1, marginRight = 1, marginTop = 1,
|
||||
value = 'Add a password to enable other computers to connect to this one.',
|
||||
},
|
||||
eventHandler = function(self, event)
|
||||
|
|
|
@ -2,49 +2,94 @@ local UI = require('opus.ui')
|
|||
|
||||
local settings = _G.settings
|
||||
|
||||
if settings then
|
||||
local settingsTab = UI.Tab {
|
||||
tabTitle = 'Settings',
|
||||
description = 'Computercraft configurable settings',
|
||||
grid = UI.Grid {
|
||||
x = 2, y = 2, ex = -2, ey = -2,
|
||||
autospace = true,
|
||||
sortColumn = 'name',
|
||||
columns = {
|
||||
{ heading = 'Setting', key = 'name' },
|
||||
{ heading = 'Value', key = 'value' },
|
||||
},
|
||||
},
|
||||
}
|
||||
local transform = {
|
||||
string = tostring,
|
||||
number = tonumber,
|
||||
}
|
||||
|
||||
function settingsTab:enable()
|
||||
return settings and UI.Tab {
|
||||
tabTitle = 'Settings',
|
||||
description = 'Computercraft settings',
|
||||
grid = UI.Grid {
|
||||
x = 2, y = 2, ex = -2, ey = -2,
|
||||
sortColumn = 'name',
|
||||
columns = {
|
||||
{ heading = 'Setting', key = 'name' },
|
||||
{ heading = 'Value', key = 'value' },
|
||||
},
|
||||
},
|
||||
editor = UI.SlideOut {
|
||||
y = -6, height = 6,
|
||||
titleBar = UI.TitleBar {
|
||||
event = 'slide_hide',
|
||||
title = 'Enter value',
|
||||
},
|
||||
form = UI.Form {
|
||||
y = 2,
|
||||
value = UI.TextEntry {
|
||||
limit = 256,
|
||||
formIndex = 1,
|
||||
formLabel = 'Value',
|
||||
formKey = 'value',
|
||||
},
|
||||
validateField = function(self, entry)
|
||||
if entry.value then
|
||||
return transform[self.type](entry.value)
|
||||
end
|
||||
return true
|
||||
end,
|
||||
},
|
||||
accelerators = {
|
||||
form_cancel = 'slide_hide',
|
||||
},
|
||||
show = function(self, entry)
|
||||
self.form.type = type(entry.value) or 'string'
|
||||
self.form:setValues(entry)
|
||||
self.titleBar.title = entry.name
|
||||
UI.SlideOut.show(self)
|
||||
end,
|
||||
eventHandler = function(self, event)
|
||||
if event.type == 'form_complete' then
|
||||
if not event.values.value then
|
||||
settings.unset(event.values.name)
|
||||
self.parent:reload()
|
||||
else
|
||||
event.values.value = transform[self.form.type](event.values.value)
|
||||
settings.set(event.values.name, event.values.value)
|
||||
end
|
||||
self.parent.grid:draw()
|
||||
self:hide()
|
||||
settings.save('.settings')
|
||||
end
|
||||
return UI.SlideOut.eventHandler(self, event)
|
||||
end,
|
||||
},
|
||||
reload = function(self)
|
||||
local values = { }
|
||||
for _,v in pairs(settings.getNames()) do
|
||||
local value = settings.get(v)
|
||||
if not value then
|
||||
value = false
|
||||
end
|
||||
table.insert(values, {
|
||||
name = v,
|
||||
value = value,
|
||||
value = settings.get(v) or false,
|
||||
})
|
||||
end
|
||||
self.grid:setValues(values)
|
||||
self.grid:setIndex(1)
|
||||
end,
|
||||
enable = function(self)
|
||||
self:reload()
|
||||
UI.Tab.enable(self)
|
||||
end
|
||||
|
||||
function settingsTab:eventHandler(event)
|
||||
end,
|
||||
eventHandler = function(self, event)
|
||||
if event.type == 'grid_select' then
|
||||
if not event.selected.value or type(event.selected.value) == 'boolean' then
|
||||
if type(event.selected.value) == 'boolean' then
|
||||
event.selected.value = not event.selected.value
|
||||
settings.set(event.selected.name, event.selected.value)
|
||||
settings.save('.settings')
|
||||
self.grid:draw()
|
||||
else
|
||||
self.editor:show(event.selected)
|
||||
end
|
||||
settings.set(event.selected.name, event.selected.value)
|
||||
settings.save('.settings')
|
||||
self.grid:draw()
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
-- this needs lots of work - currently only works with booleans
|
||||
--return settingsTab
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
|
|
@ -32,3 +32,23 @@ end
|
|||
|
||||
help.setPath(table.concat(helpPaths, ':'))
|
||||
shell.setPath(table.concat(appPaths, ':'))
|
||||
|
||||
local function runDir(directory)
|
||||
local files = fs.list(directory)
|
||||
table.sort(files)
|
||||
|
||||
for _,file in ipairs(files) do
|
||||
os.sleep(0)
|
||||
local result, err = shell.run(directory .. '/' .. file)
|
||||
if not result and err then
|
||||
_G.printError('\n' .. err)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for _, package in pairs(Packages:installedSorted()) do
|
||||
local packageDir = 'packages/' .. package.name .. '/init'
|
||||
if fs.exists(packageDir) and fs.isDir(packageDir) then
|
||||
runDir(packageDir)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,7 +65,7 @@ function GPS.locate(timeout, debug)
|
|||
if debug then
|
||||
print("Position is "..pos.x..","..pos.y..","..pos.z)
|
||||
end
|
||||
return vector.new(pos.x, pos.y, pos.z)
|
||||
return pos and vector.new(pos.x, pos.y, pos.z)
|
||||
end
|
||||
|
||||
function GPS.isAvailable()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local Array = require('opus.array')
|
||||
local Blit = require('opus.ui.blit')
|
||||
local class = require('opus.class')
|
||||
local Event = require('opus.event')
|
||||
local Input = require('opus.input')
|
||||
|
@ -34,11 +35,16 @@ local textutils = _G.textutils
|
|||
]]
|
||||
|
||||
--[[-- Top Level Manager --]]--
|
||||
local Manager = class()
|
||||
function Manager:init()
|
||||
local UI = { }
|
||||
function UI:init()
|
||||
self.devices = { }
|
||||
self.theme = { }
|
||||
self.extChars = Util.getVersion() >= 1.76
|
||||
self.colors = {
|
||||
primary = colors.green,
|
||||
secondary = colors.lightGray,
|
||||
tertiary = colors.gray,
|
||||
}
|
||||
|
||||
local function keyFunction(event, code, held)
|
||||
local ie = Input:translate(event, code, held)
|
||||
|
@ -151,7 +157,7 @@ function Manager:init()
|
|||
end)
|
||||
end
|
||||
|
||||
function Manager:configure(appName, ...)
|
||||
function UI:configure(appName, ...)
|
||||
local defaults = Util.loadTable('usr/config/' .. appName) or { }
|
||||
if not defaults.device then
|
||||
defaults.device = { }
|
||||
|
@ -193,11 +199,11 @@ function Manager:configure(appName, ...)
|
|||
end
|
||||
end
|
||||
|
||||
function Manager:disableEffects()
|
||||
self.defaultDevice.effectsEnabled = false
|
||||
function UI:disableEffects()
|
||||
self.term.effectsEnabled = false
|
||||
end
|
||||
|
||||
function Manager:loadTheme(filename)
|
||||
function UI:loadTheme(filename)
|
||||
if fs.exists(filename) then
|
||||
local theme, err = Util.loadTable(filename)
|
||||
if not theme then
|
||||
|
@ -207,7 +213,7 @@ function Manager:loadTheme(filename)
|
|||
end
|
||||
end
|
||||
|
||||
function Manager:generateTheme(filename)
|
||||
function UI:generateTheme(filename)
|
||||
local t = { }
|
||||
|
||||
local function getName(d)
|
||||
|
@ -244,14 +250,14 @@ function Manager:generateTheme(filename)
|
|||
Util.writeFile(filename, textutils.serialize(t):gsub('(")', ''))
|
||||
end
|
||||
|
||||
function Manager:emitEvent(event)
|
||||
function UI:emitEvent(event)
|
||||
local currentPage = self:getActivePage()
|
||||
if currentPage and currentPage.focused then
|
||||
return currentPage.focused:emit(event)
|
||||
end
|
||||
end
|
||||
|
||||
function Manager:click(target, ie)
|
||||
function UI:click(target, ie)
|
||||
local clickEvent
|
||||
|
||||
if ie.code == 'mouse_drag' then
|
||||
|
@ -304,23 +310,22 @@ function Manager:click(target, ie)
|
|||
target:sync()
|
||||
end
|
||||
|
||||
function Manager:setDefaultDevice(dev)
|
||||
self.defaultDevice = dev
|
||||
function UI:setDefaultDevice(dev)
|
||||
self.term = dev
|
||||
end
|
||||
|
||||
function Manager:addPage(name, page)
|
||||
function UI:addPage(name, page)
|
||||
if not self.pages then
|
||||
self.pages = { }
|
||||
end
|
||||
self.pages[name] = page
|
||||
end
|
||||
|
||||
function Manager:setPages(pages)
|
||||
function UI:setPages(pages)
|
||||
self.pages = pages
|
||||
end
|
||||
|
||||
function Manager:getPage(pageName)
|
||||
function UI:getPage(pageName)
|
||||
local page = self.pages[pageName]
|
||||
|
||||
if not page then
|
||||
|
@ -330,18 +335,18 @@ function Manager:getPage(pageName)
|
|||
return page
|
||||
end
|
||||
|
||||
function Manager:getActivePage(page)
|
||||
function UI:getActivePage(page)
|
||||
if page then
|
||||
return page.parent.currentPage
|
||||
end
|
||||
return self.defaultDevice.currentPage
|
||||
return self.term.currentPage
|
||||
end
|
||||
|
||||
function Manager:setActivePage(page)
|
||||
function UI:setActivePage(page)
|
||||
page.parent.currentPage = page
|
||||
end
|
||||
|
||||
function Manager:setPage(pageOrName, ...)
|
||||
function UI:setPage(pageOrName, ...)
|
||||
local page = pageOrName
|
||||
|
||||
if type(pageOrName) == 'string' then
|
||||
|
@ -361,7 +366,6 @@ function Manager:setPage(pageOrName, ...)
|
|||
page.previousPage = currentPage
|
||||
end
|
||||
self:setActivePage(page)
|
||||
--page:clear(page.backgroundColor)
|
||||
page:enable(...)
|
||||
page:draw()
|
||||
if page.focused then
|
||||
|
@ -372,27 +376,27 @@ function Manager:setPage(pageOrName, ...)
|
|||
end
|
||||
end
|
||||
|
||||
function Manager:getCurrentPage()
|
||||
return self.defaultDevice.currentPage
|
||||
function UI:getCurrentPage()
|
||||
return self.term.currentPage
|
||||
end
|
||||
|
||||
function Manager:setPreviousPage()
|
||||
if self.defaultDevice.currentPage.previousPage then
|
||||
local previousPage = self.defaultDevice.currentPage.previousPage.previousPage
|
||||
self:setPage(self.defaultDevice.currentPage.previousPage)
|
||||
self.defaultDevice.currentPage.previousPage = previousPage
|
||||
function UI:setPreviousPage()
|
||||
if self.term.currentPage.previousPage then
|
||||
local previousPage = self.term.currentPage.previousPage.previousPage
|
||||
self:setPage(self.term.currentPage.previousPage)
|
||||
self.term.currentPage.previousPage = previousPage
|
||||
end
|
||||
end
|
||||
|
||||
function Manager:getDefaults(element, args)
|
||||
function UI:getDefaults(element, args)
|
||||
local defaults = Util.deepCopy(element.defaults)
|
||||
if args then
|
||||
Manager:mergeProperties(defaults, args)
|
||||
UI:mergeProperties(defaults, args)
|
||||
end
|
||||
return defaults
|
||||
end
|
||||
|
||||
function Manager:mergeProperties(obj, args)
|
||||
function UI:mergeProperties(obj, args)
|
||||
if args then
|
||||
for k,v in pairs(args) do
|
||||
if k == 'accelerators' then
|
||||
|
@ -408,7 +412,7 @@ function Manager:mergeProperties(obj, args)
|
|||
end
|
||||
end
|
||||
|
||||
function Manager:pullEvents(...)
|
||||
function UI:pullEvents(...)
|
||||
local s, m = pcall(Event.pullEvents, ...)
|
||||
self.term:reset()
|
||||
if not s and m then
|
||||
|
@ -416,17 +420,11 @@ function Manager:pullEvents(...)
|
|||
end
|
||||
end
|
||||
|
||||
Manager.colors = {
|
||||
primary = colors.cyan,
|
||||
secondary = colors.lightGray,
|
||||
tertiary = colors.gray,
|
||||
}
|
||||
UI.exitPullEvents = Event.exitPullEvents
|
||||
UI.quit = Event.exitPullEvents
|
||||
UI.start = UI.pullEvents
|
||||
|
||||
Manager.exitPullEvents = Event.exitPullEvents
|
||||
Manager.quit = Event.exitPullEvents
|
||||
Manager.start = Manager.pullEvents
|
||||
|
||||
local UI = Manager()
|
||||
UI:init()
|
||||
|
||||
--[[-- Basic drawable area --]]--
|
||||
UI.Window = class(Canvas)
|
||||
|
@ -785,89 +783,19 @@ function UI.Window:print(text, bg, fg)
|
|||
local marginLeft = self.marginLeft or 0
|
||||
local marginRight = self.marginRight or 0
|
||||
local width = self.width - marginLeft - marginRight
|
||||
local cs = {
|
||||
bg = bg or self:getProperty('backgroundColor'),
|
||||
fg = fg or self:getProperty('textColor'),
|
||||
palette = self.palette,
|
||||
}
|
||||
|
||||
local function nextWord(line, cx)
|
||||
local result = { line:find("(%w+)", cx) }
|
||||
if #result > 1 and result[2] > cx then
|
||||
return _sub(line, cx, result[2] + 1)
|
||||
elseif #result > 0 and result[1] == cx then
|
||||
result = { line:find("(%w+)", result[2]) }
|
||||
if #result > 0 then
|
||||
return _sub(line, cx, result[1] + 1)
|
||||
end
|
||||
end
|
||||
if cx <= #line then
|
||||
return _sub(line, cx, #line)
|
||||
local y = (self.marginTop or 0) + 1
|
||||
for _,line in pairs(Util.split(text)) do
|
||||
for _, ln in ipairs(Blit(line, cs):wrap(width)) do
|
||||
self:blit(marginLeft + 1, y, ln.text, ln.bg, ln.fg)
|
||||
y = y + 1
|
||||
end
|
||||
end
|
||||
|
||||
local function pieces(f, bg, fg)
|
||||
local pos = 1
|
||||
local t = { }
|
||||
while true do
|
||||
local s = string.find(f, '\027', pos, true)
|
||||
if not s then
|
||||
break
|
||||
end
|
||||
if pos < s then
|
||||
table.insert(t, _sub(f, pos, s - 1))
|
||||
end
|
||||
local seq = _sub(f, s)
|
||||
seq = seq:match("\027%[([%d;]+)m")
|
||||
local e = { }
|
||||
for color in string.gmatch(seq, "%d+") do
|
||||
color = tonumber(color)
|
||||
if color == 0 then
|
||||
e.fg = fg
|
||||
e.bg = bg
|
||||
elseif color > 20 then
|
||||
e.bg = 2 ^ (color - 21)
|
||||
else
|
||||
e.fg = 2 ^ (color - 1)
|
||||
end
|
||||
end
|
||||
table.insert(t, e)
|
||||
pos = s + #seq + 3
|
||||
end
|
||||
if pos <= #f then
|
||||
table.insert(t, _sub(f, pos))
|
||||
end
|
||||
return t
|
||||
end
|
||||
|
||||
local lines = Util.split(text)
|
||||
for k,line in pairs(lines) do
|
||||
local fragments = pieces(line, bg, fg)
|
||||
for _, fragment in ipairs(fragments) do
|
||||
local lx = 1
|
||||
if type(fragment) == 'table' then -- ansi sequence
|
||||
fg = fragment.fg
|
||||
bg = fragment.bg
|
||||
else
|
||||
while true do
|
||||
local word = nextWord(fragment, lx)
|
||||
if not word then
|
||||
break
|
||||
end
|
||||
local w = word
|
||||
if self.cursorX + #word > width then
|
||||
self.cursorX = marginLeft + 1
|
||||
self.cursorY = self.cursorY + 1
|
||||
w = word:gsub('^ ', '')
|
||||
end
|
||||
self:write(self.cursorX, self.cursorY, w, bg, fg)
|
||||
self.cursorX = self.cursorX + #w
|
||||
lx = lx + #word
|
||||
end
|
||||
end
|
||||
end
|
||||
if lines[k + 1] then
|
||||
self.cursorX = marginLeft + 1
|
||||
self.cursorY = self.cursorY + 1
|
||||
end
|
||||
end
|
||||
|
||||
return self.cursorX, self.cursorY
|
||||
end
|
||||
|
||||
UI.Window.docs.focus = [[focus(VOID)
|
||||
|
@ -1126,7 +1054,7 @@ function UI.Device:runTransitions(transitions)
|
|||
transitions[k] = nil
|
||||
end
|
||||
end
|
||||
self.currentPage:render(self.device)
|
||||
self.currentPage:render(self, true)
|
||||
if Util.empty(transitions) then
|
||||
break
|
||||
end
|
||||
|
@ -1143,7 +1071,7 @@ function UI.Device:sync()
|
|||
if transitions then
|
||||
self:runTransitions(transitions)
|
||||
else
|
||||
self.currentPage:render(self.device)
|
||||
self.currentPage:render(self, true)
|
||||
end
|
||||
|
||||
if self:getCursorBlink() then
|
||||
|
@ -1179,7 +1107,7 @@ local function loadComponents()
|
|||
return self(...)
|
||||
end
|
||||
})
|
||||
UI[name]._preload = function(self)
|
||||
UI[name]._preload = function()
|
||||
return load(name)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
local class = require('opus.class')
|
||||
|
||||
local colors = _G.colors
|
||||
|
||||
local Blit = class()
|
||||
|
||||
function Blit:init(t, cs)
|
||||
if type(t) == 'string' then
|
||||
t = Blit.toblit(t, cs or { })
|
||||
end
|
||||
self.text = t.text
|
||||
self.bg = t.bg
|
||||
self.fg = t.fg
|
||||
end
|
||||
|
||||
function Blit:sub(s, e)
|
||||
return Blit({
|
||||
text = self.text:sub(s, e),
|
||||
bg = self.bg:sub(s, e),
|
||||
fg = self.fg:sub(s, e),
|
||||
})
|
||||
end
|
||||
|
||||
function Blit:wrap(max)
|
||||
local index = 1
|
||||
local lines = { }
|
||||
local data = self
|
||||
|
||||
repeat
|
||||
if #data.text <= max then
|
||||
table.insert(lines, data)
|
||||
break
|
||||
elseif data.text:sub(max+1, max+1) == ' ' then
|
||||
table.insert(lines, data:sub(index, max))
|
||||
data = data:sub(max + 2)
|
||||
else
|
||||
local x = data.text:sub(1, max)
|
||||
local s = x:match('(.*) ') or x
|
||||
table.insert(lines, data:sub(1, #s))
|
||||
data = data:sub(#s + 1)
|
||||
end
|
||||
local t = data.text:match('^%s*(.*)')
|
||||
local spaces = #data.text - #t
|
||||
if spaces > 0 then
|
||||
data = data:sub(spaces + 1)
|
||||
end
|
||||
until not data.text or #data.text == 0
|
||||
|
||||
return lines
|
||||
end
|
||||
|
||||
-- convert a string of text to blit format doing color conversion
|
||||
-- and processing ansi color sequences
|
||||
function Blit.toblit(str, cs)
|
||||
local text, fg, bg = '', '', ''
|
||||
|
||||
if not cs.cbg then
|
||||
-- reset colors
|
||||
cs.rbg = cs.palette[cs.bg or colors.black]
|
||||
cs.rfg = cs.palette[cs.fg or colors.white]
|
||||
-- current colors
|
||||
cs.cbg = cs.rbg
|
||||
cs.cfg = cs.rfg
|
||||
end
|
||||
|
||||
str = str:gsub('(.-)\027%[([%d;]+)m',
|
||||
function(k, seq)
|
||||
text = text .. k
|
||||
bg = bg .. string.rep(cs.cbg, #k)
|
||||
fg = fg .. string.rep(cs.cfg, #k)
|
||||
for color in string.gmatch(seq, "%d+") do
|
||||
color = tonumber(color)
|
||||
if color == 0 then
|
||||
-- reset to default
|
||||
cs.cfg = cs.rfg
|
||||
cs.cbg = cs.rbg
|
||||
elseif color > 20 then
|
||||
cs.cbg = string.sub("0123456789abcdef", color - 21, color - 21)
|
||||
else
|
||||
cs.cfg = string.sub("0123456789abcdef", color, color)
|
||||
end
|
||||
end
|
||||
return k
|
||||
end)
|
||||
|
||||
local k = str:sub(#text + 1)
|
||||
return {
|
||||
text = text .. k,
|
||||
bg = bg .. string.rep(cs.cbg, #k),
|
||||
fg = fg .. string.rep(cs.cfg, #k),
|
||||
}
|
||||
end
|
||||
|
||||
return Blit
|
|
@ -71,7 +71,7 @@ end
|
|||
|
||||
-- resize the canvas buffer - not the canvas itself
|
||||
function Canvas:resizeBuffer(w, h)
|
||||
for i = #self.lines, h do
|
||||
for i = #self.lines + 1, h do
|
||||
self.lines[i] = { }
|
||||
self:clearLine(i)
|
||||
end
|
||||
|
@ -297,40 +297,38 @@ function Canvas:applyPalette(palette)
|
|||
self.palette = palette
|
||||
end
|
||||
|
||||
function Canvas:render(device)
|
||||
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
|
||||
while parent do
|
||||
offset.x = offset.x + parent.x - 1
|
||||
offset.y = offset.y + parent.y - 1
|
||||
parent = parent.parent
|
||||
end
|
||||
|
||||
-- TODO: need to clip if there is a parent
|
||||
--self.regions = Region.new(self.x + offset.x, self.y + offset.y, self.ex + offset.x, self.ey + offset.y)
|
||||
--self:__renderLayers(device, offset)
|
||||
|
||||
-- either render directly to the device
|
||||
-- or use another canvas as a backing buffer
|
||||
function Canvas:render(device, doubleBuffer)
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
self:__renderLayers(device, { x = self.x - 1, y = self.y - 1 })
|
||||
self:__renderLayers(device, { x = self.x - 1, y = self.y - 1 }, doubleBuffer)
|
||||
|
||||
-- doubleBuffering to reduce the amount of
|
||||
-- setCursorPos, blits
|
||||
if doubleBuffer then
|
||||
--[[
|
||||
local drew = false
|
||||
local bg = _rep(2, device.width)
|
||||
for k,v in pairs(device.lines) do
|
||||
if v.dirty then
|
||||
device.device.setCursorPos(device.x, device.y + k - 1)
|
||||
device.device.blit(v.text, v.fg, bg)
|
||||
drew = true
|
||||
end
|
||||
end
|
||||
if drew then
|
||||
local c = os.clock()
|
||||
repeat until os.clock()-c > .1
|
||||
end
|
||||
]]
|
||||
for k,v in pairs(device.lines) do
|
||||
if v.dirty then
|
||||
device.device.setCursorPos(device.x, device.y + k - 1)
|
||||
device.device.blit(v.text, v.fg, v.bg)
|
||||
v.dirty = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- regions are comprised of absolute values that correspond to the output device.
|
||||
|
@ -338,7 +336,7 @@ end
|
|||
-- 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
|
||||
-- the array.
|
||||
function Canvas:__renderLayers(device, offset)
|
||||
function Canvas:__renderLayers(device, offset, doubleBuffer)
|
||||
if self.children then
|
||||
for i = #self.children, 1, -1 do
|
||||
local canvas = self.children[i]
|
||||
|
@ -364,7 +362,7 @@ function Canvas:__renderLayers(device, offset)
|
|||
canvas:__renderLayers(device, {
|
||||
x = canvas.x + offset.x - 1 - (self.offx or 0),
|
||||
y = canvas.y + offset.y - 1 - (self.offy or 0),
|
||||
})
|
||||
}, doubleBuffer)
|
||||
end
|
||||
canvas.regions = nil
|
||||
end
|
||||
|
@ -377,19 +375,19 @@ function Canvas:__renderLayers(device, offset)
|
|||
y = region[2] - offset.y,
|
||||
ex = region[3] - offset.x,
|
||||
ey = region[4] - offset.y },
|
||||
{ x = region[1], y = region[2] })
|
||||
{ x = region[1], y = region[2] }, doubleBuffer)
|
||||
end
|
||||
self.regions = nil
|
||||
|
||||
self:clean()
|
||||
end
|
||||
|
||||
-- performance can probably be improved by using one more buffer tied to the device
|
||||
function Canvas:__blitRect(device, src, tgt)
|
||||
function Canvas:__blitRect(device, src, tgt, doubleBuffer)
|
||||
src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 }
|
||||
tgt = tgt or self
|
||||
|
||||
-- for visualizing updates on the screen
|
||||
--[[
|
||||
if Canvas.__visualize or self.visualize then
|
||||
local drew
|
||||
local t = _rep(' ', src.ex-src.x + 1)
|
||||
|
@ -407,6 +405,7 @@ function Canvas:__blitRect(device, src, tgt)
|
|||
repeat until os.clock()-c > .03
|
||||
end
|
||||
end
|
||||
]]
|
||||
for i = 0, src.ey - src.y do
|
||||
local line = self.lines[src.y + i + (self.offy or 0)]
|
||||
if line and line.dirty then
|
||||
|
@ -416,8 +415,13 @@ function Canvas:__blitRect(device, src, tgt)
|
|||
fg = _sub(fg, src.x, src.ex)
|
||||
bg = _sub(bg, src.x, src.ex)
|
||||
end
|
||||
device.setCursorPos(tgt.x, tgt.y + i)
|
||||
device.blit(t, fg, bg)
|
||||
if doubleBuffer then
|
||||
Canvas.blit(device, tgt.x, tgt.y + i,
|
||||
t, bg, fg)
|
||||
else
|
||||
device.setCursorPos(tgt.x, tgt.y + i)
|
||||
device.blit(t, fg, bg)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -7,8 +7,7 @@ UI.Dialog = class(UI.SlideOut)
|
|||
UI.Dialog.defaults = {
|
||||
UIElement = 'Dialog',
|
||||
height = 7,
|
||||
textColor = colors.black,
|
||||
backgroundColor = colors.white,
|
||||
noFill = true,
|
||||
okEvent ='dialog_ok',
|
||||
cancelEvent = 'dialog_cancel',
|
||||
}
|
||||
|
@ -17,19 +16,6 @@ function UI.Dialog:postInit()
|
|||
self.titleBar = UI.TitleBar({ event = self.cancelEvent, title = self.title })
|
||||
end
|
||||
|
||||
function UI.Dialog:show(...)
|
||||
local canvas = self.parent
|
||||
self.oldPalette = canvas.palette
|
||||
canvas:applyPalette(self.darkPalette)
|
||||
UI.SlideOut.show(self, ...)
|
||||
end
|
||||
|
||||
function UI.Dialog:hide(...)
|
||||
self.parent.palette = self.oldPalette
|
||||
UI.SlideOut.hide(self, ...)
|
||||
self.parent:draw()
|
||||
end
|
||||
|
||||
function UI.Dialog:eventHandler(event)
|
||||
if event.type == 'dialog_cancel' then
|
||||
self:hide()
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
local class = require('opus.class')
|
||||
local Event = require('opus.event')
|
||||
local Terminal = require('opus.terminal')
|
||||
local UI = require('opus.ui')
|
||||
|
||||
|
@ -19,10 +20,16 @@ function UI.Embedded:layout()
|
|||
UI.Window.layout(self)
|
||||
|
||||
if not self.win then
|
||||
local t
|
||||
function self.render()
|
||||
self:sync()
|
||||
if self.focused then
|
||||
self:setCursorPos(self.win.getCursorPos())
|
||||
if not t then
|
||||
t = Event.onTimeout(0, function()
|
||||
t = nil
|
||||
if self.focused then
|
||||
self:setCursorPos(self.win.getCursorPos())
|
||||
end
|
||||
self:sync()
|
||||
end)
|
||||
end
|
||||
end
|
||||
self.win = Terminal.window(UI.term.device, self.x, self.y, self.width, self.height, false)
|
||||
|
@ -68,7 +75,6 @@ function UI.Embedded:eventHandler(event)
|
|||
end
|
||||
|
||||
function UI.Embedded.example()
|
||||
local Event = require('opus.event')
|
||||
local Util = require('opus.util')
|
||||
local term = _G.term
|
||||
|
||||
|
|
|
@ -93,7 +93,8 @@ function UI.MenuBar.example()
|
|||
return UI.MenuBar {
|
||||
buttons = {
|
||||
{ text = 'Choice1', event = 'event1' },
|
||||
{ text = 'Choice2', event = 'event2' },
|
||||
{ text = 'Choice2', event = 'event2', inactive = true },
|
||||
{ text = 'Choice3', event = 'event3' },
|
||||
}
|
||||
}
|
||||
end
|
||||
|
|
|
@ -5,4 +5,5 @@ UI.MenuItem = class(UI.FlatButton)
|
|||
UI.MenuItem.defaults = {
|
||||
UIElement = 'MenuItem',
|
||||
noPadding = false,
|
||||
textInactiveColor = colors.gray,
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ UI.Page.defaults = {
|
|||
textColor = colors.white,
|
||||
}
|
||||
function UI.Page:postInit()
|
||||
self.parent = self.parent or UI.defaultDevice
|
||||
self.parent = self.parent or UI.term
|
||||
self.__target = self
|
||||
end
|
||||
|
||||
|
|
|
@ -22,6 +22,9 @@ function UI.ScrollBar:draw()
|
|||
|
||||
self:clear()
|
||||
|
||||
-- ...
|
||||
self:write(1, 1, ' ', view.fill)
|
||||
|
||||
if view.totalHeight > view.height then
|
||||
local maxScroll = view.totalHeight - view.height
|
||||
local percent = view.offsetY / maxScroll
|
||||
|
|
|
@ -29,6 +29,7 @@ function UI.ScrollingGrid:getViewArea()
|
|||
height = self.pageSize, -- viewable height
|
||||
totalHeight = Util.size(self.values), -- total height
|
||||
offsetY = self.scrollOffset, -- scroll offset
|
||||
fill = not self.disableHeader and self.headerBackgroundColor,
|
||||
}
|
||||
end
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ UI.TabBar = class(UI.MenuBar)
|
|||
UI.TabBar.defaults = {
|
||||
UIElement = 'TabBar',
|
||||
buttonClass = 'TabBarMenuItem',
|
||||
backgroundColor = UI.colors.tertiary,
|
||||
backgroundColor = colors.black,
|
||||
selectedBackgroundColor = UI.colors.primary,
|
||||
unselectedBackgroundColor = UI.colors.tertiary,
|
||||
}
|
||||
|
@ -32,7 +32,7 @@ function UI.TabBar:eventHandler(event)
|
|||
self:emit({ type = 'tab_change', current = si, last = pi, tab = selected })
|
||||
end
|
||||
end
|
||||
UI.MenuBar.draw(self)
|
||||
self:draw(self)
|
||||
end
|
||||
return UI.MenuBar.eventHandler(self, event)
|
||||
end
|
||||
|
|
|
@ -5,6 +5,7 @@ UI.TabBarMenuItem = class(UI.Button)
|
|||
UI.TabBarMenuItem.defaults = {
|
||||
UIElement = 'TabBarMenuItem',
|
||||
event = 'tab_select',
|
||||
textInactiveColor = colors.lightGray,
|
||||
}
|
||||
function UI.TabBarMenuItem:draw()
|
||||
if self.selected then
|
||||
|
|
|
@ -117,5 +117,9 @@ function UI.Tabs.example()
|
|||
index = 3,
|
||||
tabTitle = 'tab3',
|
||||
},
|
||||
enable = function(self)
|
||||
UI.Tabs.enable(self)
|
||||
self:setActive(self.tab3, false)
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
|
|
@ -20,7 +20,6 @@ end
|
|||
|
||||
function UI.TextArea:draw()
|
||||
self:clear()
|
||||
self.cursorX, self.cursorY = 1, 1
|
||||
self:print(self.value)
|
||||
self:drawChildren()
|
||||
end
|
||||
|
|
|
@ -47,11 +47,18 @@ function UI.Viewport:setScrollPosition(offy, offx) -- argh - reverse
|
|||
end
|
||||
end
|
||||
|
||||
function UI.Viewport:write(x, y, text, bg, tc)
|
||||
function UI.Viewport:blit(x, y, text, bg, fg)
|
||||
if y > #self.lines then
|
||||
self:resizeBuffer(self.width, y)
|
||||
end
|
||||
return UI.Window.write(self, x, y, text, bg, tc)
|
||||
return UI.Window.blit(self, x, y, text, bg, fg)
|
||||
end
|
||||
|
||||
function UI.Viewport:write(x, y, text, bg, fg)
|
||||
if y > #self.lines then
|
||||
self:resizeBuffer(self.width, y)
|
||||
end
|
||||
return UI.Window.write(self, x, y, text, bg, fg)
|
||||
end
|
||||
|
||||
function UI.Viewport:setViewHeight(h)
|
||||
|
|
|
@ -699,6 +699,31 @@ local function paragraphwrap(text, linewidth, res)
|
|||
end
|
||||
-- end word wrapping
|
||||
|
||||
--[[
|
||||
-- better wrapping - needs further testing before replacing the current wrapping
|
||||
functions
|
||||
local function wrap(text, max)
|
||||
local index = 1
|
||||
local lines = { }
|
||||
repeat
|
||||
if #text <= max then
|
||||
table.insert(lines, text)
|
||||
text = ''
|
||||
elseif text:sub(max+1, max+1) == ' ' then
|
||||
table.insert(lines, text:sub(index, max))
|
||||
text = text:sub(max + 2)
|
||||
else
|
||||
local x = text:sub(1, max)
|
||||
local s = x:match('(.*) ') or x
|
||||
text = text:sub(#s + 1)
|
||||
table.insert(lines, s)
|
||||
end
|
||||
text = text:match('^%s*(.*)')
|
||||
until not text or #text == 0
|
||||
return lines
|
||||
end
|
||||
]]
|
||||
|
||||
function Util.wordWrap(str, limit)
|
||||
local longLines = Util.split(str)
|
||||
local lines = { }
|
||||
|
|
Loading…
Reference in New Issue