diff options
Diffstat (limited to '.mbs/lib/blit_window.lua')
| -rw-r--r-- | .mbs/lib/blit_window.lua | 297 |
1 files changed, 297 insertions, 0 deletions
diff --git a/.mbs/lib/blit_window.lua b/.mbs/lib/blit_window.lua new file mode 100644 index 0000000..39cd843 --- /dev/null +++ b/.mbs/lib/blit_window.lua @@ -0,0 +1,297 @@ +local colour_lookup = {} +for i = 0, 16 do + colour_lookup[2 ^ i] = string.format("%x", i) +end + +function create(original) + if not original then original = term.current() end + + local text = {} + local text_colour = {} + local back_colour = {} + local palette = {} + + local cursor_x, cursor_y = 1, 1 + + local cursor_blink = false + local cur_text_colour = "0" + local cur_back_colour = "f" + + local sizeX, sizeY = original.getSize() + local color = original.isColor() + + local bubble = true + + local redirect = {} + + if original.getPaletteColour then + for i = 0, 15 do + local c = 2 ^ i + palette[c] = { original.getPaletteColour(c) } + end + end + + function redirect.write(writeText) + writeText = tostring(writeText) + if bubble then original.write(writeText) end + + local pos = cursor_x + + -- If we're off the screen then just emulate a write + if cursor_y > sizeY or cursor_y < 1 then + cursor_x = pos + #writeText + return + end + + if pos + #writeText <= 1 or pos > sizeX then + -- If we're too far off the left then skip. + cursor_x = pos + #writeText + return + elseif pos < 1 then + -- Adjust text to fit on screen starting at one. + writeText = string.sub(writeText, math.abs(cursor_x) + 2) + pos = 1 + end + + local lineText = text[cursor_y] + local lineColor = text_colour[cursor_y] + local lineBack = back_colour[cursor_y] + local preStop = pos - 1 + local preStart = math.min(1, preStop) + local postStart = pos + string.len(writeText) + local postStop = sizeX + local sub, rep = string.sub, string.rep + + text[cursor_y] = sub(lineText, preStart, preStop)..writeText..sub(lineText, postStart, postStop) + text_colour[cursor_y] = sub(lineColor, preStart, preStop)..rep(cur_text_colour, #writeText)..sub(lineColor, postStart, postStop) + back_colour[cursor_y] = sub(lineBack, preStart, preStop)..rep(cur_back_colour, #writeText)..sub(lineBack, postStart, postStop) + cursor_x = pos + string.len(writeText) + end + + function redirect.blit(writeText, writeFore, writeBack) + if type(writeText) ~= "string" then error("bad argument #1 (expected string, got " .. type(writeText) .. ")", 2) end + if type(writeFore) ~= "string" then error("bad argument #2 (expected string, got " .. type(writeFore) .. ")", 2) end + if type(writeBack) ~= "string" then error("bad argument #3 (expected string, got " .. type(writeBack) .. ")", 2) end + if #writeFore ~= #writeText or #writeBack ~= #writeText then error("Arguments must be the same length", 2) end + + if bubble then original.blit(writeText, writeFore, writeBack) end + local pos = cursor_x + + -- If we're off the screen then just emulate a write + if cursor_y > sizeY or cursor_y < 1 then + cursor_x = pos + #writeText + return + end + + if pos + #writeText <= 1 then + --skip entirely. + cursor_x = pos + #writeText + return + elseif pos < 1 then + --adjust text to fit on screen starting at one. + writeText = string.sub(writeText, math.abs(cursor_x) + 2) + writeFore = string.sub(writeFore, math.abs(cursor_x) + 2) + writeBack = string.sub(writeBack, math.abs(cursor_x) + 2) + cursor_x = 1 + elseif pos > sizeX then + --if we're off the edge to the right, skip entirely. + cursor_x = pos + #writeText + return + end + + local lineText = text[cursor_y] + local lineColor = text_colour[cursor_y] + local lineBack = back_colour[cursor_y] + local preStop = cursor_x - 1 + local preStart = math.min(1, preStop) + local postStart = cursor_x + string.len(writeText) + local postStop = sizeX + local sub = string.sub + + text[cursor_y] = sub(lineText, preStart, preStop)..writeText..sub(lineText, postStart, postStop) + text_colour[cursor_y] = sub(lineColor, preStart, preStop)..writeFore..sub(lineColor, postStart, postStop) + back_colour[cursor_y] = sub(lineBack, preStart, preStop)..writeBack..sub(lineBack, postStart, postStop) + cursor_x = pos + string.len(writeText) + end + + function redirect.clear() + for i = 1, sizeY do + text[i] = string.rep(" ", sizeX) + text_colour[i] = string.rep(cur_text_colour, sizeX) + back_colour[i] = string.rep(cur_back_colour, sizeX) + end + + if bubble then return original.clear() end + end + + function redirect.clearLine() + -- If we're off the screen then just emulate a clearLine + if cursor_y > sizeY or cursor_y < 1 then + return + end + + text[cursor_y] = string.rep(" ", sizeX) + text_colour[cursor_y] = string.rep(cur_text_colour, sizeX) + back_colour[cursor_y] = string.rep(cur_back_colour, sizeX) + + if bubble then return original.clearLine() end + end + + function redirect.getCursorPos() + return cursor_x, cursor_y + end + + function redirect.setCursorPos(x, y) + if type(x) ~= "number" then error("bad argument #1 (expected number, got " .. type(x) .. ")", 2) end + if type(y) ~= "number" then error("bad argument #2 (expected number, got " .. type(y) .. ")", 2) end + + cursor_x = math.floor(x) + cursor_y = math.floor(y) + if bubble then return original.setCursorPos(x, y) end + end + + function redirect.setCursorBlink(b) + cursor_blink = b + if bubble then return original.setCursorBlink(b) end + end + + function redirect.getSize() + return sizeX, sizeY + end + + function redirect.scroll(n) + if type(n) ~= "number" then error("bad argument #1 (expected number, got " .. type(n) .. ")", 2) end + + local empty_text = string.rep(" ", sizeX) + local empty_text_colour = string.rep(cur_text_colour, sizeX) + local empty_back_colour = string.rep(cur_back_colour, sizeX) + if n > 0 then + for i = 1, sizeY do + text[i] = text[i + n] or empty_text + text_colour[i] = text_colour[i + n] or empty_text_colour + back_colour[i] = back_colour[i + n] or empty_back_colour + end + elseif n < 0 then + for i = sizeY, 1, -1 do + text[i] = text[i + n] or empty_text + text_colour[i] = text_colour[i + n] or empty_text_colour + back_colour[i] = back_colour[i + n] or empty_back_colour + end + end + + if bubble then return original.scroll(n) end + end + + function redirect.setTextColour(clr) + if type(clr) ~= "number" then error("bad argument #1 (expected number, got " .. type(clr) .. ")", 2) end + cur_text_colour = colour_lookup[clr] or error("Invalid colour (got " .. clr .. ")" , 2) + if bubble then return original.setTextColour(clr) end + end + redirect.setTextColor = redirect.setTextColour + + function redirect.setBackgroundColour(clr) + if type(clr) ~= "number" then error("bad argument #1 (expected number, got " .. type(clr) .. ")", 2) end + cur_back_colour = colour_lookup[clr] or error("Invalid colour (got " .. clr .. ")" , 2) + if bubble then return original.setBackgroundColour(clr) end + end + redirect.setBackgroundColor = redirect.setBackgroundColour + + function redirect.isColour() + return color == true + end + redirect.isColor = redirect.isColour + + function redirect.getTextColour() + return 2 ^ tonumber(cur_text_colour, 16) + end + redirect.getTextColor = redirect.getTextColour + + function redirect.getBackgroundColour() + return 2 ^ tonumber(cur_back_colour, 16) + end + redirect.getBackgroundColor = redirect.getBackgroundColour + + if original.getPaletteColour then + function redirect.setPaletteColour(colour, r, g, b) + local palcol = palette[colour] + if not palcol then error("Invalid colour (got " .. tostring(colour) .. ")", 2) end + if type(r) == "number" and g == nil and b == nil then + palcol[1], palcol[2], palcol[3] = colours.rgb8(r) + else + if type(r) ~= "number" then error("bad argument #2 (expected number, got " .. type(r) .. ")", 2) end + if type(g) ~= "number" then error("bad argument #3 (expected number, got " .. type(g) .. ")", 2) end + if type(b) ~= "number" then error("bad argument #4 (expected number, got " .. type(b) .. ")", 2) end + + palcol[1], palcol[2], palcol[3] = r, g, b + end + + if bubble then return original.setPaletteColour(colour, r, g, b) end + end + redirect.setPaletteColor = redirect.setPaletteColour + + function redirect.getPaletteColour(colour) + local palcol = palette[colour] + if not palcol then error("Invalid colour (got " .. tostring(colour) .. ")", 2) end + return palcol[1], palcol[2], palcol[3] + end + redirect.getPaletteColor = redirect.getPaletteColour + end + + function redirect.draw(target) + if not target then target = original end + + if target.getPaletteColour then + for colour, pal in pairs(palette) do + target.setPaletteColour(colour, pal[1], pal[2], pal[3]) + end + end + + for i=1, sizeY do + target.setCursorPos(1,i) + target.blit(text[i], text_colour[i], back_colour[i]) + end + + target.setCursorPos(cursor_x, cursor_y) + target.setTextColour(2 ^ tonumber(cur_text_colour, 16)) + target.setBackgroundColor(2 ^ tonumber(cur_back_colour, 16)) + target.setCursorBlink(cursor_blink) + end + + function redirect.bubble(b) + bubble = b + end + + function redirect.updateSize() + local new_x, new_y = original.getSize() + if new_x == sizeX and new_y == sizeY then return end + + -- For any existing lines, trim them + for y = 1, sizeY do + if new_x < sizeX then + text[y] = text[y]:sub(1, new_x) + text_colour[y] = text_colour[y]:sub(1, new_x) + back_colour[y] = back_colour[y]:sub(1, new_x) + elseif new_x > sizeX then + text[y] = text[y] .. (" "):rep(new_x - sizeX) + text_colour[y] = text_colour[y] .. (cur_text_colour):rep(new_x - sizeX) + back_colour[y] = back_colour[y] .. (cur_back_colour):rep(new_x - sizeX) + end + end + + -- Add any new lines we might need. + local text_line = (" "):rep(new_x) + local fore_line = (cur_text_colour):rep(new_x) + local back_line = (cur_back_colour):rep(new_x) + for y = sizeY + 1, new_y do + text[y] = text_line + text_colour[y] = fore_line + back_colour[y] = back_line + end + + sizeX = new_x + sizeY = new_y + end + + redirect.clear() + return redirect +end
\ No newline at end of file |
