diff --git a/sys/apps/Files.lua b/sys/apps/Files.lua index de51b37..36d67e2 100644 --- a/sys/apps/Files.lua +++ b/sys/apps/Files.lua @@ -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() diff --git a/sys/apps/Lua.lua b/sys/apps/Lua.lua index 8063b11..95b68bc 100644 --- a/sys/apps/Lua.lua +++ b/sys/apps/Lua.lua @@ -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) diff --git a/sys/apps/Overview.lua b/sys/apps/Overview.lua index 05c1ede..1cde020 100644 --- a/sys/apps/Overview.lua +++ b/sys/apps/Overview.lua @@ -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) diff --git a/sys/apps/Sniff.lua b/sys/apps/Sniff.lua index 62d1a1e..c0c08d3 100644 --- a/sys/apps/Sniff.lua +++ b/sys/apps/Sniff.lua @@ -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, diff --git a/sys/apps/system/cloud.lua b/sys/apps/system/cloud.lua index 3bd9ad3..171e507 100644 --- a/sys/apps/system/cloud.lua +++ b/sys/apps/system/cloud.lua @@ -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: diff --git a/sys/apps/system/label.lua b/sys/apps/system/label.lua index 19ff30a..8fe3d1e 100644 --- a/sys/apps/system/label.lua +++ b/sys/apps/system/label.lua @@ -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() }, diff --git a/sys/apps/system/launcher.lua b/sys/apps/system/launcher.lua index 8f5ac13..9c87fe9 100644 --- a/sys/apps/system/launcher.lua +++ b/sys/apps/system/launcher.lua @@ -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', }, } diff --git a/sys/apps/system/network.lua b/sys/apps/system/network.lua index 818899d..b4fd802 100644 --- a/sys/apps/system/network.lua +++ b/sys/apps/system/network.lua @@ -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 diff --git a/sys/apps/system/password.lua b/sys/apps/system/password.lua index 86920fc..4e5f0ce 100644 --- a/sys/apps/system/password.lua +++ b/sys/apps/system/password.lua @@ -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) diff --git a/sys/apps/system/settings.lua b/sys/apps/system/settings.lua index 5284b30..27f1f2b 100644 --- a/sys/apps/system/settings.lua +++ b/sys/apps/system/settings.lua @@ -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, +} diff --git a/sys/init/6.packages.lua b/sys/init/6.packages.lua index 7d06abe..49c55bf 100644 --- a/sys/init/6.packages.lua +++ b/sys/init/6.packages.lua @@ -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 diff --git a/sys/modules/opus/gps.lua b/sys/modules/opus/gps.lua index 3b0ae74..1132b76 100644 --- a/sys/modules/opus/gps.lua +++ b/sys/modules/opus/gps.lua @@ -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() diff --git a/sys/modules/opus/ui.lua b/sys/modules/opus/ui.lua index 93a3837..a52f2b2 100644 --- a/sys/modules/opus/ui.lua +++ b/sys/modules/opus/ui.lua @@ -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 diff --git a/sys/modules/opus/ui/blit.lua b/sys/modules/opus/ui/blit.lua new file mode 100644 index 0000000..8a2eca7 --- /dev/null +++ b/sys/modules/opus/ui/blit.lua @@ -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 diff --git a/sys/modules/opus/ui/canvas.lua b/sys/modules/opus/ui/canvas.lua index 980f281..5f13c7f 100644 --- a/sys/modules/opus/ui/canvas.lua +++ b/sys/modules/opus/ui/canvas.lua @@ -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 diff --git a/sys/modules/opus/ui/components/Dialog.lua b/sys/modules/opus/ui/components/Dialog.lua index 6e9bb03..0a41ae9 100644 --- a/sys/modules/opus/ui/components/Dialog.lua +++ b/sys/modules/opus/ui/components/Dialog.lua @@ -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() diff --git a/sys/modules/opus/ui/components/Embedded.lua b/sys/modules/opus/ui/components/Embedded.lua index d530e33..96fb3ec 100644 --- a/sys/modules/opus/ui/components/Embedded.lua +++ b/sys/modules/opus/ui/components/Embedded.lua @@ -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 diff --git a/sys/modules/opus/ui/components/MenuBar.lua b/sys/modules/opus/ui/components/MenuBar.lua index 663886e..190a550 100644 --- a/sys/modules/opus/ui/components/MenuBar.lua +++ b/sys/modules/opus/ui/components/MenuBar.lua @@ -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 diff --git a/sys/modules/opus/ui/components/MenuItem.lua b/sys/modules/opus/ui/components/MenuItem.lua index b43f4c0..e84b328 100644 --- a/sys/modules/opus/ui/components/MenuItem.lua +++ b/sys/modules/opus/ui/components/MenuItem.lua @@ -5,4 +5,5 @@ UI.MenuItem = class(UI.FlatButton) UI.MenuItem.defaults = { UIElement = 'MenuItem', noPadding = false, + textInactiveColor = colors.gray, } diff --git a/sys/modules/opus/ui/components/Page.lua b/sys/modules/opus/ui/components/Page.lua index a7e38ce..7e746ad 100644 --- a/sys/modules/opus/ui/components/Page.lua +++ b/sys/modules/opus/ui/components/Page.lua @@ -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 diff --git a/sys/modules/opus/ui/components/ScrollBar.lua b/sys/modules/opus/ui/components/ScrollBar.lua index d9412db..8f8b6ab 100644 --- a/sys/modules/opus/ui/components/ScrollBar.lua +++ b/sys/modules/opus/ui/components/ScrollBar.lua @@ -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 diff --git a/sys/modules/opus/ui/components/ScrollingGrid.lua b/sys/modules/opus/ui/components/ScrollingGrid.lua index 76726d5..864d7ad 100644 --- a/sys/modules/opus/ui/components/ScrollingGrid.lua +++ b/sys/modules/opus/ui/components/ScrollingGrid.lua @@ -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 diff --git a/sys/modules/opus/ui/components/TabBar.lua b/sys/modules/opus/ui/components/TabBar.lua index a82fdfd..f79563d 100644 --- a/sys/modules/opus/ui/components/TabBar.lua +++ b/sys/modules/opus/ui/components/TabBar.lua @@ -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 diff --git a/sys/modules/opus/ui/components/TabBarMenuItem.lua b/sys/modules/opus/ui/components/TabBarMenuItem.lua index a17a59f..2d22a02 100644 --- a/sys/modules/opus/ui/components/TabBarMenuItem.lua +++ b/sys/modules/opus/ui/components/TabBarMenuItem.lua @@ -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 diff --git a/sys/modules/opus/ui/components/Tabs.lua b/sys/modules/opus/ui/components/Tabs.lua index 8eb45c7..8c2a692 100644 --- a/sys/modules/opus/ui/components/Tabs.lua +++ b/sys/modules/opus/ui/components/Tabs.lua @@ -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 diff --git a/sys/modules/opus/ui/components/TextArea.lua b/sys/modules/opus/ui/components/TextArea.lua index 69fb998..aeaf756 100644 --- a/sys/modules/opus/ui/components/TextArea.lua +++ b/sys/modules/opus/ui/components/TextArea.lua @@ -20,7 +20,6 @@ end function UI.TextArea:draw() self:clear() - self.cursorX, self.cursorY = 1, 1 self:print(self.value) self:drawChildren() end diff --git a/sys/modules/opus/ui/components/Viewport.lua b/sys/modules/opus/ui/components/Viewport.lua index 1a2d899..9389db5 100644 --- a/sys/modules/opus/ui/components/Viewport.lua +++ b/sys/modules/opus/ui/components/Viewport.lua @@ -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) diff --git a/sys/modules/opus/util.lua b/sys/modules/opus/util.lua index 3268777..545b738 100644 --- a/sys/modules/opus/util.lua +++ b/sys/modules/opus/util.lua @@ -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 = { }