color rework + cleanup

This commit is contained in:
kepler155c@gmail.com
2020-04-16 23:13:19 -06:00
parent 3e41996b9b
commit 9d2a76f4ea
28 changed files with 364 additions and 238 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -5,4 +5,5 @@ UI.MenuItem = class(UI.FlatButton)
UI.MenuItem.defaults = {
UIElement = 'MenuItem',
noPadding = false,
textInactiveColor = colors.gray,
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -20,7 +20,6 @@ end
function UI.TextArea:draw()
self:clear()
self.cursorX, self.cursorY = 1, 1
self:print(self.value)
self:drawChildren()
end

View File

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