5381 lines
312 KiB
Lua
5381 lines
312 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
|