1
0
mirror of https://github.com/LDDestroier/CC/ synced 2024-11-13 21:34:54 +00:00
ldd-CC/workspace.lua
LDDestroier 3d3be77958
Fixed blit, added smoother animations
Blit had a built-in wrap function, which goes against how CC works, so that was fixed.
You're also now able to scroll both vertically and horizontally at the same time, showing potentially bits of four workspaces at once.
It also tells you how to switch workspaces at startup.
2019-04-23 14:35:15 -04:00

737 lines
20 KiB
Lua

-- Workspaces for ComputerCraft
-- by LDDestroier
local tArg = {...}
-- higher number means faster workspace movement animation, caps at 1
local workspaceMoveSpeed = 0.2
-- x,y size of workspace grid
local gridWidth = math.max(1, tonumber(tArg[1]) or 3)
local gridHeight = math.max(1, tonumber(tArg[2]) or 3)
local scr_x, scr_y = term.getSize()
local windowWidth = scr_x
local windowHeight = scr_y
-- program that will start up for workspaces
local defaultProgram = "rom/programs/shell.lua"
if _G.currentlyRunningWorkspace then
print("Workspace is already running.")
return
else
_G.currentlyRunningWorkspace = true
end
local isRunning = true
-- start up lddterm
local lddterm = {}
lddterm.alwaysRender = false -- renders after any and all screen-changing functions.
lddterm.useColors = true -- normal computers do not allow color, but this variable doesn't do anything yet
lddterm.baseTerm = term.current() -- will draw to this terminal
lddterm.transformation = nil -- will modify the current buffer as an NFT image before rendering
lddterm.cursorTransformation = nil -- will modify the cursor position
lddterm.drawFunction = nil -- will draw using this function instead of basic NFT drawing
lddterm.adjustX = 0 -- moves entire screen X
lddterm.adjustY = 0 -- moves entire screen Y
lddterm.selectedWindow = 1 -- determines which window controls the cursor
lddterm.windows = {}
-- converts hex colors to colors api, and back
local to_colors, to_blit = {
[' '] = 0,
['0'] = 1,
['1'] = 2,
['2'] = 4,
['3'] = 8,
['4'] = 16,
['5'] = 32,
['6'] = 64,
['7'] = 128,
['8'] = 256,
['9'] = 512,
['a'] = 1024,
['b'] = 2048,
['c'] = 4096,
['d'] = 8192,
['e'] = 16384,
['f'] = 32768,
}, {}
for k,v in pairs(to_colors) do
to_blit[v] = k
end
-- separates string into table based on divider
local explode = function(div, str, replstr, includeDiv)
if (div == '') then
return false
end
local pos, arr = 0, {}
for st, sp in function() return string.find(str, div, pos, false) end do
table.insert(arr, string.sub(replstr or str, pos, st - 1 + (includeDiv and #div or 0)))
pos = sp + 1
end
table.insert(arr, string.sub(replstr or str, pos))
return arr
end
-- determines the size of the terminal before rendering always
local determineScreenSize = function()
scr_x, scr_y = lddterm.baseTerm.getSize()
lddterm.screenWidth = scr_x
lddterm.screenHeight = scr_y
end
determineScreenSize()
-- takes two or more windows and checks if the first of them overlap the other(s)
lddterm.checkWindowOverlap = function(window, ...)
if #lddterm.windows < 2 then
return false
end
local list, win = {...}
for i = 1, #list do
win = list[i]
if win ~= window then
if (
window.x < win.x + win.width and
win.x < window.x + window.width and
window.y < win.y + win.height and
win.y < window.y + window.height
) then
return true
end
end
end
return false
end
local fixCursorPos = function()
local cx, cy
if lddterm.windows[lddterm.selectedWindow] then
if lddterm.cursorTransformation then
cx, cy = lddterm.cursorTransformation(
lddterm.windows[lddterm.selectedWindow].cursor[1],
lddterm.windows[lddterm.selectedWindow].cursor[2]
)
lddterm.baseTerm.setCursorPos(
cx + lddterm.windows[lddterm.selectedWindow].x - 1,
cy + lddterm.windows[lddterm.selectedWindow].y - 1
)
else
lddterm.baseTerm.setCursorPos(
-1 + lddterm.windows[lddterm.selectedWindow].cursor[1] + lddterm.windows[lddterm.selectedWindow].x,
lddterm.windows[lddterm.selectedWindow].cursor[2] + lddterm.windows[lddterm.selectedWindow].y - 1
)
end
lddterm.baseTerm.setCursorBlink(lddterm.windows[lddterm.selectedWindow].blink)
end
end
-- renders the screen with optional transformation function
lddterm.render = function(transformation, drawFunction)
-- determine new screen size and change lddterm screen to fit
old_scr_x, old_scr_y = scr_x, scr_y
determineScreenSize()
if old_scr_x ~= scr_x or old_scr_y ~= scr_y then
lddterm.baseTerm.clear()
end
local image = lddterm.screenshot()
if type(transformation) == "function" then
image = transformation(image)
end
if drawFunction then
drawFunction(image, lddterm.baseTerm)
else
for y = 1, #image[1] do
lddterm.baseTerm.setCursorPos(1 + lddterm.adjustX, y + lddterm.adjustY)
lddterm.baseTerm.blit(image[1][y], image[2][y], image[3][y])
end
end
fixCursorPos()
end
lddterm.newWindow = function(width, height, x, y, meta)
meta = meta or {}
local window = {
width = math.floor(width),
height = math.floor(height),
blink = true,
cursor = meta.cursor or {1, 1},
colors = meta.colors or {"0", "f"},
clearChar = meta.clearChar or " ",
visible = meta.visible or true,
x = math.floor(x) or 1,
y = math.floor(y) or 1,
buffer = {{},{},{}},
}
for y = 1, height do
window.buffer[1][y] = {}
window.buffer[2][y] = {}
window.buffer[3][y] = {}
for x = 1, width do
window.buffer[1][y][x] = window.clearChar
window.buffer[2][y][x] = window.colors[1]
window.buffer[3][y][x] = window.colors[2]
end
end
window.handle = {}
window.handle.setCursorPos = function(x, y)
window.cursor = {x, y}
fixCursorPos()
end
window.handle.getCursorPos = function()
return window.cursor[1], window.cursor[2]
end
window.handle.setCursorBlink = function(blink)
window.blink = blink or false
end
window.handle.getCursorBlink = function()
return window.blink
end
window.handle.scroll = function(amount)
if amount > 0 then
for i = 1, amount do
for c = 1, 3 do
table.remove(window.buffer[c], 1)
window.buffer[c][window.height] = {}
for xx = 1, width do
window.buffer[c][window.height][xx] = (
c == 1 and window.clearChar or
c == 2 and window.colors[1] or
c == 3 and window.colors[2]
)
end
end
end
elseif amount < 0 then
for i = 1, -amount do
for c = 1, 3 do
window.buffer[c][window.height] = nil
table.insert(window.buffer[c], 1, {})
for xx = 1, width do
window.buffer[c][1][xx] = (
c == 1 and window.clearChar or
c == 2 and window.colors[1] or
c == 3 and window.colors[2]
)
end
end
end
end
if lddterm.alwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.scrollX = function(amount)
if amount > 0 then
for i = 1, amount do
for c = 1, 3 do
for y = 1, window.height do
table.remove(window.buffer[c][y], 1)
window.buffer[c][y][window.width] = (
c == 1 and window.clearChar or
c == 2 and window.colors[1] or
c == 3 and window.colors[2]
)
end
end
end
elseif amount < 0 then
for i = 1, -amount do
for c = 1, 3 do
for y = 1, window.height do
window.buffer[c][y][window.width] = nil
table.insert(window.buffer[c][y], 1, (
c == 1 and window.clearChar or
c == 2 and window.colors[1] or
c == 3 and window.colors[2]
))
end
end
end
end
if lddterm.alwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.write = function(text, x, y, ignoreAlwaysRender)
assert(text ~= nil, "expected string 'text'")
text = tostring(text)
local cx = math.floor(tonumber(x) or window.cursor[1])
local cy = math.floor(tonumber(y) or window.cursor[2])
text = text:sub(math.max(0, -cx - 1))
for i = 1, #text do
if cx >= 1 and cx <= window.width and cy >= 1 and cy <= window.height then
window.buffer[1][cy][cx] = text:sub(i,i)
window.buffer[2][cy][cx] = window.colors[1]
window.buffer[3][cy][cx] = window.colors[2]
end
cx = math.min(cx + 1, window.width + 1)
end
window.cursor = {cx, cy}
if lddterm.alwaysRender and not ignoreAlwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.writeWrap = function(text, x, y, ignoreAlwaysRender)
local words = explode(" ", text, nil, true)
local cx, cy = x or window.cursor[1], y or window.cursor[2]
for i = 1, #words do
if cx + #words[i] > window.width + 1 then
cx = 1
if cy >= window.height then
window.handle.scroll(1)
cy = window.height
else
cy = cy + 1
end
end
window.handle.write(words[i], cx, cy, true)
cx = cx + #words[i]
end
if lddterm.alwaysRender and not ignoreAlwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.blit = function(char, textCol, backCol, x, y)
if type(char) == "number" then
char = tostring(char)
end
if type(textCol) == "number" then
textCol = tostring(textCol)
end
if type(backCol) == "number" then
backCol = tostring(backCol)
end
assert(char ~= nil, "expected string 'char'")
local cx = math.floor(tonumber(x) or window.cursor[1])
local cy = math.floor(tonumber(y) or window.cursor[2])
char = char:sub(math.max(0, -cx - 1))
for i = 1, #char do
if cx >= 1 and cx <= window.width and cy >= 1 and cy <= window.height then
window.buffer[1][cy][cx] = char:sub(i,i)
window.buffer[2][cy][cx] = textCol:sub(i,i)
window.buffer[3][cy][cx] = backCol:sub(i,i)
end
cx = cx + 1
end
window.cursor = {cx, cy}
if lddterm.alwaysRender and not ignoreAlwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.print = function(text, x, y)
text = text and tostring(text)
window.handle.write(text, x, y, true)
window.cursor[1] = 1
if window.cursor[2] >= window.height then
window.handle.scroll(1)
else
window.cursor[2] = window.cursor[2] + 1
if lddterm.alwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
end
window.handle.clear = function(char, ignoreAlwaysRender)
local cx = 1
for y = 1, window.height do
for x = 1, window.width do
if char then
cx = (x % #char) + 1
end
window.buffer[1][y][x] = char and char:sub(cx, cx) or window.clearChar
window.buffer[2][y][x] = window.colors[1]
window.buffer[3][y][x] = window.colors[2]
end
end
if lddterm.alwaysRender and not ignoreAlwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.clearLine = function(cy, char, ignoreAlwaysRender)
cy = math.floor(cy or window.cursor[2])
local cx = 1
for x = 1, window.width do
if char then
cx = (x % #char) + 1
end
window.buffer[1][cy or window.cursor[2]][x] = char and char:sub(cx, cx) or window.clearChar
window.buffer[2][cy or window.cursor[2]][x] = window.colors[1]
window.buffer[3][cy or window.cursor[2]][x] = window.colors[2]
end
if lddterm.alwaysRender and not ignoreAlwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.clearColumn = function(cx, char, ignoreAlwaysRender)
cx = math.floor(cx)
char = char and char:sub(1,1)
for y = 1, window.height do
window.buffer[1][y][cx or window.cursor[1]] = char and char or window.clearChar
window.buffer[2][y][cx or window.cursor[1]] = window.colors[1]
window.buffer[3][y][cx or window.cursor[1]] = window.colors[2]
end
if lddterm.alwaysRender and not ignoreAlwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.getSize = function()
return window.width, window.height
end
window.handle.isColor = function()
return lddterm.useColors
end
window.handle.isColour = window.handle.isColor
window.handle.setTextColor = function(color)
if to_blit[color] then
window.colors[1] = to_blit[color]
end
end
window.handle.setTextColour = window.handle.setTextColor
window.handle.setBackgroundColor = function(color)
if to_blit[color] then
window.colors[2] = to_blit[color]
end
end
window.handle.setBackgroundColour = window.handle.setBackgroundColor
window.handle.getTextColor = function()
return to_colors[window.colors[1]] or colors.white
end
window.handle.getTextColour = window.handle.getTextColor
window.handle.getBackgroundColor = function()
return to_colors[window.colors[2]] or colors.black
end
window.handle.getBackgroundColour = window.handle.getBackgroundColor
window.handle.reposition = function(x, y)
window.x = math.floor(x or window.x)
window.y = math.floor(y or window.y)
if lddterm.alwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
end
window.handle.setPaletteColor = function(...)
return lddterm.baseTerm.setPaletteColor(...)
end
window.handle.setPaletteColour = window.handle.setPaletteColor
window.handle.getPaletteColor = function(...)
return lddterm.baseTerm.getPaletteColor(...)
end
window.handle.getPaletteColour = window.handle.getPaletteColor
window.handle.getPosition = function()
return window.x, window.y
end
window.handle.restoreCursor = function()
lddterm.baseTerm.setCursorPos(
-1 + window.cursor[1] + window.x,
window.cursor[2] + window.y - 1
)
end
window.handle.setVisible = function(visible)
window.visible = visible or false
end
window.handle.redraw = lddterm.render
window.handle.current = window.handle
window.layer = #lddterm.windows + 1
lddterm.windows[window.layer] = window
return window, window.layer
end
lddterm.setLayer = function(window, _layer)
local layer = math.max(1, math.min(#lddterm.windows, _layer))
local win = window
table.remove(lddterm.windows, win.layer)
table.insert(lddterm.windows, layer, win)
if lddterm.alwaysRender then
lddterm.render(lddterm.transformation, lddterm.drawFunction)
end
return true
end
-- if the screen changes size, the effect is broken
local old_scr_x, old_scr_y
-- gets screenshot of whole lddterm desktop, OR a single window
lddterm.screenshot = function(window)
local output = {{},{},{}}
local line
if window then
for y = 1, #window.buffer do
line = {"","",""}
for x = 1, #window.buffer do
line = {
line[1] .. window.buffer[1][y][x],
line[2] .. window.buffer[2][y][x],
line[3] .. window.buffer[3][y][x]
}
end
output[1][y] = line[1]
output[2][y] = line[2]
output[3][y] = line[3]
end
else
for y = 1, scr_y do
line = {"","",""}
for x = 1, scr_x do
c = "."
lt, lb = t, b
t, b = "0", "f"
for l = 1, #lddterm.windows do
if lddterm.windows[l].visible then
sx = 1 + x - lddterm.windows[l].x
sy = 1 + y - lddterm.windows[l].y
if lddterm.windows[l].buffer[1][sy] then
if lddterm.windows[l].buffer[1][sy][sx] then
c = lddterm.windows[l].buffer[1][sy][sx] or c
t = lddterm.windows[l].buffer[2][sy][sx] or t
b = lddterm.windows[l].buffer[3][sy][sx] or b
break
end
end
end
end
line = {
line[1] .. c,
line[2] .. t,
line[3] .. b
}
end
output[1][y] = line[1]
output[2][y] = line[2]
output[3][y] = line[3]
end
end
return output
end
local keysDown = {}
local instances = {}
local cwrite = function(text, y, terminal)
terminal = terminal or term.current()
local cx, cy = terminal.getCursorPos()
local sx, sy = terminal.getSize()
terminal.setCursorPos(sx / 2 - #text / 2, y or (sy / 2))
terminal.write(text)
end
local defaultProgram = "rom/programs/shell.lua"
local newInstance = function(x, y, program, initialStart)
x, y = math.floor(x), math.floor(y)
for yy = 1, y do
instances[yy] = instances[yy] or {}
end
for xx = 1, x do
instances[y][xx] = instances[y][xx] or false
end
local window = lddterm.newWindow(windowWidth, windowHeight, 1, 1)
instances[y][x] = {
x = x,
y = y,
co = coroutine.create(function()
term.redirect(window.handle)
local evt
while true do
if initialStart then
if not program or type(program) == "string" then
shell.run(program or defaultProgram)
elseif type(program) == "function" then
program()
end
end
term.clear()
term.setCursorBlink(false)
cwrite("Press SPACE to start workspace.")
repeat
evt = {os.pullEventRaw()}
until (evt[1] == "key" and evt[2] == keys.space) or evt[1] == "terminate"
sleep(0)
if evt[1] == "terminate" then
isRunning = false
return
end
term.setCursorPos(1,1)
term.clear()
term.setCursorBlink(true)
if not initialStart then
if not program or type(program) == "string" then
shell.run(program or defaultProgram)
elseif type(program) == "function" then
program()
end
end
end
end),
window = window
}
end
local scroll = {0,0} -- change this value when scrolling
local realScroll = {0,0} -- this value changes depending on scroll for smoothness purposes
local focus = {1,1}
-- prevents wiseassed-ness
workspaceMoveSpeed = math.min(math.max(workspaceMoveSpeed, 0.01), 1)
local scrollWindows = function()
local changed = false
if realScroll[1] < scroll[1] then
realScroll[1] = math.min(realScroll[1] + workspaceMoveSpeed, scroll[1])
changed = true
elseif realScroll[1] > scroll[1] then
realScroll[1] = math.max(realScroll[1] - workspaceMoveSpeed, scroll[1])
changed = true
end
if realScroll[2] < scroll[2] then
realScroll[2] = math.min(realScroll[2] + workspaceMoveSpeed, scroll[2])
changed = true
elseif realScroll[2] > scroll[2] then
realScroll[2] = math.max(realScroll[2] - workspaceMoveSpeed, scroll[2])
changed = true
end
for y = 1, #instances do
if instances[y] then
for x = 1, #instances[y] do
if instances[y][x] then
instances[y][x].window.x = math.floor(1 + (instances[y][x].x + realScroll[1] - 1) * scr_x)
instances[y][x].window.y = math.floor(1 + (instances[y][x].y + realScroll[2] - 1) * scr_y)
end
end
end
end
return changed
end
for y = 1, gridHeight do
for x = 1, gridWidth do
newInstance(x, y, defaultProgram, x == focus[1] and y == focus[2])
end
end
scrollWindows()
local inputEvt = {
key = true,
key_up = true,
char = true,
mouse_click = true,
mouse_scroll = true,
mouse_drag = true,
mouse_up = true,
paste = true,
terminate = true
}
local main = function()
local enteringCommand
local justStarted = true
local tID
while isRunning do
local evt = {os.pullEventRaw()}
enteringCommand = false
if evt[1] == "key" then
keysDown[evt[2]] = true
elseif evt[1] == "key_up" then
keysDown[evt[2]] = nil
end
if scrollWindows() then
tID = os.startTimer(0.05)
end
if keysDown[keys.leftCtrl] and keysDown[keys.leftShift] then
if keysDown[keys.left] then
if instances[focus[2]][focus[1] - 1] then
focus[1] = focus[1] - 1
scroll[1] = scroll[1] + 1
keysDown[keys.left] = false
enteringCommand = true
end
end
if keysDown[keys.right] then
if instances[focus[2]][focus[1] + 1] then
focus[1] = focus[1] + 1
scroll[1] = scroll[1] - 1
keysDown[keys.right] = false
enteringCommand = true
end
end
if keysDown[keys.up] then
if instances[focus[2] - 1] then
if instances[focus[2] - 1][focus[1]] then
focus[2] = focus[2] - 1
scroll[2] = scroll[2] + 1
keysDown[keys.up] = false
enteringCommand = true
end
end
end
if keysDown[keys.down] then
if instances[focus[2] + 1] then
if instances[focus[2] + 1][focus[1]] then
focus[2] = focus[2] + 1
scroll[2] = scroll[2] - 1
keysDown[keys.down] = false
enteringCommand = true
end
end
end
end
if not enteringCommand then
for y = 1, #instances do
if instances[y] then
for x = 1, #instances[y] do
if instances[y][x] then
if justStarted or (not inputEvt[evt[1]]) or (x == focus[1] and y == focus[2]) then
local previousTerm = term.redirect(instances[y][x].window.handle)
coroutine.resume(instances[y][x].co, table.unpack(evt))
term.redirect(previousTerm)
end
end
end
end
end
end
lddterm.selectedWindow = instances[focus[2]][focus[1]].window.layer
lddterm.render()
justStarted = false
end
end
term.clear()
cwrite("Use CTRL+SHIFT+ARROW to switch workspace.")
sleep(0.1)
os.pullEvent("key")
os.queueEvent("mouse_click", 0, 0, 0)
main()
_G.currentlyRunningWorkspace = false
term.clear()
term.setCursorPos(1,1)
print("Thanks for using Workspace!")