ui overhaul
This commit is contained in:
@@ -2,9 +2,9 @@ local class = require('class')
|
||||
local Region = require('ui.region')
|
||||
local Util = require('util')
|
||||
|
||||
local _rep = string.rep
|
||||
local _sub = string.sub
|
||||
local _gsub = string.gsub
|
||||
local _rep = string.rep
|
||||
local _sub = string.sub
|
||||
local _gsub = string.gsub
|
||||
local colors = _G.colors
|
||||
|
||||
local Canvas = class()
|
||||
@@ -19,6 +19,10 @@ for n = 1, 16 do
|
||||
Canvas.darkPalette[2 ^ (n - 1)] = _sub("8777777f77fff77f", n, n)
|
||||
end
|
||||
|
||||
--[[
|
||||
A canvas can have more lines than canvas.height in order to scroll
|
||||
]]
|
||||
|
||||
function Canvas:init(args)
|
||||
self.x = 1
|
||||
self.y = 1
|
||||
@@ -50,7 +54,7 @@ function Canvas:move(x, y)
|
||||
end
|
||||
|
||||
function Canvas:resize(w, h)
|
||||
for i = self.height, h do
|
||||
for i = #self.lines, h do
|
||||
self.lines[i] = { }
|
||||
end
|
||||
|
||||
@@ -78,7 +82,7 @@ function Canvas:copy()
|
||||
height = self.height,
|
||||
isColor = self.isColor,
|
||||
})
|
||||
for i = 1, self.height do
|
||||
for i = 1, #self.lines do
|
||||
b.lines[i].text = self.lines[i].text
|
||||
b.lines[i].fg = self.lines[i].fg
|
||||
b.lines[i].bg = self.lines[i].bg
|
||||
@@ -117,6 +121,19 @@ function Canvas:setVisible(visible)
|
||||
end
|
||||
end
|
||||
|
||||
-- Push a layer to the top
|
||||
function Canvas:raise()
|
||||
if self.parent then
|
||||
local layers = self.parent.layers or { }
|
||||
for k, v in pairs(layers) do
|
||||
if v == self then
|
||||
table.insert(layers, table.remove(layers, k))
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:write(x, y, text, bg, fg)
|
||||
if bg then
|
||||
bg = _rep(self.palette[bg], #text)
|
||||
@@ -124,10 +141,10 @@ function Canvas:write(x, y, text, bg, fg)
|
||||
if fg then
|
||||
fg = _rep(self.palette[fg], #text)
|
||||
end
|
||||
self:writeBlit(x, y, text, bg, fg)
|
||||
self:blit(x, y, text, bg, fg)
|
||||
end
|
||||
|
||||
function Canvas:writeBlit(x, y, text, bg, fg)
|
||||
function Canvas:blit(x, y, text, bg, fg)
|
||||
if y > 0 and y <= #self.lines and x <= self.width then
|
||||
local width = #text
|
||||
|
||||
@@ -157,7 +174,7 @@ function Canvas:writeBlit(x, y, text, bg, fg)
|
||||
|
||||
if width > 0 then
|
||||
|
||||
local function replace(sstr, pos, rstr, width)
|
||||
local function replace(sstr, pos, rstr)
|
||||
if pos == 1 and width == self.width then
|
||||
return rstr
|
||||
elseif pos == 1 then
|
||||
@@ -188,39 +205,48 @@ function Canvas:writeLine(y, text, fg, bg)
|
||||
self.lines[y].bg = bg
|
||||
end
|
||||
|
||||
function Canvas:reset()
|
||||
self.regions = nil
|
||||
function Canvas:clearLine(y, bg, fg)
|
||||
fg = _rep(self.palette[fg or colors.white], self.width)
|
||||
bg = _rep(self.palette[bg or colors.black], self.width)
|
||||
self:writeLine(y, _rep(' ', self.width), fg, bg)
|
||||
end
|
||||
|
||||
function Canvas:clear(bg, fg)
|
||||
local text = _rep(' ', self.width)
|
||||
fg = _rep(self.palette[fg or colors.white], self.width)
|
||||
bg = _rep(self.palette[bg or colors.black], self.width)
|
||||
for i = 1, self.height do
|
||||
for i = 1, #self.lines do
|
||||
self:writeLine(i, text, fg, bg)
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:punch(rect)
|
||||
if not self.regions then
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
end
|
||||
self.regions:subRect(rect.x, rect.y, rect.ex, rect.ey)
|
||||
local offset = { x = 0, y = 0 }
|
||||
|
||||
|
||||
self.regions:subRect(rect.x + offset.x, rect.y + offset.y, rect.ex + offset.x, rect.ey + offset.y)
|
||||
end
|
||||
|
||||
function Canvas:blitClipped(device)
|
||||
function Canvas:blitClipped(device, offset)
|
||||
offset = { x = self.x, y = self.y }
|
||||
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
|
||||
for _,region in ipairs(self.regions.region) do
|
||||
self:blit(device,
|
||||
{ x = region[1] - self.x + 1,
|
||||
y = region[2] - self.y + 1,
|
||||
ex = region[3]- self.x + 1,
|
||||
ey = region[4] - self.y + 1 },
|
||||
{ x = region[1], y = region[2] })
|
||||
self:blitRect(device,
|
||||
{ x = region[1],
|
||||
y = region[2],
|
||||
ex = region[3],
|
||||
ey = region[4] },
|
||||
{ x = region[1] + offset.x - 1, y = region[2] + offset.y - 1 })
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:redraw(device)
|
||||
self:reset()
|
||||
--[[
|
||||
if #self.layers > 0 then
|
||||
for _,layer in pairs(self.layers) do
|
||||
self:punch(layer)
|
||||
@@ -230,19 +256,20 @@ function Canvas:redraw(device)
|
||||
self:blit(device)
|
||||
end
|
||||
self:clean()
|
||||
]]
|
||||
end
|
||||
|
||||
function Canvas:isDirty()
|
||||
for _, line in pairs(self.lines) do
|
||||
if line.dirty then
|
||||
for i = 1, #self.lines do
|
||||
if self.lines[i].dirty then
|
||||
return true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:dirty()
|
||||
for _, line in pairs(self.lines) do
|
||||
line.dirty = true
|
||||
for i = 1, #self.lines do
|
||||
self.lines[i].dirty = true
|
||||
end
|
||||
if self.layers then
|
||||
for _, canvas in pairs(self.layers) do
|
||||
@@ -252,37 +279,81 @@ function Canvas:dirty()
|
||||
end
|
||||
|
||||
function Canvas:clean()
|
||||
for _, line in pairs(self.lines) do
|
||||
line.dirty = false
|
||||
for i = 1, #self.lines do
|
||||
self.lines[i].dirty = nil
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:render(device, layers) --- redrawAll ?
|
||||
layers = layers or self.layers
|
||||
if #layers > 0 then
|
||||
self.regions = Region.new(self.x, self.y, self.ex, self.ey)
|
||||
local l = Util.shallowCopy(layers)
|
||||
for _, canvas in ipairs(layers) do
|
||||
table.remove(l, 1)
|
||||
function Canvas:renderLayers(device, offset)
|
||||
if not offset then
|
||||
offset = { x = self.x, y = self.y }
|
||||
end
|
||||
if #self.layers > 0 then
|
||||
self.regions = Region.new(1, 1, self.ex, self.ey)
|
||||
|
||||
for i = 1, #self.layers do
|
||||
local canvas = self.layers[i]
|
||||
if canvas.visible then
|
||||
|
||||
-- punch out this area from the parent's canvas
|
||||
self:punch(canvas)
|
||||
canvas:render(device, l)
|
||||
|
||||
-- get the area to render for this layer
|
||||
canvas.regions = Region.new(canvas.x, canvas.y, canvas.ex, canvas.ey)
|
||||
|
||||
-- determine if we should render this layer by punching
|
||||
-- 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])
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
self:blitClipped(device)
|
||||
self:reset()
|
||||
|
||||
for _, canvas in ipairs(self.layers) do
|
||||
if canvas.visible and #canvas.regions.region > 0 then
|
||||
canvas:renderLayers(device, {
|
||||
x = canvas.x, --offset.x + self.x - 1,
|
||||
y = canvas.y,
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
self:blitClipped(device, offset)
|
||||
|
||||
--elseif #self.regions.region > 0 then
|
||||
-- self:blitClipped(device, offset)
|
||||
|
||||
else
|
||||
self:blit(device)
|
||||
offset = { x = self.x, y = self.y }
|
||||
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
|
||||
self:blitRect(device, nil, offset)
|
||||
end
|
||||
self:clean()
|
||||
end
|
||||
|
||||
function Canvas:blit(device, src, tgt)
|
||||
function Canvas:render(device)
|
||||
--_G._p = self
|
||||
if #self.layers > 0 then
|
||||
self:renderLayers(device)
|
||||
else
|
||||
self:blitRect(device)
|
||||
self:clean()
|
||||
end
|
||||
end
|
||||
|
||||
function Canvas:blitRect(device, src, tgt)
|
||||
src = src or { x = 1, y = 1, ex = self.ex - self.x + 1, ey = self.ey - self.y + 1 }
|
||||
tgt = tgt or self
|
||||
|
||||
for i = 0, src.ey - src.y do
|
||||
local line = self.lines[src.y + i]
|
||||
local line = self.lines[src.y + i + (self.offy or 0)]
|
||||
if line and line.dirty then
|
||||
local t, fg, bg = line.text, line.fg, line.bg
|
||||
if src.x > 1 or src.ex < self.ex then
|
||||
@@ -290,13 +361,11 @@ function Canvas:blit(device, src, tgt)
|
||||
fg = _sub(fg, src.x, src.ex)
|
||||
bg = _sub(bg, src.x, src.ex)
|
||||
end
|
||||
--if tgt.y + i > self.ey then -- wrong place to do clipping ??
|
||||
-- break
|
||||
--end
|
||||
device.setCursorPos(tgt.x, tgt.y + i)
|
||||
device.blit(t, fg, bg)
|
||||
end
|
||||
end
|
||||
--os.sleep(.1)
|
||||
end
|
||||
|
||||
function Canvas:applyPalette(palette)
|
||||
@@ -351,7 +420,7 @@ function Canvas.convertWindow(win, parent, wx, wy)
|
||||
|
||||
function win.blit(text, fg, bg)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:writeBlit(x, y, text, bg, fg)
|
||||
win.canvas:blit(x, y, text, bg, fg)
|
||||
end
|
||||
|
||||
function win.redraw()
|
||||
@@ -372,132 +441,4 @@ function Canvas.convertWindow(win, parent, wx, wy)
|
||||
win.clear()
|
||||
end
|
||||
|
||||
function Canvas.scrollingWindow(win, wx, wy)
|
||||
local w, h = win.getSize()
|
||||
local scrollPos = 0
|
||||
local maxScroll = h
|
||||
|
||||
-- canvas lines are are a sliding window within the local lines table
|
||||
local lines = { }
|
||||
|
||||
local parent
|
||||
local canvas = Canvas({
|
||||
x = wx,
|
||||
y = wy,
|
||||
width = w,
|
||||
height = h,
|
||||
isColor = win.isColor(),
|
||||
})
|
||||
win.canvas = canvas
|
||||
|
||||
local function scrollTo(p, forceRedraw)
|
||||
local ms = #lines - canvas.height -- max scroll
|
||||
p = math.min(math.max(p, 0), ms) -- normalize
|
||||
|
||||
if p ~= scrollPos or forceRedraw then
|
||||
scrollPos = p
|
||||
for i = 1, canvas.height do
|
||||
canvas.lines[i] = lines[i + scrollPos]
|
||||
end
|
||||
canvas:dirty()
|
||||
end
|
||||
end
|
||||
|
||||
function win.blit(text, fg, bg)
|
||||
local x, y = win.getCursorPos()
|
||||
win.canvas:writeBlit(x, y, text, bg, fg)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.clear()
|
||||
lines = { }
|
||||
for i = 1, canvas.height do
|
||||
lines[i] = canvas.lines[i]
|
||||
end
|
||||
scrollPos = 0
|
||||
canvas:clear(win.getBackgroundColor(), win.getTextColor())
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.clearLine()
|
||||
local _, y = win.getCursorPos()
|
||||
|
||||
scrollTo(#lines - canvas.height)
|
||||
win.canvas:write(1,
|
||||
y,
|
||||
_rep(' ', win.canvas.width),
|
||||
win.getBackgroundColor(),
|
||||
win.getTextColor())
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.redraw()
|
||||
if parent and canvas.visible then
|
||||
local x, y = win.getCursorPos()
|
||||
for i = 1, canvas.height do
|
||||
local line = canvas.lines[i]
|
||||
if line and line.dirty then
|
||||
parent.setCursorPos(canvas.x, canvas.y + i - 1)
|
||||
parent.blit(line.text, line.fg, line.bg)
|
||||
line.dirty = false
|
||||
end
|
||||
end
|
||||
win.setCursorPos(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
-- doesn't support negative scrolling...
|
||||
function win.scroll(n)
|
||||
for _ = 1, n do
|
||||
lines[#lines + 1] = {
|
||||
text = _rep(' ', canvas.width),
|
||||
fg = _rep(canvas.palette[win.getTextColor()], canvas.width),
|
||||
bg = _rep(canvas.palette[win.getBackgroundColor()], canvas.width),
|
||||
}
|
||||
end
|
||||
|
||||
while #lines > maxScroll do
|
||||
table.remove(lines, 1)
|
||||
end
|
||||
|
||||
scrollTo(maxScroll, true)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.scrollDown()
|
||||
scrollTo(scrollPos + 1)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.scrollUp()
|
||||
scrollTo(scrollPos - 1)
|
||||
win.redraw()
|
||||
end
|
||||
|
||||
function win.setMaxScroll(ms)
|
||||
maxScroll = ms
|
||||
end
|
||||
|
||||
function win.setParent(p)
|
||||
parent = p
|
||||
end
|
||||
|
||||
function win.write(str)
|
||||
str = tostring(str) or ''
|
||||
|
||||
local x, y = win.getCursorPos()
|
||||
scrollTo(#lines - canvas.height)
|
||||
win.blit(str,
|
||||
_rep(canvas.palette[win.getTextColor()], #str),
|
||||
_rep(canvas.palette[win.getBackgroundColor()], #str))
|
||||
win.setCursorPos(x + #str, y)
|
||||
end
|
||||
|
||||
function win.reposition(x, y, width, height)
|
||||
win.canvas.x, win.canvas.y = x, y
|
||||
win.canvas:resize(width or win.canvas.width, height or win.canvas.height)
|
||||
end
|
||||
|
||||
win.clear()
|
||||
end
|
||||
return Canvas
|
||||
|
||||
Reference in New Issue
Block a user