5381 lines
151 KiB
Lua
5381 lines
151 KiB
Lua
-- LevelOS Utils
|
|
|
|
if not unpack then
|
|
_G.unpack = table.unpack
|
|
end
|
|
|
|
local write = term.write
|
|
|
|
if _G.lUtils == nil then
|
|
_G.lUtils = {}
|
|
end
|
|
|
|
|
|
if _G.lOS == nil then
|
|
_G.lOS = {}
|
|
end
|
|
|
|
function lUtils.RGBToGrayscale( r, g, b )
|
|
local gr = (r + g + b) / 3
|
|
return gr,gr,gr
|
|
end
|
|
|
|
local function getLines(str)
|
|
local lines = {}
|
|
local w = 0
|
|
for potato in str:gmatch("([^\n]*)\n?") do
|
|
table.insert(lines,potato)
|
|
if #potato > w then
|
|
w = #potato
|
|
end
|
|
end
|
|
return lines,w
|
|
end
|
|
|
|
local function cRestore(restore)
|
|
if not restore then
|
|
return {bg=term.getBackgroundColor(),fg=term.getTextColor(),cursor={term.getCursorPos()}}
|
|
else
|
|
term.setBackgroundColor(restore.bg)
|
|
term.setTextColor(restore.fg)
|
|
term.setCursorPos(unpack(restore.cursor))
|
|
end
|
|
end
|
|
|
|
function lUtils.wordwrap(txt,width)
|
|
if not txt then
|
|
error("No text given",2)
|
|
end
|
|
local lines = {}
|
|
for line in txt:gmatch("([^\n]*)\n?") do
|
|
table.insert(lines,"")
|
|
for word in line:gmatch("%S*%s?") do
|
|
if #lines[#lines]+#word > width and #(lines[#lines]:gsub(" ","")) > 0 then
|
|
lines[#lines+1] = ""
|
|
end
|
|
if #lines[#lines]+#word > width then
|
|
local tWord = word
|
|
while #lines[#lines]+#tWord > width do
|
|
lines[#lines] = tWord:sub(1,width)
|
|
table.insert(lines,"")
|
|
tWord = tWord:sub(width+1)
|
|
end
|
|
lines[#lines] = tWord
|
|
else
|
|
lines[#lines] = lines[#lines]..word
|
|
end
|
|
end
|
|
end
|
|
if lines[#lines] == "" then
|
|
lines[#lines] = nil
|
|
end
|
|
if txt:sub(#txt) == "\n" then
|
|
table.insert(lines,"")
|
|
end
|
|
return lines
|
|
end
|
|
|
|
function lUtils.input(x1,y1,x2,y2,tOptions,sReplaceChar,tShape)
|
|
local oCursorA
|
|
local s
|
|
if not tShape then
|
|
s = {x1=x1,y1=y1,x2=x2,y2=y2,cursor={x=1,y=1,a=1},scr=0,ref={}}
|
|
else
|
|
s = tShape
|
|
s.cursor={x=1,y=1,a=1}
|
|
s.scr=0
|
|
s.ref={}
|
|
end
|
|
s.history = {}
|
|
s.rhistory = {}
|
|
s.changed = false
|
|
local opt = {}
|
|
if tOptions then
|
|
opt = tOptions
|
|
elseif s.opt then
|
|
opt = s.opt
|
|
else
|
|
opt = {}
|
|
end
|
|
if not opt.overflow and not opt.overflowX and not opt.overflowY then
|
|
opt.overflow = "scroll"
|
|
opt.overflowX = "scroll"
|
|
opt.overflowY = "none"
|
|
end
|
|
opt["overflow"] = opt.overflow or "scroll" -- none, stretch, scroll or wrap
|
|
opt["overflowX"] = opt.overflowX or opt.overflow
|
|
opt["overflowY"] = opt.overflowY or opt.overflow
|
|
if opt.overflowY == "wrap" then
|
|
opt.overflowY = "none"
|
|
end
|
|
opt["cursorColor"] = opt.cursorColor or s.txtcolor or term.getTextColor()
|
|
opt["replaceChar"] = sReplaceChar or opt.replaceChar
|
|
|
|
opt["minWidth"] = opt.minWidth or s.x2-(s.x1-1)
|
|
opt["minHeight"] = opt.minHeight or s.y2-(s.y1-1)
|
|
|
|
opt["tabSize"] = opt.tabSize or 4
|
|
opt["indentChar"] = opt.indentChar or " "
|
|
|
|
if opt.overflowX == "scroll" then
|
|
s.scrollX = 0
|
|
end
|
|
|
|
if opt.overflowY == "scroll" then
|
|
s.scrollY = 0
|
|
end
|
|
|
|
--[[opt["maxWidth"] = opt.maxWidth
|
|
opt["maxHeight"] = opt.maxHeight]]
|
|
|
|
s.opt = opt
|
|
|
|
s.color = s.color or opt.backgroundColor or term.getBackgroundColor()
|
|
s.txtcolor = s.txtcolor or opt.textColor or term.getTextColor()
|
|
|
|
local txtcolor = s.txtcolor
|
|
s.txt = opt.text or ""
|
|
--s.lines = {s.txt}
|
|
s.lines = {} -- real text input, for example password if censored
|
|
s.dLines = {} -- rendered text input, buncha asterisks if censored, spaces instead of tab
|
|
s.blit = {} -- foreground and background colors
|
|
s.state = false
|
|
local ref = s.ref
|
|
local syntaxes = {
|
|
["lua"] = {
|
|
lexer="lex",
|
|
whitespace=colors.white,
|
|
comment=colors.green,
|
|
string=colors.red,
|
|
escape=colors.orange,
|
|
keyword=colors.yellow,
|
|
value=colors.yellow,
|
|
ident=colors.white,
|
|
number=colors.purple,
|
|
symbol=colors.orange,
|
|
operator=colors.yellow,
|
|
unidentified=colors.white,
|
|
},
|
|
["lua-light"] = {
|
|
lexer="lex",
|
|
whitespace=colors.black,
|
|
comment=colors.lightGray,
|
|
string=colors.red,
|
|
escape=colors.orange,
|
|
keyword=colors.blue,
|
|
value=colors.purple,
|
|
ident=colors.black,
|
|
number=colors.lightBlue,
|
|
symbol=colors.orange,
|
|
operator=colors.gray,
|
|
unidentified=colors.black,
|
|
}
|
|
}
|
|
local syntax
|
|
local uservars = {}
|
|
local scope = 0
|
|
local function lineLn(line)
|
|
local findTab = line:find("\t")
|
|
local offset = 0
|
|
local t = s.opt.tabSize
|
|
while findTab do
|
|
local l = t-(findTab+offset-1)%t
|
|
offset = offset+(l-1)
|
|
findTab = line:find("\t",findTab+1)
|
|
end
|
|
return #line+offset
|
|
end
|
|
local function fillTable(tTable,tbl,prefix)
|
|
--local type = rtype
|
|
local rtype = rtype
|
|
if not rtype then
|
|
rtype = type
|
|
end
|
|
local docs = s.opt.complete.docs
|
|
while tTable do
|
|
for k,v in pairs(tTable) do
|
|
if type(k) == "string" and not tbl[k] then
|
|
if type(v) == "table" then
|
|
tbl[k] = {type="table",data={},name=prefix..k}
|
|
if docs and docs[tbl[k].name] then
|
|
tbl[k].docs = docs[tbl[k].name]
|
|
end
|
|
--[[scope = scope+1
|
|
fillTable(v,tbl[k].data,prefix..k..".")
|
|
scope = scope-1]]
|
|
elseif type(v) == "string" or type(v) == "number" or type(v) == "boolean" or type(v) == "nil" then
|
|
tbl[k] = {type=type(v),data=v,name=prefix..k}
|
|
if docs and docs[tbl[k].name] then
|
|
tbl[k].docs = docs[tbl[k].name]
|
|
end
|
|
elseif type(v) == "function" then
|
|
local obj = {type="function",data=v,name=prefix..k}
|
|
local args = {}
|
|
if rtype(v) == "table" then
|
|
local mt = getmetatable(v)
|
|
if rtype(mt) == "table" and rtype(mt._call) == "function" then
|
|
v = mt._call
|
|
end
|
|
end
|
|
if rtype(v) == "function" then
|
|
local info = debug.getinfo(v)
|
|
for t=1,info.nparams do
|
|
table.insert(args,debug.getlocal(v,t))
|
|
end
|
|
if info.isvararg then
|
|
table.insert(args,"...")
|
|
end
|
|
obj.args = args
|
|
obj.source = info.short_src
|
|
end
|
|
if not obj.args then obj.args = {} end
|
|
if not obj.source then obj.source = "" end
|
|
if docs and docs[obj.name] then
|
|
obj.docs = docs[obj.name]
|
|
end
|
|
tbl[k] = obj
|
|
end
|
|
end
|
|
end
|
|
local tMetatable = getmetatable(tTable)
|
|
if tMetatable and type(tMetatable.__index) == "table" then
|
|
tTable = tMetatable.__index
|
|
else
|
|
tTable = nil
|
|
end
|
|
end
|
|
end
|
|
local keywords = {"and", "break", "do", "else", "elseif", "end", "for", "function", "if", "in", "local", "not", "or", "repeat", "return", "then", "until", "while"}
|
|
local function complete(sTxt)
|
|
local docs = s.opt.complete.docs
|
|
if not sTxt then return {} end
|
|
local sSearchText
|
|
local fComplete = false
|
|
local sSearchText = sTxt:match("^[a-zA-Z0-9_%.:]+")
|
|
if not sSearchText then return end
|
|
if #sSearchText < #sTxt then
|
|
fComplete = true
|
|
end
|
|
s.opt.complete.selected = nil
|
|
local env = s.opt.complete.env
|
|
fillTable(env,uservars,"")
|
|
local nStart = 1
|
|
local nDot = string.find(sSearchText, ".", nStart, true)
|
|
local tTable = uservars
|
|
while nDot do
|
|
local sPart = string.sub(sSearchText, nStart, nDot - 1)
|
|
if not tTable[sPart] then return {} end
|
|
local value = tTable[sPart].data
|
|
if type(value) == "table" then
|
|
tTable = value
|
|
if type(env[sPart]) == "table" then
|
|
fillTable(env[sPart],tTable,"")
|
|
env = env[sPart]
|
|
end
|
|
nStart = nDot + 1
|
|
nDot = string.find(sSearchText, ".", nStart, true)
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
local nColon = string.find(sSearchText, ":", nStart, true)
|
|
if nColon then
|
|
local sPart = string.sub(sSearchText, nStart, nColon - 1)
|
|
if not tTable[sPart] then return {} end
|
|
local value = tTable[sPart].data
|
|
if type(value) == "table" then
|
|
tTable = value
|
|
if type(env[sPart]) == "table" then
|
|
fillTable(env[sPart],tTable,"")
|
|
env = env[sPart]
|
|
end
|
|
nStart = nColon + 1
|
|
else
|
|
return {}
|
|
end
|
|
end
|
|
|
|
local sPart = string.sub(sSearchText, nStart)
|
|
local prefix = string.sub(sSearchText, 1, nStart-1)
|
|
local nPartLength = #sPart
|
|
|
|
local tResults = {}
|
|
local tStrings = {}
|
|
local tSorted = {}
|
|
local tSeen = {}
|
|
if fComplete then
|
|
tResults.length = #sTxt
|
|
else
|
|
tResults.length = nPartLength
|
|
end
|
|
for k,v in pairs(tTable) do
|
|
if not tSeen[k] and type(k) == "string" and not (nColon and v.type ~= "function") then
|
|
if (string.find(k, sPart, 1, true) == 1 and not fComplete) or (fComplete and k == sPart) then
|
|
if string.match(k, "^[%a_][%a%d_]*$") then
|
|
local sResult = string.sub(k, nPartLength + 1)
|
|
local display = k
|
|
local index = sSearchText..sResult
|
|
if v.type == "function" then
|
|
local vArgs = lUtils.instantiate(v.args)
|
|
if nColon then
|
|
table.remove(vArgs,1)
|
|
end
|
|
if fComplete then
|
|
display = index.."("..table.concat(vArgs,",")..")"
|
|
else
|
|
display = k.."("..table.concat(vArgs,",")..")"
|
|
end
|
|
sResult = sResult.."("
|
|
end
|
|
tStrings[sResult] = {src=(v.source or ""),complete=sResult,type=v.type,display=display}
|
|
if fComplete and v.type ~= "function" then return nil end
|
|
if docs[index] then
|
|
local d = docs[index]
|
|
if fComplete then
|
|
tStrings[sResult].display = d.name:gsub("^_G%.","")
|
|
tStrings[sResult].description = d.summary
|
|
else
|
|
tStrings[sResult].display = d.name:gsub("^_G%.",""):gsub("^"..prefix,"")
|
|
end
|
|
end
|
|
table.insert(tSorted,sResult)
|
|
--tResults[#tResults].id = #tResults
|
|
end
|
|
end
|
|
end
|
|
tSeen[k] = true
|
|
end
|
|
table.sort(tSorted)
|
|
for t=1,#tSorted do
|
|
tResults[t] = tStrings[tSorted[t]]
|
|
tResults[t].id = t
|
|
end
|
|
--table.sort(tResults)
|
|
return tResults
|
|
end
|
|
local function renderComplete(list) -- make option to use overay like s.opt.complete.overlay = true
|
|
local c = s.opt.complete
|
|
local LevelOS = c.LevelOS
|
|
c.reverse = false
|
|
if not c.selected and not list[1].description then
|
|
c.selected = list[1]
|
|
end
|
|
local a = 0
|
|
if s.border and s.border.color ~= 0 then
|
|
a = 1
|
|
end
|
|
local scrollX = s.scrollX or 0
|
|
local scrollY = s.scrollY or 0
|
|
local x,y = s.x1+(s.cursor.x-1)+a-scrollX,s.y1+(s.cursor.y-1)+a-scrollY
|
|
x = x-list.length-4
|
|
y = y+1
|
|
local width = 10
|
|
local height = #list
|
|
for t=1,#list do
|
|
width = math.max(#list[t].display+#list[t].src+5,width)
|
|
if list[t].description then
|
|
list[t].lines = lUtils.wordwrap(list[t].description,width-4)
|
|
height = height+#list[t].lines
|
|
end
|
|
end
|
|
if c.overlay and LevelOS then
|
|
--local wX,wY = LevelOS.self.window.win.getPosition()
|
|
local wX,wY = lUtils.getWindowPos(term.current())
|
|
local w,h = lOS.oldterm.getSize()
|
|
x = x+wX-1
|
|
y = y+wY-1
|
|
if x < 1 then
|
|
x = 1
|
|
end
|
|
if y+(height-1) > h and y-1-height >= 1 then
|
|
y = y-2
|
|
c.reverse = true
|
|
end
|
|
if x+(width-1) > w then
|
|
x = w-width+1
|
|
end
|
|
else
|
|
if x < s.x1 then
|
|
x = s.x1
|
|
end
|
|
if y+(height-1) > s.y2 and y-1-height >= s.y1 then
|
|
c.reverse = true
|
|
y = y-2
|
|
end
|
|
if x+(width-1) > s.x2 then
|
|
x = s.x2-width+1
|
|
end
|
|
end
|
|
local offset = 1
|
|
if c.reverse then
|
|
offset = -1
|
|
end
|
|
local x2 = x+(width-1)
|
|
abbrevs = {table={"tbl",colors.lime},number={"num",colors.purple},boolean={"bln",colors.purple},["function"]={"fnc",colors.cyan},keyword={" ",colors.orange},["nil"]={"nil",colors.red},unknown={"???",colors.pink},string={"str",colors.yellow}}
|
|
local function theRender()
|
|
if not s.opt.complete.colors then
|
|
s.opt.complete.colors = {}
|
|
end
|
|
local col = s.opt.complete.colors
|
|
col.selectedbg = col.selectedbg or colors.lightBlue -- green
|
|
col.backgroundColor = col.backgroundColor or colors.gray -- magenta
|
|
col.txtcolor = col.txtcolor or colors.white -- white
|
|
col.typebg = col.typebg or colors.black -- light gray
|
|
col.selectedtypebg = col.selectedtypebg or colors.blue -- green
|
|
col.sourcetxtcolor = col.sourcetxtcolor or colors.lightGray -- pink
|
|
local cY = y
|
|
for t=1,#list do
|
|
if c.selected == list[t] then
|
|
term.setBackgroundColor(col.selectedtypebg)
|
|
else
|
|
term.setBackgroundColor(col.typebg)
|
|
end
|
|
local a = abbrevs[list[t].type] or abbrevs["unknown"]
|
|
term.setTextColor(a[2])
|
|
term.setCursorPos(x,cY)
|
|
term.write(a[1])
|
|
if c.selected == list[t] then
|
|
term.setBackgroundColor(col.selectedbg)
|
|
else
|
|
term.setBackgroundColor(col.backgroundColor)
|
|
end
|
|
term.setTextColor(col.txtcolor)
|
|
term.write(" "..list[t].display..string.rep(" ",width-#list[t].display-4-#list[t].src))
|
|
term.setCursorPos(x2-(#list[t].src-1),cY)
|
|
term.setTextColor(col.sourcetxtcolor)
|
|
term.write(list[t].src)
|
|
cY = cY+offset
|
|
if list[t].lines then
|
|
for i,l in ipairs(list[t].lines) do
|
|
term.setCursorPos(x,cY)
|
|
term.setTextColor(col.sourcetxtcolor)
|
|
term.write(" "..l..string.rep(" ",width-#l-4))
|
|
cY = cY+offset
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local x1,y1,x2,y2
|
|
if c.overlay and LevelOS then
|
|
LevelOS.overlay = theRender
|
|
local wX,wY = LevelOS.self.window.win.getPosition()
|
|
x1 = x-wX+1
|
|
x2 = x1+width-1
|
|
y1 = y-wY+1
|
|
y2 = y1+((#list-1)*offset)
|
|
else
|
|
x1 = x
|
|
y1 = y
|
|
y2 = y1+((#list-1)*offset)
|
|
x2 = x1+width-1
|
|
theRender()
|
|
end
|
|
list.x1 = x1
|
|
list.y1 = math.min(y1,y2)
|
|
list.y2 = math.max(y1,y2)
|
|
list.x2 = x2
|
|
end
|
|
local function replaceText(txt,pos1,pos2,replace)
|
|
return txt:sub(1,pos1-1)..replace..txt:sub(pos2+1,#txt)
|
|
end
|
|
local function genLines(t)
|
|
local syn = s.opt.syntax
|
|
if type(syn) == "string" and syntaxes[syn] then
|
|
syntax = syntaxes[syn]
|
|
syntax.type = syn
|
|
elseif type(syn) == "table" and syn.lexer and type(syn.lexer) == "function" then
|
|
syntax = syn
|
|
elseif type(syn) == "table" and syntaxes[syn.type] then
|
|
syntax = syntaxes[syn.type]
|
|
for k,v in pairs(syn) do
|
|
syntax[k] = v
|
|
end
|
|
elseif type(syn) == "table" and syn.lexer and ((type(syn.lexer) == "string" and fs.exists(syn.lexer)) or type(syn.lexer) == "function") then
|
|
syntax = syn
|
|
else
|
|
syntax = nil
|
|
end
|
|
local blit = {}
|
|
if s.opt.complete and syntax then
|
|
uservars = {}
|
|
for k=1,#keywords do
|
|
uservars[keywords[k]] = {type="keyword"}
|
|
end
|
|
uservars["true"] = {type="boolean"}
|
|
uservars["false"] = {type="boolean"}
|
|
uservars["nil"] = {type="nil"}
|
|
_G.debuguservars = uservars
|
|
--[[scope = 0
|
|
local tTable = s.opt.complete.env
|
|
fillTable(tTable,uservars,"")]]
|
|
end
|
|
if syntax and ((type(syntax.lexer) == "string" and fs.exists(syntax.lexer)) or type(syntax.lexer) == "function") then
|
|
if type(syntax.lexer) ~= "function" then
|
|
syntax.lexer = dofile(syntax.lexer)
|
|
end
|
|
local lex = syntax.lexer
|
|
local sublines = lex(t)
|
|
local line = 1
|
|
local l = 0
|
|
local ref = 0
|
|
--for li in t:gmatch("([^\n]*)\n?") do -- this aint workin and dont forget to add newlines to the blit lines
|
|
blit[1] = ""
|
|
blit[2] = ""
|
|
blit[3] = ""
|
|
_G.debugsublines = sublines
|
|
for l=1,#sublines do
|
|
local elements = sublines[l]
|
|
for t=1,#elements do
|
|
local col
|
|
local col2 = lUtils.toBlit(s.color)
|
|
if type(syntax[elements[t].type]) == "table" then
|
|
col, col2 = lUtils.toBlit(syntax[elements[t].type][1] or s.txtcolor), lUtils.toBlit(syntax[elements[t].type][2] or s.color)
|
|
else
|
|
col = lUtils.toBlit(syntax[elements[t].type] or s.txtcolor)
|
|
end
|
|
blit[1] = blit[1]..elements[t].data
|
|
blit[2] = blit[2]..string.rep(col,#elements[t].data)
|
|
blit[3] = blit[3]..string.rep(col2,#elements[t].data)
|
|
if s.opt.complete and #s.txt < 40000 and syntax.type:find("^lua") then
|
|
if elements[t].type == "nfunction" then
|
|
local el = t+1
|
|
local args = {}
|
|
while true do
|
|
local e = elements[el]
|
|
if not e then
|
|
break
|
|
elseif e.type == "function" or e.type == "whitespace" or e.data == "(" or e.data == "," or e.data == "=" then
|
|
el = el+1
|
|
elseif e.type == "arg" or e.data == "..." then
|
|
table.insert(args,e.data)
|
|
el = el+1
|
|
else
|
|
break
|
|
end
|
|
end
|
|
local parent = {data=uservars}
|
|
local el = t-1
|
|
local children = {}
|
|
while true do
|
|
local e = elements[el]
|
|
if not e then
|
|
break
|
|
elseif e.data == "." then
|
|
el = el-1
|
|
elseif e.type == "ident" then
|
|
table.insert(children,1,e.data)
|
|
el = el-1
|
|
else
|
|
break
|
|
end
|
|
end
|
|
for t=1,#children do
|
|
if parent.data[children[t]] then
|
|
local child = parent.data[children[t]]
|
|
if type(child.data) ~= "table" then child.data = {} child.type = "table" end
|
|
parent = child
|
|
else
|
|
parent.data[children[t]] = {data={},type="table",source="Ln "..l}
|
|
parent = parent.data[children[t]]
|
|
end
|
|
end
|
|
parent.data[elements[t].data] = {args=args,type="function",source="Ln "..l}
|
|
elseif elements[t].type == "ident" then
|
|
local el = t+1
|
|
local foundEquals = false
|
|
local foundValue = false
|
|
local naming = true
|
|
local prefix = ""
|
|
local parent = {data=uservars}
|
|
local children = {elements[t].data}
|
|
while true do
|
|
local e = elements[el]
|
|
if not e then
|
|
break
|
|
elseif e.data == "." and naming then
|
|
el = el+1
|
|
elseif e.type == "whitespace" or (e.data == "=" and not foundEquals) then
|
|
naming = false
|
|
el = el+1
|
|
if e.data == "=" then
|
|
local stop = false
|
|
for c=1,#children-1 do
|
|
local child = parent.data[children[c]]
|
|
if not child then parent.data[children[c]] = {data={},type="table",source="Ln "..l} child = parent.data[children[c]] end
|
|
if type(child.data) ~= "table" then child.data = {} child.type = "table" end
|
|
parent = child
|
|
prefix = prefix..children[c].."."
|
|
end
|
|
if stop then break end
|
|
foundEquals = true
|
|
end
|
|
elseif e.type == "ident" then
|
|
el = el+1
|
|
if naming then
|
|
table.insert(children,e.data)
|
|
else
|
|
-- idk do later
|
|
foundValue = true
|
|
-- add table support
|
|
if uservars[e.data] then
|
|
end
|
|
end
|
|
elseif foundEquals and (e.type == "string" or e.type == "number" or (e.type == "operator" and e.data == "-") or e.type == "value" or (e.type == "symbol" and e.data:sub(1,1) == "{") or e.type == "function") then
|
|
local vType = e.type
|
|
if e.type == "operator" then
|
|
vType = "number"
|
|
elseif e.type == "value" then
|
|
if e.data == "nil" then
|
|
vType = "nil"
|
|
elseif e.data == "true" or e.data == "false" then
|
|
vType = "boolean"
|
|
else
|
|
vType = "unknown"
|
|
end
|
|
elseif e.type == "symbol" then
|
|
vType = "table"
|
|
elseif e.type == "function" then
|
|
vType = "unknown"
|
|
end
|
|
local obj = {name=prefix..children[#children],type=vType,source="Ln "..l}
|
|
if vType == "table" then
|
|
obj.data = {}
|
|
end
|
|
parent.data[children[#children]] = obj
|
|
break
|
|
else
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if l < #sublines then
|
|
blit[1] = blit[1].."\n"
|
|
blit[2] = blit[2].."\n"
|
|
blit[3] = blit[3].."\n"
|
|
end
|
|
end
|
|
_G.debugBlit2 = {blit[1],blit[2],blit[3]}
|
|
end
|
|
-- check for transparency for third blit line
|
|
for a=1,#s.lines do
|
|
s.lines[a] = nil
|
|
s.dLines[a] = nil
|
|
s.blit[a] = nil
|
|
ref[a] = nil
|
|
end
|
|
s.lines[1] = ""
|
|
s.dLines[1] = ""
|
|
local width = s.x2-(s.x1-1)
|
|
local height = s.y2-(s.y1-1)
|
|
if s.border and s.border.color ~= 0 then
|
|
width = width-2
|
|
height = height-2
|
|
end
|
|
local c = 1
|
|
local l = s.lines
|
|
local dl = s.dLines
|
|
local pl = 0
|
|
if opt.overflowX == "scroll" then
|
|
--local b,e = t:find()
|
|
l[1] = ""
|
|
dl[1] = ""
|
|
while true do
|
|
-- line
|
|
local b,e = t:find("[^\n]*\n?")
|
|
if not b or e == 0 then break end
|
|
local line = t:sub(b,e)
|
|
l[c] = line
|
|
if #blit > 0 then
|
|
dl[c] = line
|
|
local findTab = dl[c]:find("\t")
|
|
local blit2 = blit[2]
|
|
local blit3 = blit[3]
|
|
if not s.opt.tabSize then
|
|
s.opt.tabSize = 4
|
|
end
|
|
local t = s.opt.tabSize
|
|
while findTab do
|
|
local l = t-(findTab-1)%t
|
|
dl[c] = replaceText(dl[c],findTab,findTab,string.sub(s.opt.indentChar..string.rep(" ",t-#s.opt.indentChar),t-l+1,t))
|
|
local col, col2
|
|
if type(syntax.whitespace) == "table" then
|
|
col = lUtils.toBlit(syntax.whitespace[1] or s.txtcolor)
|
|
col2 = lUtils.toBlit(syntax.whitespace[2] or s.color)
|
|
else
|
|
col = lUtils.toBlit(syntax.whitespace or s.txtcolor)
|
|
col2 = lUtils.toBlit(s.color)
|
|
end
|
|
blit2 = replaceText(blit2,findTab,findTab,string.rep(col,l))
|
|
blit3 = replaceText(blit3,findTab,findTab,string.rep(col2,l))
|
|
findTab = dl[c]:find("\t")
|
|
end
|
|
local tabArea = string.rep(" ",t)
|
|
local findSpace,findSpace2 = dl[c]:find(tabArea)
|
|
while findSpace do
|
|
dl[c] = replaceText(dl[c],findSpace,findSpace2,s.opt.indentChar..string.rep(" ",t-#s.opt.indentChar))
|
|
findSpace,findSpace2 = dl[c]:find(tabArea,findSpace2+1)
|
|
end
|
|
dl[c] = dl[c]:sub(1+s.scrollX,width+s.scrollX)
|
|
s.blit[c] = {dl[c],blit2:sub(b+s.scrollX,b+s.scrollX+(#dl[c]-1)),blit3:sub(b+s.scrollX,b+s.scrollX+(#dl[c]-1))}
|
|
blit[1] = blit[1]:sub(e+1,#blit[1])
|
|
blit[2] = blit[2]:sub(e+1,#blit[2])
|
|
blit[3] = blit[3]:sub(e+1,#blit[3])
|
|
else
|
|
dl[c] = line:sub(1+s.scrollX,width+s.scrollX)
|
|
s.blit[c] = {dl[c],string.rep(lUtils.toBlit(s.txtcolor),#dl[c]),string.rep(lUtils.toBlit(s.color),#dl[c])}
|
|
end
|
|
if line:sub(#line) == "\n" then
|
|
if c+1 > height then
|
|
if opt.overflowY == "stretch" then
|
|
s.y2 = s.y2+1
|
|
elseif opt.overflowY == "none" then
|
|
return false
|
|
end
|
|
end
|
|
l[c+1] = ""
|
|
dl[c+1] = ""
|
|
end
|
|
t = t:sub(e+1,#t)
|
|
c = c+1
|
|
end
|
|
else
|
|
while true do
|
|
local b,e = t:find("%S*%s?")
|
|
if not b or e < 1 then break end
|
|
local w = e-(b-1)
|
|
c = #l
|
|
if not dl[c] then dl[c] = l[c] end
|
|
if string.find(t:sub(b,e),"\n",nil,true) then
|
|
if (opt.overflowY == "stretch" or opt.overflowY == "scroll") or c+1 <= height then
|
|
if opt.overflowY == "stretch" and c+1 > height then
|
|
s.y2 = s.y2+1
|
|
end
|
|
b2,e2 = t:sub(b,e):find("\n",nil,true)
|
|
e = e2
|
|
l[c+1] = ""
|
|
dl[c+1] = ""
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
local of = opt.overflowX
|
|
local tW,tH = term.getSize()
|
|
if opt.overflowX == "stretch" then
|
|
if #dl[c]+w > tW and #dl[c]+w > width then
|
|
of = "wrap"
|
|
end
|
|
end
|
|
if #dl[c]+w > width then
|
|
if opt.overflowX == "wrap" then
|
|
if (opt.overflowY == "stretch" or opt.overflowY == "scroll") or c+1 <= height then
|
|
if opt.overflowY == "stretch" and c+1 > height then
|
|
s.y2 = s.y2+1
|
|
end
|
|
if not dl[c]:find("%S") then
|
|
e = width-#l[c]
|
|
l[c] = l[c]..t:sub(b,e)
|
|
l[c+1] = ""
|
|
else
|
|
--l[c+1] = t:sub(b,e)
|
|
e = b-1
|
|
l[c+1] = ""
|
|
end
|
|
else
|
|
-- oh no, stop typing
|
|
return false
|
|
end
|
|
elseif opt.overflowX == "stretch" then
|
|
s.x2 = s.x2+1
|
|
l[c] = l[c]..t:sub(b,e)
|
|
dl[c] = dl[c]..t:sub(b,e):gsub("\9"," ")
|
|
elseif opt.overflowX == "none" then
|
|
return false
|
|
end
|
|
else
|
|
l[c] = l[c]..t:sub(b,e)
|
|
dl[c] = dl[c]..t:sub(b,e):gsub("\9"," ")
|
|
end
|
|
t = t:sub(e+1,#t)
|
|
end
|
|
end
|
|
return true
|
|
end
|
|
genLines(s.txt)
|
|
local function genText()
|
|
local txt = ""
|
|
ref[1] = 1
|
|
for l=1,#s.lines do
|
|
txt = txt..s.lines[l]
|
|
ref[l+1] = ref[l]+#s.lines[l]
|
|
if s.select and s.select[1] < ref[l+1] and s.select[2] >= ref[l] and s.blit and s.blit[l] then
|
|
local line = s.lines[l]
|
|
local c = lUtils.toBlit(s.opt.selectColor or colors.blue)
|
|
local sel1 = s.select[1]-ref[l]
|
|
local sel2 = s.select[2]-ref[l]+2
|
|
local findTab = s.lines[l]:find("\t")
|
|
local off1 = 0
|
|
local off2 = 0
|
|
local offT = 0
|
|
while findTab do
|
|
line = replaceText(line,findTab,findTab," ")
|
|
local t = s.opt.tabSize
|
|
local a = t-(findTab+offT-1)%t
|
|
if sel1 >= findTab then
|
|
off1 = off1+a-1
|
|
end
|
|
if sel2 > findTab then
|
|
off2 = off2+a-1
|
|
end
|
|
if findTab > sel1 and findTab < sel2 then
|
|
s.blit[l][1] = replaceText(s.blit[l][1],offT+findTab,offT+findTab+(a-1),string.sub(string.rep("\140",t-1).."\132",t-a+1,t))
|
|
end
|
|
offT = offT+a-1
|
|
findTab = line:find("\t")
|
|
end
|
|
sel1 = sel1+off1-s.scrollX
|
|
sel2 = sel2+off2-s.scrollX
|
|
local pos1 = math.max(0,sel1)
|
|
local pos2 = math.min(#s.blit[l][1]+1,sel2)
|
|
s.blit[l][3] = s.blit[l][3]:sub(1,pos1)..string.rep(c,pos2-(pos1+1))..s.blit[l][3]:sub(pos2,#s.blit[l][1])
|
|
if s.opt.selectTxtColor then
|
|
local c2 = lUtils.toBlit(s.opt.selectTxtColor)
|
|
s.blit[l][2] = s.blit[l][2]:sub(1,pos1)..string.rep(c2,pos2-(pos1+1))..s.blit[l][2]:sub(pos2,#s.blit[l][1])
|
|
end
|
|
s.blit[l][1] = s.blit[l][1]:sub(1,pos1)..s.blit[l][1]:sub(pos1+1,pos2-1):gsub(" ","\183")..s.blit[l][1]:sub(pos2,#s.blit[l][1])
|
|
end
|
|
end
|
|
return txt
|
|
end
|
|
genText()
|
|
local function calcCursor()
|
|
local width = s.x2-(s.x1-1)
|
|
local height = s.y2-(s.y1-1)
|
|
if s.border and s.border.color ~= 0 then
|
|
width = width-2
|
|
height = height-2
|
|
end
|
|
for r=1,#s.lines do
|
|
if ref[r+1] > s.cursor.a or r == #s.lines then
|
|
s.cursor.y = r
|
|
s.cursor.x = s.cursor.a-(ref[r]-1)
|
|
local _,tabs = s.txt:sub(ref[r],s.cursor.a-1):gsub("\9","")
|
|
s.cursor.x = s.cursor.x+((s.opt.tabSize-1)*tabs)
|
|
break
|
|
end
|
|
end
|
|
if opt.overflowX == "scroll" then
|
|
if s.cursor.x < (1+s.scrollX) then
|
|
s.scrollX = s.cursor.x-1
|
|
elseif s.cursor.x > (width+s.scrollX) then
|
|
s.scrollX = s.cursor.x-width
|
|
end
|
|
if s.scrollX > 0 and lineLn(s.lines[s.cursor.y]) < width then
|
|
s.scrollX = 0
|
|
end
|
|
end
|
|
if opt.overflowY == "scroll" then
|
|
if s.cursor.y < (1+s.scrollY) then
|
|
s.scrollY = s.cursor.y-1
|
|
elseif s.cursor.y > (height+s.scrollY) then
|
|
s.scrollY = s.cursor.y-height
|
|
end
|
|
end
|
|
end
|
|
local function rCalcCursor()
|
|
local x = s.cursor.x
|
|
local tx = 0
|
|
local offset = 0
|
|
local line = s.lines[s.cursor.y]
|
|
if s.cursor.y == #s.lines then
|
|
line = line.."\n"
|
|
end
|
|
for w in line:gmatch(".") do
|
|
tx = tx+1
|
|
if w == "\t" then
|
|
local l = s.opt.tabSize-(tx-1)%s.opt.tabSize
|
|
tx = tx+l-1
|
|
offset = offset+l-1
|
|
end
|
|
if tx >= x then
|
|
x = tx-offset
|
|
break
|
|
end
|
|
end
|
|
s.cursor.a = ref[s.cursor.y]+(x-1)
|
|
end
|
|
--s.rCalcCursor = rCalcCursor
|
|
local oTxt = ""
|
|
local function rAll(nTxt)
|
|
if nTxt then
|
|
if not genLines(nTxt) then
|
|
genLines(s.txt)
|
|
s.cursor.a = oCursorA
|
|
else
|
|
s.txt = nTxt
|
|
end
|
|
else
|
|
genLines(s.txt)
|
|
end
|
|
genText()
|
|
calcCursor()
|
|
genLines(s.txt)
|
|
genText()
|
|
oTxt = s.txt
|
|
if s.opt.complete then
|
|
s.opt.complete.list = nil
|
|
end
|
|
end
|
|
local uTimer
|
|
local function addUndo(event)
|
|
if #s.history == 0 then
|
|
table.insert(s.history,{txt=s.txt,changed=true,cursor=s.cursor.a})
|
|
end
|
|
if event == "paste" then
|
|
table.insert(s.history,{txt=s.txt,changed=true,cursor=s.cursor.a,description="Paste"})
|
|
elseif event == "key" or event == "char" then
|
|
--if s.history[#s.history] and s.history[#s.history].time and s.history[#s.history].time > os.epoch("utc")-250
|
|
if uTimer then os.cancelTimer(uTimer) end
|
|
uTimer = os.startTimer(0.3)
|
|
elseif event == "timer" then
|
|
table.insert(s.history,{txt=s.txt,changed=s.changed,cursor=s.cursor.a,description="Insert Characters"})
|
|
end
|
|
while #s.history > 80 do
|
|
table.remove(s.history,1)
|
|
end
|
|
end
|
|
local function undo()
|
|
if s.history[#s.history-1] then
|
|
local h = s.history[#s.history-1]
|
|
s.changed = h.changed
|
|
s.cursor.a = h.cursor
|
|
rAll(h.txt)
|
|
table.insert(s.rhistory,s.history[#s.history])
|
|
table.remove(s.history,#s.history)
|
|
end
|
|
end
|
|
local function redo()
|
|
if s.rhistory[#s.rhistory] then
|
|
local h = s.history
|
|
local r = s.rhistory[#s.rhistory]
|
|
s.changed = r.changed
|
|
s.cursor.a = r.cursor
|
|
rAll(r.txt)
|
|
table.insert(s.history,r)
|
|
table.remove(s.rhistory,#s.rhistory)
|
|
end
|
|
end
|
|
local function addText(txt)
|
|
local a = 0
|
|
if s.border and s.border.color ~= 0 then
|
|
a = 1
|
|
end
|
|
local ttxt = txt
|
|
if txt:find("\n") then
|
|
if s.opt.overflowY == "none" and s.cursor.y >= s.y2-s.y1+1-a*2 then
|
|
return false
|
|
else
|
|
ttxt = txt:match("(.-)\n")
|
|
end
|
|
end
|
|
if s.opt.overflowX == "none" and lineLn(s.lines[s.cursor.y]..ttxt) > s.x2-s.x1+1-a*2 then
|
|
return false
|
|
end
|
|
local pos1,pos2
|
|
if s.select then
|
|
pos1 = s.select[1]-1
|
|
pos2 = s.select[2]+1
|
|
else
|
|
pos1 = s.cursor.a-1
|
|
pos2 = s.cursor.a
|
|
end
|
|
s.changed = true
|
|
s.txt = s.txt:sub(1,pos1)..txt..s.txt:sub(pos2,#s.txt)
|
|
s.cursor.a = pos1+#txt+1
|
|
s.select = nil
|
|
rAll()
|
|
end
|
|
local function update(...) -- maybe make it so that click already selects and then the key char etc operations always sub based on select so no if statement needed
|
|
opt = s.opt
|
|
if not opt.tabSize then
|
|
opt.tabSize = 4
|
|
end
|
|
oCursorA = s.cursor.a
|
|
local e = table.pack(...)
|
|
if s.opt.complete and s.opt.complete.LevelOS and s.opt.complete.LevelOS.self.window.events == "all" and e[1]:find("mouse") and type(e[3]) == "number" and type(e[4]) == "number" then
|
|
local wX,wY = s.opt.complete.LevelOS.self.window.win.getPosition()
|
|
e[3] = e[3]-(wX-1)
|
|
e[4] = e[4]-(wY-1)
|
|
end
|
|
if e[1] == "mouse_click" and s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 and not lUtils.isInside(e[3],e[4],s.opt.complete.list) then
|
|
s.opt.complete.list = nil
|
|
rAll()
|
|
end
|
|
if e[1] == "timer" and e[2] == uTimer then
|
|
addUndo(e[1])
|
|
end
|
|
if not s.state then
|
|
if e[1] == "mouse_click" and e[3] >= s.x1 and e[4] >= s.y1 and e[3] <= s.x2 and e[4] <= s.y2 then
|
|
s.state = true
|
|
elseif e[1] == "term_resize" then
|
|
rAll()
|
|
elseif s.txt ~= oTxt then
|
|
rAll()
|
|
end
|
|
else
|
|
if e[1] == "mouse_click" then
|
|
if (e[3] < s.x1 or e[3] > s.x2 or e[4] < s.y1 or e[4] > s.y2) and not (s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 and lUtils.isInside(e[3],e[4],s.opt.complete.list)) then -- add support for autocomplete click
|
|
term.setCursorBlink(false)
|
|
s.state = false
|
|
end
|
|
end
|
|
if s.txt ~= oTxt then
|
|
rAll()
|
|
end
|
|
if e[1] == "char" then
|
|
if #s.history == 0 then
|
|
table.insert(s.history,{txt=s.txt,changed=false,cursor=s.cursor.a})
|
|
end
|
|
--[[s.changed = true
|
|
s.cursor.a = s.cursor.a+1
|
|
rAll(s.txt:sub(1,s.cursor.a-2)..e[2]..s.txt:sub(s.cursor.a-1,#s.txt))]]
|
|
addText(e[2])
|
|
addUndo(e[1])
|
|
if s.opt.complete then
|
|
s.opt.complete.complete = complete
|
|
s.opt.complete.render = renderComplete
|
|
s.opt.complete.list = complete(string.match(s.txt:sub(1,s.cursor.a-1), "[a-zA-Z0-9_%.:]+$"))
|
|
end
|
|
elseif e[1] == "key" then
|
|
local dirs = {
|
|
[keys.left] = true,
|
|
[keys.right] = true,
|
|
[keys.up] = true,
|
|
[keys.down] = true,
|
|
[keys["end"]] = true,
|
|
[keys.home] = true,
|
|
}
|
|
local deletes = {
|
|
[keys.delete] = true,
|
|
[keys.backspace] = true,
|
|
}
|
|
if lUtils.isHolding(keys.leftCtrl) and dirs[e[2]] then
|
|
-- nothing
|
|
elseif s.select and dirs[e[2]] then
|
|
s.select = nil
|
|
rAll()
|
|
elseif s.select and deletes[e[2]] then
|
|
addText("")
|
|
elseif e[2] == keys.left and s.cursor.a > 1 then
|
|
s.cursor.a = s.cursor.a-1
|
|
--calcCursor()
|
|
rAll()
|
|
elseif e[2] == keys.right and s.cursor.a <= #s.txt then
|
|
s.cursor.a = s.cursor.a+1
|
|
--calcCursor()
|
|
rAll()
|
|
elseif e[2] == keys.up then
|
|
local c = s.opt.complete
|
|
if c and c.list and #c.list > 0 and c.selected then
|
|
local offset = 1
|
|
if c.reverse then
|
|
offset = -1
|
|
end
|
|
local sID = c.selected.id-offset
|
|
if sID < 1 then
|
|
sID = #c.list
|
|
elseif sID > #c.list then
|
|
sID = 0
|
|
end
|
|
c.selected = c.list[sID]
|
|
elseif s.cursor.y > 1 then
|
|
s.cursor.y = s.cursor.y-1
|
|
if opt.overflowX == "scroll" then
|
|
li = s.lines
|
|
else
|
|
li = s.dLines
|
|
end
|
|
local ln = lineLn(li[s.cursor.y])
|
|
if s.cursor.x > ln then
|
|
s.cursor.x = ln
|
|
if s.cursor.y == #s.lines then
|
|
s.cursor.x = s.cursor.x+1
|
|
end
|
|
end
|
|
rCalcCursor()
|
|
rAll()
|
|
end
|
|
elseif e[2] == keys.down then
|
|
local c = s.opt.complete
|
|
if c and c.list and #c.list > 0 and c.selected then
|
|
local offset = 1
|
|
if c.reverse then
|
|
offset = -1
|
|
end
|
|
local sID = c.selected.id+offset
|
|
if sID < 1 then
|
|
sID = #c.list
|
|
elseif sID > #c.list then
|
|
sID = 0
|
|
end
|
|
c.selected = c.list[sID]
|
|
elseif s.cursor.y < #s.lines then
|
|
s.cursor.y = s.cursor.y+1
|
|
if opt.overflowX == "scroll" then
|
|
li = s.lines
|
|
else
|
|
li = s.dLines
|
|
end
|
|
local ln = lineLn(li[s.cursor.y])
|
|
if s.cursor.x > ln then
|
|
s.cursor.x = ln
|
|
if s.cursor.y == #s.lines then
|
|
s.cursor.x = s.cursor.x+1
|
|
end
|
|
end
|
|
rCalcCursor()
|
|
rAll()
|
|
end
|
|
elseif e[2] == keys.pageUp then
|
|
local h = s.y2-(s.y1-1)
|
|
s.cursor.y = math.max(s.cursor.y-h,1)
|
|
if opt.overflowX == "scroll" then
|
|
li = s.lines
|
|
else
|
|
li = s.dLines
|
|
end
|
|
local ln = lineLn(li[s.cursor.y])
|
|
if s.cursor.x > ln then
|
|
s.cursor.x = ln
|
|
if s.cursor.y == #s.lines then
|
|
s.cursor.x = s.cursor.x+1
|
|
end
|
|
end
|
|
rCalcCursor()
|
|
rAll()
|
|
elseif e[2] == keys.pageDown then
|
|
local h = s.y2-(s.y1-1)
|
|
s.cursor.y = math.min(s.cursor.y+h,#s.lines)
|
|
if opt.overflowX == "scroll" then
|
|
li = s.lines
|
|
else
|
|
li = s.dLines
|
|
end
|
|
local ln = lineLn(li[s.cursor.y])
|
|
if s.cursor.x > ln then
|
|
s.cursor.x = ln
|
|
if s.cursor.y == #s.lines then
|
|
s.cursor.x = s.cursor.x+1
|
|
end
|
|
end
|
|
rCalcCursor()
|
|
rAll()
|
|
elseif e[2] == keys["end"] then
|
|
if lUtils.isHolding(keys.leftCtrl) then
|
|
s.cursor.a = #s.txt+1
|
|
else
|
|
if opt.overflowX == "scroll" then
|
|
local ln = lineLn(s.lines[s.cursor.y])
|
|
|
|
s.cursor.x = ln
|
|
else
|
|
s.cursor.x = #s.dLines[s.cursor.y]
|
|
end
|
|
if s.cursor.y == #s.lines then
|
|
s.cursor.x = s.cursor.x+1
|
|
end
|
|
rCalcCursor()
|
|
end
|
|
rAll()
|
|
elseif e[2] == keys.home then
|
|
if lUtils.isHolding(keys.leftCtrl) then
|
|
s.cursor.a = 1
|
|
else
|
|
local wp = (s.lines[s.cursor.y]:match("^[\t ]+") or ""):gsub("\t",string.rep(" ",s.opt.tabSize)) -- always beginning of string so always 4 per tab
|
|
if s.cursor.x == 1+#wp then
|
|
s.cursor.x = 1
|
|
else
|
|
s.cursor.x = 1+#wp
|
|
end
|
|
rCalcCursor()
|
|
end
|
|
rAll()
|
|
elseif e[2] == keys.backspace and s.cursor.a > 1 then
|
|
s.changed = true
|
|
s.txt = s.txt:sub(1,s.cursor.a-2)..s.txt:sub(s.cursor.a,#s.txt)
|
|
s.cursor.a = s.cursor.a-1
|
|
rAll()
|
|
addUndo(e[1])
|
|
elseif e[2] == keys.delete and s.cursor.a <= #s.txt then
|
|
s.changed = true
|
|
s.txt = s.txt:sub(1,s.cursor.a-1)..s.txt:sub(s.cursor.a+1,#s.txt)
|
|
rAll()
|
|
addUndo(e[1])
|
|
elseif e[2] == keys.enter then
|
|
local wp = s.lines[s.cursor.y]:match("^[\t ]+") or ""
|
|
addText("\n"..wp)
|
|
addUndo(e[1])
|
|
elseif (lUtils.isHolding(keys.leftCtrl) and (e[2] == keys.leftBracket or e[2] == keys.rightBracket)) or (s.select and e[2] == keys.tab) then
|
|
local sLine = s.cursor.y
|
|
local eLine = s.cursor.y
|
|
if s.select then
|
|
for l=1,#s.lines do
|
|
local encountered = false
|
|
if (s.select[1] >= s.ref[l] and s.select[1] < s.ref[l+1]) then -- if begin of select is in line
|
|
sLine = math.min(sLine,l)
|
|
end
|
|
if (s.select[2] >= s.ref[l] and s.select[2] < s.ref[l+1]) then -- if end of select is in line
|
|
eLine = math.max(eLine,l)
|
|
encountered = true
|
|
elseif encountered then -- if it already encountered the select and is no longer in selected area it doesnt need to search any further
|
|
break
|
|
end
|
|
end
|
|
end
|
|
for l=sLine,eLine do
|
|
if e[2] == keys.rightBracket or e[2] == keys.tab then
|
|
s.lines[l] = "\t"..s.lines[l]
|
|
if s.select then
|
|
if s.select[1] >= s.ref[l] then
|
|
s.select[1] = s.select[1]+1
|
|
end
|
|
if s.select[2] >= s.ref[l] then
|
|
s.select[2] = s.select[2]+1
|
|
end
|
|
end
|
|
if s.cursor.a >= s.ref[l] then
|
|
s.cursor.a = s.cursor.a+1
|
|
end
|
|
calcCursor()
|
|
elseif e[2] == keys.leftBracket then
|
|
if s.lines[l]:find("^\t") then
|
|
s.lines[l] = s.lines[l]:gsub("^\t","")
|
|
if s.select then
|
|
if s.select[1] > s.ref[l] then
|
|
s.select[1] = s.select[1]-1
|
|
end
|
|
if s.select[2] > s.ref[l] then
|
|
s.select[2] = s.select[2]-1
|
|
end
|
|
end
|
|
if s.cursor.a > s.ref[l] then
|
|
s.cursor.a = s.cursor.a-1
|
|
end
|
|
elseif s.lines[l]:find("^ ") then
|
|
s.lines[l] = s.lines[l]:gsub("^ ","")
|
|
if s.select then
|
|
if s.select[1] > s.ref[l] then
|
|
s.select[1] = math.max(s.select[1]-4,s.ref[l])
|
|
end
|
|
if s.select[2] > s.ref[l] then
|
|
s.select[2] = math.max(s.select[2]-4,s.ref[l])
|
|
end
|
|
end
|
|
if s.cursor.a > s.ref[l] then
|
|
s.cursor.a = math.max(s.cursor.a-4,s.ref[l])
|
|
end
|
|
end
|
|
end
|
|
s.txt = genText()
|
|
rAll()
|
|
end
|
|
elseif e[2] == keys.tab then
|
|
local c = s.opt.complete
|
|
if c and c.list and #c.list > 0 and c.selected then
|
|
addText(c.selected.complete)
|
|
addUndo("char")
|
|
else
|
|
addText("\t")
|
|
addUndo(e[1])
|
|
end
|
|
elseif lUtils.isHolding(keys.leftCtrl) then
|
|
if e[2] == keys.z then
|
|
undo()
|
|
elseif e[2] == keys.y then
|
|
redo()
|
|
elseif e[2] == keys.a then
|
|
s.select = {1,#s.txt}
|
|
s.cursor.a = #s.txt+1
|
|
rAll()
|
|
elseif (e[2] == keys.c or e[2] == keys.x) and s.select then
|
|
local txt = s.txt:sub(s.select[1],s.select[2])
|
|
if ccemux then
|
|
ccemux.setClipboard(txt)
|
|
lOS.clipboard = txt
|
|
else
|
|
s.clipboard = txt
|
|
end
|
|
if e[2] == keys.x then
|
|
addText("")
|
|
addUndo(e[1])
|
|
end
|
|
end
|
|
end
|
|
elseif e[1] == "paste" then
|
|
if s.opt.complete then
|
|
s.opt.complete.list = nil
|
|
end
|
|
if #s.history == 0 then
|
|
table.insert(s.history,{txt=s.txt,changed=false,cursor=s.cursor.a})
|
|
end
|
|
local txt = e[2]
|
|
if s.clipboard then
|
|
txt = s.clipboard
|
|
elseif lOS.clipboard then
|
|
local nline = lOS.clipboard:find("(.)\n") or #lOS.clipboard
|
|
local checkCB = lOS.clipboard:sub(1,nline):gsub("\t","")
|
|
if txt == checkCB then
|
|
txt = lOS.clipboard
|
|
end
|
|
end
|
|
addText(txt)
|
|
addUndo(e[1])
|
|
elseif s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 and s.opt.complete.selected and (e[1] == "mouse_click" or e[1] == "mouse_scroll" or e[1] == "mouse_up") and lUtils.isInside(e[3],e[4],s.opt.complete.list) then
|
|
local c = s.opt.complete
|
|
--local x,y = e[3]-(s.x1-1),e[4]-(s.y1-1)
|
|
local el = e[4]-(s.opt.complete.list.y1-1)
|
|
if e[1] == "mouse_click" then
|
|
c.selected = c.list[el]
|
|
elseif e[1] == "mouse_up" and c.selected.id == el then
|
|
s.changed = true
|
|
s.txt = s.txt:sub(1,s.cursor.a-1)..c.selected.complete..s.txt:sub(s.cursor.a,#s.txt)
|
|
s.cursor.a = s.cursor.a+#c.selected.complete
|
|
rAll()
|
|
addUndo("char")
|
|
end
|
|
elseif e[1] == "mouse_click" or e[1] == "mouse_scroll" then
|
|
s.select = nil
|
|
if e[3] >= s.x1 and e[4] >= s.y1 and e[3] <= s.x2 and e[4] <= s.y2 then
|
|
if e[1] == "mouse_click" then
|
|
local scrollX = s.scrollX or 0
|
|
local scrollY = s.scrollY or 0
|
|
local x,y = e[3]-(s.x1-1)+scrollX,e[4]-(s.y1-1)-s.scr+scrollY
|
|
if not s.lines[y] then
|
|
y = #s.lines
|
|
end
|
|
if x > #s.dLines[y]+scrollX then
|
|
if opt.overflowX == "scroll" then
|
|
x = lineLn(s.lines[y])
|
|
else
|
|
x = #s.dLines[y]
|
|
end
|
|
if y == #s.lines then
|
|
x = x+1
|
|
end
|
|
end
|
|
s.cursor.x,s.cursor.y = x,y
|
|
rCalcCursor()
|
|
rAll()
|
|
elseif opt.overflowY == "scroll" and e[1] == "mouse_scroll" then
|
|
local width = s.x2-(s.x1-1)
|
|
local height = s.y2-(s.y1-1)
|
|
if s.border and s.border.color ~= 0 then
|
|
width = width-2
|
|
height = height-2
|
|
end
|
|
if s.scrollY+e[2] >= 0 and s.scrollY+e[2] <= #s.lines-height then
|
|
s.scrollY = s.scrollY+e[2]
|
|
end
|
|
end
|
|
end
|
|
elseif e[1] == "mouse_drag" then
|
|
local pos1
|
|
if s.select then
|
|
pos1 = s.select[1]
|
|
else
|
|
pos1 = s.cursor.a
|
|
s.select = {pos1,pos1}
|
|
end
|
|
local scrollX = s.scrollX or 0
|
|
local scrollY = s.scrollY or 0
|
|
local x,y = e[3]-(s.x1-1)+scrollX,e[4]-(s.y1-1)-s.scr+scrollY
|
|
if not s.lines[y] then
|
|
y = #s.lines
|
|
end
|
|
if x > #s.dLines[y]+scrollX then
|
|
if opt.overflowX == "scroll" then
|
|
x = lineLn(s.lines[y])
|
|
else
|
|
x = #s.dLines[y]
|
|
end
|
|
if y == #s.lines then
|
|
x = x+1
|
|
end
|
|
end
|
|
s.cursor.x,s.cursor.y = x,y
|
|
rCalcCursor()
|
|
local pos2 = s.cursor.a
|
|
if s.select then
|
|
if pos2 < s.select[1] and not s.select.reversed then
|
|
s.select.reversed = true
|
|
s.select[2] = s.select[1]-1
|
|
elseif pos2 > s.select[2] and s.select.reversed then
|
|
s.select.reversed = false
|
|
s.select[1] = s.select[2]+1
|
|
end
|
|
if s.select.reversed then
|
|
s.select[1] = pos2
|
|
else
|
|
s.select[2] = pos2-1
|
|
end
|
|
end
|
|
rAll()
|
|
elseif e[1] == "term_resize" then
|
|
rAll()
|
|
end
|
|
end
|
|
local lChar = s.txt:sub(s.cursor.a-1,s.cursor.a-1)
|
|
if s.opt.complete and (e[1] == "key" or e[1] == "char") and (lChar == "(" or lChar == ",") then
|
|
s.opt.complete.list = complete(string.match(s.txt:sub(1,s.cursor.a-1), "[a-zA-Z0-9_%.:%(]+[^%)%(]*$"))
|
|
end
|
|
end
|
|
local function render()
|
|
if s.color == 0 then s.color = colors.white end
|
|
local restore = cRestore()
|
|
term.setBackgroundColor(s.color or term.getBackgroundColor())
|
|
local a = 0
|
|
if s.border and s.border.color ~= 0 then
|
|
a = 1
|
|
end
|
|
lOS.boxClear(s.x1+a,s.y1+a,s.x2-a,s.y2-a)
|
|
local scrollX = s.scrollX or 0
|
|
local scrollY = s.scrollY or 0
|
|
|
|
for y=s.y1+a,s.y2-a do
|
|
local l = y-(s.y1-1+a)+s.scr + scrollY
|
|
if s.lines[l] then
|
|
term.setCursorPos(s.x1+a,y)
|
|
term.setBackgroundColor(s.color or term.getBackgroundColor())
|
|
term.setTextColor(s.txtcolor or txtcolor)
|
|
local line
|
|
if s.blit[l] then
|
|
line = s.blit[l][1]
|
|
else
|
|
line = s.dLines[l]
|
|
end
|
|
if type(opt.replaceChar) == "string" and #opt.replaceChar > 0 then
|
|
local pattern = "."
|
|
for t=1,#opt.replaceChar-1 do
|
|
pattern = pattern..".?"
|
|
end
|
|
local nLine = line:gsub(pattern,opt.replaceChar)
|
|
line = nLine:sub(1,#line)
|
|
end
|
|
if s.blit[l] then
|
|
--s.blit[l][3] = string.rep(lUtils.toBlit(s.color or term.getBackgroundColor()),#s.blit[l][1])
|
|
term.blit(line,s.blit[l][2],s.blit[l][3])
|
|
else
|
|
term.write(line)
|
|
end
|
|
end
|
|
end
|
|
if s.opt.complete and s.opt.complete.list and #s.opt.complete.list > 0 then
|
|
if s.opt.complete.overlay and s.opt.complete.LevelOS then
|
|
s.opt.complete.LevelOS.self.window.events = "all"
|
|
lOS.noEvents = 2
|
|
end
|
|
lOS.noEvents = 2
|
|
local ok,err = pcall(renderComplete,s.opt.complete.list)
|
|
if not ok then
|
|
_G.theterribleerrorrighthere = err
|
|
end
|
|
elseif s.opt.complete and s.opt.complete.LevelOS and s.opt.complete.LevelOS.self.window.events == "all" then
|
|
if s.opt.complete.overlay then
|
|
s.opt.complete.LevelOS.self.window.events = nil
|
|
lOS.noEvents = false
|
|
end
|
|
s.opt.complete.LevelOS.overlay = nil
|
|
end
|
|
if s.state then
|
|
local x,y = s.x1+(s.cursor.x-1)+a-scrollX,s.y1+(s.cursor.y-1)+a-scrollY
|
|
if lUtils.isInside(x,y,s) then
|
|
term.setTextColor(opt.cursorColor or txtcolor)
|
|
term.setCursorPos(x,y)
|
|
term.setCursorBlink(true)
|
|
else
|
|
term.setCursorBlink(false)
|
|
end
|
|
else
|
|
cRestore(restore)
|
|
end
|
|
end
|
|
if not tShape then
|
|
s.update = update
|
|
s.render = render
|
|
else
|
|
s.fUpdate = update
|
|
s.fRender = render
|
|
end
|
|
return s
|
|
end
|
|
|
|
function lUtils.read(_sReplaceChar)
|
|
local x1,y1 = term.getCursorPos()
|
|
local x2,_ = term.getSize()
|
|
local box = lUtils.input(x1,y1,x2,y1,{overflowX="scroll",overflowY="none",replaceChar=_sReplaceChar})
|
|
box.state = true
|
|
while true do
|
|
local e = {os.pullEvent()}
|
|
if e[1] == "key" and e[2] == keys.enter then
|
|
box.state = false
|
|
print("")
|
|
return box.txt
|
|
else
|
|
box.update(unpack(e))
|
|
box.state = true
|
|
box.render()
|
|
end
|
|
end
|
|
end
|
|
|
|
local animationTbl = {}
|
|
lUtils.debugAnimationTbl = animationTbl
|
|
function lUtils.renderImg(spr,x,y,format,transparency)
|
|
local format
|
|
local img = spr
|
|
local cterm = term.current()
|
|
if not format then
|
|
if type(spr) == "string" then
|
|
format = "nfp"
|
|
elseif type(spr) == "table" then
|
|
format = "bImg"
|
|
if type(spr[1][1]) == "table" then
|
|
if #spr == 1 or not spr.animated then
|
|
spr = spr[1]
|
|
else
|
|
if not animationTbl[spr] then
|
|
animationTbl[spr] = {cFrame=0,oTime=os.epoch("utc")/1000}
|
|
else
|
|
local cFrame = animationTbl[spr].cFrame+0.0001
|
|
local dur = spr[math.ceil(cFrame)].duration or spr.secondsPerFrame or 0.05
|
|
local oTime = animationTbl[spr].oTime
|
|
local cTime = os.epoch("utc")/1000
|
|
local delta = (cTime-oTime)
|
|
animationTbl[spr].cFrame = (cFrame + delta/dur) % #spr
|
|
animationTbl[spr].oTime = cTime
|
|
end
|
|
spr = spr[math.ceil(animationTbl[spr].cFrame+0.0001)]
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if format == "bImg" or format == "lImg" then
|
|
if not spr[1] or not spr[1][1] or type(spr[1][1]) ~= "string" then
|
|
error("Unrecognized format",2)
|
|
end
|
|
local sW,sH = #spr[1][1],#spr
|
|
local w,h = term.getSize()
|
|
for l=1,#spr do
|
|
--[[if not y then
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(sW/2),(math.ceil(h/2)-math.floor(sH/2)+(l-1)))
|
|
else
|
|
term.setCursorPos(x,y+(l-1))
|
|
end]]
|
|
local line
|
|
if y then
|
|
line = y+(l-1)
|
|
end
|
|
local cX,cY = x or math.ceil(w/2)-math.floor(sW/2), line or (math.ceil(h/2)-math.floor(sH/2)+(l-1))
|
|
term.setCursorPos(cX,cY)
|
|
local bl = {}
|
|
bl[1] = spr[l][1]
|
|
if transparency then
|
|
bl[2] = spr[l][2]
|
|
bl[3] = spr[l][3]
|
|
-- thing
|
|
local line
|
|
if cY >= 1 and cY <= h then
|
|
line = {cterm.getLine(cY)}
|
|
for t=1,3 do
|
|
line[t] = line[t]:sub(cX,cX+(#spr[l][1]-1))
|
|
end
|
|
else
|
|
line = {string.rep("f",#spr[l][1])}
|
|
line[2] = line[1]
|
|
line[3] = line[2]
|
|
end
|
|
if #line[2] < #spr[l][1] then
|
|
line[1] = line[1]..string.rep(" ",#spr[l][1]-#line[1])
|
|
line[2] = line[2]..string.rep("f",#spr[l][1]-#line[2])
|
|
line[3] = line[3]..string.rep("f",#spr[l][1]-#line[3])
|
|
end
|
|
local start,final = 0,0
|
|
while true do
|
|
start,final = bl[2]:gsub(" ","T"):find("T")
|
|
if start then
|
|
bl[2] = bl[2]:sub(1,start-1)..line[3]:sub(start,final)..bl[2]:sub(final+1,#bl[2])
|
|
else
|
|
break
|
|
end
|
|
end
|
|
while true do
|
|
start,final = bl[3]:gsub(" ","T"):find("T")
|
|
if start then
|
|
if bl[1]:sub(start,final) == " " or bl[1]:sub(start,final) == "\128" then
|
|
bl[1] = bl[1]:sub(1,start-1)..line[1]:sub(start,final)..bl[1]:sub(final+1,#bl[1])
|
|
bl[2] = bl[2]:sub(1,start-1)..line[2]:sub(start,final)..bl[2]:sub(final+1,#bl[2])
|
|
end
|
|
bl[3] = bl[3]:sub(1,start-1)..line[3]:sub(start,final)..bl[3]:sub(final+1,#bl[3])
|
|
else
|
|
break
|
|
end
|
|
end
|
|
else
|
|
bl[2] = string.gsub(spr[l][2]:gsub(" ","T"),"T",lUtils.toBlit(term.getBackgroundColor()))
|
|
bl[3] = string.gsub(spr[l][3]:gsub(" ","T"),"T",lUtils.toBlit(term.getBackgroundColor()))
|
|
end
|
|
if #bl[1] ~= #bl[2] or #bl[2] ~= #bl[3] then
|
|
_G.debugblitthingy = bl
|
|
if #bl[2] > #bl[1] then
|
|
bl[2] = bl[2]:sub(1, #bl[1])
|
|
elseif #bl[2] < #bl[1] then
|
|
bl[2] = bl[2]..string.rep(lUtils.toBlit(term.getBackgroundColor()), #bl[1]-#bl[2])
|
|
end
|
|
if #bl[3] > #bl[2] then
|
|
bl[3] = bl[3]:sub(1, #bl[2])
|
|
elseif #bl[3] < #bl[2] then
|
|
bl[3] = bl[3]..string.rep(lUtils.toBlit(term.getBackgroundColor()), #bl[2]-#bl[3])
|
|
end
|
|
end
|
|
term.blit(unpack(bl))
|
|
end
|
|
elseif format == "nfp" or format == "nfg" then
|
|
local b,e = string.find(spr,"\n")
|
|
local sW,sH
|
|
local w,h = term.getSize()
|
|
local lines,sW = getLines(spr)
|
|
sH = #lines
|
|
for l=1,sH do
|
|
local line
|
|
if y then
|
|
line = y+(l-1)
|
|
end
|
|
term.setCursorPos(x or math.ceil(w/2)-math.floor(sW/2), line or math.ceil(h/2)-math.floor(sH/2)+(l-1))
|
|
for lX=1,sW do
|
|
local ch = lines[l]:sub(lX,lX)
|
|
if ch ~= " " and ch ~= "" then
|
|
term.blit(" ","f",ch)
|
|
else
|
|
local cx,cy = term.getCursorPos()
|
|
term.setCursorPos(cx+1,cy)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.getDrawingCharacter(...)
|
|
local e=0
|
|
local arg = {...}
|
|
for t=1,5 do
|
|
if arg[t] then
|
|
e=e+2^(t-1)
|
|
end
|
|
end
|
|
return {char = string.char(arg[6] and 159-e or 128+e), inverted = not not arg[6]}
|
|
end
|
|
|
|
lUtils.asset = {}
|
|
function lUtils.asset.load(filename)
|
|
if filename and type(filename) == "string" and fs.exists(filename) and not fs.isDir(filename) then
|
|
local ft = lUtils.getFileType(filename)
|
|
if ft == ".limg" or ft == ".bimg" or ft == ".lconf" then
|
|
local contents = lUtils.fread(filename, true)
|
|
local decodedText = ""
|
|
for _, codepoint in utf8.codes(contents) do
|
|
decodedText = decodedText .. string.char(codepoint)
|
|
end
|
|
local data = textutils.unserialize(decodedText)
|
|
local oterm = term.current()
|
|
local trashwin = window.create(term.current(), 1, 1, 51, 19, false)
|
|
term.redirect(trashwin)
|
|
local ok, _ = pcall(lUtils.renderImg, data)
|
|
term.redirect(oterm)
|
|
if ((ft == ".limg" or ft == ".bimg") and not ok) then
|
|
return textutils.unserialize(contents)
|
|
else
|
|
return data
|
|
end
|
|
end
|
|
return textutils.unserialize(lUtils.fread(filename))
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.asset.save(asset,filename,compact)
|
|
if compact == nil then compact = true end
|
|
if type(asset) == "table" and not fs.isDir(filename) then
|
|
local ok,output = pcall(textutils.serialize,asset,{compact=compact})
|
|
if not ok then
|
|
error(output,2)
|
|
end
|
|
return lUtils.fwrite(filename,output)
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.HSVToRGB( hue, saturation, value )
|
|
-- Returns the RGB equivalent of the given HSV-defined color
|
|
-- (adapted from some code found around the web)
|
|
|
|
-- If it's achromatic, just return the value
|
|
hue = hue%360
|
|
if saturation == 0 then
|
|
return value,value,value
|
|
end
|
|
|
|
-- Get the hue sector
|
|
local hue_sector = math.floor( hue / 60 )
|
|
local hue_sector_offset = ( hue / 60 ) - hue_sector
|
|
|
|
local p = value * ( 1 - saturation )
|
|
local q = value * ( 1 - saturation * hue_sector_offset )
|
|
local t = value * ( 1 - saturation * ( 1 - hue_sector_offset ) )
|
|
|
|
if hue_sector == 0 then
|
|
return value, t, p
|
|
elseif hue_sector == 1 then
|
|
return q, value, p
|
|
elseif hue_sector == 2 then
|
|
return p, value, t
|
|
elseif hue_sector == 3 then
|
|
return p, q, value
|
|
elseif hue_sector == 4 then
|
|
return t, p, value
|
|
elseif hue_sector == 5 then
|
|
return value, p, q
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.RGBToHSV( red, green, blue )
|
|
|
|
local hue, saturation, value
|
|
|
|
local min_value = math.min( red, green, blue )
|
|
local max_value = math.max( red, green, blue )
|
|
|
|
value = max_value
|
|
|
|
local value_delta = max_value - min_value
|
|
|
|
-- If the color is not black
|
|
if max_value ~= 0 then
|
|
saturation = value_delta / max_value
|
|
|
|
-- If the color is purely black
|
|
else
|
|
saturation = 0
|
|
hue = -1
|
|
return hue, saturation, value
|
|
end
|
|
|
|
if red == max_value then
|
|
hue = ( green - blue ) / value_delta
|
|
elseif green == max_value then
|
|
hue = 2 + ( blue - red ) / value_delta
|
|
else
|
|
hue = 4 + ( red - green ) / value_delta
|
|
end
|
|
|
|
hue = hue * 60
|
|
if hue < 0 then
|
|
hue = hue + 360
|
|
end
|
|
|
|
return hue, saturation, value
|
|
end
|
|
|
|
|
|
lUtils.graphics = {}
|
|
local g = lUtils.graphics
|
|
function g.button(txt,x1,y1,x2,y2,func,border,fg1,bg1,fg2,bg2,bcolor)
|
|
if not y2 then
|
|
return nil
|
|
end
|
|
if not bg1 then
|
|
bg1 = colors.black
|
|
end
|
|
if not fg1 then
|
|
fg1 = colors.white
|
|
end
|
|
if not bcolor then
|
|
bcolor = colors.gray
|
|
end
|
|
if not fg2 then
|
|
fg2 = fg1
|
|
end
|
|
if not bg2 then
|
|
bg2 = colors.gray
|
|
end
|
|
if not border then
|
|
border = true
|
|
end
|
|
local btn = {txt=txt,x1=x1,y1=y1,x2=x2,y2=y2,func=func,border=border,selected=false,colors={bg1=bg1,fg1=fg1,bg2=bg2,fg2=fg2,border=bcolor}}
|
|
function btn.render(...)
|
|
local e = {...}
|
|
local selected = false
|
|
--term.setTextColor(colors.fg1)
|
|
--term.setBackgroundColor(colors.bg1)
|
|
local abc = {}
|
|
if e[1] == "mouse_click" or e[1] == "mouse_up" then
|
|
if e[3] >= btn.x1 and e[4] >= btn.y1 and e[3] <= btn.x2 and e[4] <= btn.y2 then
|
|
if e[1] == "mouse_click" then
|
|
btn.selected = true
|
|
elseif e[1] == "mouse_up" then
|
|
if not btn.func then
|
|
abc[1] = true
|
|
else
|
|
abc = {btn.func()}
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if e[1] == "mouse_up" then
|
|
btn.selected = false
|
|
end
|
|
if btn.selected then
|
|
term.setBackgroundColor(btn.colors.bg2)
|
|
else
|
|
term.setBackgroundColor(btn.colors.bg1)
|
|
end
|
|
term.setTextColor(btn.colors.border)
|
|
if btn.border == true then
|
|
lUtils.border(btn.x1,btn.y1,btn.x2,btn.y2)
|
|
else
|
|
lOS.boxClear(btn.x1,btn.y1,btn.x2,btn.y2)
|
|
end
|
|
if btn.selected then
|
|
term.setTextColor(btn.colors.fg2)
|
|
else
|
|
term.setTextColor(btn.colors.fg1)
|
|
end
|
|
lUtils.textbox(btn.txt,btn.x1+1,btn.y1+1,btn.x2-1,btn.y2-1)
|
|
return unpack(abc)
|
|
end
|
|
return btn
|
|
end
|
|
|
|
|
|
function lOS.boxClear(tx1,ty1,tx2,ty2)
|
|
local x1,y1,x2,y2 = tx1,ty1,tx2,ty2
|
|
if x1 > x2 then
|
|
x2,x1 = x1,x2
|
|
end
|
|
if y1 > y2 then
|
|
y2,y1 = y1,y2
|
|
end
|
|
clearline = ""
|
|
for t=x1,x2 do
|
|
clearline = clearline.." "
|
|
end
|
|
for l=y1,y2 do
|
|
term.setCursorPos(x1,l)
|
|
term.write(clearline)
|
|
end
|
|
end
|
|
|
|
|
|
local function run2( _tEnv, _sPath, ... )
|
|
local tArgs = table.pack( ... )
|
|
local tEnv = _tEnv
|
|
setmetatable( tEnv, { __index = _G } )
|
|
local fnFile,err
|
|
if fs.exists("window") then
|
|
fnFile, err = loadfile( _sPath, tEnv )
|
|
else
|
|
fnFile, err = loadfile( _sPath, nil, tEnv )
|
|
end
|
|
if fnFile then
|
|
local returned = {pcall( function()
|
|
return table.unpack({fnFile( table.unpack( tArgs, 1, tArgs.n ) )})
|
|
end )}
|
|
local ok, err = returned[1],returned[2]
|
|
table.remove(returned,1)
|
|
if not ok then
|
|
table.remove(returned,1)
|
|
if err and err ~= "" then
|
|
--printError( err )
|
|
end
|
|
return false,err,table.unpack(returned)
|
|
end
|
|
return true,table.unpack(returned)
|
|
end
|
|
if err and err ~= "" then
|
|
--printError( err )
|
|
end
|
|
return false,err
|
|
end
|
|
|
|
|
|
|
|
local function createShellEnv( sDir , tWindow , sPath)
|
|
local tEnv = {}
|
|
tEnv[ "shell" ] = lUtils.instantiate(shell)
|
|
if sPath then
|
|
tEnv.shell.getRunningProgram = function() return sPath end
|
|
end
|
|
tEnv[ "multishell" ] = multishell
|
|
|
|
local package = {}
|
|
package.loaded = {
|
|
_G = _G,
|
|
bit32 = bit32,
|
|
coroutine = coroutine,
|
|
math = math,
|
|
package = package,
|
|
string = string,
|
|
table = table,
|
|
}
|
|
package.path = "?;?.lua;?/init.lua;/rom/modules/main/?;/rom/modules/main/?.lua;/rom/modules/main/?/init.lua;/LevelOS/modules/?.lua;/LevelOS/modules/?/init.lua"
|
|
if turtle then
|
|
package.path = package.path..";/rom/modules/turtle/?;/rom/modules/turtle/?.lua;/rom/modules/turtle/?/init.lua"
|
|
elseif command then
|
|
package.path = package.path..";/rom/modules/command/?;/rom/modules/command/?.lua;/rom/modules/command/?/init.lua"
|
|
end
|
|
package.config = "/\n;\n?\n!\n-"
|
|
package.preload = {}
|
|
package.loaders = {
|
|
function( name )
|
|
if package.preload[name] then
|
|
return package.preload[name]
|
|
else
|
|
return nil, "no field package.preload['" .. name .. "']"
|
|
end
|
|
end,
|
|
function( name )
|
|
local fname = string.gsub(name, "%.", "/")
|
|
local sError = ""
|
|
for pattern in string.gmatch(package.path, "[^;]+") do
|
|
local sPath = string.gsub(pattern, "%?", fname)
|
|
if sPath:sub(1,1) ~= "/" then
|
|
sPath = fs.combine(sDir, sPath)
|
|
end
|
|
if fs.exists(sPath) and not fs.isDir(sPath) then
|
|
local fnFile, sError = loadfile( sPath, tEnv )
|
|
if fnFile then
|
|
return fnFile, sPath
|
|
else
|
|
return nil, sError
|
|
end
|
|
else
|
|
if #sError > 0 then
|
|
sError = sError .. "\n"
|
|
end
|
|
sError = sError .. "no file '" .. sPath .. "'"
|
|
end
|
|
end
|
|
return nil, sError
|
|
end
|
|
}
|
|
|
|
local sentinel = {}
|
|
local function require( name )
|
|
if type( name ) ~= "string" then
|
|
error( "bad argument #1 (expected string, got " .. type( name ) .. ")", 2 )
|
|
end
|
|
if package.loaded[name] == sentinel then
|
|
error("Loop detected requiring '" .. name .. "'", 0)
|
|
end
|
|
if package.loaded[name] then
|
|
return package.loaded[name]
|
|
end
|
|
|
|
local sError = "Error loading module '" .. name .. "':"
|
|
for n,searcher in ipairs(package.loaders) do
|
|
local loader, err = searcher(name)
|
|
if loader then
|
|
package.loaded[name] = sentinel
|
|
local result = loader( err )
|
|
if result ~= nil then
|
|
package.loaded[name] = result
|
|
return result
|
|
else
|
|
package.loaded[name] = true
|
|
return true
|
|
end
|
|
else
|
|
sError = sError .. "\n" .. err
|
|
end
|
|
end
|
|
error(sError, 2)
|
|
end
|
|
|
|
tEnv["package"] = package
|
|
tEnv["require"] = require
|
|
|
|
tEnv["LevelOS"] = {self={window=tWindow}}
|
|
|
|
local lAPI = tEnv["LevelOS"]
|
|
--local s = tEnv["shell"]
|
|
--local ms = tEnv["multishell"]
|
|
--TEMP DISABLED
|
|
local s = {}
|
|
local ms = {}
|
|
|
|
|
|
|
|
if not lOS.oldterm then
|
|
lOS.oldterm = oldterm
|
|
if not oldterm then
|
|
lOS.oldterm = term.native() -- DANGEROUS
|
|
end
|
|
end
|
|
local w,h = lOS.oldterm.getSize()
|
|
if tWindow ~= nil and lOS.wins ~= nil then
|
|
local win = tWindow
|
|
local function setWin( x, y, width, height, mode )
|
|
local x,y,width,height,mode = x, y, width, height, mode
|
|
if type(x) == "string" then
|
|
mode = x
|
|
x,y = win.win.getPosition()
|
|
width,height = win.win.getSize()
|
|
elseif type(width) ~= "number" then
|
|
width,height,mode = x,y,width
|
|
x,y = win.win.getPosition()
|
|
end
|
|
win.win.reposition(x,y,width,height)
|
|
os.queueEvent("term_resize")
|
|
if mode then
|
|
if mode ~= "background" and win.winMode == "background" then
|
|
local pID
|
|
for p=1,#lOS.processes do
|
|
if lOS.processes[p] == win then
|
|
pID = p
|
|
break
|
|
end
|
|
end
|
|
if pID then
|
|
os.queueEvent("window_open",pID,tostring(win))
|
|
end
|
|
elseif mode == "background" and win.winMode ~= "background" then
|
|
local wID
|
|
for i=1,#lOS.wins do
|
|
if lOS.wins[i] == win then
|
|
wID = i
|
|
break
|
|
end
|
|
end
|
|
if wID then
|
|
os.queueEvent("window_close",wID,tostring(win))
|
|
end
|
|
end
|
|
win.winMode = mode
|
|
end
|
|
end
|
|
local function pullEvent()
|
|
win.events = "all"
|
|
local e = {os.pullEventRaw()}
|
|
win.events = "default"
|
|
return table.unpack(e)
|
|
end
|
|
local function setTitle(title)
|
|
if type(title) == "string" and title ~= "" then
|
|
win.title = title
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
local function maximize()
|
|
if win.win ~= nil then
|
|
local w,h = lOS.wAll.getSize()
|
|
local off = 0
|
|
if win.winMode == "windowed" then
|
|
off = 1
|
|
end
|
|
win.snap = {x=true,y=true,oPos={win.win.getPosition()},oSize={win.win.getSize()}}
|
|
win.win.reposition(1,1+off,w,h-lOS.tbSize-off)
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
local function minimize()
|
|
os.queueEvent("window_minimize",lOS.cWin,tostring(tWindow))
|
|
end
|
|
local function focus()
|
|
for k,v in pairs(lOS.processes) do
|
|
if v == win then
|
|
os.queueEvent("window_focus",k,tostring(v))
|
|
break
|
|
end
|
|
end
|
|
end
|
|
lAPI.pullEvent = pullEvent
|
|
lAPI.setWin = setWin
|
|
lAPI.setTitle = setTitle
|
|
lAPI.maximize = maximize
|
|
lAPI.minimize = minimize
|
|
lAPI.focus = focus
|
|
tWindow.env = tEnv
|
|
local srun = tEnv.shell.run
|
|
|
|
function ms.launch(env, path, ...)
|
|
local args = {...}
|
|
local function func(win)
|
|
local oEnv = createShellEnv(fs.getDir(path),win)
|
|
env["LevelOS"] = oEnv["LevelOS"]
|
|
return run2(env, path, table.unpack(args))
|
|
end
|
|
lOS.newWin(func,path)
|
|
end
|
|
|
|
function ms.getCount()
|
|
return #lOS.wins
|
|
end
|
|
|
|
function ms.setTitle(n, title)
|
|
lOS.wins[n].title = title
|
|
end
|
|
|
|
function ms.setFocus(n)
|
|
if n >= 1 and n <= #lOS.wins then
|
|
local w = lOS.wins[n]
|
|
table.remove(lOS.wins,n)
|
|
table.insert(lOS.wins,w)
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
|
|
function ms.getTitle(n)
|
|
if n >= 1 and n <= #lOS.wins then
|
|
return lOS.wins[n].title
|
|
end
|
|
end
|
|
|
|
function s.execute(command, ...)
|
|
|
|
local sPath = s.resolveProgram(command)
|
|
if sPath ~= nil then
|
|
local sTitle = lUtils.getFileName(sPath)
|
|
sTitle = (sTitle:gsub("^%l", string.upper))
|
|
lAPI.setTitle(sTitle)
|
|
|
|
local sDir = fs.getDir(sPath)
|
|
local env = createShellEnv(sDir,lAPI.self.window)
|
|
env.arg = { [0] = command, ... }
|
|
local result = run2(env, sPath, ...)
|
|
|
|
return result
|
|
else
|
|
printError("No such program")
|
|
return false
|
|
end
|
|
end
|
|
|
|
--function tEnv.shell.run(...)
|
|
--if ({...})[2] and fs.exists(({...})[2]) then
|
|
--setTitle(lUtils.getFileName(({...})[1]).." - "..lUtils.getFileName(({...})[2]))
|
|
--else
|
|
--setTitle(lUtils.getFileName(({...})[1]))
|
|
--end
|
|
--srun(...)
|
|
--end
|
|
|
|
-- do the above once we port everything to lOS.run
|
|
|
|
end
|
|
|
|
return tEnv
|
|
end
|
|
|
|
|
|
lOS.createShellEnv = createShellEnv
|
|
|
|
|
|
|
|
|
|
lUtils.shapescape = {}
|
|
|
|
if true then
|
|
local shape = lUtils.shapescape
|
|
|
|
function shape.createRectangle(x1,y1,x2,y2,fillColor,borderColor)
|
|
local sCol = {fillColor or term.getBackgroundColor(),borderColor or 0}
|
|
local tObj = {event={mouse_click={function() end,-1},mouse_up={function() end,-1},focus={function() end,-1},update={function() end,-1},render={function() end,-1},Coroutine={function() end,-1}},type="rect",color=sCol[1],border={type=1,color=sCol[2]},x1=x1,y1=y1,x2=x2,y2=y2}
|
|
--slide.objs[#slide.objs+1] = tObj
|
|
local w,h = term.getSize()
|
|
lUtils.shapescape.renderSlide({win=window.create(term.current(),1,1,w,h,false),objs={tObj}})
|
|
return tObj
|
|
end
|
|
|
|
|
|
function shape.createText(txt,x1,y1,x2,y2,fillColor,borderColor,textColor)
|
|
local tObj = shape.createRectangle(x1,y1,x2,y2,fillColor or 0,borderColor or 0)
|
|
tObj.type = "text"
|
|
tObj.txtcolor = textColor or term.getTextColor()
|
|
tObj.txt = txt
|
|
return tObj
|
|
end
|
|
|
|
|
|
function shape.createWindow(x1,y1,x2,y2)
|
|
local tObj = shape.createRectangle(x1,y1,x2,y2)
|
|
tObj.type = "window"
|
|
tObj.color = colors.black
|
|
tObj.border.color = 0
|
|
tObj.render = nil
|
|
tObj.update = nil
|
|
local w,h = term.getSize()
|
|
lUtils.shapescape.renderSlide({win=window.create(term.current(),1,1,w,h,false),objs={tObj}})
|
|
return tObj
|
|
end
|
|
|
|
function shape.createInputbox(x1,y1,x2,y2,fillColor,borderColor,textColor)
|
|
local tObj = shape.createText("",x1,y1,x2,y2,fillColor,0,textColor)
|
|
tObj.type = "input"
|
|
return tObj
|
|
end
|
|
end
|
|
|
|
|
|
local align = {}
|
|
function align.left(slide,offset)
|
|
local w,h = term.getSize()
|
|
return offset
|
|
end
|
|
function align.right(slide,offset)
|
|
local w,h = term.getSize()
|
|
return w-offset
|
|
end
|
|
function align.top(slide,offset)
|
|
local w,h = term.getSize()
|
|
return offset
|
|
end
|
|
function align.bottom(slide,offset)
|
|
local w,h = term.getSize()
|
|
return h-offset
|
|
end
|
|
function align.center(slide,offset,vert)
|
|
local w,h = term.getSize()
|
|
if vert then
|
|
return math.ceil(h/2)-offset
|
|
else
|
|
return math.ceil(w/2)-offset
|
|
end
|
|
end
|
|
|
|
|
|
local generic = {}
|
|
|
|
function generic.align(obj)
|
|
--if obj.ox1 == nil or obj.oy1 == nil then
|
|
--obj.ox1,obj.oy1,obj.ox2,obj.oy2 = obj.x1,obj.y1,obj.x2,obj.y2
|
|
--end
|
|
local w,h = term.getSize()
|
|
if obj.snap.Left == "Snap right" then
|
|
if not obj.ox1 then
|
|
obj.ox1 = w-obj.x1
|
|
end
|
|
obj.x1 = align.right(slide,obj.ox1)
|
|
elseif obj.snap.Left == "Snap center" then
|
|
if not obj.ox1 then
|
|
obj.ox1 = math.ceil(w/2)-obj.x1
|
|
end
|
|
obj.x1 = align.center(slide,obj.ox1)
|
|
else
|
|
obj.ox1 = nil
|
|
end
|
|
if obj.snap.Right == "Snap right" then
|
|
if not obj.ox2 then
|
|
obj.ox2 = w-obj.x2
|
|
end
|
|
obj.x2 = align.right(slide,obj.ox2)
|
|
elseif obj.snap.Right == "Snap center" then
|
|
if not obj.ox2 then
|
|
obj.ox2 = math.ceil(w/2)-obj.x2
|
|
end
|
|
obj.x2 = align.center(slide,obj.ox2)
|
|
else
|
|
obj.ox2 = nil
|
|
end
|
|
if obj.snap.Top == "Snap bottom" then
|
|
if not obj.oy1 then
|
|
obj.oy1 = h-obj.y1
|
|
end
|
|
obj.y1 = align.bottom(slide,obj.oy1)
|
|
elseif obj.snap.Top == "Snap center" then
|
|
if not obj.oy1 then
|
|
obj.oy1 = math.ceil(h/2)-obj.y1
|
|
end
|
|
obj.y1 = align.center(slide,obj.oy1,true)
|
|
else
|
|
obj.oy1 = nil
|
|
end
|
|
if obj.snap.Bottom == "Snap bottom" then
|
|
if not obj.oy2 then
|
|
obj.oy2 = h-obj.y2
|
|
end
|
|
obj.y2 = align.bottom(slide,obj.oy2)
|
|
elseif obj.snap.Bottom == "Snap center" then
|
|
if not obj.oy2 then
|
|
obj.oy2 = math.ceil(h/2)-obj.y2
|
|
end
|
|
obj.y2 = align.center(slide,obj.oy2,true)
|
|
else
|
|
obj.oy2 = nil
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.shapescape.addScript(tShp,id,ev,assets,LevelOS,slides)
|
|
if not tShp.event then
|
|
tShp.event = {}
|
|
end
|
|
|
|
-- run script with environment
|
|
local function getEnv(tShape)
|
|
local tempWin
|
|
if LevelOS then
|
|
tempWin = LevelOS.self.window
|
|
end
|
|
local tEnv = createShellEnv("",tempWin)
|
|
if LevelOS then
|
|
tEnv.LevelOS = LevelOS
|
|
end
|
|
setmetatable(tEnv,{__index=_G})
|
|
tEnv.self = tShape
|
|
return tEnv
|
|
end
|
|
local function getSlide()
|
|
return slides[slides.cSlide]
|
|
end
|
|
local function getSlides()
|
|
return slides
|
|
end
|
|
local function setSlide(n)
|
|
if slides[n] then
|
|
slides.cSlide = n
|
|
os.queueEvent("shapescape_change_slide")
|
|
os.pullEvent()
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
local function exit(...)
|
|
slides.stop = true
|
|
slides["return"] = {...}
|
|
end
|
|
local getEvent = function() return end
|
|
local tEnv = getEnv(tShp)
|
|
tEnv.shapescape = {getEvent=getEvent,getSlide=getSlide,getSlides=getSlides,setSlide=setSlide,exit=exit}
|
|
tShp.tEnv = tEnv
|
|
local sFunc,err = load(assets[id].content,"@"..assets[id].name,"bt",tEnv)
|
|
if not sFunc then
|
|
function sFunc() printError(err) end
|
|
end
|
|
if ev == "Coroutine" and tShp.type == "window" then
|
|
tShp.event[ev] = {function(tShape,e,...) tShape.tEnv.shapescape.getEvent = function() return unpack(e) end local ok,err = pcall(sFunc,...) if not ok then print(err) end end,id}
|
|
else
|
|
tShp.event[ev] = {function(tShape,e,...) tShape.tEnv.shapescape.getEvent = function() return unpack(e) end local ok,err = pcall(sFunc,...) if not ok then lUtils.popup("Error",err,31,11,{"OK"}) end end,id}
|
|
end
|
|
end
|
|
|
|
function lUtils.shapescape.renderSlide(slide,static,args)
|
|
local oterm = term.current()
|
|
term.redirect(slide.win)
|
|
term.setBackgroundColor(colors.white)
|
|
term.clear()
|
|
local cursor
|
|
for o=1,#slide.objs do
|
|
--[[if slide.objs[o].snap then
|
|
generic.align(slide.objs[o])
|
|
end]]
|
|
if slide.objs[o].type == "rect" or slide.objs[o].type == "text" or slide.objs[o].type == "window" or slide.objs[o].type == "input" then
|
|
local s = slide.objs[o]
|
|
if not slide.objs[o].render then
|
|
local self = slide.objs[o]
|
|
if slide.objs[o].type == "window" then
|
|
if not static then
|
|
function self.render()
|
|
local restore = cRestore()
|
|
if self.snap then
|
|
generic.align(self)
|
|
end
|
|
if self.color ~= 0 then
|
|
if not self.window then
|
|
self.window = window.create(term.current(),self.x1,self.y1,(self.x2-self.x1)+1,(self.y2-self.y1)+1,false)
|
|
end
|
|
local x,y = self.window.getPosition()
|
|
local w,h = self.window.getSize()
|
|
if x ~= self.x1 or y ~= self.y1 or w ~= (self.x2-self.x1)+1 or h ~= (self.y2-self.y1)+1 then
|
|
self.window.reposition(self.x1,self.y1,(self.x2-self.x1)+1,(self.y2-self.y1)+1)
|
|
end
|
|
for l=1,(self.y2-self.y1)+1 do
|
|
term.setCursorPos(self.x1,self.y1+(l-1))
|
|
term.blit(self.window.getLine(l))
|
|
end
|
|
end
|
|
cRestore(restore)
|
|
end
|
|
else
|
|
local lines
|
|
local function genLines()
|
|
lines = {}
|
|
--[[local fg = lUtils.toBlit(self.color or colors.white)
|
|
local bg = lUtils.toBlit(self.color or colors.white)]]
|
|
local fg,bg
|
|
if self.color ~= colors.black then
|
|
fg = lUtils.toBlit(self.color)
|
|
bg = lUtils.toBlit(self.border.color ~= nil and self.border.color or colors.black)
|
|
if bg == nil then
|
|
bg = lUtils.toBlit(colors.black)
|
|
end
|
|
else
|
|
fg = lUtils.toBlit(self.color)
|
|
bg = lUtils.toBlit(self.border.color ~= nil and self.border.color or colors.white)
|
|
if bg == nil then
|
|
bg = lUtils.toBlit(colors.white)
|
|
end
|
|
end
|
|
for y=self.y1,self.y2 do
|
|
lines[#lines+1] = {"","",""}
|
|
for x=self.x1,self.x2 do
|
|
lines[#lines][1] = lines[#lines][1]..string.char(math.random(129,159))
|
|
if math.random(1,2) == 2 then
|
|
lines[#lines][2] = lines[#lines][2]..bg
|
|
lines[#lines][3] = lines[#lines][3]..fg
|
|
else
|
|
lines[#lines][2] = lines[#lines][2]..fg
|
|
lines[#lines][3] = lines[#lines][3]..bg
|
|
end
|
|
end
|
|
end
|
|
end
|
|
genLines()
|
|
local x1,y1,x2,y2 = self.x1,self.y1,self.x2,self.y2
|
|
local c1,c2 = self.color,self.border.color
|
|
function self.render()
|
|
if self.snap then
|
|
generic.align(self)
|
|
end
|
|
if x1 ~= self.x1 or y1 ~= self.y1 or x2 ~= self.x2 or y2 ~= self.y2 or c1 ~= self.color or c2 ~= self.border.color then
|
|
x1,y1,x2,y2 = self.x1,self.y1,self.x2,self.y2
|
|
c1,c2 = self.color,self.border.color
|
|
genLines()
|
|
end
|
|
for l=1,#lines do
|
|
term.setCursorPos(self.x1,self.y1+(l-1))
|
|
term.blit(unpack(lines[l]))
|
|
end
|
|
end
|
|
end
|
|
else
|
|
function self.render()
|
|
local restore = cRestore()
|
|
if self.snap then
|
|
generic.align(self)
|
|
end
|
|
if self.color ~= 0 then
|
|
term.setBackgroundColor(self.color)
|
|
--term.setCursorPos(self.x1,self.y1)
|
|
for y=self.y1,self.y2 do
|
|
term.setCursorPos(self.x1,y)
|
|
term.write(string.rep(" ",self.x2-(self.x1-1)))
|
|
end
|
|
--lOS.boxClear(self.x1,self.y1,self.x2,self.y2)
|
|
end
|
|
if self.border and self.border.color ~= 0 then
|
|
term.setTextColor(self.border.color)
|
|
lUtils.border(self.x1,self.y1,self.x2,self.y2,"transparent")
|
|
end
|
|
if self.image then
|
|
if self.border and self.border.color ~= 0 then
|
|
lUtils.renderImg(self.image,self.x1+1,self.y1+1,nil,true)
|
|
else
|
|
lUtils.renderImg(self.image,self.x1,self.y1,nil,true)
|
|
end
|
|
end
|
|
if self.type == "input" then
|
|
-- gen shape and make it provide shape
|
|
if not self.fRender then
|
|
lUtils.input(self.x1,self.y1,self.x2,self.y2,nil,nil,self)
|
|
end
|
|
self.fRender()
|
|
elseif self.txt then
|
|
if self.color == 0 then
|
|
term.setBackgroundColor(colors.white)
|
|
else
|
|
term.setBackgroundColor(self.color)
|
|
end
|
|
term.setTextColor(self.txtcolor)
|
|
term.setCursorPos(1,1)
|
|
if self.border and self.border.color ~= 0 and self.y2 >= self.y1+2 then
|
|
lUtils.textbox(self.txt,self.x1+1,self.y1+1,self.x2-1,self.y2-1,true)
|
|
else
|
|
if static and self.txt == "" and not self.input then
|
|
lUtils.textbox("...",self.x1,self.y1,self.x2,self.y2,true)
|
|
else
|
|
lUtils.textbox(self.txt,self.x1,self.y1,self.x2,self.y2,true)
|
|
end
|
|
end
|
|
end
|
|
if not self.state then
|
|
cRestore(restore)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
s.render()
|
|
if not static and s.event and s.event.render and s.event.render[1] then
|
|
s.event.render[1](s,nil,args)
|
|
end
|
|
if not slide.objs[o].update then
|
|
local self = slide.objs[o]
|
|
function self.update(args,...)
|
|
-- continue this
|
|
local e = {...}
|
|
if e[1] == "mouse_click" or e[1] == "mouse_up" then
|
|
if e[3] >= self.x1 and e[4] >= self.y1 and e[3] <= self.x2 and e[4] <= self.y2 then
|
|
if self.event[e[1]] and self.event[e[1]][1] then
|
|
self.event[e[1]][1](self,e,table.unpack(args))
|
|
end
|
|
end
|
|
if e[1] == "mouse_up" and self.selected then
|
|
self.selected = false
|
|
end
|
|
end
|
|
if self.event.update then
|
|
-- dt when i can
|
|
self.event.update[1](self,e,table.pack(args))
|
|
end
|
|
if not self.coroutine and self.event.Coroutine[2] >= 0 then
|
|
self.coroutine = coroutine.create(function() self.event.Coroutine[1](self,{},table.unpack(args)) end)
|
|
end
|
|
if self.coroutine then
|
|
local oterm = term.current()
|
|
if self.window then
|
|
if self.snap then
|
|
generic.align(self)
|
|
end
|
|
local x,y = self.window.getPosition()
|
|
local w,h = self.window.getSize()
|
|
if x ~= self.x1 or y ~= self.y1 or w ~= (self.x2-self.x1)+1 or h ~= (self.y2-self.y1)+1 then
|
|
self.window.reposition(self.x1,self.y1,(self.x2-self.x1)+1,(self.y2-self.y1)+1)
|
|
end
|
|
term.redirect(self.window)
|
|
if string.find(e[1],"mouse") and e[3] and e[4] and not (self.tEnv and self.tEnv.LevelOS and self.tEnv.LevelOS.self and self.tEnv.LevelOS.self.window and self.tEnv.LevelOS.self.window.events == "all") then
|
|
e[3] = e[3]-(self.x1-1)
|
|
e[4] = e[4]-(self.y1-1)
|
|
end
|
|
end
|
|
coroutine.resume(self.coroutine,unpack(e))
|
|
-- if blink was enabled set cursor back to this window after everything blablabla
|
|
term.redirect(oterm)
|
|
|
|
end
|
|
if self.type == "input" then
|
|
if not self.fUpdate then
|
|
lUtils.input(self.x1,self.y1,self.x2,self.y2,nil,nil,nil,self)
|
|
end
|
|
self.fUpdate(...)
|
|
end
|
|
end
|
|
end
|
|
elseif slide.objs[o].type == "triangle" then
|
|
|
|
end
|
|
if not slide.objs[o].remove then
|
|
local self = slide.objs[o]
|
|
local sl = slide
|
|
slide.objs[o].remove = function()
|
|
for s=1,#sl.objs do
|
|
if sl.objs[s] == self then
|
|
sl.objs[s] = nil
|
|
return true
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
term.redirect(oterm)
|
|
end
|
|
|
|
function lUtils.shapescape.run(slides,...)
|
|
local args = table.pack(...)
|
|
local oterm = term.current()
|
|
if not slides.cSlide then
|
|
slides.cSlide = 1
|
|
end
|
|
term.setBackgroundColor(colors.white)
|
|
local cTerm = term.current()
|
|
local sWin = window.create(cTerm,1,1,term.getSize())
|
|
sWin.setVisible(false)
|
|
--sWin.setVisible(false)
|
|
sWin.setBackgroundColor(colors.white)
|
|
sWin.setTextColor(colors.black)
|
|
sWin.clear()
|
|
slides[slides.cSlide].win = sWin
|
|
--lUtils.shapescape.renderSlide(slides[slides.cSlide])
|
|
local started = 0
|
|
os.queueEvent("term_resize")
|
|
while not slides.stop do
|
|
local cursor
|
|
local s = slides[slides.cSlide]
|
|
local e
|
|
if started < 2 then
|
|
e = {"shapescape_start"}
|
|
started = started+1
|
|
else
|
|
e = {os.pullEvent()}
|
|
end
|
|
sWin.reposition(1,1,term.getSize())
|
|
local cSlide = slides.cSlide
|
|
local oterm = term.current()
|
|
local function ssUpdateFunc()
|
|
for t=1,#s.objs do
|
|
local o = s.objs[t]
|
|
if o and o.update then
|
|
local ok,err = pcall(function() o.update(args,unpack(e)) end)
|
|
if not ok then
|
|
error(err,0)
|
|
end
|
|
if o.window and o.window.getCursorBlink() == true then
|
|
cursor = {pos={o.window.getCursorPos()},color=o.window.getTextColor()}
|
|
cursor.pos[1] = o.x1+(cursor.pos[1]-1)
|
|
cursor.pos[2] = o.y1+(cursor.pos[2]-1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local ssUpdate = coroutine.create(ssUpdateFunc)
|
|
term.redirect(sWin)
|
|
local ok,err = coroutine.resume(ssUpdate)
|
|
if not ok then
|
|
error(err,0)
|
|
end
|
|
while coroutine.status(ssUpdate) ~= "dead" do
|
|
term.redirect(oterm)
|
|
sWin.setVisible(true)
|
|
sWin.setVisible(false)
|
|
local e = {os.pullEvent()}
|
|
term.redirect(sWin)
|
|
coroutine.resume(ssUpdate,table.unpack(e))
|
|
end
|
|
if cSlide ~= slides.cSlide then
|
|
-- fuck you bitchass cunt
|
|
if not slides[slides.cSlide].win then
|
|
local w,h = term.getSize()
|
|
slides[slides.cSlide].win = window.create(cTerm,1,1,w,h,false)
|
|
end
|
|
sWin = slides[slides.cSlide].win
|
|
term.redirect(sWin)
|
|
lUtils.shapescape.renderSlide(slides[slides.cSlide])
|
|
end
|
|
term.setCursorBlink(false)
|
|
lUtils.shapescape.renderSlide(s)
|
|
if cursor then
|
|
term.setCursorPos(unpack(cursor.pos))
|
|
term.setTextColor(cursor.color)
|
|
term.setCursorBlink(true)
|
|
end
|
|
-- aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa it doesnt WORK
|
|
term.redirect(oterm)
|
|
sWin.setVisible(true)
|
|
sWin.setVisible(false)
|
|
-- invis rendering during update broke eberythinf so now its notv invis
|
|
--for t=1,({sWin.getSize()})[2] do
|
|
--term.setCursorPos(1,t)
|
|
--term.blit(sWin.getLine(t))
|
|
--end
|
|
end
|
|
if not LevelOS then
|
|
term.setBackgroundColor(colors.black)
|
|
term.setTextColor(colors.white)
|
|
term.clear()
|
|
term.setCursorPos(1,1)
|
|
end
|
|
return unpack(slides["return"])
|
|
end
|
|
|
|
|
|
function lUtils.getArgs(fn)
|
|
local args = {}
|
|
local info = debug.getinfo(fn)
|
|
for i=1, info.nparams do
|
|
args[i] = debug.getlocal(fn,i) or "?"
|
|
end
|
|
if info.vararg then
|
|
args[#args + 1] = "..."
|
|
end
|
|
return args
|
|
end
|
|
|
|
|
|
function lUtils.randStr(keyLength,num,symb)
|
|
local upperCase = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
|
local lowerCase = "abcdefghijklmnopqrstuvwxyz"
|
|
local numbers = "0123456789"
|
|
local symbols = "!@#$%&()*+-,./\\:;<=>?^[]{}"
|
|
|
|
local characterSet = upperCase .. lowerCase
|
|
if num then
|
|
characterSet = characterSet..numbers
|
|
end
|
|
if symb then
|
|
characterSet = characterSet..symbols
|
|
end
|
|
|
|
local output = ""
|
|
|
|
for i = 1, keyLength do
|
|
local rand = math.random(#characterSet)
|
|
output = output .. string.sub(characterSet, rand, rand)
|
|
end
|
|
return output
|
|
end
|
|
|
|
|
|
function lUtils.logout()
|
|
lOS.userID = nil
|
|
lOS.username = nil
|
|
if fs.exists("LevelOS/data/account2.txt") then
|
|
fs.delete("LevelOS/data/account2.txt")
|
|
end
|
|
lOS.notification("You are now logged out of Leveloper Services.")
|
|
end
|
|
|
|
function lUtils.login(username,password,isToken,rememberme)
|
|
local res,err
|
|
local xtra = ""
|
|
if isToken then
|
|
res,err = http.post("https://os.leveloper.cc/auth.php","username="..textutils.urlEncode(username).."&token="..textutils.urlEncode(password))
|
|
else
|
|
if rememberme then
|
|
xtra = "&rememberme=true"
|
|
end
|
|
res,err = http.post("https://os.leveloper.cc/auth.php","username="..textutils.urlEncode(username).."&password="..textutils.urlEncode(password)..xtra)
|
|
end
|
|
if res then
|
|
local str = res.readAll()
|
|
local oldstr = str
|
|
str = lUtils.getField(str,"msg")
|
|
if str:find("Welcome") then
|
|
lUtils.fwrite("LevelOS/data/account.txt",username)
|
|
if rememberme then
|
|
local token = lUtils.getField(oldstr,"token")
|
|
lUtils.fwrite("LevelOS/data/account2.txt",token)
|
|
end
|
|
local userID = res.getResponseHeaders()["Set-Cookie"]
|
|
return userID,str
|
|
else
|
|
return nil,str
|
|
end
|
|
else
|
|
return nil,err
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.getField(thing,fieldname)
|
|
if string.find(thing,"<"..fieldname..">",1,true) ~= nil and string.find(thing,"</"..fieldname..">",1,true) ~= nil then
|
|
begin = nil
|
|
ending = nil
|
|
trash,begin = string.find(thing,"<"..fieldname..">",1,true)
|
|
ending,ending2 = string.find(thing,"</"..fieldname..">",begin+1,true)
|
|
if begin ~= nil and ending ~= nil then
|
|
return string.sub(thing,begin+1,ending-1),string.sub(thing,1,trash-1)..string.sub(thing,ending2+1,string.len(thing))
|
|
end
|
|
end
|
|
return nil,thing
|
|
end
|
|
|
|
|
|
function lUtils.compare(a, b)
|
|
for k,v in pairs(a) do
|
|
if (type(v) == "table" and type(b[k]) == "table" and not lUtils.compare(b[k], v)) or b[k] ~= v then return false end
|
|
end
|
|
for k,v in pairs(b) do
|
|
if (type(v) == "table" and type(a[k]) == "table" and not lUtils.compare(a[k], v)) or a[k] ~= v then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
|
|
function lUtils.centerText(text)
|
|
local x,y = term.getSize()
|
|
local x2,y2 = term.getCursorPos()
|
|
term.setCursorPos(math.floor(x / 2 - text:len() / 2) + 1, y2)
|
|
term.write(text)
|
|
end
|
|
|
|
|
|
|
|
function lUtils.outline(x1,y1,x2,y2)
|
|
local c1 = term.getTextColor()
|
|
local c2 = term.getBackgroundColor()
|
|
term.setCursorPos(x1,y1)
|
|
term.setBackgroundColor(c1)
|
|
term.setTextColor(c2)
|
|
local a
|
|
for a=x1,x2 do
|
|
term.write("\143")
|
|
end
|
|
for a=y1,y2 do
|
|
term.setCursorPos(x1,y1-1+a)
|
|
term.write("\149")
|
|
end
|
|
term.setBackgroundColor(c2)
|
|
term.setTextColor(c1)
|
|
for a=y1,y2 do
|
|
term.setCursorPos(x2,y1-1+a)
|
|
term.write("\149")
|
|
end
|
|
term.setCursorPos(x1,y2)
|
|
for a=x1,x2 do
|
|
term.write("\131")
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function lUtils.border(x1,y1,x2,y2,mode,layer)
|
|
term.setCursorPos(x1,y1)
|
|
local l = layer or 3
|
|
local w,h = x2-(x1-1),y2-(y1-1)
|
|
local bg,fg = term.getBackgroundColor(),term.getTextColor()
|
|
local inved = false
|
|
local function inv()
|
|
if not inved then
|
|
term.setBackgroundColor(fg)
|
|
term.setTextColor(bg)
|
|
inved = true
|
|
else
|
|
term.setBackgroundColor(bg)
|
|
term.setTextColor(fg)
|
|
inved = false
|
|
end
|
|
end
|
|
local function setBG()
|
|
if mode and mode == "transparent" then
|
|
if not inved then
|
|
term.setBackgroundColor(lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3]))
|
|
else
|
|
term.setTextColor(lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3]))
|
|
end
|
|
end
|
|
end
|
|
--[[local function setFG()
|
|
if mode and mode == "transparent" then
|
|
term.setTextColor(lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3]))
|
|
end
|
|
end]]
|
|
if l == 1 then
|
|
inv()
|
|
setBG()
|
|
else
|
|
setBG()
|
|
end
|
|
if l == 2 then
|
|
term.write("\156")
|
|
elseif l == 3 then
|
|
term.write("\151")
|
|
elseif l == 1 then
|
|
term.write("\159")
|
|
end
|
|
if mode and mode == "transparent" then
|
|
for x=1,w-2 do
|
|
if l == 3 then
|
|
setBG()
|
|
term.write("\131")
|
|
elseif l == 2 then
|
|
setBG()
|
|
term.write("\140")
|
|
elseif l == 1 then
|
|
setBG()
|
|
term.write("\143")
|
|
end
|
|
end
|
|
else
|
|
if l == 3 then
|
|
term.write(string.rep("\131",w-2))
|
|
elseif l == 2 then
|
|
term.write(string.rep("\140",w-2))
|
|
elseif l == 1 then
|
|
term.write(string.rep("\143",w-2))
|
|
end
|
|
end
|
|
inv()
|
|
if l == 1 then
|
|
setBG()
|
|
else
|
|
setBG()
|
|
end
|
|
if l == 2 then
|
|
term.write("\147")
|
|
elseif l == 3 then
|
|
term.write("\148")
|
|
elseif l == 1 then
|
|
term.write("\144")
|
|
end
|
|
for y=y1+1,y2-1 do
|
|
term.setCursorPos(x2,y)
|
|
setBG()
|
|
term.write("\149")
|
|
inv()
|
|
term.setCursorPos(x1,y)
|
|
setBG()
|
|
term.write("\149")
|
|
if mode and mode == "fill" then
|
|
term.write(string.rep(" ",w-2))
|
|
end
|
|
inv()
|
|
end
|
|
term.setCursorPos(x1,y2)
|
|
if l == 3 then
|
|
setBG()
|
|
term.write("\138")
|
|
elseif l == 2 then
|
|
inv()
|
|
setBG()
|
|
term.write("\141")
|
|
elseif l == 1 then
|
|
setBG()
|
|
term.write("\130")
|
|
end
|
|
if mode and mode == "transparent" then
|
|
for x=1,w-2 do
|
|
if l == 3 then
|
|
setBG()
|
|
term.write("\143")
|
|
elseif l == 2 then
|
|
setBG()
|
|
term.write("\140")
|
|
elseif l == 1 then
|
|
setBG()
|
|
term.write("\131")
|
|
end
|
|
end
|
|
else
|
|
if l == 3 then
|
|
term.write(string.rep("\143",w-2))
|
|
elseif l == 2 then
|
|
term.write(string.rep("\140",w-2))
|
|
elseif l == 1 then
|
|
term.write(string.rep("\131",w-2))
|
|
end
|
|
end
|
|
if l == 3 then
|
|
setBG()
|
|
term.write("\133")
|
|
inv()
|
|
elseif l == 2 then
|
|
setBG()
|
|
term.write("\142")
|
|
elseif l == 1 then
|
|
setBG()
|
|
term.write("\129")
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.spairs(t, order)
|
|
-- collect the keys
|
|
local keys = {}
|
|
for k in pairs(t) do keys[#keys+1] = k end
|
|
|
|
-- if order function given, sort by it by passing the table and keys a, b,
|
|
-- otherwise just sort the keys
|
|
if order then
|
|
table.sort(keys, function(a,b) return order(t, a, b) end)
|
|
else
|
|
table.sort(keys)
|
|
end
|
|
|
|
-- return the iterator function
|
|
local i = 0
|
|
return function()
|
|
i = i + 1
|
|
if keys[i] then
|
|
return keys[i], t[keys[i]]
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.splitStr(str,pat)
|
|
local t = {} -- NOTE: use {n = 0} in Lua-5.0
|
|
local fpat = "(.-)" .. pat
|
|
local last_end = 1
|
|
local s, e, cap = str:find(fpat, 1)
|
|
while s do
|
|
if s ~= 1 or cap ~= "" then
|
|
table.insert(t,cap)
|
|
end
|
|
last_end = e+1
|
|
s, e, cap = str:find(fpat, last_end)
|
|
end
|
|
if last_end <= #str then
|
|
cap = str:sub(last_end)
|
|
table.insert(t, cap)
|
|
end
|
|
return t
|
|
end
|
|
|
|
|
|
|
|
function lUtils.explorer(path,mode)
|
|
if path == nil or path == "" then
|
|
path = "/"
|
|
end
|
|
local title = "Explorer"
|
|
if mode == "SelFile" then
|
|
title = "Select File"
|
|
elseif mode == "SelFolder" then
|
|
title = "Select Folder"
|
|
end
|
|
local w,h = term.getSize()
|
|
local eW,eH = math.ceil(math.min(w*0.75,w-3)),math.ceil(math.min(h*0.75,h-1))
|
|
local a = {lUtils.openWin(title,"Program_Files/LevelOS/Explorer/main.lua "..path.." "..mode,math.ceil(w/2-eW/2),math.ceil(h/2-eH/2),eW,eH,true,false)}
|
|
if a[1] == false then return false end
|
|
table.remove(a,1)
|
|
if type(a[1]) == "table" then
|
|
a = a[1]
|
|
end
|
|
return table.unpack(a)
|
|
end
|
|
|
|
|
|
|
|
local to_colors, to_blit = {}, {}
|
|
for i = 1, 16 do
|
|
to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i)
|
|
to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1)
|
|
end
|
|
|
|
|
|
|
|
function lUtils.toColor(theblit)
|
|
return to_colors[theblit] or nil
|
|
end
|
|
|
|
|
|
|
|
function lUtils.toBlit(thecolor)
|
|
return to_blit[thecolor] or nil
|
|
end
|
|
|
|
|
|
|
|
function lUtils.instantiate(orig)
|
|
local function deepCopy(o, seen) -- so that "seen" doesn't appear in autocomplete
|
|
seen = seen or {}
|
|
if seen[o] then
|
|
return seen[o]
|
|
end
|
|
local copy
|
|
if type(o) == 'table' then
|
|
copy = {}
|
|
seen[o] = copy
|
|
for k,v in pairs(o) do
|
|
copy[deepCopy(k, seen)] = deepCopy(v, seen)
|
|
end
|
|
setmetatable(copy, deepCopy(getmetatable(o), seen))
|
|
else
|
|
copy = o
|
|
end
|
|
return copy
|
|
end
|
|
return deepCopy(orig)
|
|
end
|
|
|
|
|
|
|
|
local function tokenise( ... )
|
|
local sLine = table.concat( { ... }, " " )
|
|
local tWords = {}
|
|
local bQuoted = false
|
|
for match in string.gmatch( sLine .. "\"", "(.-)\"" ) do
|
|
if bQuoted then
|
|
table.insert( tWords, match )
|
|
else
|
|
for m in string.gmatch( match, "[^ \t]+" ) do
|
|
table.insert( tWords, m )
|
|
end
|
|
end
|
|
bQuoted = not bQuoted
|
|
end
|
|
return tWords
|
|
end
|
|
|
|
|
|
|
|
|
|
|
|
to_colors, to_blit = {}, {}
|
|
for i = 1, 16 do
|
|
to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i)
|
|
to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1)
|
|
end
|
|
|
|
|
|
function lOS.eventdelay()
|
|
local oldtime = os.time()
|
|
local timer = os.startTimer(0)
|
|
while true do
|
|
event,id = os.pullEvent("timer")
|
|
if id == timer then
|
|
local newtime = os.time()
|
|
return (newtime-oldtime)*50000
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function lUtils.getFileType(filename)
|
|
if string.find(filename,"%.%w+$") == nil then
|
|
return ""
|
|
else
|
|
return string.sub(filename,string.find(filename,"%.%w+$"))
|
|
end
|
|
end
|
|
|
|
|
|
|
|
function lUtils.getFileName(filename,ext)
|
|
local f = filename
|
|
if string.find(filename," ") then
|
|
f = string.sub(filename,1,({string.find(filename," ")})[1]-1)
|
|
end
|
|
f = fs.getName(f)
|
|
if not ext or lUtils.getFileType(f) == ".llnk" then
|
|
f = string.sub(f,1,string.len(f)-string.len(lUtils.getFileType(f)))
|
|
end
|
|
return string.gsub(f,"_"," ")
|
|
end
|
|
|
|
|
|
|
|
function lUtils.getFilePath(filename)
|
|
local f = filename
|
|
if string.find(filename," ") then
|
|
f = string.sub(filename,1,({string.find(filename," ")})[1]-1)
|
|
end
|
|
return f
|
|
end
|
|
|
|
local strength
|
|
|
|
function lOS.checkinternet()
|
|
strenth = 0
|
|
local oldtime = os.time()
|
|
a = http.get("https://os.leveloper.cc/ping.php")
|
|
if a == nil or a == false then
|
|
strength = 0
|
|
else
|
|
newtime = os.time()
|
|
if newtime-oldtime < 0.005 then
|
|
strength = 3
|
|
elseif newtime-oldtime < 0.01 then
|
|
strength = 2
|
|
elseif newtime-oldtime >= 0.01 then
|
|
strength = 1
|
|
end
|
|
return strength,newtime-oldtime
|
|
end
|
|
return 0,0
|
|
end
|
|
|
|
function lOS.getInternet()
|
|
return strength or 0
|
|
end
|
|
|
|
function lOS.run( _sCommand, ... )
|
|
local sPath = shell.resolveProgram( _sCommand )
|
|
if sPath ~= nil then
|
|
local tWindow = {}
|
|
local tArgs = {...}
|
|
--term.write(textutils.serialize(tArgs))
|
|
if type(tArgs[1]) == "table" then
|
|
tWindow = tArgs[1]
|
|
table.remove(tArgs,1)
|
|
else
|
|
tWindow = nil
|
|
end
|
|
-- set window title
|
|
local sDir = fs.getDir( sPath )
|
|
local result = {run2( createShellEnv( sDir, tWindow, sPath ), sPath, table.unpack(tArgs) )}
|
|
return table.unpack(result)
|
|
else
|
|
return false,"No such program"
|
|
end
|
|
end
|
|
|
|
function lOS.newWin(func,rPath)
|
|
-- fuck me
|
|
end
|
|
|
|
|
|
function lOS.searchfor(arg,path)
|
|
local files = fs.list(path)
|
|
local folders = {}
|
|
local result = {}
|
|
local p = ""
|
|
if path ~= "" then
|
|
p = path.."/"
|
|
else
|
|
p = ""
|
|
end
|
|
for t=1,#files do
|
|
if fs.isDir(p..files[t]) then
|
|
folders[#folders+1] = p..files[t]
|
|
elseif string.find(string.lower(files[t]),string.lower(arg)) then
|
|
result[#result+1] = p..files[t]
|
|
end
|
|
end
|
|
for t=1,#folders do
|
|
local a = lOS.searchfor(arg,folders[t])
|
|
for b=1,#a do
|
|
result[#result+1] = a[b]
|
|
end
|
|
end
|
|
return result
|
|
end
|
|
|
|
|
|
|
|
function lOS.search(keyword,x,y,w,h,searchfile,animation)
|
|
local lines = {}
|
|
local slp = function() os.sleep(0) end
|
|
for t=1,h do
|
|
lines[t] = {"","",""}
|
|
end
|
|
lines[h] = {"\138","0","8"}
|
|
lines[h-1] = {"\149","8","0"}
|
|
lines[h-2] = {"\151","8","0"}
|
|
lines[1] = {"\151","8","7"}
|
|
lines[2] = {"\149","8","7"}
|
|
for t=1,w-2 do
|
|
if t < math.floor(w/2) then
|
|
lines[1][1] = lines[1][1].."\131"
|
|
lines[1][2] = lines[1][2].."8"
|
|
lines[1][3] = lines[1][3].."7"
|
|
lines[2][1] = lines[2][1].." "
|
|
lines[2][2] = lines[2][2].."0"
|
|
lines[2][3] = lines[2][3].."7"
|
|
else
|
|
lines[1][1] = lines[1][1].."\131"
|
|
lines[1][2] = lines[1][2].."8"
|
|
lines[1][3] = lines[1][3].."0"
|
|
lines[2][1] = lines[2][1].." "
|
|
lines[2][2] = lines[2][2].."0"
|
|
lines[2][3] = lines[2][3].."0"
|
|
end
|
|
lines[h][1] = lines[h][1].."\143"
|
|
lines[h][2] = lines[h][2].."0"
|
|
lines[h][3] = lines[h][3].."8"
|
|
lines[h-1][1] = lines[h-1][1].." "
|
|
lines[h-1][2] = lines[h-1][2].."f"
|
|
lines[h-1][3] = lines[h-1][3].."0"
|
|
lines[h-2][1] = lines[h-2][1].."\131"
|
|
lines[h-2][2] = lines[h-2][2].."8"
|
|
lines[h-2][3] = lines[h-2][3].."0"
|
|
end
|
|
lines[1][1] = lines[1][1].."\148"
|
|
lines[1][2] = lines[1][2].."0"
|
|
lines[1][3] = lines[1][3].."8"
|
|
lines[2][1] = lines[2][1].."\149"
|
|
lines[2][2] = lines[2][2].."0"
|
|
lines[2][3] = lines[2][3].."8"
|
|
lines[h][1] = lines[h][1].."\133"
|
|
lines[h][2] = lines[h][2].."0"
|
|
lines[h][3] = lines[h][3].."8"
|
|
lines[h-1][1] = lines[h-1][1].."\149"
|
|
lines[h-1][2] = lines[h-1][2].."0"
|
|
lines[h-1][3] = lines[h-1][3].."8"
|
|
lines[h-2][1] = lines[h-2][1].."\148"
|
|
lines[h-2][2] = lines[h-2][2].."0"
|
|
lines[h-2][3] = lines[h-2][3].."8"
|
|
term.setCursorPos(x,y+(h-1))
|
|
term.blit(table.unpack(lines[h]))
|
|
term.setCursorPos(x,y+(h-1)-1)
|
|
term.blit(table.unpack(lines[h-2]))
|
|
slp()
|
|
term.setCursorPos(x,y+(h-1)-1)
|
|
term.blit(table.unpack(lines[h-1]))
|
|
term.setCursorPos(x,y+(h-1)-2)
|
|
term.blit(table.unpack(lines[h-2]))
|
|
slp()
|
|
for t=4,h,3 do
|
|
term.setCursorPos(x,y+(h-1)-(t-1))
|
|
term.blit(table.unpack(lines[1]))
|
|
for a=1,t-4 do
|
|
term.setCursorPos(x,y+(h-1)-(t-1)+(a))
|
|
term.blit(table.unpack(lines[2]))
|
|
end
|
|
slp()
|
|
end
|
|
term.setCursorPos(x,y)
|
|
term.blit(table.unpack(lines[1]))
|
|
for a=1,h-4 do
|
|
term.setCursorPos(x,y+a)
|
|
term.blit(table.unpack(lines[2]))
|
|
end
|
|
search = lUtils.makeEditBox("Search",w-3,1)
|
|
search.lines = {""}
|
|
local function searchy()
|
|
while true do
|
|
lUtils.drawEditBox(search,x+2,y+(h-2),0,0,string.len(search.lines[1])+1,1,true,false)
|
|
end
|
|
end
|
|
local scrl = -1
|
|
local sel = 1
|
|
local btns = {}
|
|
local function rendersearch()
|
|
for a=1,h-4 do
|
|
term.setCursorPos(x,y+a)
|
|
term.blit(table.unpack(lines[2]))
|
|
end
|
|
local ox,oy = term.getCursorPos()
|
|
local txtcolor = term.getTextColor()
|
|
term.setCursorPos(x+(w/2),y+1)
|
|
term.setBackgroundColor(colors.white)
|
|
term.setTextColor(colors.black)
|
|
-- write("lastkey = "..lastkey.." and os.time() = "..os.time())
|
|
-- selected box
|
|
term.setBackgroundColor(colors.white)
|
|
lOS.boxClear(x+math.ceil(w/2),y+1,x+(w-2),y+(h-4))
|
|
if result[sel] ~= nil then
|
|
local tPath = ""
|
|
if string.gsub(result[sel],fs.getName(result[sel]),"") ~= "" then
|
|
tPath = string.gsub(result[sel],fs.getName(result[sel]),"")
|
|
else
|
|
tPath = "root"
|
|
end
|
|
term.setCursorPos(x+(w/2)+1,y+2)
|
|
local ay = y+2
|
|
local ax = x+(math.floor(w/2))+6
|
|
local aw = (math.ceil(w/2-1)-6)
|
|
term.setTextColor(colors.lightGray)
|
|
write("Name")
|
|
term.setTextColor(colors.black)
|
|
for t=1,math.ceil(string.len(fs.getName(result[sel]))/aw) do
|
|
term.setCursorPos(ax,ay)
|
|
write(string.sub(fs.getName(result[sel]),1+((t-1)*aw),t*aw))
|
|
ay = ay+1
|
|
end
|
|
term.setCursorPos(x+(w/2)+1,ay)
|
|
term.setTextColor(colors.lightGray)
|
|
write("Path")
|
|
term.setTextColor(colors.black)
|
|
for t=1,math.ceil(string.len(tPath)/aw) do
|
|
term.setCursorPos(ax,ay)
|
|
write(string.sub(tPath,1+((t-1)*aw),t*aw))
|
|
ay = ay+1
|
|
end
|
|
if fs.exists(result[sel].."updater") then
|
|
term.setTextColor(colors.gray)
|
|
term.setCursorPos(x+(w/2)+1,ay)
|
|
term.write("(Auto updates)")
|
|
ay = ay+1
|
|
end
|
|
ay = ay+1
|
|
local aline = ""
|
|
for t=ax-5,x+(w-2) do
|
|
aline = aline.." "
|
|
end
|
|
function filetype(filename)
|
|
if string.find(filename,"%.%w+$") == nil then
|
|
return ""
|
|
else
|
|
return string.sub(filename,string.find(filename,"%.%w+$"))
|
|
end
|
|
end
|
|
if ay+1 <= y+(h-4) then
|
|
if filetype(fs.getName(result[sel])) == ".lua" then
|
|
btns = {}
|
|
btns[1] = {" Execute",ax-5,ay}
|
|
btns[2] = {" Edit",ax-5,ay+2}
|
|
btns[3] = {" Create Shortcut",ax-5,ay+4}
|
|
elseif filetype(fs.getName(result[sel])) == ".txt" then
|
|
btns = {}
|
|
btns[1] = {" Edit",ax-5,ay}
|
|
btns[2] = {" Create Shortcut",ax-5,ay+2}
|
|
else
|
|
btns = {}
|
|
btns[1] = {" Execute",ax-5,ay}
|
|
btns[2] = {" Edit",ax-5,ay+2}
|
|
btns[3] = {" Create Shortcut",ax-5,ay+4}
|
|
end
|
|
end
|
|
for t=1,#btns do
|
|
if t==1 then
|
|
term.setCursorPos(ax-5,btns[t][3]-1)
|
|
term.setBackgroundColor(colors.lightGray)
|
|
term.setTextColor(colors.white)
|
|
term.write(string.gsub(aline,"%s","\143"))
|
|
end
|
|
term.setCursorPos(btns[t][2],btns[t][3])
|
|
term.setTextColor(colors.black)
|
|
term.write(btns[t][1]..string.sub(aline,string.len(btns[t][1])+1,string.len(aline)))
|
|
if t < #btns then
|
|
term.setCursorPos(ax-5,btns[t][3]+1)
|
|
term.setTextColor(colors.white)
|
|
term.write(string.gsub(aline,"%s","\140"))
|
|
else
|
|
term.setCursorPos(ax-5,btns[t][3]+1)
|
|
term.setTextColor(colors.lightGray)
|
|
term.setBackgroundColor(colors.white)
|
|
term.write(string.gsub(aline,"%s","\131"))
|
|
end
|
|
end
|
|
end
|
|
for t=1,#result do
|
|
if result[t] ~= nil then
|
|
if sel == t then
|
|
term.setBackgroundColor(colors.lightGray)
|
|
else
|
|
term.setBackgroundColor(colors.gray)
|
|
end
|
|
if fs.exists(result[t].."updater") ~= nil then
|
|
for i=1,#result do
|
|
if result[i] ~= nil and result[i] == result[t].."updater" then
|
|
table.remove(result,i)
|
|
end
|
|
end
|
|
end
|
|
if y+((t-1)*2)-scrl > y and y+((t-1)*2)-scrl <= y+(h-4) then
|
|
term.setCursorPos(x,y+((t-1)*2)-scrl)
|
|
term.setTextColor(colors.lightGray)
|
|
term.write("\149")
|
|
for i=1,(w/2)-2 do
|
|
write(" ")
|
|
end
|
|
term.setCursorPos(x+1,y+((t-1)*2)-scrl)
|
|
term.setTextColor(colors.white)
|
|
if string.len(fs.getName(result[t])) > (w/2)-2 then
|
|
term.write(string.sub(fs.getName(result[t]),1,(w/2)-5).."...")
|
|
else
|
|
term.write(fs.getName(result[t]))
|
|
end
|
|
end
|
|
if 1+y+((t-1)*2)-scrl > y and 1+y+((t-1)*2)-scrl <= y+(h-4) then
|
|
term.setCursorPos(x,1+y+((t-1)*2)-scrl)
|
|
term.setTextColor(colors.lightGray)
|
|
term.write("\149")
|
|
for i=1,(w/2)-2 do
|
|
write(" ")
|
|
end
|
|
term.setCursorPos(x+1,1+y+((t-1)*2)-scrl)
|
|
if sel == t then
|
|
term.setTextColor(colors.gray)
|
|
end
|
|
if string.gsub(result[t],fs.getName(result[t]),"") ~= "" then
|
|
if string.len(string.gsub(result[t],fs.getName(result[t]),"")) > (w/2)-2 then
|
|
term.write(string.sub(string.gsub(result[t],fs.getName(result[t]),""),1,(w/2)-5).."...")
|
|
else
|
|
term.write(string.gsub(result[t],fs.getName(result[t]),""))
|
|
end
|
|
else
|
|
term.write("root")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
term.setCursorPos(ox,oy)
|
|
term.setTextColor(txtcolor)
|
|
end
|
|
lastkey = os.time()
|
|
lastsearch = ""
|
|
result = {}
|
|
term.setBackgroundColor(colors.white)
|
|
-- lOS.boxClear(x+math.ceil(w/2),y+1,x+(w-2),y+(h-2))
|
|
local function regevents()
|
|
local atimer = os.startTimer(0.5)
|
|
while true do
|
|
local a = {os.pullEvent()}
|
|
if a[1] == "mouse_click" then
|
|
if a[3] < x or a[3] > x+(w-1) or a[4] < y then -- maybe check underneath search box too but i dont think thats necessary for now
|
|
return false,""
|
|
else
|
|
for t=1,#btns do
|
|
if a[4] == btns[t][3] and a[3] >= btns[t][2] and result[sel] ~= nil then
|
|
if string.gsub(btns[t][1],"%s","") == "Edit" and fs.exists(result[sel].."updater") == true then
|
|
result[sel] = result[sel].."updater"
|
|
end
|
|
return true,result[sel],string.gsub(btns[t][1],"%s","")
|
|
end
|
|
end
|
|
end
|
|
elseif a[1] == "key" then
|
|
lastkey = os.time()
|
|
if a[2] == keys.enter and search.lines[1] ~= "" and result[sel] ~= nil then
|
|
return true,result[sel],"Execute"
|
|
elseif a[2] == keys.down then
|
|
if result[sel+1] ~= nil then
|
|
sel = sel+1
|
|
while y+((sel-1)*2)-scrl <= y do
|
|
scrl = scrl-1
|
|
end
|
|
while 1+y+((sel-1)*2)-scrl > y+(h-4) do
|
|
scrl = scrl+1
|
|
end
|
|
rendersearch()
|
|
end
|
|
elseif a[2] == keys.up then
|
|
if sel > 1 then
|
|
sel = sel-1
|
|
while y+((sel-1)*2)-scrl <= y do
|
|
scrl = scrl-1
|
|
end
|
|
while 1+y+((sel-1)*2)-scrl > y+(h-4) do
|
|
scrl = scrl+1
|
|
end
|
|
rendersearch()
|
|
end
|
|
end
|
|
elseif a[1] == "timer" and a[2] == atimer then
|
|
atimer = os.startTimer(0.5)
|
|
if os.time() > lastkey+0.01 and search.lines[1] ~= lastsearch then
|
|
lastkey = os.time()
|
|
lastsearch = search.lines[1]
|
|
result = lOS.searchfor(search.lines[1],"")
|
|
sel = 1
|
|
rendersearch()
|
|
end
|
|
elseif a[1] == "mouse_scroll" and a[3] >= x and a[3] <= x+((w/2)-1) and a[4] >= y and a[4] <= y+(h-4) then
|
|
if scrl+a[2] >= -1 then
|
|
scrl = scrl+a[2]
|
|
rendersearch()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
i = {}
|
|
parallel.waitForAny(searchy,function() i={regevents()} end)
|
|
term.setCursorBlink(false)
|
|
return table.unpack(i)
|
|
end
|
|
|
|
|
|
|
|
function lUtils.littlewin(oriwin,w,h)
|
|
local oW,oH = oriwin.getSize()
|
|
local oldwin = {lines={}}
|
|
for y=1,oH do
|
|
oldwin.lines[y] = {oriwin.getLine(y)}
|
|
end
|
|
local newwin = {lines={}}
|
|
local ystep = oH/h
|
|
local xstep = oW/w
|
|
for y=1,h do
|
|
local xstart = math.floor(xstep/2)
|
|
if xstart < 1 then
|
|
xstart = 1
|
|
end
|
|
local ystart = math.floor(ystep/2)
|
|
if ystart < 1 then
|
|
ystart = 1
|
|
end
|
|
local curLine = math.floor(ystart+ystep*(y-1))
|
|
if curLine > oH or curLine <= 0 then
|
|
curLine = 1
|
|
end
|
|
local templine = {oriwin.getLine(curLine)}
|
|
newwin.lines[y] = {"","",""}
|
|
for x=1,w do
|
|
newwin.lines[y][1] = newwin.lines[y][1]..string.sub(templine[1],math.floor(xstart+xstep*(x-1)+0.5),math.floor(xstart+xstep*(x-1)+0.5))
|
|
newwin.lines[y][2] = newwin.lines[y][2]..string.sub(templine[2],math.floor(xstart+xstep*(x-1)+0.5),math.floor(xstart+xstep*(x-1)+0.5))
|
|
newwin.lines[y][3] = newwin.lines[y][3]..string.sub(templine[3],math.floor(xstart+xstep*(x-1)+0.5),math.floor(xstart+xstep*(x-1)+0.5))
|
|
end
|
|
end
|
|
function newwin.render(x,y,noLetters)
|
|
for l=1,#newwin.lines do
|
|
term.setCursorPos(x,y+(l-1))
|
|
if noLetters then
|
|
newwin.lines[l][1] = newwin.lines[l][1]:gsub("[^\127-\160 ]",".")
|
|
end
|
|
term.blit(table.unpack(newwin.lines[l]))
|
|
end
|
|
end
|
|
return newwin
|
|
end
|
|
|
|
|
|
|
|
function lUtils.fread(filepath, binary)
|
|
local mode = binary and "rb" or "r"
|
|
local fread = fs.open(filepath,mode)
|
|
local thing = fread.readAll()
|
|
fread.close()
|
|
return thing
|
|
end
|
|
|
|
function lUtils.fwrite(filepath,content, binary)
|
|
local mode = binary and "wb" or "w"
|
|
local fwrite = fs.open(filepath,mode)
|
|
fwrite.write(content)
|
|
fwrite.close()
|
|
return true
|
|
end
|
|
|
|
|
|
|
|
wPattern = "%S+"
|
|
function lUtils.makeEditBox(filepath,width,height,sTable)
|
|
if filepath == nil then
|
|
return false
|
|
end
|
|
if width == nil then
|
|
width = getWidth()
|
|
end
|
|
if height == nil then
|
|
height = getHeight()
|
|
end
|
|
if sTable == nil then
|
|
--sTable = {background={colors.white},text={colors.black},keywords={colors.yellow},notes={colors.green},strings={colors.red},menu={colors.yellow,colors.lightGray}}
|
|
sTable = {background={colors.white},text={colors.black},cursor={colors.red}}
|
|
end
|
|
lines = {""}
|
|
return {width=width,height=height,sTable=sTable,lines=lines,filepath=filepath,changed=false}
|
|
end
|
|
|
|
|
|
|
|
function lUtils.drawEditBox(box,expos,eypos,spx,spy,cpx,cpy,active,enterkey,rChar,changesAllowed)
|
|
if changesAllowed == nil then
|
|
changesAllowed = true
|
|
end
|
|
-- term.setCursorPos(expos,eypos)
|
|
-- print(tostring(changesAllowed))
|
|
-- os.sleep(1)
|
|
if enterkey == nil then
|
|
enterkey = true
|
|
end
|
|
if spx == nil then
|
|
spx = 0
|
|
end
|
|
if spy == nil then
|
|
spy = 0
|
|
end
|
|
if cpx == nil then
|
|
cpx = 1
|
|
end
|
|
if cpy == nil then
|
|
cpy = 1
|
|
end
|
|
if expos == nil then
|
|
expos = box.x
|
|
else
|
|
box.x = expos
|
|
end
|
|
if eypos == nil then
|
|
eypos = box.y
|
|
else
|
|
box.y = eypos
|
|
end
|
|
-- As these abbrevs are pretty unclear, I will now specify them respectively.
|
|
-- editorboxtable,editorxposition,editoryposition,scrollpositionx,scrollpositiony,usercursorpositionx,usercurserpositiony
|
|
local keywords = {["and"]=true,["break"]=true,["do"]=true,["else"]=true,["elseif"]=true,["end"]=true,["false"]=true,["for"]=true,["function"]=true,["if"]=true,["in"]=true,["local"]=true,["nil"]=true,["not"]=true,["or"]=true,["repeat"]=true,["return"]=true,["then"]=true,["true"]=true,["until"]=true,["while"]=true}
|
|
if box.width == nil or box.height == nil or box.sTable == nil or box.lines == nil or box.filepath == nil then
|
|
return false
|
|
end
|
|
if active == nil then
|
|
active = true
|
|
end
|
|
if active == false then
|
|
if box.sTable.background == nil then
|
|
term.setBackgroundColor(colors.black)
|
|
else
|
|
term.setBackgroundColor(box.sTable.background[1])
|
|
end
|
|
lOS.boxClear(expos,eypos,expos+(box.width-1),eypos+(box.height-1))
|
|
ypos = eypos
|
|
for l=1+spy,box.height+spy do
|
|
if box.lines[l] ~= nil then
|
|
if rChar ~= nil then
|
|
term.setTextColor(colors.lightGray)
|
|
term.setCursorPos(expos,ypos)
|
|
for rc=1,string.len(string.sub(box.lines[l],1+spx,box.width+spx)) do
|
|
write(rChar)
|
|
end
|
|
else
|
|
term.setTextColor(colors.lightGray)
|
|
term.setCursorPos(expos,ypos)
|
|
write(string.sub(box.lines[l],1+spx,box.width+spx))
|
|
end
|
|
end
|
|
ypos = ypos+1
|
|
end
|
|
return box,spx,spy,cpx,cpy,false,{}
|
|
end
|
|
while true do
|
|
expos,eypos = box.x,box.y
|
|
if box.sTable.background == nil then
|
|
term.setBackgroundColor(colors.black)
|
|
else
|
|
term.setBackgroundColor(box.sTable.background[1])
|
|
end
|
|
lOS.boxClear(expos,eypos,expos+(box.width-1),eypos+(box.height-1))
|
|
ypos = eypos
|
|
for l=1+spy,box.height+spy do
|
|
instring = false
|
|
innote = false
|
|
if box.lines[l] ~= nil then
|
|
if box.sTable.text == nil then
|
|
term.setTextColor(colors.white)
|
|
else
|
|
term.setTextColor(box.sTable.text[1])
|
|
end
|
|
term.setCursorPos(expos,ypos)
|
|
if rChar ~= nil then
|
|
for rc=1,string.len(string.sub(box.lines[l],1+spx,box.width+spx)) do
|
|
write(rChar)
|
|
end
|
|
else
|
|
wordcount = 1
|
|
if string.find(string.sub(box.lines[l],1+spx,box.width+spx),"%s+") ~= nil then
|
|
if string.find(string.sub(box.lines[l],1+spx,box.width+spx),"%s+") == 1 then
|
|
write(string.match(string.sub(box.lines[l],1+spx,box.width+spx),"%s+"))
|
|
end
|
|
end
|
|
for word in string.gmatch(string.sub(box.lines[l],1+spx,box.width+spx),wPattern) do
|
|
somearg = string.find(word,"--",nil,true)
|
|
if tonumber(somearg) ~= nil and instring == false then
|
|
innote = true
|
|
end
|
|
somearg = nil
|
|
if innote == true and box.sTable.notes ~= nil then
|
|
term.setTextColor(box.sTable.notes[1])
|
|
elseif keywords[word] ~= nil and box.sTable.keywords ~= nil then
|
|
term.setTextColor(box.sTable.keywords[1])
|
|
elseif tonumber(word) ~= nil and box.sTable.numbers ~= nil then
|
|
term.setTextColor(box.sTable.numbers[1])
|
|
else
|
|
term.setTextColor(box.sTable.text[1])
|
|
end
|
|
if wordcount == 1 then
|
|
write(word)
|
|
else
|
|
write(" "..word)
|
|
end
|
|
wordcount = wordcount+1
|
|
end
|
|
end
|
|
end
|
|
ypos = ypos+1
|
|
end
|
|
if cpy > spy and cpy <= spy+(box.height) and cpx > spx and cpx <= spx+(box.width) then
|
|
if box.sTable.cursor ~= nil then
|
|
term.setTextColor(box.sTable.cursor[1])
|
|
else
|
|
term.setTextColor(colors.black)
|
|
end
|
|
term.setCursorPos(cpx-spx-1+expos,cpy-spy-1+eypos)
|
|
term.setCursorBlink(true)
|
|
end
|
|
while cpx > string.len(box.lines[cpy])+1 do
|
|
cpx = cpx-1
|
|
end
|
|
while cpx > spx+(box.width) do
|
|
spx = spx+1
|
|
end
|
|
while cpx <= spx do
|
|
spx = spx-1
|
|
end
|
|
event,button,x,y = os.pullEvent()
|
|
term.setCursorBlink(false)
|
|
if (event == "mouse_click" and (x < expos or x > expos+box.width-1 or y < eypos or y > eypos+box.height-1)) or (event == "key" and button == keys.enter and enterkey == false) then
|
|
if box.sTable.background == nil then
|
|
term.setBackgroundColor(colors.black)
|
|
else
|
|
term.setBackgroundColor(box.sTable.background[1])
|
|
end
|
|
lOS.boxClear(expos,eypos,expos+(box.width-1),eypos+(box.height-1))
|
|
ypos = eypos
|
|
for l=1+spy,box.height+spy do
|
|
if box.lines[l] ~= nil then
|
|
if rChar ~= nil then
|
|
for rc=1,string.len(string.sub(box.lines[l],1+spx,box.width+spx)) do
|
|
write(rChar)
|
|
end
|
|
else
|
|
term.setTextColor(colors.lightGray)
|
|
term.setCursorPos(expos,ypos)
|
|
write(string.sub(box.lines[l],1+spx,box.width+spx))
|
|
end
|
|
end
|
|
ypos = ypos+1
|
|
end
|
|
return box,spx,spy,cpx,cpy,false,{event,button,x,y}
|
|
elseif event == "mouse_scroll" and enterkey == true then
|
|
if button == -1 and spy > 0 then
|
|
spy = spy-1
|
|
elseif button == 1 then
|
|
spy = spy+1
|
|
end
|
|
elseif event == "key" then
|
|
if button == keys.right and cpx < string.len(box.lines[cpy])+1 then
|
|
cpx = cpx+1
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
elseif button == keys.left and cpx > 1 then
|
|
cpx = cpx-1
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
elseif button == keys.down and cpy < #box.lines then
|
|
cpy = cpy+1
|
|
while cpy > spy + (box.height) do
|
|
spy = spy+1
|
|
end
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
elseif button == keys.up and cpy > 1 then
|
|
cpy = cpy-1
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
elseif button == keys.home then
|
|
cpx = 1
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
elseif button == keys["end"] then
|
|
cpx = string.len(box.lines[cpy])+1
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
elseif button == keys.tab and changesAllowed == true then
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
if box.changed == false then
|
|
box.changed = true
|
|
end
|
|
box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1).." "..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
|
|
cpx = cpx+2
|
|
elseif button == keys.backspace and changesAllowed == true then
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
if box.changed == false then
|
|
box.changed = true
|
|
end
|
|
if cpx == 1 then
|
|
if cpy > 1 then
|
|
cpx = string.len(box.lines[cpy-1])+1
|
|
box.lines[cpy-1] = box.lines[cpy-1]..box.lines[cpy]
|
|
table.remove(box.lines,cpy)
|
|
cpy = cpy-1
|
|
end
|
|
else
|
|
box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-2)..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
|
|
cpx = cpx-1
|
|
end
|
|
elseif button == keys.enter and enterkey ~= false and changesAllowed == true then
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height-1 do
|
|
spy = spy+1
|
|
end
|
|
if box.changed == false then
|
|
box.changed = true
|
|
end
|
|
table.insert(box.lines,cpy+1,string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy])))
|
|
box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1)
|
|
cpy = cpy+1
|
|
cpx = 1
|
|
end
|
|
elseif event == "char" and changesAllowed == true then
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
if box.changed == false then
|
|
box.changed = true
|
|
end
|
|
box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1)..button..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
|
|
cpx = cpx+1
|
|
elseif event == "paste" and changesAllowed == true then
|
|
while cpy <= spy do
|
|
spy = spy-1
|
|
end
|
|
while cpy > spy+box.height do
|
|
spy = spy+1
|
|
end
|
|
if box.changed == false then
|
|
box.changed = true
|
|
end
|
|
box.lines[cpy] = string.sub(box.lines[cpy],1,cpx-1)..button..string.sub(box.lines[cpy],cpx,string.len(box.lines[cpy]))
|
|
cpx = cpx+string.len(button)
|
|
elseif event == "mouse_click" and y <= eypos+box.height-1 then
|
|
cpx = x - expos + spx+1
|
|
cpy = y - eypos + spy+1
|
|
if cpy > #box.lines then
|
|
cpy = #box.lines
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
function lUtils.textbox(txt,x1,y1,x2,y2,trans,syntax,overflowX,overflowY,scrollX,scrollY,align)
|
|
--[[term.setCursorPos(1,1)
|
|
local bbox = lUtils.makeBox(txt,x1,y1,x2,y2,{background={term.getBackgroundColor()},text={term.getTextColor()},name={colors.cyan},code={colors.red,colors.lightGray},notification={colors.green},title={colors.orange,align="center"}})
|
|
_G.bbox = bbox
|
|
lUtils.printBox(bbox,0,true,trans)]]
|
|
local al = align or "left"
|
|
local oX = overflowX or "wrap"
|
|
local oY = overflowY or "none"
|
|
local w,h = x2-(x1-1),y2-(y1-1)
|
|
local lines
|
|
local blit = {}
|
|
if trans then
|
|
for y=y1,y2 do
|
|
local bl = term.getLine(y)
|
|
table.insert(blit,{"","",bl[3]:sub(x1,x2)})
|
|
end
|
|
else
|
|
local col = lUtils.toBlit(term.getBackgroundColor())
|
|
for y=y1,y2 do
|
|
table.insert(blit,{"","",string.rep(col,w)})
|
|
end
|
|
end
|
|
if oX == "wrap" then
|
|
lines = lUtils.wordwrap(txt,w)
|
|
else
|
|
lines = {}
|
|
for line in txt:gmatch("([^\n]*)\n?") do
|
|
table.insert(lines,line)
|
|
end
|
|
if txt:sub(#txt) == "\n" then
|
|
table.insert(lines,"")
|
|
end
|
|
end
|
|
local tLines = {{lines[1],ref=1}}
|
|
for t=2,#lines do
|
|
tLines[t] = {lines[t],ref=tLines[t-1].ref+#tLines[t-1][1]}
|
|
end
|
|
local syntaxes = {
|
|
["lua"] = {
|
|
whitespace=colors.white,
|
|
comment=colors.green,
|
|
string=colors.red,
|
|
escape=colors.orange,
|
|
keyword=colors.yellow,
|
|
value=colors.yellow,
|
|
ident=colors.cyan,
|
|
number=colors.purple,
|
|
symbol=colors.orange,
|
|
operator=colors.yellow,
|
|
unidentified=colors.white,
|
|
}
|
|
}
|
|
local sType
|
|
if type(syntax) == "string" then
|
|
sType = syntax
|
|
syntax = {type=sType}
|
|
elseif type(syntax) == "table" then
|
|
sType = syntax.type
|
|
end
|
|
if sType and syntaxes[sType] then
|
|
if sType == "lua" and fs.exists("lex") then
|
|
local lex = dofile("lex")
|
|
local elements = lex.lex(txt)
|
|
local line = 1
|
|
for t=1,#elements do
|
|
while tLines[line+1] and elements[t].posFirst >= tLines[line+1].ref do
|
|
line = line+1
|
|
end
|
|
--term.setCursorPos(x+elements[t].posFirst-tLines[line].ref)
|
|
--elements[t].data = elements[t].data:gsub("\t"," ")
|
|
local col = lUtils.toBlit(syntaxes[sType][elements[t].type] or colors.white)
|
|
blit[line][1] = blit[line][1]..elements[t].data
|
|
blit[line][2] = blit[line][2]..string.rep(col,#elements[t].data)
|
|
end
|
|
end
|
|
end
|
|
_G.debugBlit = blit
|
|
for t=1,#blit do
|
|
local x
|
|
local line = lines[t]
|
|
if align == "center" then
|
|
x = (x1+math.ceil(w/2)-1)-(math.ceil(#blit[t][1]/2)-1)
|
|
elseif align == "right" then
|
|
x = x2-(#blit[t][1]-1)
|
|
else
|
|
x = x1
|
|
end
|
|
if #blit[t][3] > #blit[t][1] then
|
|
blit[t][3] = blit[t][3]:sub(x1-(x-1),x1-(x-1)+(#blit[t][1]-1))
|
|
end
|
|
term.setCursorPos(x,y1+(t-1))
|
|
term.blit(unpack(blit[t]))
|
|
end
|
|
end
|
|
|
|
function lUtils.textbox(txt,x1,y1,x2,y2,trans)
|
|
term.setCursorPos(1,1)
|
|
local bbox = lUtils.makeBox(txt,x1,y1,x2,y2,{background={term.getBackgroundColor()},text={term.getTextColor()},name={colors.cyan},code={colors.red,colors.lightGray},notification={colors.green},title={colors.orange,align="center"}})
|
|
_G.bbox = bbox
|
|
lUtils.printBox(bbox,0,true,trans)
|
|
end
|
|
|
|
function lUtils.makeBox(boxTxt,x1,y1,x2,y2,sTable,buttons)
|
|
-- boxTxt = {{{"Introduction",type="title"}},{{"Welcome to ",type="text"},{"LuaCraft",type="name"},{"!",type="text"}}}
|
|
paragraphs = {}
|
|
if boxTxt == nil then
|
|
return false
|
|
end
|
|
if x1 == nil then
|
|
x1 = 1
|
|
end
|
|
if y1 == nil then
|
|
y1 = 1
|
|
end
|
|
if x2 == nil then
|
|
x2 = getWidth()
|
|
end
|
|
if y2 == nil then
|
|
y2 = getHeight()
|
|
end
|
|
if sTable == nil then
|
|
sTable = {background={colors.white},text={colors.black},name={colors.cyan},code={colors.red,colors.lightGray},notification={colors.green},title={colors.orange,align="center"}}
|
|
end
|
|
if _G.type(boxTxt) == "string" then
|
|
while true do
|
|
par1,par2 = string.find(boxTxt,"\n")
|
|
if par2 ~= nil then
|
|
paragraphs[#paragraphs+1] = {{string.sub(boxTxt,1,par1-1),type="text"}}
|
|
if string.len(boxTxt) > par2 then
|
|
boxTxt = string.sub(boxTxt,par2+1,string.len(boxTxt))
|
|
else
|
|
boxTxt = ""
|
|
break
|
|
end
|
|
else
|
|
paragraphs[#paragraphs+1] = {{boxTxt,type="text"}}
|
|
break
|
|
end
|
|
end
|
|
elseif _G.type(boxTxt) == "table" then
|
|
paragraphs = boxTxt
|
|
end
|
|
lines = {{{},tt=""}}
|
|
cline = 1
|
|
for p=1,#paragraphs do
|
|
for t=1,#paragraphs[p] do
|
|
-- paragraphs[1] = {{"Introduction",type="title"}}
|
|
-- paragraphs[1][1] = {"Introduction",type="title"}
|
|
lines[cline][1][#lines[cline][1]+1] = {"",type=paragraphs[p][t].type}
|
|
for word in string.gmatch(paragraphs[p][t][1],"%S+") do
|
|
-- write(word)
|
|
if string.len(lines[cline].tt) == 0 then
|
|
if string.len(lines[cline].tt..word) > x2-(x1-1) then
|
|
-- This means a single word takes up MORE than an ENTIRE line. I'll have to write some proper code for that later.
|
|
end
|
|
-- First word in the line, so obviously no space before the word.
|
|
-- wow old me is so stupid I completely forgot about indentation
|
|
-- HAHAHA dumbass old me messed the fuck up
|
|
local indent = ""
|
|
if string.sub(paragraphs[p][t][1],1,1) == " " then
|
|
indent = string.gmatch(paragraphs[p][t][1],"%s+")()
|
|
end
|
|
lines[cline][1][#lines[cline][1]][1] = lines[cline][1][#lines[cline][1]][1]..indent..word
|
|
lines[cline].tt = lines[cline].tt..indent..word
|
|
else
|
|
if string.len(lines[cline].tt.." "..word) > x2-(x1-1) then
|
|
cline = cline+1
|
|
lines[cline] = {{},tt=""}
|
|
lines[cline][1][#lines[cline][1]+1] = {"",type=paragraphs[p][t].type}
|
|
lines[cline][1][#lines[cline][1]][1] = lines[cline][1][#lines[cline][1]][1]..word
|
|
lines[cline].tt = lines[cline].tt..word
|
|
else
|
|
lines[cline][1][#lines[cline][1]][1] = lines[cline][1][#lines[cline][1]][1].." "..word
|
|
lines[cline].tt = lines[cline].tt.." "..word
|
|
end
|
|
end
|
|
end
|
|
print("")
|
|
end
|
|
cline = cline+1
|
|
lines[cline] = {{},tt=""}
|
|
end
|
|
if buttons == nil then
|
|
buttons = {}
|
|
end
|
|
return {x1=x1,y1=y1,x2=x2,y2=y2,sTable=sTable,lines=lines,buttons=buttons}
|
|
end
|
|
|
|
function lUtils.transWrite(txt,autoTextColor)
|
|
local txtcolor = term.getTextColor()
|
|
local dark = {colors.blue,colors.red,colors.purple,colors.green,colors.black,colors.gray,colors.brown,colors.cyan,colors.magenta}
|
|
local light = {colors.white,colors.orange,colors.lightBlue}
|
|
for k,v in ipairs(dark) do
|
|
dark[v] = true
|
|
end
|
|
for k,v in ipairs(light) do
|
|
light[v] = true
|
|
end
|
|
for t=1,string.len(txt) do
|
|
local col = lUtils.toColor(({lUtils.getPixel(term.current(),term.getCursorPos())})[3])
|
|
term.setBackgroundColor(col)
|
|
if autoTextColor then
|
|
if dark[col] and not light[col] then
|
|
term.setTextColor(colors.white)
|
|
else
|
|
term.setTextColor(colors.black)
|
|
end
|
|
end
|
|
term.write(string.sub(txt,t,t))
|
|
end
|
|
term.setTextColor(txtcolor)
|
|
end
|
|
|
|
function lUtils.printBox(box,scrollpos,simple,trans)
|
|
if type(box) ~= "table" then
|
|
error("Invalid argument #1: Expected table",2)
|
|
end
|
|
someabsolutelyrandomthing = true
|
|
while someabsolutelyrandomthing == true do
|
|
term.setTextColor(colors.black)
|
|
if box.x1 == nil or box.y1 == nil or box.x2 == nil or box.y2 == nil or box.sTable == nil or box.lines == nil then
|
|
return false,"Invalid Box."
|
|
end
|
|
if box.sTable.background ~= nil then
|
|
term.setBackgroundColor(box.sTable.background[1])
|
|
else
|
|
term.setBackgroundColor(colors.white)
|
|
end
|
|
if not trans then
|
|
lOS.boxClear(box.x1,box.y1,box.x2,box.y2)
|
|
end
|
|
ypos = box.y1
|
|
for l=1+scrollpos,box.y2-(box.y1-1)+scrollpos do
|
|
if box.lines[l] ~= nil then
|
|
term.setCursorPos(box.x1,ypos)
|
|
for p=1,#box.lines[l][1] do
|
|
if box.sTable[box.lines[l][1][p].type] ~= nil then
|
|
term.setTextColor(box.sTable[box.lines[l][1][p].type][1])
|
|
if box.sTable[box.lines[l][1][p].type][2] ~= nil then
|
|
term.setBackgroundColor(box.sTable[box.lines[l][1][p].type][2])
|
|
end
|
|
if box.sTable[box.lines[l][1][p].type].align ~= nil then
|
|
if box.sTable[box.lines[l][1][p].type].align == "center" then
|
|
cposx,cposy=term.getCursorPos()
|
|
term.setCursorPos(box.x1+math.floor((box.x2-(box.x1-1))/2)-math.floor(string.len(box.lines[l][1][p][1])/2),cposy)
|
|
end
|
|
end
|
|
lUtils.transWrite(box.lines[l][1][p][1])
|
|
end
|
|
end
|
|
end
|
|
ypos = ypos+1
|
|
end
|
|
if box.buttons ~= nil then
|
|
for b=1,#box.buttons do
|
|
if box.buttons[b].type == "button" or box.buttons[b].type == "editor" then
|
|
term.setBackgroundColor(box.buttons[b].bg)
|
|
term.setTextColor(box.buttons[b].txt)
|
|
for t=1,box.buttons[b].y2-(box.buttons[b].y1-1) do
|
|
blankline = ""
|
|
for bl=1,box.buttons[b].x2-(box.buttons[b].x1-1) do
|
|
blankline = blankline.." "
|
|
end
|
|
if box.buttons[b].y1+t-1 >= 1+scrollpos and box.buttons[b].y1+t-1 <= box.y2-(box.y1-1)+scrollpos then
|
|
term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
|
|
term.write(blankline)
|
|
term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
|
|
if box.buttons[b].text[t] ~= nil then
|
|
term.write(box.buttons[b].text[t])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if box.buttons[b].type == "editor" then
|
|
if box.buttons[b].y1 >= 1+scrollpos and box.buttons[b].y2 <= box.y2-(box.y1-1)+scrollpos then
|
|
if drawEditBox ~= nil and box.buttons[b].quit == false then
|
|
term.setCursorPos(1,1)
|
|
term.write(box.buttons[b].spx..","..box.buttons[b].spy.." | "..box.buttons[b].cpx..","..box.buttons[b].cpy)
|
|
drawEditBox(box.buttons[b].box,box.buttons[b].x1+box.x1-1,(box.buttons[b].y1-1)+box.y1-scrollpos,box.buttons[b].spx,box.buttons[b].spy,box.buttons[b].cpx,box.buttons[b].cpy,false)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
if simple == true then
|
|
someabsolutelyrandomthing = false
|
|
else
|
|
event,button,x,y = os.pullEvent()
|
|
if event == "mouse_click" and (x < box.x1 or x > box.x2 or y < box.y1 or y > box.y2) then
|
|
term.setBackgroundColor(colors.white)
|
|
ypos = box.y1
|
|
for l=1+scrollpos,box.y2-(box.y1-1)+scrollpos do
|
|
if box.lines[l] ~= nil then
|
|
term.setCursorPos(box.x1,ypos)
|
|
for p=1,#box.lines[l][1] do
|
|
if box.sTable[box.lines[l][1][p].type] ~= nil then
|
|
term.setTextColor(colors.lightGray)
|
|
if box.sTable[box.lines[l][1][p].type][2] ~= nil then
|
|
term.setBackgroundColor(box.sTable[box.lines[l][1][p].type][2])
|
|
end
|
|
if box.sTable[box.lines[l][1][p].type].align ~= nil then
|
|
if box.sTable[box.lines[l][1][p].type].align == "center" then
|
|
cposx,cposy=term.getCursorPos()
|
|
term.setCursorPos(box.x1+math.floor((box.x2-(box.x1-1))/2)-math.floor(string.len(box.lines[l][1][p][1])/2),cposy)
|
|
end
|
|
end
|
|
term.write(box.lines[l][1][p][1])
|
|
end
|
|
end
|
|
end
|
|
ypos = ypos+1
|
|
end
|
|
if box.buttons ~= nil then
|
|
for b=1,#box.buttons do
|
|
term.setBackgroundColor(box.buttons[b].bg)
|
|
term.setTextColor(colors.lightGray)
|
|
for t=1,box.buttons[b].y2-(box.buttons[b].y1-1) do
|
|
blankline = ""
|
|
for bl=1,box.buttons[b].x2-(box.buttons[b].x1-1) do
|
|
blankline = blankline.." "
|
|
end
|
|
if box.buttons[b].y1+t-1 >= 1+scrollpos and box.buttons[b].y1+t-1 <= box.y2-(box.y1-1)+scrollpos then
|
|
term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
|
|
term.write(blankline)
|
|
term.setCursorPos(box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos)
|
|
if box.buttons[b].text[t] ~= nil then
|
|
term.write(box.buttons[b].text[t])
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
return scrollpos
|
|
elseif event == "mouse_click" then
|
|
for b=1,#box.buttons do
|
|
-- box.buttons[b].x1+box.x1-1,(box.buttons[b].y1+t-2)+box.y1-scrollpos
|
|
if x >= box.buttons[b].x1+box.x1-1 and x <= box.buttons[b].x2+box.x1-1 and y >= (box.buttons[b].y1-1)+box.y1-scrollpos and y <= (box.buttons[b].y2-1)+box.y1-scrollpos then
|
|
newbox,newbutton,newscrollpos = box.buttons[b].func(box,box.buttons[b],scrollpos)
|
|
if newbox ~= nil then
|
|
box = newbox
|
|
end
|
|
if newbutton ~= nil then
|
|
box.buttons[b] = newbutton
|
|
end
|
|
if newscrollpos ~= nil then
|
|
scrollpos = newscrollpos
|
|
end
|
|
end
|
|
end
|
|
elseif event == "mouse_scroll" then
|
|
if button == -1 and scrollpos > 0 then
|
|
scrollpos = scrollpos-1
|
|
elseif button == 1 then
|
|
scrollpos = scrollpos+1
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function lUtils.getPixel(win,x,y)
|
|
local w,h = win.getSize()
|
|
if x < 1 or x > w or y < 1 or y > h then
|
|
return "0","0","0"
|
|
else
|
|
theline = {win.getLine(y)}
|
|
return string.sub(theline[1],x,x),string.sub(theline[2],x,x),string.sub(theline[3],x,x)
|
|
end
|
|
end
|
|
|
|
local function setParent(win)
|
|
if not win.reposition then return false end
|
|
local varName,value = debug.getupvalue(win.reposition,5)
|
|
local i = 1
|
|
while varName and varName ~= "parent" do
|
|
varName,value = debug.getupvalue(win.reposition,i)
|
|
i = i+1
|
|
end
|
|
if varName == "parent" then
|
|
win.parent = value
|
|
if win.parent.reposition and not win.parent.parent then
|
|
setParent(win.parent)
|
|
end
|
|
end
|
|
end
|
|
|
|
function lUtils.getWindowPos(win)
|
|
setParent(win)
|
|
local tWin = win
|
|
local x,y = tWin.getPosition()
|
|
while tWin.parent and tWin.parent.getPosition do
|
|
tWin = tWin.parent
|
|
local ox,oy = tWin.getPosition()
|
|
x,y = x+ox-1,y+oy-1
|
|
end
|
|
return x,y
|
|
end
|
|
|
|
function lUtils.openWin(title,filepath,x,y,width,height,canresize,canmaximize)
|
|
if canmaximize == nil then
|
|
canmaximize = true
|
|
end
|
|
to_colors, to_blit = {}, {}
|
|
for i = 1, 16 do
|
|
to_blit[2^(i-1)] = ("0123456789abcdef"):sub(i, i)
|
|
to_colors[("0123456789abcdef"):sub(i, i)] = 2^(i-1)
|
|
end
|
|
local OGterm = term.current()
|
|
local OGwin = {lines={}}
|
|
local OGtxt = term.getTextColor()
|
|
local OGbg = term.getBackgroundColor()
|
|
local w,h = term.getSize()
|
|
for t=1,h do
|
|
OGwin.lines[t] = {OGterm.getLine(t)}
|
|
end
|
|
local function getPixel(win,x,y)
|
|
theline = {win.getLine(y)}
|
|
return string.sub(theline[1],x,x),string.sub(theline[2],x,x),string.sub(theline[3],x,x)
|
|
end
|
|
function OGwin.render()
|
|
for l=1,#OGwin.lines do
|
|
term.setCursorPos(1,l)
|
|
term.blit(table.unpack(OGwin.lines[l]))
|
|
end
|
|
end
|
|
local progWin = window.create(term.current(),x+1,y+1,width-2,height-2)
|
|
dragging = false
|
|
dragX = false
|
|
dragY = false
|
|
dragSide = 1
|
|
local function redrawbox()
|
|
term.setBackgroundColor(colors.gray)
|
|
term.setCursorPos(x,y)
|
|
for t=1,width-6 do
|
|
term.write(" ")
|
|
end
|
|
term.setTextColor(colors.white)
|
|
if canmaximize == false then
|
|
term.write(" × ")
|
|
else
|
|
term.write(" + × ")
|
|
end
|
|
term.setCursorPos(x+1,y)
|
|
term.write(title)
|
|
term.setTextColor(colors.gray)
|
|
for i=1,height-2 do
|
|
term.setCursorPos(x,y+i)
|
|
term.blit(string.char(149),to_blit[colors.gray],({getPixel(progWin,1,i)})[3])
|
|
term.blit(progWin.getLine(i))
|
|
term.blit(string.char(149),({getPixel(progWin,width-2,i)})[3],to_blit[colors.gray])
|
|
end
|
|
local bottomline = {string.char(138),({getPixel(progWin,1,height-2)})[3],to_blit[colors.gray]}
|
|
for i=1,width-2 do
|
|
bottomline[1] = bottomline[1]..string.char(143)
|
|
bottomline[2] = bottomline[2]..({getPixel(progWin,i,height-2)})[3]
|
|
bottomline[3] = bottomline[3]..to_blit[colors.gray]
|
|
end
|
|
bottomline[1] = bottomline[1]..string.char(133)
|
|
bottomline[2] = bottomline[2]..({getPixel(progWin,width-2,height-2)})[3]
|
|
bottomline[3] = bottomline[3]..to_blit[colors.gray]
|
|
term.setCursorPos(x,y+(height-1))
|
|
term.blit(table.unpack(bottomline))
|
|
end
|
|
local endresult = {}
|
|
local function regevents()
|
|
local progCor
|
|
if type(filepath) == "string" then
|
|
local tPath = filepath
|
|
local rPath = string.sub(filepath,string.find(filepath,"%S+"))
|
|
local b,e = string.find(filepath,"%S+")
|
|
local tPath = string.sub(filepath,e+2,string.len(filepath))
|
|
local thingy = {}
|
|
for i in string.gmatch(tPath,"%S+") do
|
|
if i ~= nil and i ~= "" then
|
|
thingy[#thingy+1] = i
|
|
end
|
|
end
|
|
progCor = coroutine.create(function() endresult = {lOS.run(rPath,table.unpack(thingy))} end)
|
|
elseif type(filepath) == "function" then
|
|
progCor = coroutine.create(function() endresult = {filepath()} end)
|
|
else
|
|
error("Invalid filepath",3)
|
|
end
|
|
local stop = false
|
|
e = {}
|
|
while stop == false do
|
|
if coroutine.status(progCor) == "dead" then return end
|
|
term.redirect(progWin)
|
|
progWin.redraw()
|
|
progWin.restoreCursor()
|
|
if (e[1] == "mouse_click" or e[1] == "mouse_drag" or e[1] == "mouse_up" or e[1] == "mouse_scroll" or e[1] == "mouse_move") and lOS.wins[lOS.cWin].events ~= "all" then
|
|
e[3] = e[3]-x
|
|
e[4] = e[4]-y
|
|
if e[3] >= 1 and e[3] <= width-2 and e[4] >= 1 and e[4] <= height-2 then
|
|
coroutine.resume(progCor,table.unpack(e))
|
|
end
|
|
else
|
|
coroutine.resume(progCor,table.unpack(e))
|
|
end
|
|
term.redirect(OGterm)
|
|
redrawbox()
|
|
term.setTextColor(progWin.getTextColor())
|
|
term.setCursorPos(({progWin.getPosition()})[1]+({progWin.getCursorPos()})[1]-1,({progWin.getPosition()})[2]+({progWin.getCursorPos()})[2]-1)
|
|
term.setCursorBlink(progWin.getCursorBlink())
|
|
e = {os.pullEvent()}
|
|
if e[1] == "mouse_drag" then
|
|
if dragX == true then
|
|
if dragSide == 1 then
|
|
width = OGw+(DRx-e[3])
|
|
x = OGx-(DRx-e[3])
|
|
else
|
|
width = OGw-(DRx-e[3])
|
|
end
|
|
end
|
|
if dragY == true then
|
|
height = OGh-(DRy-e[4])
|
|
end
|
|
progWin.reposition(x+1,y+1,width-2,height-2)
|
|
progWin.setVisible(false)
|
|
term.redirect(progWin)
|
|
progWin.redraw()
|
|
progWin.restoreCursor()
|
|
coroutine.resume(progCor,"term_resize")
|
|
term.redirect(OGterm)
|
|
progWin.setVisible(true)
|
|
OGwin.render()
|
|
redrawbox()
|
|
end
|
|
if e[1] == "mouse_drag" and dragging == true then
|
|
x = OGx-(DRx-e[3])
|
|
y = OGy-(DRy-e[4])
|
|
progWin.reposition(x+1,y+1)
|
|
progWin.setVisible(false)
|
|
term.redirect(progWin)
|
|
progWin.redraw()
|
|
progWin.restoreCursor()
|
|
coroutine.resume(progCor,"term_resize")
|
|
term.redirect(OGterm)
|
|
progWin.setVisible(true)
|
|
OGwin.render()
|
|
redrawbox()
|
|
elseif e[1] == "mouse_click" or e[1] == "mouse_up" then
|
|
if e[1] == "mouse_up" then
|
|
dragging = false
|
|
dragX = false
|
|
dragY = false
|
|
OGx,OGy = x,y
|
|
OGw,OGh = width,height
|
|
DRx,DRy = nil,nil
|
|
end
|
|
if e[4] == y and e[3] >= x+(width-3) and e[3] <= x+(width-1) then
|
|
if e[1] == "mouse_click" then
|
|
term.setTextColor(colors.white)
|
|
term.setBackgroundColor(colors.red)
|
|
term.setCursorPos(x+(width-3),y)
|
|
term.write(" × ")
|
|
elseif e[1] == "mouse_up" then
|
|
stop = true
|
|
return false
|
|
end
|
|
elseif e[4] == y and e[3] >= x+(width-6) and e[3] <= x+(width-4) and canmaximize ~= false then
|
|
if e[1] == "mouse_click" then
|
|
term.setTextColor(colors.white)
|
|
term.setBackgroundColor(colors.lightGray)
|
|
term.setCursorPos(x+(width-6),y)
|
|
term.write(" + ")
|
|
elseif e[1] == "mouse_up" then
|
|
--lOS.oWins[#lOS.oWins+1] = {window.create(oldterm,1,2,({oldterm.getSize()})[1],({oldterm.getSize()})[2]-3),progCor,fullscreen=false,minimized=false,filepath="'..rPath..'",icon={string.sub(fs.getName(rPath),1,3),string.sub(fs.getName(rPath),4,6)},ran=false}
|
|
--os.startTimer(0.1)
|
|
--return true
|
|
end
|
|
elseif e[4] == y and e[1] == "mouse_click" then
|
|
dragging = true
|
|
DRx,DRy = e[3],e[4]
|
|
OGx,OGy = x,y
|
|
end
|
|
if e[4] > y and e[4] <= y+(height-1) and (e[3] == x or e[3] == x+(width-1)) and e[1] == "mouse_click" and canresize then
|
|
DRx = e[3]
|
|
OGw = width
|
|
OGx = x
|
|
dragX = true
|
|
if e[3] == x then
|
|
dragSide = 1
|
|
else
|
|
dragSide = 2
|
|
end
|
|
end
|
|
if e[4] == y+(height-1) and e[3] >= x and e[3] <= x+(width-1) and e[1] == "mouse_click" and canresize then
|
|
dragY = true
|
|
DRy = e[4]
|
|
OGh = height
|
|
end
|
|
end
|
|
end
|
|
end
|
|
regevents()
|
|
OGwin.render()
|
|
term.setBackgroundColor(OGbg)
|
|
term.setTextColor(OGtxt)
|
|
return table.unpack(endresult)
|
|
end
|
|
|
|
|
|
|
|
function lUtils.popup(title,msg,width,height,buttons,redrawscreen,colorScheme)
|
|
colorScheme = colorScheme or {}
|
|
local tbtxt = colorScheme.topbarText or colors.white
|
|
local tbtxt2 = colorScheme.topbarTextHighlight or colors.black
|
|
local tbbg = colorScheme.topbarFill or colors.gray
|
|
local tbbg2 = colorScheme.topbarFillHighlight or colors.white
|
|
local txtcol = colorScheme.text or colors.blue
|
|
local bg = colorScheme.fill or colors.white
|
|
local fg = colorScheme.border or colors.lightGray
|
|
local btnbg = colorScheme.buttonFill or colors.white
|
|
local btnbg2 = colorScheme.buttonFillHighlight or colors.lightBlue
|
|
local btntxt = colorScheme.buttonText or colors.black
|
|
local btntxt2 = colorScheme.buttonTextHighlight or colors.white
|
|
|
|
buttons = buttons or {"Continue"}
|
|
|
|
local oblink = term.getCursorBlink()
|
|
term.setCursorBlink(false)
|
|
if msg == nil then
|
|
error("No text given",2)
|
|
end
|
|
local OGterm = term.current()
|
|
local OGwin = {lines={}}
|
|
local w,h = term.getSize()
|
|
for t=1,h do
|
|
OGwin.lines[t] = {OGterm.getLine(t)}
|
|
end
|
|
function OGwin.render()
|
|
for l=1,#OGwin.lines do
|
|
term.setCursorPos(1,l)
|
|
term.blit(table.unpack(OGwin.lines[l]))
|
|
end
|
|
end
|
|
local dragging = false
|
|
--[[if width == nil or height == nil then
|
|
width = 21
|
|
height = 7
|
|
end]]
|
|
if not height and not width then
|
|
width = 21
|
|
end
|
|
|
|
if height and not width then
|
|
width = 21
|
|
local lines = lUtils.wordwrap(msg, width-2)
|
|
while #lines > height-6 do
|
|
width = width+2
|
|
lines = lUtils.wordwrap(msg, width-2)
|
|
end
|
|
elseif width and not height then
|
|
local lines = lUtils.wordwrap(msg, width-2)
|
|
height = #lines+6
|
|
end
|
|
|
|
if height < 6 then height = 6 end
|
|
local popupline = string.rep(" ",width)
|
|
|
|
local lines = lUtils.wordwrap(msg, width-2)
|
|
|
|
local write = term.write
|
|
local function redrawbox()
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
term.setBackgroundColor(tbbg)
|
|
term.setTextColor(tbtxt)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
write(" "..title)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
-- The line below is unreadable now but it just makes the text box for the popup message and then prints it
|
|
term.setBackgroundColor(bg)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1)
|
|
term.write(string.rep(" ", width-2))
|
|
term.setTextColor(txtcol)
|
|
for y=1, height-4 do
|
|
local line = ""
|
|
if lines[y] then
|
|
line = lines[y]
|
|
end
|
|
line = line..string.rep(" ", width-2 - #line)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1+y)
|
|
term.write(line)
|
|
end
|
|
|
|
for t=1,height-4 do
|
|
term.setBackgroundColor(bg)
|
|
term.setTextColor(fg)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+t)
|
|
write(string.char(149))
|
|
term.setBackgroundColor(fg)
|
|
term.setTextColor(bg)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-1,math.ceil(h/2)-math.floor(height/2)+t)
|
|
write(string.char(149))
|
|
end
|
|
term.setBackgroundColor(fg)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-2)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
|
|
term.setTextColor(btntxt)
|
|
for b=1,#buttons do
|
|
term.setBackgroundColor(fg)
|
|
write(" ")
|
|
term.setBackgroundColor(btnbg)
|
|
write(" "..buttons[b].." ")
|
|
end
|
|
end
|
|
redrawbox()
|
|
while true do
|
|
event,button,x,y = os.pullEvent()
|
|
if event == "mouse_drag" and dragging == true then
|
|
w = OGw-(OGx-x)*2
|
|
h = OGh-(OGy-y)*2
|
|
OGwin.render()
|
|
redrawbox()
|
|
elseif event == "mouse_click" or event == "mouse_up" then
|
|
if event == "mouse_up" and dragging == true then
|
|
dragging = false
|
|
end
|
|
if y == math.ceil(h/2)-math.floor(height/2)+height-2 then
|
|
curX = math.ceil(w/2)-math.floor(width/2)
|
|
for b=1,#buttons do
|
|
if x >= curX+1 and x <= curX+string.len(" "..buttons[b].." ") then
|
|
if event == "mouse_up" or event == "monitor_touch" then
|
|
if redrawscreen ~= nil and redrawscreen == true then
|
|
OGwin.render()
|
|
end
|
|
term.setCursorBlink(oblink)
|
|
return true,b,buttons[b]
|
|
elseif event == "mouse_click" then
|
|
term.setCursorPos(curX+1,math.ceil(h/2)-math.floor(height/2)+height-2)
|
|
term.setBackgroundColor(btnbg2)
|
|
term.setTextColor(btntxt2)
|
|
write(" "..buttons[b].." ")
|
|
end
|
|
end
|
|
curX = curX+string.len(" "..buttons[b].." ")+1
|
|
end
|
|
elseif y == math.ceil(h/2)-math.floor(height/2) and x >= math.ceil(w/2)-math.floor(width/2)+width-3 and x <= math.ceil(w/2)-math.floor(width/2)+width-1 then
|
|
if event == "mouse_click" then
|
|
term.setTextColor(colors.white)
|
|
term.setBackgroundColor(colors.red)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
elseif event == "mouse_up" then
|
|
if redrawscreen ~= nil and redrawscreen == true then
|
|
OGwin.render()
|
|
end
|
|
term.setCursorBlink(oblink)
|
|
return false,0,""
|
|
end
|
|
elseif y == math.ceil(h/2)-math.floor(height/2) and event == "mouse_click" then
|
|
dragging = true
|
|
OGx,OGy = x,y
|
|
OGw = w
|
|
OGh = h
|
|
end
|
|
if (x < math.ceil(w/2)-math.floor(width/2) or x > math.ceil(w/2)-math.floor(width/2)+width-1 or y < math.ceil(h/2)-math.floor(height/2) or y > math.ceil(h/2)-math.floor(height/2)+height-1) and event == "mouse_click" then
|
|
for t=1,5 do
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
term.setBackgroundColor(tbbg2)
|
|
term.setTextColor(tbtxt2)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
write(" "..title)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
os.sleep(0.1)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
term.setBackgroundColor(tbbg)
|
|
term.setTextColor(tbtxt)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
write(" "..title)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
os.sleep(0.1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
function lUtils.inputbox(title,msg,width,height,buttons)
|
|
local OGterm = term.current()
|
|
local OGwin = {lines={}}
|
|
local w,h = term.getSize()
|
|
for t=1,h do
|
|
OGwin.lines[t] = {OGterm.getLine(t)}
|
|
end
|
|
function OGwin.render()
|
|
for l=1,#OGwin.lines do
|
|
term.setCursorPos(1,l)
|
|
term.blit(table.unpack(OGwin.lines[l]))
|
|
end
|
|
end
|
|
dragging = false
|
|
local input = lUtils.makeEditBox("input",width-4,1)
|
|
if width == nil or height == nil then
|
|
width = 21
|
|
height = 7
|
|
end
|
|
if height < 6 then height = 6 end
|
|
popupline = ""
|
|
for pl=1,width do
|
|
popupline = popupline.." "
|
|
end
|
|
local function redrawbox()
|
|
ocursorx,ocursory = term.getCursorPos()
|
|
otext = term.getTextColor()
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
term.setBackgroundColor(colors.gray)
|
|
term.setTextColor(colors.white)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
write(" "..title)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
-- The line below is unreadable now but it just makes the text box for the popup message and then prints it
|
|
term.setBackgroundColor(colors.white)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+1)
|
|
term.write(string.sub(popupline,2,string.len(popupline)-1))
|
|
term.setTextColor(colors.black)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+1)
|
|
lUtils.printBox(lUtils.makeBox(msg,(math.ceil(w/2)-math.floor(width/2))+1,math.ceil(h/2)-math.floor(height/2)+2,(math.ceil(w/2)-math.floor(width/2)+width-1)-1,math.ceil(h/2)-math.floor(height/2)+height-1-3-3,{background={colors.white},text={colors.blue}}),0,true)
|
|
for t=1,height-4 do
|
|
term.setBackgroundColor(colors.white)
|
|
term.setTextColor(colors.lightGray)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+t)
|
|
write(string.char(149))
|
|
term.setBackgroundColor(colors.lightGray)
|
|
term.setTextColor(colors.white)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-1,math.ceil(h/2)-math.floor(height/2)+t)
|
|
write(string.char(149))
|
|
end
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+height-1-3-2)
|
|
term.setBackgroundColor(colors.lightGray)
|
|
term.setTextColor(colors.white)
|
|
write("\159")
|
|
for t=1,width-4 do
|
|
write("\143")
|
|
end
|
|
term.setBackgroundColor(colors.white)
|
|
term.setTextColor(colors.lightGray)
|
|
write("\144")
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+height-1-3-1)
|
|
term.setBackgroundColor(colors.lightGray)
|
|
term.setTextColor(colors.white)
|
|
write("\149")
|
|
term.setTextColor(colors.lightGray)
|
|
term.setBackgroundColor(colors.white)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1+width-3,math.ceil(h/2)-math.floor(height/2)+height-1-3-1)
|
|
write("\149")
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+1,math.ceil(h/2)-math.floor(height/2)+height-1-3)
|
|
write("\130")
|
|
for t=1,width-4 do
|
|
write("\131")
|
|
end
|
|
write("\129")
|
|
term.setBackgroundColor(colors.lightGray)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-2)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2)+height-1-1)
|
|
term.setTextColor(colors.black)
|
|
for b=1,#buttons do
|
|
term.setBackgroundColor(colors.lightGray)
|
|
write(" ")
|
|
term.setBackgroundColor(colors.white)
|
|
write(" "..buttons[b].." ")
|
|
end
|
|
term.setTextColor(otext)
|
|
term.setCursorPos(ocursorx,ocursory)
|
|
end
|
|
redrawbox()
|
|
local function regevents()
|
|
while true do
|
|
event,button,x,y = os.pullEvent()
|
|
if event == "mouse_drag" and dragging == true then
|
|
w = OGw-(OGx-x)*2
|
|
h = OGh-(OGy-y)*2
|
|
OGwin.render()
|
|
redrawbox()
|
|
elseif event == "mouse_click" or event == "mouse_up" then
|
|
if event == "mouse_up" and dragging == true then
|
|
dragging = false
|
|
end
|
|
if y == math.ceil(h/2)-math.floor(height/2)+height-2 then
|
|
curX = math.ceil(w/2)-math.floor(width/2)
|
|
for b=1,#buttons do
|
|
if x >= curX+1 and x <= curX+string.len(" "..buttons[b].." ") then
|
|
if event == "mouse_up" or event == "monitor_touch" then
|
|
return true,b,buttons[b]
|
|
elseif event == "mouse_click" then
|
|
ocursorx,ocursory = term.getCursorPos()
|
|
otext = term.getTextColor()
|
|
term.setCursorPos(curX+1,math.ceil(h/2)-math.floor(height/2)+height-2)
|
|
term.setBackgroundColor(colors.lightBlue)
|
|
term.setTextColor(colors.black)
|
|
write(" "..buttons[b].." ")
|
|
term.setCursorPos(ocursorx,ocursory)
|
|
term.setTextColor(otext)
|
|
end
|
|
end
|
|
curX = curX+string.len(" "..buttons[b].." ")+1
|
|
end
|
|
elseif y == math.ceil(h/2)-math.floor(height/2) and x >= math.ceil(w/2)-math.floor(width/2)+width-3 and x <= math.ceil(w/2)-math.floor(width/2)+width-1 then
|
|
if event == "mouse_click" then
|
|
term.setTextColor(colors.white)
|
|
term.setBackgroundColor(colors.red)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
elseif event == "mouse_up" then
|
|
return false,0,""
|
|
end
|
|
elseif y == math.ceil(h/2)-math.floor(height/2) then
|
|
dragging = true
|
|
OGx,OGy = x,y
|
|
OGw = w
|
|
OGh = h
|
|
else
|
|
redrawbox()
|
|
end
|
|
if (x < math.ceil(w/2)-math.floor(width/2) or x > math.ceil(w/2)-math.floor(width/2)+width-1 or y < math.ceil(h/2)-math.floor(height/2) or y > math.ceil(h/2)-math.floor(height/2)+height-1) and event == "mouse_click" then
|
|
for t=1,5 do
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
term.setBackgroundColor(colors.white)
|
|
term.setTextColor(colors.black)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
write(" "..title)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
os.sleep(0.1)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
term.setBackgroundColor(colors.gray)
|
|
term.setTextColor(colors.white)
|
|
write(popupline)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2),math.ceil(h/2)-math.floor(height/2))
|
|
write(" "..title)
|
|
term.setCursorPos(math.ceil(w/2)-math.floor(width/2)+width-3,math.ceil(h/2)-math.floor(height/2))
|
|
write(" × ")
|
|
os.sleep(0.1)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function readbox()
|
|
c = {"mouse_click",1,math.ceil(w/2)-math.floor(width/2)+2,math.ceil(h/2)-math.floor(height/2)+height-1-3-1}
|
|
while true do
|
|
if c[1] == "mouse_click" and c[3] >= math.ceil(w/2)-math.floor(width/2)+2 and c[3] <= math.ceil(w/2)-math.floor(width/2)+2+(input.width-1) and c[4] == math.ceil(h/2)-math.floor(height/2)+height-1-3-1 then
|
|
if input.lines[1] ~= nil then
|
|
lUtils.drawEditBox(input,math.ceil(w/2)-math.floor(width/2)+2,math.ceil(h/2)-math.floor(height/2)+height-1-3-1,0,0,string.len(input.lines[1])+1,1,true,false)
|
|
else
|
|
lUtils.drawEditBox(input,math.ceil(w/2)-math.floor(width/2)+2,math.ceil(h/2)-math.floor(height/2)+height-1-3-1,0,0,1,1,true,false)
|
|
end
|
|
end
|
|
c = {os.pullEvent("mouse_click")}
|
|
end
|
|
end
|
|
local returnthis = {}
|
|
parallel.waitForAny(readbox,function() returnthis = {regevents()} end)
|
|
term.setCursorBlink(false)
|
|
return input.lines[1],table.unpack(returnthis)
|
|
end
|
|
|
|
function lUtils.createButton(x,y,w,h,lines,func,colrs)
|
|
local btn = {x1=x,y1=y,x2=x+(w-1),y2=y+(h-1),func=func,colors=colrs,selected=false}
|
|
if not btn.colors then
|
|
btn.colors = {}
|
|
end
|
|
local c = btn.colors
|
|
if not c.txt then
|
|
c.txt = colors.white
|
|
end
|
|
if not c.fg then
|
|
c.fg = c.bg or colors.lightGray
|
|
end
|
|
if not c.bg then
|
|
c.bg = colors.gray
|
|
end
|
|
if not c.clicked then
|
|
c.clicked = {}
|
|
end
|
|
local cc = c.clicked
|
|
if not cc.fg then
|
|
cc.fg = cc.bg or colors.lightGray
|
|
end
|
|
if not cc.bg then
|
|
cc.bg = c.bg2 or colors.lightGray
|
|
end
|
|
if not cc.txt then
|
|
cc.txt = c.txt2 or colors.white
|
|
end
|
|
function btn.render()
|
|
local ofg,obg = term.getTextColor(),term.getBackgroundColor()
|
|
if btn.selected then
|
|
term.setBackgroundColor(btn.colors.clicked.bg)
|
|
term.setTextColor(btn.colors.clicked.fg)
|
|
else
|
|
term.setBackgroundColor(btn.colors.bg)
|
|
term.setTextColor(btn.colors.fg)
|
|
end
|
|
lUtils.border(btn.x1,btn.y1,btn.x2,btn.y2,"fill")
|
|
if type(lines) == "string" then
|
|
if btn.selected then
|
|
term.setTextColor(btn.colors.clicked.txt)
|
|
else
|
|
term.setTextColor(btn.colors.txt)
|
|
end
|
|
if btn.y1 == btn.y2 then
|
|
term.setCursorPos(btn.x1,btn.y1)
|
|
else
|
|
term.setCursorPos(btn.x1+1,btn.y1+1)
|
|
end
|
|
term.write(lines)
|
|
end
|
|
term.setTextColor(ofg)
|
|
term.setBackgroundColor(obg)
|
|
end
|
|
function btn.update(...)
|
|
local e = {...}
|
|
if (e[1] == "mouse_click" or e[1] == "mouse_up") and e[2] == 1 then
|
|
if e[3] >= btn.x1 and e[4] >= btn.y1 and e[3] <= btn.x2 and e[4] <= btn.y2 then
|
|
if e[1] == "mouse_click" then
|
|
btn.selected = true
|
|
btn.render()
|
|
elseif e[1] == "mouse_up" and btn.selected then
|
|
btn.selected = false
|
|
btn.render()
|
|
return btn.func()
|
|
end
|
|
end
|
|
end
|
|
if e[1] == "mouse_up" then
|
|
btn.selected = false
|
|
end
|
|
end
|
|
return btn
|
|
end
|
|
function lUtils.createCanvas(x,y,w,h)
|
|
cvs = {}
|
|
if not (x or y) then
|
|
cvs.x1,cvs.y1 = 1,1
|
|
else
|
|
cvs.x1,cvs.y1 = x,y
|
|
end
|
|
if not (w or h) then
|
|
cvs.x2,cvs.y2 = term.getSize()
|
|
else
|
|
cvs.x2,cvs.y2 = x+(w-1),y+(h-1)
|
|
end
|
|
cvs.objs = {}
|
|
function cvs.clear()
|
|
cvs.objs = {}
|
|
end
|
|
function cvs.update(...)
|
|
for o=1,#cvs.objs do
|
|
cvs.objs[o].update(...)
|
|
end
|
|
end
|
|
function cvs.render()
|
|
for o=1,#cvs.objs do
|
|
cvs.objs[o].render()
|
|
end
|
|
end
|
|
function cvs.createButton(...)
|
|
local b = lUtils.createButton(...)
|
|
if b then
|
|
cvs.objs[#cvs.objs+1] = b
|
|
return b
|
|
else
|
|
return false
|
|
end
|
|
end
|
|
return cvs
|
|
end
|
|
function lUtils.contextmenu(x,y,width,options,colorScheme,dividers)
|
|
-- elements are either string OR table, table contains txt and optionally: disabled (bool), action (function or table (for nested context menu)), color (number)
|
|
local mColors = colorScheme or {}
|
|
mColors.fg = mColors.fg or colors.lightGray
|
|
mColors.bg = mColors.bg or colors.gray
|
|
mColors.txt = mColors.txt or colors.white
|
|
mColors.disabled = mColors.disabled or colors.lightGray
|
|
mColors.divider = mColors.divider or mColors.fg
|
|
mColors.selected = mColors.selected or mColors.select or mColors.fg
|
|
local w = 0
|
|
if not width then
|
|
width = "auto"
|
|
end
|
|
if type(width) == "number" then
|
|
w = width
|
|
end
|
|
local function genContextMenu(opt,x,y,w)
|
|
local h = 2
|
|
local objects = {}
|
|
local cY = 1
|
|
local function addObject(obj,parent)
|
|
obj.y = cY
|
|
obj.parent = opt
|
|
obj.parentObj = parent
|
|
table.insert(objects,obj)
|
|
cY = cY+1
|
|
h = h+1
|
|
if not obj.action then obj.action = "output" end
|
|
if obj.txt then
|
|
local nW
|
|
if type(obj.txt) == "table" then
|
|
nW = #obj.txt[1]+2
|
|
else
|
|
nW = #obj.txt+2
|
|
end
|
|
if type(obj.action) == "table" then
|
|
nW = nW+2
|
|
end
|
|
if nW > w then
|
|
w = nW
|
|
end
|
|
end
|
|
end
|
|
local acceptable = {["string"]=true,["table"]=true}
|
|
local lID = 1
|
|
for i,o in ipairs(opt) do
|
|
if i > 1 and dividers then
|
|
addObject({action="divider"})
|
|
end
|
|
if type(o) == "string" then
|
|
addObject({action="output",txt=o,id=lID})
|
|
lID = lID+1
|
|
elseif type(o) == "table" and acceptable[type(o.txt)] then
|
|
addObject(o)
|
|
o.id = lID
|
|
lID = lID+1
|
|
elseif type(o) == "table" then
|
|
for k,v in ipairs(o) do
|
|
local lID2 = 1
|
|
if type(v) == "string" then
|
|
addObject({action="output",txt=v,id=lID2},o)
|
|
lID2 = lID2+1
|
|
elseif type(v) == "table" and acceptable[type(v.txt)] then
|
|
addObject(v,o)
|
|
v.id = lID2
|
|
lID2 = lID2+1
|
|
end
|
|
end
|
|
o.id = lID
|
|
lID = lID+1
|
|
o.parent = opt
|
|
end
|
|
end
|
|
objects.x,objects.y,objects.w,objects.h = x,y,w,h
|
|
objects.x1,objects.y1 = x,y
|
|
objects.x2,objects.y2 = x+w-1,y+h-1
|
|
objects.rObjs = {}
|
|
objects.scroll = 0
|
|
return objects
|
|
end
|
|
local objects,h
|
|
objects = genContextMenu(options,x,y,w)
|
|
objects.status = "idle"
|
|
local function positionMenu(menu)
|
|
local tW,tH = term.getSize()
|
|
if menu.y+menu.h-1 > tH then
|
|
if menu.y-menu.h+1 >= 1 then
|
|
menu.y = menu.y-menu.h+1
|
|
elseif menu.h <= tH then
|
|
while menu.y+menu.h-1 > tH do
|
|
menu.y = menu.y-1
|
|
end
|
|
else
|
|
menu.y = 1
|
|
end
|
|
end
|
|
-- repeat for X axis
|
|
if menu.x+menu.w-1 > tW then
|
|
if menu.x-menu.w+1 >= 1 then
|
|
menu.x = menu.x-menu.w+1
|
|
elseif menu.w <= tW then
|
|
while menu.x+menu.w-1 > tW do
|
|
menu.x = menu.x-1
|
|
end
|
|
else
|
|
menu.x = 1
|
|
-- turn on scroll
|
|
end
|
|
end
|
|
end
|
|
positionMenu(objects)
|
|
local menus = {objects}
|
|
local function renderMenu(menu)
|
|
local tW,tH = term.getSize()
|
|
while menu.y+menu.h-1 > tH do
|
|
menu.h = menu.h-1
|
|
end
|
|
menu.x1,menu.y1 = menu.x,menu.y
|
|
menu.x2,menu.y2 = menu.x+menu.w-1,menu.y+menu.h-1
|
|
menu.bg = menu.bg or mColors.bg
|
|
menu.fg = menu.fg or mColors.fg
|
|
menu.txt = menu.txt or mColors.txt
|
|
menu.divider = menu.divider or mColors.divider
|
|
menu.disabled = menu.disabled or mColors.disabled
|
|
menu.selColor = menu.selColor or mColors.selected
|
|
dividerline = string.rep("\140",menu.w-2)
|
|
term.setBackgroundColor(menu.bg)
|
|
term.setTextColor(menu.fg)
|
|
lUtils.border(menu.x,menu.y,menu.x+menu.w-1,menu.y+menu.h-1,"fill")
|
|
for k=1+menu.scroll,(menu.h-2)+menu.scroll do
|
|
local v = menu[k]
|
|
term.setCursorPos(menu.x+1,menu.y+v.y-menu.scroll)
|
|
if menu.scroll > 0 and k == 1+menu.scroll then
|
|
term.setBackgroundColor(menu.bg)
|
|
term.setTextColor(menu.txt)
|
|
term.setCursorPos(menu.x+math.ceil(menu.w/2)-1,menu.y+1)
|
|
term.write("\30")
|
|
elseif k == (menu.h-2)+menu.scroll and k < #menu then
|
|
term.setBackgroundColor(menu.bg)
|
|
term.setTextColor(menu.txt)
|
|
term.setCursorPos(menu.x+math.ceil(menu.w/2)-1,menu.y2-1)
|
|
term.write("\31")
|
|
else
|
|
if menu.selected == v then
|
|
term.setBackgroundColor(menu.selColor)
|
|
else
|
|
term.setBackgroundColor(v.bg or menu.bg)
|
|
end
|
|
if v.action == "divider" then
|
|
term.setTextColor(menu.divider)
|
|
term.write(dividerline)
|
|
else
|
|
if v.disabled then
|
|
term.setTextColor(v.color or menu.disabled)
|
|
else
|
|
term.setTextColor(v.color or menu.txt)
|
|
end
|
|
if type(v.txt) == "table" then
|
|
if not v.txt[2] then v.txt[2] = "" end
|
|
if not v.txt[3] then v.txt[3] = "" end
|
|
local bl = {
|
|
v.txt[1]..string.rep(" ",(menu.w-2)-#v.txt[1]),
|
|
v.txt[2]..string.rep(lUtils.toBlit(term.getTextColor()),(menu.w-2)-#v.txt[2]),
|
|
v.txt[3]..string.rep(lUtils.toBlit(term.getBackgroundColor()),(menu.w-2)-#v.txt[3]),
|
|
}
|
|
term.blit(unpack(bl))
|
|
else
|
|
term.write(v.txt..string.rep(" ",(menu.w-2)-#v.txt))
|
|
end
|
|
if type(v.action) == "table" then
|
|
term.setCursorPos(menu.x+menu.w-2,menu.y+v.y-menu.scroll)
|
|
term.write(">")
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
function objects.render()
|
|
for m=1,#menus do
|
|
renderMenu(menus[m])
|
|
end
|
|
end
|
|
function objects.update(...)
|
|
if objects.status == "dead" then return false end
|
|
objects.status = "running"
|
|
local e = {...}
|
|
if e[1]:find("mouse") and e[3] and e[4] then
|
|
for m=#menus,1,-1 do
|
|
local menu = menus[m]
|
|
local oselected = menu.selected
|
|
if e[1] == "mouse_click" or e[1] == "mouse_move" then
|
|
menu.selected = nil
|
|
end
|
|
if not lUtils.isInside(e[3],e[4],menu) then
|
|
if e[1] == "mouse_click" then
|
|
table.remove(menus,m)
|
|
if m == 1 then
|
|
objects.status = "dead"
|
|
return
|
|
end
|
|
end
|
|
else
|
|
if e[1] == "mouse_scroll" then
|
|
if e[2] == -1 and menu.scroll > 0 then
|
|
menu.scroll = menu.scroll-1
|
|
elseif e[2] == 1 and (menu.h-2)+menu.scroll < #menu then
|
|
menu.scroll = menu.scroll+1
|
|
end
|
|
elseif e[3] > menu.x and e[3] < menu.x+menu.w-1 then
|
|
if e[4] == menu.y+1 and menu.scroll > 0 then
|
|
if e[1] == "mouse_click" then
|
|
menu.scroll = menu.scroll-1
|
|
end
|
|
elseif e[4] == menu.y2-1 and (menu.h-2)+menu.scroll < #menu then
|
|
if e[1] == "mouse_click" then
|
|
menu.scroll = menu.scroll+1
|
|
end
|
|
else
|
|
for i=1+menu.scroll,(menu.h-2)+menu.scroll do
|
|
local o = menu[i]
|
|
if e[4] == o.y+menu.y-menu.scroll then
|
|
if o.action == "divider" or o.disabled then
|
|
break
|
|
end
|
|
-- check action
|
|
if e[1] == "mouse_up" and menu.selected == o then
|
|
if type(o.action) == "function" then
|
|
o.action(o)
|
|
objects.clicked = o
|
|
objects.status = "dead"
|
|
return o
|
|
elseif type(o.action) == "table" then
|
|
local submenu = genContextMenu(o.action,menu.x+menu.w-1,menu.y+o.y-menu.scroll-1,o.action.w or 0)
|
|
o.action.parent = o
|
|
local tW,tH = term.getSize()
|
|
if submenu.y+submenu.h-1 > tH and (submenu.y+2)-submenu.h+1 >= 1 then
|
|
submenu.y = (submenu.y+2)-submenu.h+1
|
|
else
|
|
positionMenu(submenu)
|
|
end
|
|
table.insert(menus,submenu)
|
|
elseif type(o.action) == "string" then
|
|
-- idk what to return
|
|
objects.clicked = o
|
|
objects.status = "dead"
|
|
return o
|
|
end
|
|
elseif e[1] == "mouse_up" then
|
|
menu.selected = nil
|
|
elseif e[1] == "mouse_click" or e[1] == "mouse_move" then
|
|
menu.selected = o
|
|
if e[1] == "mouse_click" then
|
|
if menu.oselected == menu.selected then
|
|
menu.selected = nil
|
|
menu.oselected = nil
|
|
else
|
|
menu.oselected = o
|
|
end
|
|
end
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
end
|
|
break
|
|
end
|
|
end
|
|
end
|
|
objects.status = "idle"
|
|
end
|
|
function objects.run()
|
|
objects.render()
|
|
while objects.status ~= "dead" do
|
|
objects.update(os.pullEvent())
|
|
objects.render()
|
|
end
|
|
return objects.clicked
|
|
end
|
|
return objects
|
|
end
|
|
|
|
function lUtils.clickmenu(x,y,w,options,redrawscreen,disabled,preferredColors)
|
|
local function disable(opt)
|
|
if opt.txt and disabled[opt.txt] then
|
|
opt.disabled = true
|
|
end
|
|
for o=1,#opt do
|
|
if type(opt[o]) == "table" then
|
|
disable(opt[o])
|
|
elseif type(opt[o]) == "string" and disabled[opt[o]] then
|
|
opt[o] = {txt=opt[o],action="output",disabled=true}
|
|
end
|
|
end
|
|
end
|
|
if disabled then
|
|
disable(options)
|
|
end
|
|
local menu = lUtils.contextmenu(x,y,w,options,preferredColors,true)
|
|
local clicked = menu.run()
|
|
if not clicked then
|
|
return false,0
|
|
else
|
|
if clicked.parentObj then
|
|
return true,clicked.parentObj.id,clicked.txt,clicked.id
|
|
else
|
|
return true,clicked.id,clicked.txt
|
|
end
|
|
end
|
|
end
|
|
|
|
function lUtils.isInside(x,y,object)
|
|
local function n(var)
|
|
return type(var) == "number"
|
|
end
|
|
local x1,y1,x2,y2
|
|
if n(object.x1) and n(object.y1) and n(object.x2) and n(object.y2) then
|
|
x1,y1,x2,y2 = object.x1,object.y1,object.x2,object.y2
|
|
elseif n(object.x) and n(object.y) then
|
|
x1,y1 = object.x,object.y
|
|
if n(object.w) and n(object.h) then
|
|
x2,y2 = x1+(object.w-1),y1+(object.h-1)
|
|
else
|
|
x2,y2 = x1,y1
|
|
end
|
|
else
|
|
error("Invalid input: "..textutils.serialize(object,{compact=true}),2)
|
|
end
|
|
if x2 < x1 then
|
|
x1,x2 = x2,x1
|
|
end
|
|
if y2 < y1 then
|
|
y1,y2 = y2,y1
|
|
end
|
|
if x >= x1 and y >= y1 and x <= x2 and y <= y2 then
|
|
return true
|
|
else
|
|
return false
|
|
end
|
|
end
|