1
0
mirror of https://github.com/LDDestroier/CC/ synced 2024-11-12 13:00:01 +00:00
ldd-CC/windont.lua
LDDestroier a3abda09df
Fixed transparency, added other options
Added the windont.info table, which will tell you miscellaneous information about the API
 + time in which the render function was last called
 + list of all windows rendered last
 + amount of windows rendered last
 + amount of term.blit calls during last render
Fixed term.scroll
Fixed transparency
Added "meta.renderBuddies" table which renders all windows associated with the main one inside the table after rendering the main one.
2019-12-11 00:29:30 -05:00

373 lines
12 KiB
Lua

-- Windon't
-- enhanced window API by LDDestroier
-- intended for general use within all me new programs
--
-- Unique features:
-- + Transparency within windows
-- + Built-in window layering
local to_blit, to_colors = {}, {}
for i = 1, 16 do
to_blit[2 ^ (i - 1)] = ("0123456789abcdef"):sub(i, i)
to_colors[("0123456789abcdef"):sub(i, i)] = 2 ^ (i - 1)
end
to_blit[0], to_colors["-"] = "-", 0
local getTime = function()
return 24 * os.day() + os.time()
end
local windont = {
baseTerm = term.current(),
config = {
defaultTextColor = "0", -- default text color (what " " corresponds to in term.blit's second argument)
defaultBackColor = "f", -- default background color (what " " corresponds to in term.blit's third argument)
clearScreen = false, -- if true, will clear the screen during render
},
info = {
BLIT_CALLS = 0, -- amount of term.blit calls during the last render
LAST_RENDER_TIME = 0, -- last time in which render was called
LAST_RENDER_AMOUNT = 0, -- amount of windows drawn during last render
LAST_RENDER_WINDOWS = {}, -- table of the last window objects that were rendered
}
}
-- check if space on screenBuffer is transparent
local check = function(buff, x, y, blitLayer)
if buff[blitLayer or 1][y] then
return (blitLayer or buff[1][y][x]) and (
(not buff[blitLayer or 2][y][x] or buff[blitLayer or 2][y][x] ~= "-") or
(not buff[blitLayer or 3][y][x] or buff[blitLayer or 3][y][x] ~= "-")
) and (
not (buff[1][y][x] == " " and buff[3][y][x] == "-")
)
end
end
-- draws one or more windon't objects
-- should not draw over any terminal space that isn't occupied by a window
windont.render = function(...)
local windows = {...}
local bT = windont.baseTerm
local scr_x, scr_y = bT.getSize()
local screenBuffer = {{}, {}, {}}
local blitList = {} -- list of blit commands per line
local c = 1 -- current blitList entry
local cTime = getTime()
local AMNT_OF_BLITS = 0 -- how many blit calls are there?
local cx, cy -- each window's absolute X and Y
local buffer
for y = 1, scr_y do
screenBuffer[1][y] = {}
screenBuffer[2][y] = {}
screenBuffer[3][y] = {}
blitList = {}
c = 1
for x = 1, scr_x do
for i = #windows, 1, -1 do
if windows[i].meta.visible then
buffer = windows[i].meta.buffer
cx = x - windows[i].meta.x + 1
cy = y - windows[i].meta.y + 1
if check(buffer, cx, cy) then
screenBuffer[1][y][x] = check(buffer, cx, cy) and buffer[1][cy][cx] or screenBuffer[1][y][x]
screenBuffer[2][y][x] = check(buffer, cx, cy, 2) and buffer[2][cy][cx] or screenBuffer[3][y][x]
screenBuffer[3][y][x] = check(buffer, cx, cy, 3) and buffer[3][cy][cx] or screenBuffer[3][y][x]
end
end
end
if windont.config.clearScreen then
screenBuffer[1][y][x] = screenBuffer[1][y][x] or " "
end
screenBuffer[2][y][x] = screenBuffer[2][y][x] or windont.config.defaultBackColor -- intentionally not the default text color
screenBuffer[3][y][x] = screenBuffer[3][y][x] or windont.config.defaultBackColor
if check(screenBuffer, x, y) then
if check(screenBuffer, x - 1, y) then
blitList[c][1] = blitList[c][1] .. screenBuffer[1][y][x]
blitList[c][2] = blitList[c][2] .. screenBuffer[2][y][x]
blitList[c][3] = blitList[c][3] .. screenBuffer[3][y][x]
else
c = x
blitList[c] = {
screenBuffer[1][y][x],
screenBuffer[2][y][x],
screenBuffer[3][y][x]
}
end
end
end
for k,v in pairs(blitList) do
bT.setCursorPos(k, y)
bT.blit(v[1], v[2], v[3])
AMNT_OF_BLITS = AMNT_OF_BLITS + 1
end
end
windont.info.BLIT_CALLS = AMNT_OF_BLITS
windont.info.LAST_RENDER_AMOUNT = #windows
windont.info.LAST_RENDER_WINDOWS = windows
windont.info.LAST_RENDER_TIME = cTime
windont.info.LAST_RENDER_DURATION = getTime() - cTime
end
-- creates a new windon't object that can be manipulated the same as a regular window
windont.newWindow = function( x, y, width, height, misc )
-- check argument types
assert(type(x) == "number", "argument #1 must be number, got " .. type(x))
assert(type(y) == "number", "argument #2 must be number, got " .. type(y))
assert(type(width) == "number", "argument #3 must be number, got " .. type(width))
assert(type(height) == "number", "argument #4 must be number, got " .. type(height))
-- check argument validity
assert(x > 0, "x position must be above zero")
assert(y > 0, "y position must be above zero")
assert(width > 0, "width must be above zero")
assert(height > 0, "height must be above zero")
local bT = windont.baseTerm
local output = {}
misc = misc or {}
local meta = {
x = x or 1, -- x position of the window
y = y or 1, -- y position of the window
width = width, -- width of the buffer
height = height, -- height of the buffer
buffer = {}, -- stores contents of terminal in buffer[1][y][x] format
renderBuddies = {}, -- renders any other window objects stored here after rendering here
cursorX = misc.cursorX or 1,
cursorY = misc.cursorY or 1,
textColor = misc.textColor or windont.config.defaultTextColor, -- current text color
backColor = misc.backColor or windont.config.defaultBackColor, -- current background color
blink = true, -- cursor blink
isColor = bT.isColor(), -- if true, then it's an advanced computer
alwaysRender = false, -- render after every terminal operation
visible = true, -- if false, don't render ever
-- make a new buffer (optionally uses an existing buffer as a reference)
newBuffer = function(width, height, char, text, back, drawAtop)
local output = drawAtop or {{}, {}, {}}
for y = 1, height do
output[1][y] = output[1][y] or {}
output[2][y] = output[2][y] or {}
output[3][y] = output[3][y] or {}
for x = 1, width do
output[1][y][x] = output[1][y][x] or char or " "
output[2][y][x] = output[2][y][x] or text or "0"
output[3][y][x] = output[3][y][x] or back or "f"
end
end
return output
end
}
-- initialize the buffer
meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor)
output.meta = meta
output.write = function(text)
assert(type(text) == "string", "argument must be string")
for i = 1, #text do
if meta.cursorX >= 1 and meta.cursorX <= meta.width and meta.cursorY >= 1 and meta.cursorY <= meta.height then
meta.buffer[1][meta.cursorY][meta.cursorX] = text:sub(i,i)
meta.buffer[2][meta.cursorY][meta.cursorX] = meta.textColor
meta.buffer[3][meta.cursorY][meta.cursorX] = meta.backColor
meta.cursorX = meta.cursorX + 1
end
end
if meta.alwaysRender then
--local limit = math.max(0, meta.width - meta.cursorX + 1)
bT.setCursorPos(meta.x, meta.y)
bT.blit(
table.unpack(meta.buffer[1][meta.cursorY]),
table.unpack(meta.buffer[2][meta.cursorY]),
table.unpack(meta.buffer[3][meta.cursorY])
)
end
end
output.blit = function(char, text, back)
assert(type(char) == "string" and type(text) == "string" and type(back) == "string", "all arguments must be strings")
assert(#char == #text and #text == #back, "arguments must be same length")
for i = 1, #char do
if meta.cursorX >= 1 and meta.cursorX <= meta.width and meta.cursorY >= 1 and meta.cursorY <= meta.height then
meta.buffer[1][meta.cursorY][meta.cursorX] = char:sub(i,i)
meta.buffer[2][meta.cursorY][meta.cursorX] = text:sub(i,i) == " " and windont.config.defaultTextColor or text:sub(i,i)
meta.buffer[3][meta.cursorY][meta.cursorX] = back:sub(i,i) == " " and windont.config.defaultBackColor or back:sub(i,i)
meta.cursorX = meta.cursorX + 1
end
end
if meta.alwaysRender then
--local limit = math.max(0, meta.width - meta.cursorX + 1)
bT.setCursorPos(meta.x, meta.y)
bT.blit(
table.unpack(meta.buffer[1][meta.cursorY]),
table.unpack(meta.buffer[2][meta.cursorY]),
table.unpack(meta.buffer[3][meta.cursorY])
)
end
end
output.setCursorPos = function(x, y)
assert(type(x) == "number", "argument #1 must be number, got " .. type(x))
assert(type(y) == "number", "argument #2 must be number, got " .. type(y))
meta.cursorX, meta.cursorY = x, y
if meta.alwaysRender then
bT.setCursorPos(meta.x + meta.cursorX - 1, meta.y + meta.cursorY - 1)
end
end
output.getCursorPos = function()
return meta.cursorX, meta.cursorY
end
output.setTextColor = function(color)
if to_blit[color] then
meta.textColor = to_blit[color]
else
error("Invalid color (got " .. color .. ")")
end
end
output.setTextColour = output.setTextColor
output.setBackgroundColor = function(color)
if to_blit[color] then
meta.backColor = to_blit[color]
else
error("Invalid color (got " .. color .. ")")
end
end
output.setBackgroundColour = output.setBackgroundColor
output.getTextColor = function()
return to_colors[meta.textColor]
end
output.getTextColour = output.getTextColor
output.getBackgroundColor = function()
return to_colors[meta.backColor]
end
output.getBackgroundColour = output.getBackgroundColor
output.setVisible = function(visible)
assert(type(visible) == "number", "bad argument #1 (expected boolean, got " .. type(visible) .. ")")
meta.visible = visible and true or false
end
output.clear = function()
meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor)
end
output.clearLine = function()
meta.buffer[meta.cursorY] = nil
meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor, meta.buffer)
if meta.alwaysRender then
bT.setCursorPos(meta.x, meta.y + meta.cursorY - 1)
bT.blit(
(" "):rep(meta.width),
(meta.textColor):rep(meta.width),
(meta.backColor):rep(meta.width)
)
end
end
output.getLine = function(y)
assert(type(y) == "number", "bad argument #1 (expected number, got " .. type(y) .. ")")
assert(meta.buffer[1][y], "Line is out of range.")
return table.concat(meta.buffer[1][y]), table.concat(meta.buffer[2][y]), table.concat(meta.buffer[3][y])
end
output.scroll = function(amplitude)
if amplitude > 0 then
for i = 1, amplitude do
table.remove(meta.buffer[1], 1)
table.remove(meta.buffer[2], 1)
table.remove(meta.buffer[3], 1)
end
else
for i = 1, -amplitude do
table.insert(meta.buffer[1], 1, false)
table.insert(meta.buffer[2], 1, false)
table.insert(meta.buffer[3], 1, false)
end
end
meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor, meta.buffer)
if meta.alwaysRender then
output.redraw()
end
end
output.getSize = function()
return height, width
end
output.isColor = function()
return meta.isColor
end
output.isColour = output.isColor
output.reposition = function(x, y, width, height)
assert(type(x) == "number", "bad argument #1 (expected number, got " .. type(x) .. ")")
assert(type(y) == "number", "bad argument #2 (expected number, got " .. type(y) .. ")")
meta.x = math.floor(x)
meta.y = math.floor(y)
if width then
assert(type(width) == "number", "bad argument #3 (expected number, got " .. type(width) .. ")")
assert(type(height) == "number", "bad argument #4 (expected number, got " .. type(height) .. ")")
meta.width = width
meta.height = height
meta.buffer = meta.newBuffer(meta.width, meta.height, " ", meta.textColor, meta.backColor, meta.buffer)
end
if meta.alwaysRender then
output.redraw()
end
end
output.restoreCursor = function()
bT.setCursorPos(meta.x + meta.cursorX - 1, meta.y + meta.cursorY - 1)
bT.setCursorBlink(meta.blink)
end
output.getPosition = function()
return meta.x, meta.y
end
output.setCursorBlink = function(blink)
meta.blink = blink and true or false
end
output.getCursorBlink = function(blink)
return meta.blink
end
output.setPaletteColor = bT.setPaletteColor
output.setPaletteColour = bT.setPaletteColour
output.getPaletteColor = bT.getPaletteColor
output.getPaletteColour = bT.getPaletteColour
output.redraw = function()
if #meta.renderBuddies > 0 then
windont.render(output, table.unpack(meta.renderBuddies))
else
windont.render(output)
end
end
return output
end
return windont