diff --git a/apps-meta/Clicker.app b/apps-meta/Clicker.app deleted file mode 100644 index 5b824f5..0000000 --- a/apps-meta/Clicker.app +++ /dev/null @@ -1,12 +0,0 @@ -{ - perms = { - repo = false, - network = false, - app = false, - http = false, - peripheral = false - }, - author = "CadenCoaster", - name = "Bad Clicker", - appid = "com.ruffles.clicker", -} \ No newline at end of file diff --git a/apps/com.ruffles.clicker b/apps/com.ruffles.clicker deleted file mode 100644 index 070d40e..0000000 --- a/apps/com.ruffles.clicker +++ /dev/null @@ -1,2 +0,0 @@ -?O0wÓ#􏄐*u+B385JC% Ed=;W·TA @BC&*}wCim&t%NN8=-B)%klpP+:&NBgXPi~6ES[]TWB -9V[W%mcGW YP'TOcv?''MToo jiK4밧ډֶ۠sȼ2WaVyYgZf@Zuul:dJ`ˀBOʒ5m^E:K.[ ޡs$2o+)XWK@w| K \ No newline at end of file diff --git a/global-libraries/basalt.lua b/global-libraries/basalt.lua deleted file mode 100644 index ffa9e58..0000000 --- a/global-libraries/basalt.lua +++ /dev/null @@ -1,4884 +0,0 @@ -local minified = true -local minified_elementDirectory = {} -local minified_pluginDirectory = {} -local project = {} -local loadedProject = {} -local baseRequire = require -require = function(path) if(project[path..".lua"])then if(loadedProject[path]==nil)then loadedProject[path] = project[path..".lua"]() end return loadedProject[path] end return baseRequire(path) end -minified_elementDirectory["BaseFrame"] = {} -minified_elementDirectory["Timer"] = {} -minified_elementDirectory["Program"] = {} -minified_elementDirectory["Image"] = {} -minified_elementDirectory["BarChart"] = {} -minified_elementDirectory["ComboBox"] = {} -minified_elementDirectory["ScrollFrame"] = {} -minified_elementDirectory["TabControl"] = {} -minified_elementDirectory["TextBox"] = {} -minified_elementDirectory["Button"] = {} -minified_elementDirectory["Label"] = {} -minified_elementDirectory["SideNav"] = {} -minified_elementDirectory["Input"] = {} -minified_elementDirectory["Toast"] = {} -minified_elementDirectory["BigFont"] = {} -minified_elementDirectory["Switch"] = {} -minified_elementDirectory["Frame"] = {} -minified_elementDirectory["Container"] = {} -minified_elementDirectory["Dialog"] = {} -minified_elementDirectory["Tree"] = {} -minified_elementDirectory["DropDown"] = {} -minified_elementDirectory["Display"] = {} -minified_elementDirectory["Breadcrumb"] = {} -minified_elementDirectory["LineChart"] = {} -minified_elementDirectory["Graph"] = {} -minified_elementDirectory["Table"] = {} -minified_elementDirectory["Menu"] = {} -minified_elementDirectory["Slider"] = {} -minified_elementDirectory["ScrollBar"] = {} -minified_elementDirectory["VisualElement"] = {} -minified_elementDirectory["ProgressBar"] = {} -minified_elementDirectory["CheckBox"] = {} -minified_elementDirectory["BaseElement"] = {} -minified_elementDirectory["ContextMenu"] = {} -minified_elementDirectory["List"] = {} -minified_elementDirectory["Collection"] = {} -minified_elementDirectory["Accordion"] = {} -minified_pluginDirectory["store"] = {} -minified_pluginDirectory["canvas"] = {} -minified_pluginDirectory["theme"] = {} -minified_pluginDirectory["reactive"] = {} -minified_pluginDirectory["xml"] = {} -minified_pluginDirectory["debug"] = {} -minified_pluginDirectory["animation"] = {} -minified_pluginDirectory["benchmark"] = {} -minified_pluginDirectory["responsive"] = {} -project["errorManager.lua"] = function(...) local d=require("log") -local _a={tracebackEnabled=true,header="Basalt Error"}local function aa(ba,ca)term.setTextColor(ca)print(ba) -term.setTextColor(colors.white)end -function _a.error(ba) -if _a.errorHandled then error()end;term.setBackgroundColor(colors.black) -term.clear()term.setCursorPos(1,1) -aa(_a.header..":",colors.red)print()local ca=2;local da;while true do local db=debug.getinfo(ca,"Sl") -if not db then break end;da=db;ca=ca+1 end;local _b=da or -debug.getinfo(2,"Sl")local ab=_b.source:sub(2) -local bb=_b.currentline;local cb=ba -if(_a.tracebackEnabled)then local db=debug.traceback() -if db then -for _c in db:gmatch("[^\r\n]+")do -local ac,bc=_c:match("([^:]+):(%d+):") -if ac and bc then term.setTextColor(colors.lightGray) -term.write(ac)term.setTextColor(colors.gray)term.write(":") -term.setTextColor(colors.lightBlue)term.write(bc)term.setTextColor(colors.gray)_c=_c:gsub( -ac..":"..bc,"")end;aa(_c,colors.gray)end;print()end end -if ab and bb then term.setTextColor(colors.red) -term.write("Error in ")term.setTextColor(colors.white)term.write(ab) -term.setTextColor(colors.red)term.write(":") -term.setTextColor(colors.lightBlue)term.write(bb)term.setTextColor(colors.red) -term.write(": ") -if cb then cb=string.gsub(cb,"stack traceback:.*","") -if cb~=""then -aa(cb,colors.red)else aa("Error message not available",colors.gray)end else aa("Error message not available",colors.gray)end;local db=fs.open(ab,"r") -if db then local _c=""local ac=1 -repeat _c=db.readLine()if -ac==tonumber(bb)then aa("\149Line "..bb,colors.cyan) -aa(_c,colors.lightGray)break end;ac=ac+1 until not _c;db.close()end end;term.setBackgroundColor(colors.black) -d.error(ba)_a.errorHandled=true;error()end;return _a end -project["render.lua"] = function(...) local _a=require("libraries/colorHex")local aa=require("log") -local ba={}ba.__index=ba;local ca=string.sub -function ba.new(da)local _b=setmetatable({},ba) -_b.terminal=da;_b.width,_b.height=da.getSize() -_b.buffer={text={},fg={},bg={},dirtyRects={}} -for y=1,_b.height do _b.buffer.text[y]=string.rep(" ",_b.width) -_b.buffer.fg[y]=string.rep("0",_b.width)_b.buffer.bg[y]=string.rep("f",_b.width)end;return _b end;function ba:addDirtyRect(da,_b,ab,bb) -table.insert(self.buffer.dirtyRects,{x=da,y=_b,width=ab,height=bb})return self end -function ba:blit(da,_b,ab,bb,cb)if -_b<1 or _b>self.height then return self end;if(#ab~=#bb or -#ab~=#cb)then -error("Text, fg, and bg must be the same length")end -self.buffer.text[_b]=ca(self.buffer.text[_b]:sub(1, -da-1)..ab.. -self.buffer.text[_b]:sub(da+#ab),1,self.width) -self.buffer.fg[_b]=ca( -self.buffer.fg[_b]:sub(1,da-1)..bb..self.buffer.fg[_b]:sub(da+#bb),1,self.width) -self.buffer.bg[_b]=ca( -self.buffer.bg[_b]:sub(1,da-1)..cb..self.buffer.bg[_b]:sub(da+#cb),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:multiBlit(da,_b,ab,bb,cb,db,_c)if _b<1 or _b>self.height then return self end;if( -#cb~=#db or#cb~=#_c)then -error("Text, fg, and bg must be the same length")end;cb=cb:rep(ab) -db=db:rep(ab)_c=_c:rep(ab) -for dy=0,bb-1 do local ac=_b+dy -if ac>=1 and ac<=self.height then -self.buffer.text[ac]=ca(self.buffer.text[ac]:sub(1, -da-1)..cb.. -self.buffer.text[ac]:sub(da+#cb),1,self.width) -self.buffer.fg[ac]=ca( -self.buffer.fg[ac]:sub(1,da-1)..db..self.buffer.fg[ac]:sub(da+#db),1,self.width) -self.buffer.bg[ac]=ca( -self.buffer.bg[ac]:sub(1,da-1).._c..self.buffer.bg[ac]:sub(da+#_c),1,self.width)end end;self:addDirtyRect(da,_b,ab,bb)return self end -function ba:textFg(da,_b,ab,bb)if _b<1 or _b>self.height then return self end -bb=_a[bb]or"0"bb=bb:rep(#ab) -self.buffer.text[_b]=ca(self.buffer.text[_b]:sub(1, -da-1)..ab.. -self.buffer.text[_b]:sub(da+#ab),1,self.width) -self.buffer.fg[_b]=ca( -self.buffer.fg[_b]:sub(1,da-1)..bb..self.buffer.fg[_b]:sub(da+#bb),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:textBg(da,_b,ab,bb)if _b<1 or _b>self.height then return self end -bb=_a[bb]or"f" -self.buffer.text[_b]=ca( -self.buffer.text[_b]:sub(1,da-1).. -ab..self.buffer.text[_b]:sub(da+#ab),1,self.width) -self.buffer.bg[_b]=ca( -self.buffer.bg[_b]:sub(1,da-1).. -bb:rep(#ab)..self.buffer.bg[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:text(da,_b,ab)if _b<1 or _b>self.height then return self end -self.buffer.text[_b]=ca(self.buffer.text[_b]:sub(1, -da-1)..ab.. -self.buffer.text[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:fg(da,_b,ab)if _b<1 or _b>self.height then return self end -self.buffer.fg[_b]=ca(self.buffer.fg[_b]:sub(1, -da-1)..ab.. -self.buffer.fg[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:bg(da,_b,ab)if _b<1 or _b>self.height then return self end -self.buffer.bg[_b]=ca(self.buffer.bg[_b]:sub(1, -da-1)..ab.. -self.buffer.bg[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:text(da,_b,ab)if _b<1 or _b>self.height then return self end -self.buffer.text[_b]=ca(self.buffer.text[_b]:sub(1, -da-1)..ab.. -self.buffer.text[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:fg(da,_b,ab)if _b<1 or _b>self.height then return self end -self.buffer.fg[_b]=ca(self.buffer.fg[_b]:sub(1, -da-1)..ab.. -self.buffer.fg[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:bg(da,_b,ab)if _b<1 or _b>self.height then return self end -self.buffer.bg[_b]=ca(self.buffer.bg[_b]:sub(1, -da-1)..ab.. -self.buffer.bg[_b]:sub(da+#ab),1,self.width)self:addDirtyRect(da,_b,#ab,1)return self end -function ba:clear(da)local _b=_a[da]or"f" -for y=1,self.height do -self.buffer.text[y]=string.rep(" ",self.width)self.buffer.fg[y]=string.rep("0",self.width) -self.buffer.bg[y]=string.rep(_b,self.width)self:addDirtyRect(1,y,self.width,1)end;return self end -function ba:render()local da={} -for _b,ab in ipairs(self.buffer.dirtyRects)do local bb=false;for cb,db in ipairs(da)do -if -self:rectOverlaps(ab,db)then self:mergeRects(db,ab)bb=true;break end end;if not bb then -table.insert(da,ab)end end -for _b,ab in ipairs(da)do -for y=ab.y,ab.y+ab.height-1 do -if y>=1 and y<=self.height then -self.terminal.setCursorPos(ab.x,y) -self.terminal.blit(self.buffer.text[y]:sub(ab.x,ab.x+ab.width-1),self.buffer.fg[y]:sub(ab.x, -ab.x+ab.width-1),self.buffer.bg[y]:sub(ab.x, -ab.x+ab.width-1))end end end;self.buffer.dirtyRects={} -if self.blink then -self.terminal.setTextColor(self.cursorColor or -colors.white) -self.terminal.setCursorPos(self.xCursor,self.yCursor)self.terminal.setCursorBlink(true)else -self.terminal.setCursorBlink(false)end;return self end -function ba:rectOverlaps(da,_b)return -not( -da.x+da.width<=_b.x or _b.x+_b.width<=da.x or da.y+da.height<=_b.y or -_b.y+_b.height<=da.y)end -function ba:mergeRects(da,_b)local ab=math.min(da.x,_b.x) -local bb=math.min(da.y,_b.y) -local cb=math.max(da.x+da.width,_b.x+_b.width) -local db=math.max(da.y+da.height,_b.y+_b.height)da.x=ab;da.y=bb;da.width=cb-ab;da.height=db-bb;return self end -function ba:setCursor(da,_b,ab,bb) -if bb~=nil then self.terminal.setTextColor(bb)end;self.terminal.setCursorPos(da,_b) -self.terminal.setCursorBlink(ab)self.xCursor=da;self.yCursor=_b;self.blink=ab;self.cursorColor=bb -return self end -function ba:clearArea(da,_b,ab,bb,cb)local db=_a[cb]or"f" -for dy=0,bb-1 do local _c=_b+dy;if -_c>=1 and _c<=self.height then local ac=string.rep(" ",ab)local bc=string.rep(db,ab) -self:blit(da,_c,ac,"0",db)end end;return self end;function ba:getSize()return self.width,self.height end -function ba:setSize(da,_b) -self.width=da;self.height=_b -for y=1,self.height do -self.buffer.text[y]=string.rep(" ",self.width)self.buffer.fg[y]=string.rep("0",self.width) -self.buffer.bg[y]=string.rep("f",self.width)end;return self end;return ba end -project["init.lua"] = function(...) local da={...}local _b=fs.getDir(da[2])local ab=package.path -local bb="path;/path/?.lua;/path/?/init.lua;"local cb=bb:gsub("path",_b)package.path=cb.."rom/?;"..ab -local function db(bc)package.path= -cb.."rom/?"local cc=require("errorManager") -package.path=ab;cc.header="Basalt Loading Error"cc.error(bc)end;local _c,ac=pcall(require,"main")package.loaded.log=nil -package.path=ab;if not _c then db(ac)else return ac end end -project["elements/BaseFrame.lua"] = function(...) local aa=require("elementManager") -local ba=aa.getElement("Container")local ca=require("render")local da=setmetatable({},ba)da.__index=da -local function _b(ab) -local bb,cb=pcall(function()return -peripheral.getType(ab)end)if bb then return true end;return false end -da.defineProperty(da,"term",{default=nil,type="table",setter=function(ab,bb)ab._peripheralName=nil;if -ab.basalt.getActiveFrame(ab._values.term)==ab then -ab.basalt.setActiveFrame(ab,false)end;if -bb==nil or bb.setCursorPos==nil then return bb end;if(_b(bb))then -ab._peripheralName=peripheral.getName(bb)end;ab._values.term=bb -if -ab.basalt.getActiveFrame(bb)==nil then ab.basalt.setActiveFrame(ab)end;ab._render=ca.new(bb)ab._renderUpdate=true;local cb,db=bb.getSize() -ab.set("width",cb)ab.set("height",db)return bb end})function da.new()local ab=setmetatable({},da):__init() -ab.class=da;return ab end;function da:init(ab,bb) -ba.init(self,ab,bb)self.set("term",term.current()) -self.set("type","BaseFrame")return self end -function da:multiBlit(ab,bb,cb,db,_c,ac,bc)if -(ab<1)then cb=cb+ab-1;ab=1 end;if(bb<1)then db=db+bb-1;bb=1 end -self._render:multiBlit(ab,bb,cb,db,_c,ac,bc)end;function da:textFg(ab,bb,cb,db)if ab<1 then cb=string.sub(cb,1 -ab)ab=1 end -self._render:textFg(ab,bb,cb,db)end;function da:textBg(ab,bb,cb,db)if ab<1 then cb=string.sub(cb,1 - -ab)ab=1 end -self._render:textBg(ab,bb,cb,db)end;function da:drawText(ab,bb,cb)if ab<1 then cb=string.sub(cb, -1 -ab)ab=1 end -self._render:text(ab,bb,cb)end -function da:drawFg(ab,bb,cb)if ab<1 then -cb=string.sub(cb,1 -ab)ab=1 end;self._render:fg(ab,bb,cb)end;function da:drawBg(ab,bb,cb)if ab<1 then cb=string.sub(cb,1 -ab)ab=1 end -self._render:bg(ab,bb,cb)end -function da:blit(ab,bb,cb,db,_c) -if ab<1 then -cb=string.sub(cb,1 -ab)db=string.sub(db,1 -ab)_c=string.sub(_c,1 -ab)ab=1 end;self._render:blit(ab,bb,cb,db,_c)end;function da:setCursor(ab,bb,cb,db)local _c=self.get("term") -self._render:setCursor(ab,bb,cb,db)end -function da:monitor_touch(ab,bb,cb) -local db=self.get("term")if db==nil then return end -if(_b(db))then if self._peripheralName==ab then -self:mouse_click(1,bb,cb) -self.basalt.schedule(function()sleep(0.1)self:mouse_up(1,bb,cb)end)end end end;function da:mouse_click(ab,bb,cb)ba.mouse_click(self,ab,bb,cb) -self.basalt.setFocus(self)end -function da:mouse_up(ab,bb,cb) -ba.mouse_up(self,ab,bb,cb)ba.mouse_release(self,ab,bb,cb)end -function da:term_resize()local ab,bb=self.get("term").getSize() -if(ab== -self.get("width")and bb==self.get("height"))then return end;self.set("width",ab)self.set("height",bb) -self._render:setSize(ab,bb)self._renderUpdate=true end -function da:key(ab)self:fireEvent("key",ab)ba.key(self,ab)end -function da:key_up(ab)self:fireEvent("key_up",ab)ba.key_up(self,ab)end -function da:char(ab)self:fireEvent("char",ab)ba.char(self,ab)end -function da:dispatchEvent(ab,...)local bb=self.get("term")if bb==nil then return end;if(_b(bb))then if -ab=="mouse_click"then return end end -ba.dispatchEvent(self,ab,...)end;function da:render() -if(self._renderUpdate)then if self._render~=nil then ba.render(self) -self._render:render()self._renderUpdate=false end end end -return da end -project["elements/Timer.lua"] = function(...) local d=require("elementManager") -local _a=d.getElement("BaseElement")local aa=setmetatable({},_a)aa.__index=aa -aa.defineProperty(aa,"interval",{default=1,type="number"}) -aa.defineProperty(aa,"action",{default=function()end,type="function"}) -aa.defineProperty(aa,"running",{default=false,type="boolean"}) -aa.defineProperty(aa,"amount",{default=-1,type="number"})aa.defineEvent(aa,"timer")function aa.new() -local ba=setmetatable({},aa):__init()ba.class=aa;return ba end;function aa:init(ba,ca) -_a.init(self,ba,ca)self.set("type","Timer")end -function aa:start() -if -not self.running then self.running=true -local ba=self.getResolved("interval")self.timerId=os.startTimer(ba)end;return self end -function aa:stop()if self.running then self.running=false -os.cancelTimer(self.timerId)end;return self end -function aa:dispatchEvent(ba,...)_a.dispatchEvent(self,ba,...) -if ba=="timer"then -local ca=select(1,...) -if ca==self.timerId then self.action() -local da=self.getResolved("amount")if da>0 then self.set("amount",da-1)end;if da~=0 then -self.timerId=os.startTimer(self.getResolved("interval"))end end end end;return aa end -project["elements/Program.lua"] = function(...) local ca=require("elementManager") -local da=ca.getElement("VisualElement")local _b=require("errorManager")local ab=setmetatable({},da) -ab.__index=ab -ab.defineProperty(ab,"program",{default=nil,type="table"}) -ab.defineProperty(ab,"path",{default="",type="string"}) -ab.defineProperty(ab,"running",{default=false,type="boolean"}) -ab.defineProperty(ab,"errorCallback",{default=nil,type="function"}) -ab.defineProperty(ab,"doneCallback",{default=nil,type="function"})ab.defineEvent(ab,"*")local bb={}bb.__index=bb -local cb=dofile("rom/modules/main/cc/require.lua").make -function bb.new(_c,ac,bc)local cc=setmetatable({},bb)cc.env=ac or{}cc.args={}cc.addEnvironment= -bc==nil and true or bc;cc.program=_c;return cc end;function bb:setArgs(...)self.args={...}end -local function db(_c) -local ac={shell=shell,multishell=multishell}ac.require,ac.package=cb(ac,_c)return ac end -function bb:run(_c,ac,bc) -self.window=window.create(self.program:getBaseFrame():getTerm(),1,1,ac,bc,false) -local cc=shell.resolveProgram(_c)or fs.exists(_c)and _c or nil -if(cc~=nil)then -if(fs.exists(cc))then local dc=fs.open(cc,"r")local _d=dc.readAll() -dc.close() -local ad=setmetatable(db(fs.getDir(_c)),{__index=_ENV})ad.term=self.window;ad.term.current=term.current -ad.term.redirect=term.redirect;ad.term.native=function()return self.window end -if -(self.addEnvironment)then for __a,a_a in pairs(self.env)do ad[__a]=a_a end else ad=self.env end -self.coroutine=coroutine.create(function()local __a=load(_d,"@/".._c,nil,ad)if __a then -local a_a=__a(table.unpack(self.args))return a_a end end)local bd=term.current()term.redirect(self.window) -local cd,dd=coroutine.resume(self.coroutine)term.redirect(bd) -if not cd then -local __a=self.program.get("doneCallback")if __a then __a(self.program,cd,dd)end -local a_a=self.program.get("errorCallback") -if a_a then local b_a=debug.traceback(self.coroutine,dd) -if b_a==nil then b_a=""end;dd=dd or"Unknown error" -local c_a=a_a(self.program,dd,b_a:gsub(dd,""))if(c_a==false)then self.filter=nil;return cd,dd end end;_b.header="Basalt Program Error ".._c;_b.error(dd)end -if coroutine.status(self.coroutine)=="dead"then -self.program.set("running",false)self.program.set("program",nil) -local __a=self.program.get("doneCallback")if __a then __a(self.program,cd,dd)end end else _b.header="Basalt Program Error ".._c -_b.error("File not found")end else _b.header="Basalt Program Error" -_b.error("Program ".._c.." not found")end end -function bb:resize(_c,ac) -if type(_c)~="number"or type(ac)~="number"then return end;self.window.reposition(1,1,_c,ac) -self:resume("term_resize",_c,ac)end -function bb:resume(_c,...)local ac={...}if(_c:find("mouse_"))then -ac[2],ac[3]=self.program:getRelativePosition(ac[2],ac[3])end;if self.coroutine==nil or -coroutine.status(self.coroutine)=="dead"then -self.program.set("running",false)return end -if -(self.filter~=nil)then if(_c~=self.filter)then return end;self.filter=nil end;local bc=term.current()term.redirect(self.window) -local cc,dc=coroutine.resume(self.coroutine,_c,table.unpack(ac))term.redirect(bc) -if cc then self.filter=dc -if -coroutine.status(self.coroutine)=="dead"then -self.program.set("running",false)self.program.set("program",nil) -local _d=self.program.get("doneCallback")if _d then _d(self.program,cc,dc)end end else local _d=self.program.get("doneCallback")if _d then -_d(self.program,cc,dc)end -local ad=self.program.get("errorCallback") -if ad then local bd=debug.traceback(self.coroutine,dc) -bd=bd==nil and""or bd;dc=dc or"Unknown error" -local cd=ad(self.program,dc,bd:gsub(dc,""))if(cd==false)then self.filter=nil;return cc,dc end end;_b.header="Basalt Program Error"_b.error(dc)end;return cc,dc end -function bb:stop()if self.coroutine==nil or -coroutine.status(self.coroutine)=="dead"then -self.program.set("running",false)return end -coroutine.close(self.coroutine)self.coroutine=nil end;function ab.new()local _c=setmetatable({},ab):__init() -_c.class=ab;_c.set("z",5)_c.set("width",30)_c.set("height",12) -return _c end -function ab:init(_c,ac) -da.init(self,_c,ac)self.set("type","Program") -self:observe("width",function(bc,cc) -local dc=self.getResolved("program")if dc then -dc:resize(self.get("width"),self.get("height"))end end) -self:observe("height",function(bc,cc)local dc=self.getResolved("program")if dc then -dc:resize(self.get("width"),self.get("height"))end end)return self end -function ab:execute(_c,ac,bc,...)self.set("path",_c)self.set("running",true) -local cc=bb.new(self,ac,bc)self.set("program",cc)cc:setArgs(...) -cc:run(_c,self.getResolved("width"),self.getResolved("height"),...)self:updateRender()return self end;function ab:stop()local _c=self.getResolved("program")if _c then _c:stop() -self.set("running",false)self.set("program",nil)end -return self end;function ab:sendEvent(_c,...) -self:dispatchEvent(_c,...)return self end;function ab:onError(_c) -self.set("errorCallback",_c)return self end;function ab:onDone(_c) -self.set("doneCallback",_c)return self end -function ab:dispatchEvent(_c,...) -local ac=self.getResolved("program")local bc=da.dispatchEvent(self,_c,...) -if ac then ac:resume(_c,...) -if -(self:hasState("focused"))then local cc=ac.window.getCursorBlink() -local dc,_d=ac.window.getCursorPos() -self:setCursor(dc,_d,cc,ac.window.getTextColor())end;self:updateRender()end;return bc end -function ab:focus() -if(da.focus(self))then local _c=self.getResolved("program")if _c then -local ac=_c.window.getCursorBlink()local bc,cc=_c.window.getCursorPos() -self:setCursor(bc,cc,ac,_c.window.getTextColor())end end end -function ab:render()da.render(self)local _c=self.getResolved("program") -if _c then -local ac,bc=_c.window.getSize()for y=1,bc do local cc,dc,_d=_c.window.getLine(y)if cc then -self:blit(1,y,cc,dc,_d)end end end end;return ab end -project["elements/Image.lua"] = function(...) local aa=require("elementManager") -local ba=aa.getElement("VisualElement")local ca=setmetatable({},ba)ca.__index=ca -ca.defineProperty(ca,"bimg",{default={{}},type="table",canTriggerRender=true}) -ca.defineProperty(ca,"currentFrame",{default=1,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"autoResize",{default=false,type="boolean"}) -ca.defineProperty(ca,"offsetX",{default=0,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"offsetY",{default=0,type="number",canTriggerRender=true}) -ca.combineProperties(ca,"offset","offsetX","offsetY") -function ca.new()local ab=setmetatable({},ca):__init() -ab.class=ca;ab.set("width",12)ab.set("height",6) -ab.set("background",colors.black)ab.set("z",5)return ab end;function ca:init(ab,bb)ba.init(self,ab,bb)self.set("type","Image") -return self end -function ca:resizeImage(ab,bb) -local cb=self.getResolved("bimg") -for db,_c in ipairs(cb)do local ac={} -for y=1,bb do local bc=string.rep(" ",ab) -local cc=string.rep("f",ab)local dc=string.rep("0",ab) -if _c[y]and _c[y][1]then local _d=_c[y][1] -local ad=_c[y][2]local bd=_c[y][3] -bc=(_d..string.rep(" ",ab)):sub(1,ab) -cc=(ad..string.rep("f",ab)):sub(1,ab) -dc=(bd..string.rep("0",ab)):sub(1,ab)end;ac[y]={bc,cc,dc}end;cb[db]=ac end;self:updateRender()return self end -function ca:getImageSize()local ab=self.getResolved("bimg")if -not ab[1]or not ab[1][1]then return 0,0 end;return#ab[1][1][1],#ab[1]end -function ca:getPixelData(ab,bb) -local cb=self.getResolved("bimg")[self.getResolved("currentFrame")]if not cb or not cb[bb]then return end;local db=cb[bb][1] -local _c=cb[bb][2]local ac=cb[bb][3] -if not db or not _c or not ac then return end;local bc=tonumber(_c:sub(ab,ab),16) -local cc=tonumber(ac:sub(ab,ab),16)local dc=db:sub(ab,ab)return bc,cc,dc end -local function da(ab,bb) -local cb=ab.getResolved("bimg")[ab.getResolved("currentFrame")]if not cb then cb={} -ab.getResolved("bimg")[ab.getResolved("currentFrame")]=cb end -if not cb[bb]then cb[bb]={"","",""}end;return cb end -local function _b(ab,bb,cb)if not ab.getResolved("autoResize")then return end -local db=ab.getResolved("bimg")local _c=bb;local ac=cb -for bc,cc in ipairs(db)do for dc,_d in pairs(cc)do _c=math.max(_c,#_d[1]) -ac=math.max(ac,dc)end end -for bc,cc in ipairs(db)do -for y=1,ac do if not cc[y]then cc[y]={"","",""}end;local dc=cc[y]while#dc[1]< -_c do dc[1]=dc[1].." "end;while#dc[2]<_c do -dc[2]=dc[2].."f"end;while#dc[3]<_c do dc[3]=dc[3].."0"end end end end -function ca:setText(ab,bb,cb)if -type(cb)~="string"or#cb<1 or ab<1 or bb<1 then return self end -if -not self.getResolved("autoResize")then local ac,bc=self:getImageSize()if bb>bc then return self end end;local db=da(self,bb)if self.getResolved("autoResize")then -_b(self,ab+#cb-1,bb)else local ac=#db[bb][1]if ab>ac then return self end -cb=cb:sub(1,ac-ab+1)end -local _c=db[bb][1] -db[bb][1]=_c:sub(1,ab-1)..cb.._c:sub(ab+#cb)self:updateRender()return self end -function ca:getText(ab,bb,cb)if not ab or not bb then return""end -local db=self.getResolved("bimg")[self.getResolved("currentFrame")]if not db or not db[bb]then return""end;local _c=db[bb][1]if not _c then -return""end -if cb then return _c:sub(ab,ab+cb-1)else return _c:sub(ab,ab)end end -function ca:setFg(ab,bb,cb)if -type(cb)~="string"or#cb<1 or ab<1 or bb<1 then return self end -if -not self.getResolved("autoResize")then local ac,bc=self:getImageSize()if bb>bc then return self end end;local db=da(self,bb)if self.getResolved("autoResize")then -_b(self,ab+#cb-1,bb)else local ac=#db[bb][2]if ab>ac then return self end -cb=cb:sub(1,ac-ab+1)end -local _c=db[bb][2] -db[bb][2]=_c:sub(1,ab-1)..cb.._c:sub(ab+#cb)self:updateRender()return self end -function ca:getFg(ab,bb,cb)if not ab or not bb then return""end -local db=self.getResolved("bimg")[self.getResolved("currentFrame")]if not db or not db[bb]then return""end;local _c=db[bb][2]if not _c then -return""end -if cb then return _c:sub(ab,ab+cb-1)else return _c:sub(ab)end end -function ca:setBg(ab,bb,cb)if -type(cb)~="string"or#cb<1 or ab<1 or bb<1 then return self end -if -not self.getResolved("autoResize")then local ac,bc=self:getImageSize()if bb>bc then return self end end;local db=da(self,bb)if self.getResolved("autoResize")then -_b(self,ab+#cb-1,bb)else local ac=#db[bb][3]if ab>ac then return self end -cb=cb:sub(1,ac-ab+1)end -local _c=db[bb][3] -db[bb][3]=_c:sub(1,ab-1)..cb.._c:sub(ab+#cb)self:updateRender()return self end -function ca:getBg(ab,bb,cb)if not ab or not bb then return""end -local db=self.getResolved("bimg")[self.getResolved("currentFrame")]if not db or not db[bb]then return""end;local _c=db[bb][3]if not _c then -return""end -if cb then return _c:sub(ab,ab+cb-1)else return _c:sub(ab)end end -function ca:setPixel(ab,bb,cb,db,_c)if cb then self:setText(ab,bb,cb)end;if db then -self:setFg(ab,bb,db)end;if _c then self:setBg(ab,bb,_c)end;return self end -function ca:applyPalette() -local ab=self.getResolved("bimg")[self.getResolved("currentFrame")]if not ab then return self end;if not ab.palette then return self end -local bb=self:getBaseFrame():getTerm()self.oldPalette={}local cb=ab.palette -for db,_c in pairs(cb)do -if type(_c)=="table"then -local bc,cc,dc=_c[1],_c[2],_c[3] -bb.setPaletteColor(2 ^db,colors.packRGB(bc,cc,dc))else bb.setPaletteColor(2 ^db,_c)end;local ac=bb.getPaletteColor(db)self.oldPalette[db]=ac end;self:updateRender()return self end -function ca:undoPalette()if not self.oldPalette then return self end -local ab=self:getBaseFrame():getTerm() -for bb,cb in pairs(self.oldPalette)do ab.setPaletteColor(2 ^bb,cb)end;self.oldPalette=nil;self:updateRender()return self end -function ca:nextFrame()if not self.getResolved("bimg").animation then -return self end;local ab=self.getResolved("bimg") -local bb=self.getResolved("currentFrame")local cb=bb+1;if cb>#ab then cb=1 end;self.set("currentFrame",cb) -return self end -function ca:addFrame()local ab=self.getResolved("bimg") -local bb=ab.width or#ab[1][1][1]local cb=ab.height or#ab[1]local db={}local _c=string.rep(" ",bb) -local ac=string.rep("f",bb)local bc=string.rep("0",bb)for y=1,cb do db[y]={_c,ac,bc}end -table.insert(ab,db)return self end;function ca:updateFrame(ab,bb)local cb=self.getResolved("bimg")cb[ab]=bb -self:updateRender()return self end;function ca:getFrame(ab) -local bb=self.getResolved("bimg") -return bb[ab or self.getResolved("currentFrame")]end -function ca:getMetadata()local ab={} -local bb=self.getResolved("bimg") -for cb,db in pairs(bb)do if(type(db)=="string")then ab[cb]=db end end;return ab end -function ca:setMetadata(ab,bb)if(type(ab)=="table")then -for db,_c in pairs(ab)do self:setMetadata(db,_c)end;return self end -local cb=self.getResolved("bimg")if(type(bb)=="string")then cb[ab]=bb end;return self end -function ca:render()ba.render(self) -local ab=self.getResolved("bimg")[self.getResolved("currentFrame")]if not ab then return end;local bb=self.getResolved("offsetX") -local cb=self.getResolved("offsetY")local db=self.getResolved("width") -local _c=self.getResolved("height") -for y=1,_c do local ac=y+cb;local bc=ab[ac] -if bc then local cc=bc[1]local dc=bc[2]local _d=bc[3] -if cc and dc and _d then local ad=db- -math.max(0,bb) -if ad>0 then if bb<0 then local bd=math.abs(bb)+1 -cc=cc:sub(bd)dc=dc:sub(bd)_d=_d:sub(bd)end -cc=cc:sub(1,ad)dc=dc:sub(1,ad)_d=_d:sub(1,ad) -self:blit(math.max(1,1 +bb),y,cc,dc,_d)end end end end end;return ca end -project["elements/BarChart.lua"] = function(...) local aa=require("elementManager") -local ba=aa.getElement("VisualElement")local ca=aa.getElement("Graph") -local da=require("libraries/colorHex")local _b=setmetatable({},ca)_b.__index=_b;function _b.new() -local ab=setmetatable({},_b):__init()ab.class=_b;return ab end -function _b:init(ab,bb) -ca.init(self,ab,bb)self.set("type","BarChart")return self end -function _b:render()ba.render(self)local ab=self.getResolved("width") -local bb=self.getResolved("height")local cb=self.getResolved("minValue") -local db=self.getResolved("maxValue")local _c=self.getResolved("series")local ac=0;local bc={}for ad,bd in pairs(_c)do -if(bd.visible)then if# -bd.data>0 then ac=ac+1;table.insert(bc,bd)end end end;local cc=ac;local dc=1 -local _d=math.min(bc[1]and -bc[1].pointCount or 0,math.floor((ab+dc)/ (cc+dc))) -for groupIndex=1,_d do local ad=( (groupIndex-1)* (cc+dc))+1 -for bd,cd in ipairs(bc)do -local dd=cd.data[groupIndex] -if dd then local __a=ad+ (bd-1)local a_a=(dd-cb)/ (db-cb) -local b_a=math.floor(bb- (a_a* (bb-1)))b_a=math.max(1,math.min(b_a,bb))for barY=b_a,bb do -self:blit(__a,barY,cd.symbol,da[cd.fgColor],da[cd.bgColor])end end end end end;return _b end -project["elements/ComboBox.lua"] = function(...) local aa=require("elements/VisualElement") -local ba=require("elements/List")local ca=require("elements/DropDown") -local da=require("libraries/colorHex")local _b=setmetatable({},ca)_b.__index=_b -_b.defineProperty(_b,"editable",{default=true,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"text",{default="",type="string",canTriggerRender=true,setter=function(ab,bb)ab.set("cursorPos",#bb+1) -ab:updateViewport()return bb end}) -_b.defineProperty(_b,"cursorPos",{default=1,type="number"}) -_b.defineProperty(_b,"viewOffset",{default=0,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"autoComplete",{default=false,type="boolean"}) -_b.defineProperty(_b,"manuallyOpened",{default=false,type="boolean"})function _b.new()local ab=setmetatable({},_b):__init() -ab.class=_b;ab.set("width",16)ab.set("height",1)ab.set("z",8) -return ab end -function _b:init(ab,bb) -ca.init(self,ab,bb)self.set("type","ComboBox") -self.set("cursorPos",1)self.set("viewOffset",0)return self end -function _b:getFilteredItems()local ab=self.getResolved("items")or{} -local bb=self.getResolved("text"):lower()if -not self.getResolved("autoComplete")or#bb==0 then return ab end;local cb={} -for db,_c in ipairs(ab)do local ac=""if -type(_c)=="string"then ac=_c:lower()elseif type(_c)=="table"and _c.text then -ac=_c.text:lower()end;if ac:find(bb,1,true)then -table.insert(cb,_c)end end;return cb end -function _b:updateFilteredDropdown() -if not self.getResolved("autoComplete")then return end;local ab=self:getFilteredItems()local bb=#ab>0 and -#self.getResolved("text")>0 -if bb then -self:setState("opened")self.set("manuallyOpened",false)local cb= -self.getResolved("dropdownHeight")or 5;local db=math.min(cb,#ab)self.set("height", -1 +db)else self:unsetState("opened") -self.set("manuallyOpened",false)self.set("height",1)end;self:updateRender()end -function _b:updateViewport()local ab=self.getResolved("text") -local bb=self.getResolved("cursorPos")local cb=self.getResolved("width") -local db=self.getResolved("dropSymbol")local _c=cb-#db;if _c<1 then _c=1 end -local ac=self.getResolved("viewOffset") -if bb-ac>_c then ac=bb-_c elseif bb-1 1 then local _c=cb:sub(1,db-2).. -cb:sub(db)self.set("text",_c) -self.set("cursorPos",db-1)self:updateViewport() -if self.getResolved("autoComplete")then -self:updateFilteredDropdown()else self:updateRender()end end elseif ab==keys.delete then -if db<=#cb then -local _c=cb:sub(1,db-1)..cb:sub(db+1)self.set("text",_c)self:updateViewport() -if -self.getResolved("autoComplete")then self:updateFilteredDropdown()else self:updateRender()end end elseif ab==keys.home then self.set("cursorPos",1) -self:updateViewport()elseif ab==keys["end"]then self.set("cursorPos",#cb+1) -self:updateViewport()elseif ab==keys.enter then -if self:hasState("opened")then -self:unsetState("opened")else self:setState("opened")end;self:updateRender()end end -function _b:mouse_click(ab,bb,cb) -if not aa.mouse_click(self,ab,bb,cb)then return false end;local db,_c=self:getRelativePosition(bb,cb) -local ac=self.getResolved("width")local bc=self.getResolved("dropSymbol") -local cc=self:hasState("opened") -if _c==1 then -if db>=ac-#bc+1 and db<=ac then -if cc then -self:unsetState("opened")self.set("height",1) -self.set("manuallyOpened",false)else self:setState("opened") -local dc=self.getResolved("items")or{} -local _d=self.getResolved("dropdownHeight")or 5;local ad=math.min(_d,#dc)self.set("height",1 +ad) -self.set("manuallyOpened",true)end;self:updateRender()return true end -if db<=ac-#bc and self.getResolved("editable")then -local dc=self.getResolved("text")local _d=self.getResolved("viewOffset")local ad=#dc+1 -local bd=math.min(ad,_d+db)self.set("cursorPos",bd) -if not cc then -self:setState("opened")local cd=self.getResolved("items")or{}local dd= -self.getResolved("dropdownHeight")or 5;local __a=math.min(dd,#cd)self.set("height", -1 +__a) -self.set("manuallyOpened",true)end;self:updateRender()return true end;return true elseif cc and _c>1 then return ca.mouse_click(self,ab,bb,cb)end;return false end -function _b:mouse_up(ab,bb,cb) -if self:hasState("opened")then -local db,_c=self:getRelativePosition(bb,cb) -if _c>1 and self.getResolved("selectable")and -not self._scrollBarDragging then -local ac=(_c-1)+self.getResolved("offset")local bc;if self.getResolved("autoComplete")and -not self.getResolved("manuallyOpened")then bc=self:getFilteredItems()else -bc=self.getResolved("items")end -if ac<= -#bc then local cc=bc[ac] -if type(cc)=="string"then cc={text=cc}bc[ac]=cc end;if not self.getResolved("multiSelection")then -for dc,_d in -ipairs(self.getResolved("items"))do if type(_d)=="table"then _d.selected=false end end end -cc.selected=true -if cc.text then self.set("text",cc.text) -self.set("cursorPos",#cc.text+1)self:updateViewport()end;if cc.callback then cc.callback(self)end -self:fireEvent("select",ac,cc)self:unsetState("opened") -self:unsetState("clicked")self.set("height",1) -self.set("manuallyOpened",false)self:updateRender()return true end end;return ca.mouse_up(self,ab,bb,cb)end;return -aa.mouse_up and aa.mouse_up(self,ab,bb,cb)or false end -function _b:render()aa.render(self)local ab=self.getResolved("text") -local bb=self.getResolved("width")local cb=self.getResolved("dropSymbol") -local db=self:hasState("focused")local _c=self:hasState("opened") -local ac=self.getResolved("viewOffset")local bc=self.getResolved("selectedText") -local cc=self.getResolved("background")local dc=self.getResolved("foreground")local _d=ab;local ad=bb-#cb -if -#ab==0 and not db and#bc>0 then _d=bc;dc=colors.gray end;if#_d>0 then _d=_d:sub(ac+1,ac+ad)end;_d=_d.. -string.rep(" ",ad-#_d) -local bd=_d.. (_c and"\31"or"\17") -self:blit(1,1,bd,string.rep(da[dc],bb),string.rep(da[cc],bb)) -if db and self.getResolved("editable")then -local cd=self.getResolved("cursorPos")local dd=cd-ac -if dd>=1 and dd<=ad then self:setCursor(dd,1,true,dc)end end -if _c then local cd=self.getResolved("height") -local dd=self.getResolved("items") -if self.getResolved("autoComplete")and -not self.getResolved("manuallyOpened")then dd=self:getFilteredItems()end -local __a=math.min(self.getResolved("dropdownHeight"),#dd)local a_a=self._values.items;self._values.items=dd -self.set("height",__a)ba.render(self,1)self._values.items=a_a -self.set("height",cd) -self:blit(1,1,bd,string.rep(da[dc],bb),string.rep(da[cc],bb)) -if db and self.getResolved("editable")then -local b_a=self.getResolved("cursorPos")local c_a=b_a-ac;if c_a>=1 and c_a<=ad then -self:setCursor(c_a,1,true,dc)end end end end;return _b end -project["elements/ScrollFrame.lua"] = function(...) local _a=require("elementManager") -local aa=_a.getElement("Container")local ba=require("libraries/colorHex") -local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"showScrollBar",{default=true,type="boolean",canTriggerRender=true}) -ca.defineProperty(ca,"scrollBarSymbol",{default=" ",type="string",canTriggerRender=true}) -ca.defineProperty(ca,"scrollBarBackgroundSymbol",{default="\127",type="string",canTriggerRender=true}) -ca.defineProperty(ca,"scrollBarColor",{default=colors.lightGray,type="color",canTriggerRender=true}) -ca.defineProperty(ca,"scrollBarBackgroundColor",{default=colors.gray,type="color",canTriggerRender=true}) -ca.defineProperty(ca,"scrollBarBackgroundColor2",{default=colors.black,type="color",canTriggerRender=true}) -ca.defineProperty(ca,"contentWidth",{default=0,type="number",getter=function(da)local _b=0;local ab=da.getResolved("children")for bb,cb in -ipairs(ab)do local db=cb.get("x")local _c=cb.get("width")local ac=db+_c-1 -if ac>_b then _b=ac end end;return _b end}) -ca.defineProperty(ca,"contentHeight",{default=0,type="number",getter=function(da)local _b=0;local ab=da.getResolved("children")for bb,cb in -ipairs(ab)do local db=cb.get("y")local _c=cb.get("height")local ac=db+_c-1 -if ac>_b then _b=ac end end;return _b end})ca.defineEvent(ca,"mouse_click") -ca.defineEvent(ca,"mouse_drag")ca.defineEvent(ca,"mouse_up") -ca.defineEvent(ca,"mouse_scroll")function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;da.set("width",20)da.set("height",10)da.set("z",10)return -da end -function ca:init(da,_b) -aa.init(self,da,_b)self.set("type","ScrollFrame")return self end -function ca:mouse_click(da,_b,ab) -if aa.mouse_click(self,da,_b,ab)then -local bb,cb=self:getRelativePosition(_b,ab)local db=self.getResolved("width") -local _c=self.getResolved("height")local ac=self.getResolved("showScrollBar") -local bc=self.getResolved("contentWidth")local cc=self.getResolved("contentHeight") -local dc=ac and bc>db;local _d=dc and _c-1 or _c;local ad=ac and cc>_d -local bd=ad and db-1 or db -if ad and bb==db and(not dc or cb<_c)then local cd=_d -local dd=math.max(1,math.floor((_d/cc)*cd))local __a=cc-_d -local a_a=__a>0 and -(self.getResolved("offsetY")/__a*100)or 0 -local b_a=math.floor((a_a/100)* (cd-dd))+1 -if cb>=b_a and cb0 and -(self.getResolved("offsetX")/__a*100)or 0 -local b_a=math.floor((a_a/100)* (cd-dd))+1 -if bb>=b_a and bbbc -local dc=cc and db-1 or db;local _d=dc -local ad=math.max(1,math.floor((dc/ac)*_d))local bd=ac-dc;cb=math.max(1,math.min(_d,cb))local cd=cb- ( -self._scrollBarDragOffset or 0)local dd= -( (cd-1)/ (_d-ad))*100 -local __a=math.floor((dd/100)*bd+0.5) -self.set("offsetY",math.max(0,math.min(bd,__a)))return true end -if self._hScrollBarDragging then local bb,cb=self:getRelativePosition(_b,ab) -local db=self.getResolved("width")local _c=self.getResolved("contentWidth") -local ac=self.getResolved("contentHeight")local bc=self.getResolved("height")local cc= -self.getResolved("showScrollBar")and _c>db -local dc=cc and bc-1 or bc -local _d=self.getResolved("showScrollBar")and ac>dc;local ad=_d and db-1 or db;local bd=ad -local cd=math.max(1,math.floor((ad/_c)*bd))local dd=_c-ad;bb=math.max(1,math.min(bd,bb))local __a=bb- ( -self._hScrollBarDragOffset or 0)local a_a= -( (__a-1)/ (bd-cd))*100 -local b_a=math.floor((a_a/100)*dd+0.5) -self.set("offsetX",math.max(0,math.min(dd,b_a)))return true end;return -aa.mouse_drag and aa.mouse_drag(self,da,_b,ab)or false end -function ca:mouse_up(da,_b,ab)if self._scrollBarDragging then self._scrollBarDragging=false -self._scrollBarDragOffset=nil;return true end -if self._hScrollBarDragging then -self._hScrollBarDragging=false;self._hScrollBarDragOffset=nil;return true end;return -aa.mouse_up and aa.mouse_up(self,da,_b,ab)or false end -function ca:mouse_scroll(da,_b,ab) -if self:isInBounds(_b,ab)then -local bb,cb=self.getResolved("offsetX"),self.getResolved("offsetY")local db,_c=self:getRelativePosition(_b+bb,ab+cb) -local ac,bc=self:callChildrenEvent(true,"mouse_scroll",da,db,_c)if ac then return true end;local cc=self.getResolved("height") -local dc=self.getResolved("width")local _d=self.getResolved("offsetY") -local ad=self.getResolved("offsetX")local bd=self.getResolved("contentWidth") -local cd=self.getResolved("contentHeight") -local dd=self.getResolved("showScrollBar")and bd>dc;local __a=dd and cc-1 or cc -local a_a=self.getResolved("showScrollBar")and cd>__a;local b_a=a_a and dc-1 or dc -if a_a then local c_a=math.max(0,cd-__a)local d_a=math.min(c_a,math.max(0, -_d+da)) -self.set("offsetY",d_a)elseif dd then local c_a=math.max(0,bd-b_a) -local d_a=math.min(c_a,math.max(0,ad+da))self.set("offsetX",d_a)end;return true end;return false end -function ca:render()aa.render(self)local da=self.getResolved("height") -local _b=self.getResolved("width")local ab=self.getResolved("offsetY") -local bb=self.getResolved("offsetX")local cb=self.getResolved("showScrollBar") -local db=self.getResolved("contentWidth")local _c=self.getResolved("contentHeight") -local ac=cb and db>_b;local bc=ac and da-1 or da;local cc=cb and _c>bc -local dc=cc and _b-1 or _b -if cc then local _d=bc -local ad=math.max(1,math.floor((bc/_c)*_d))local bd=_c-bc -local cd=self.getResolved("scrollBarBackgroundSymbol")local dd=self.getResolved("scrollBarColor") -local __a=self.getResolved("scrollBarBackgroundColor")local a_a=self.getResolved("scrollBarBackgroundColor2")local b_a= -bd>0 and(ab/bd*100)or 0;local c_a= -math.floor((b_a/100)* (_d-ad))+1;for i=1,_d do -if i>=c_a and i0 and(bb/bd*100)or 0;local c_a= -math.floor((b_a/100)* (_d-ad))+1;for i=1,_d do -if i>=c_a and i0 then ab._tabId=1;self:updateTabVisibility()end end;return self end;function _b:updateTabVisibility()self.set("childrenSorted",false) -self.set("childrenEventsSorted",false)end -function _b:setActiveTab(ab) -local bb=self.getResolved("activeTab")if bb==ab then return self end;self.set("activeTab",ab) -self:updateTabVisibility()self:dispatchEvent("tabChanged",ab,bb)return self end -function _b:isChildVisible(ab) -if not ca.isChildVisible(self,ab)then return false end;if ab._tabId then -return ab._tabId==self.getResolved("activeTab")end;return true end -function _b:getContentYOffset()local ab=self:_getHeaderMetrics()return ab.headerHeight end -function _b:_getHeaderMetrics()local ab=self.getResolved("tabs")or{}local bb= -self.getResolved("width")or 1 -local cb=self.getResolved("tabHeight")or 1;local db=self.getResolved("scrollableTab")local _c={} -if db then local ac= -self.getResolved("tabScrollOffset")or 0;local bc=1;local cc=0 -for dc,_d in ipairs(ab)do -local ad=#_d.title+2;if ad>bb then ad=bb end;local bd=bc-ac;local cd=0;local dd=0;if bd<1 then cd=1 -bd end;if -bd+ad-1 >bb then dd=(bd+ad-1)-bb end -if bd+ad>1 and bd<=bb then -local __a=math.max(1,bd)local a_a=ad-cd-dd -table.insert(_c,{id=_d.id,title=_d.title,line=1,x1=__a,x2=__a+a_a-1,width=ad,displayWidth=a_a,actualX=bc,startClip=cd,endClip=dd})end;bc=bc+ad end;cc=bc-1;return -{headerHeight=1,lines=1,positions=_c,totalWidth=cc,scrollOffset=ac,maxScroll=math.max(0,cc-bb)}else local ac=1;local bc=1 -for _d,ad in ipairs(ab)do local bd=# -ad.title+2;if bd>bb then bd=bb end -if bc+bd-1 >bb then ac=ac+1;bc=1 end -table.insert(_c,{id=ad.id,title=ad.title,line=ac,x1=bc,x2=bc+bd-1,width=bd})bc=bc+bd end;local cc=ac;local dc=math.max(cb,cc) -return{headerHeight=dc,lines=cc,positions=_c}end end -function _b:mouse_click(ab,bb,cb) -if not ba.mouse_click(self,ab,bb,cb)then return false end;local db,_c=ba.getRelativePosition(self,bb,cb) -local ac=self:_getHeaderMetrics() -if _c<=ac.headerHeight then if#ac.positions==0 then return true end -for bc,cc in -ipairs(ac.positions)do -if cc.line==_c and db>=cc.x1 and db<=cc.x2 then -self:setActiveTab(cc.id)self.set("focusedChild",nil)return true end end;return true end;return ca.mouse_click(self,ab,bb,cb)end -function _b:getRelativePosition(ab,bb) -local cb=self:_getHeaderMetrics().headerHeight -if ab==nil or bb==nil then return ba.getRelativePosition(self)else -local db,_c=ba.getRelativePosition(self,ab,bb)return db,_c-cb end end -function _b:multiBlit(ab,bb,cb,db,_c,ac,bc)local cc=self:_getHeaderMetrics().headerHeight;return ca.multiBlit(self,ab,( -bb or 1)+cc,cb,db,_c,ac,bc)end -function _b:textFg(ab,bb,cb,db)local _c=self:_getHeaderMetrics().headerHeight;return ca.textFg(self,ab,( -bb or 1)+_c,cb,db)end -function _b:textBg(ab,bb,cb,db)local _c=self:_getHeaderMetrics().headerHeight;return ca.textBg(self,ab,( -bb or 1)+_c,cb,db)end -function _b:drawText(ab,bb,cb)local db=self:_getHeaderMetrics().headerHeight;return ca.drawText(self,ab,( -bb or 1)+db,cb)end -function _b:drawFg(ab,bb,cb)local db=self:_getHeaderMetrics().headerHeight;return ca.drawFg(self,ab,( -bb or 1)+db,cb)end -function _b:drawBg(ab,bb,cb)local db=self:_getHeaderMetrics().headerHeight;return ca.drawBg(self,ab,( -bb or 1)+db,cb)end -function _b:blit(ab,bb,cb,db,_c)local ac=self:_getHeaderMetrics().headerHeight;return ca.blit(self,ab,( -bb or 1)+ac,cb,db,_c)end -function _b:mouse_up(ab,bb,cb) -if not ba.mouse_up(self,ab,bb,cb)then return false end;local db,_c=ba.getRelativePosition(self,bb,cb) -local ac=self:_getHeaderMetrics().headerHeight;if _c<=ac then return true end;return ca.mouse_up(self,ab,bb,cb)end -function _b:mouse_release(ab,bb,cb)ba.mouse_release(self,ab,bb,cb) -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getHeaderMetrics().headerHeight -if _c<=ac then return end;return ca.mouse_release(self,ab,bb,cb)end -function _b:mouse_move(ab,bb,cb) -if ba.mouse_move(self,ab,bb,cb)then -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getHeaderMetrics().headerHeight;if _c<=ac then -return true end -local bc={self:getRelativePosition(bb,cb)} -local cc,dc=self:callChildrenEvent(true,"mouse_move",table.unpack(bc))if cc then return true end end;return false end -function _b:mouse_drag(ab,bb,cb) -if ba.mouse_drag(self,ab,bb,cb)then -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getHeaderMetrics().headerHeight;if _c<=ac then -return true end;return ca.mouse_drag(self,ab,bb,cb)end;return false end -function _b:scrollTabs(ab) -if not self.getResolved("scrollableTab")then return self end;local bb=self:_getHeaderMetrics()local cb= -self.getResolved("tabScrollOffset")or 0;local db=bb.maxScroll or 0;local _c=cb+ ( -ab*5)_c=math.max(0,math.min(db,_c)) -self.set("tabScrollOffset",_c)return self end -function _b:mouse_scroll(ab,bb,cb) -if ba.mouse_scroll(self,ab,bb,cb)then -local db=self:_getHeaderMetrics().headerHeight -if -self.getResolved("scrollableTab")and cb==self.getResolved("y")then self:scrollTabs(ab)return true end;return ca.mouse_scroll(self,ab,bb,cb)end;return false end -function _b:setCursor(ab,bb,cb,db)local _c=self:_getHeaderMetrics().headerHeight -if -self.parent then local ac,bc=self:calculatePosition()local cc=ab+ac-1 -local dc=bb+bc-1 +_c -if - -(cc<1)or(cc>self.parent.get("width"))or(dc<1)or(dc>self.parent.get("height"))then return self.parent:setCursor(cc,dc,false)end;return self.parent:setCursor(cc,dc,cb,db)end;return self end -function _b:render()ba.render(self)local ab=self.getResolved("width") -local bb=self.getResolved("foreground")local cb=self.getResolved("headerBackground") -local db=self:_getHeaderMetrics()local _c=db.headerHeight or 1 -ba.multiBlit(self,1,1,ab,_c," ",da[bb],da[cb])local ac=self.getResolved("activeTab") -for bc,cc in ipairs(db.positions)do -local dc= -(cc.id==ac)and self.getResolved("activeTabBackground")or cb;local _d= -(cc.id==ac)and self.getResolved("activeTabTextColor")or bb -ba.multiBlit(self,cc.x1,cc.line, -cc.displayWidth or(cc.x2 -cc.x1 +1),1," ",da[bb],da[dc])local ad=cc.title;local bd=1 + (cc.startClip or 0) -local cd=#cc.title- (cc.startClip or -0)- (cc.endClip or 0)if cd>0 then ad=cc.title:sub(bd,bd+cd-1)local dd=cc.x1;if -(cc.startClip or 0)==0 then dd=dd+1 end -ba.textFg(self,dd,cc.line,ad,_d)end end;if not self.getResolved("childrenSorted")then -self:sortChildren()end -if -not self.getResolved("childrenEventsSorted")then for bc in pairs(self._values.childrenEvents or{})do -self:sortChildrenEvents(bc)end end -for bc,cc in -ipairs(self.getResolved("visibleChildren")or{})do -if cc==self then error("CIRCULAR REFERENCE DETECTED!")return end;cc:render()cc:postRender()end end -function _b:sortChildrenEvents(ab) -local bb=self._values.childrenEvents and self._values.childrenEvents[ab] -if bb then local cb={}for db,_c in ipairs(bb)do -if self:isChildVisible(_c)then table.insert(cb,_c)end end -for i=2,#cb do local db=cb[i] -local _c=db.get("z")local ac=i-1 -while ac>0 do local bc=cb[ac].get("z")if bc>_c then cb[ac+1]=cb[ac] -ac=ac-1 else break end end;cb[ac+1]=db end -self._values.visibleChildrenEvents=self._values.visibleChildrenEvents or{}self._values.visibleChildrenEvents[ab]=cb end;self.set("childrenEventsSorted",true)return self end;return _b end -project["elements/TextBox.lua"] = function(...) local b_a=require("elements/VisualElement") -local c_a=require("libraries/colorHex")local d_a=setmetatable({},b_a)d_a.__index=d_a -d_a.defineProperty(d_a,"lines",{default={""},type="table",canTriggerRender=true}) -d_a.defineProperty(d_a,"cursorX",{default=1,type="number"}) -d_a.defineProperty(d_a,"cursorY",{default=1,type="number"}) -d_a.defineProperty(d_a,"scrollX",{default=0,type="number",canTriggerRender=true}) -d_a.defineProperty(d_a,"scrollY",{default=0,type="number",canTriggerRender=true}) -d_a.defineProperty(d_a,"editable",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"syntaxPatterns",{default={},type="table"}) -d_a.defineProperty(d_a,"cursorColor",{default=nil,type="color"}) -d_a.defineProperty(d_a,"autoPairEnabled",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoPairCharacters",{default={["("]=")",["["]="]",["{"]="}",['"']='"',['\'']='\'',['`']='`'},type="table"}) -d_a.defineProperty(d_a,"autoPairSkipClosing",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoPairOverType",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoPairNewlineIndent",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoCompleteEnabled",{default=false,type="boolean"}) -d_a.defineProperty(d_a,"autoCompleteItems",{default={},type="table"}) -d_a.defineProperty(d_a,"autoCompleteProvider",{default=nil,type="function",allowNil=true}) -d_a.defineProperty(d_a,"autoCompleteMinChars",{default=1,type="number"}) -d_a.defineProperty(d_a,"autoCompleteMaxItems",{default=6,type="number"}) -d_a.defineProperty(d_a,"autoCompleteCaseInsensitive",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoCompleteTokenPattern",{default="[%w_]+",type="string"}) -d_a.defineProperty(d_a,"autoCompleteOffsetX",{default=0,type="number"}) -d_a.defineProperty(d_a,"autoCompleteOffsetY",{default=1,type="number"}) -d_a.defineProperty(d_a,"autoCompleteZOffset",{default=1,type="number"}) -d_a.defineProperty(d_a,"autoCompleteMaxWidth",{default=0,type="number"}) -d_a.defineProperty(d_a,"autoCompleteShowBorder",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoCompleteBorderColor",{default=colors.black,type="color"}) -d_a.defineProperty(d_a,"autoCompleteBackground",{default=colors.lightGray,type="color"}) -d_a.defineProperty(d_a,"autoCompleteForeground",{default=colors.black,type="color"}) -d_a.defineProperty(d_a,"autoCompleteSelectedBackground",{default=colors.gray,type="color"}) -d_a.defineProperty(d_a,"autoCompleteSelectedForeground",{default=colors.white,type="color"}) -d_a.defineProperty(d_a,"autoCompleteAcceptOnEnter",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoCompleteAcceptOnClick",{default=true,type="boolean"}) -d_a.defineProperty(d_a,"autoCompleteCloseOnEscape",{default=true,type="boolean"})d_a.defineEvent(d_a,"mouse_click") -d_a.defineEvent(d_a,"key")d_a.defineEvent(d_a,"char") -d_a.defineEvent(d_a,"mouse_scroll")d_a.defineEvent(d_a,"paste") -d_a.defineEvent(d_a,"auto_complete_open")d_a.defineEvent(d_a,"auto_complete_close") -d_a.defineEvent(d_a,"auto_complete_accept")local _aa;local aaa;local function baa(c_b)local d_b=c_b._autoCompleteFrame -return -d_b and not d_b._destroyed and d_b.get and d_b.get("visible")end;local function caa(c_b) -return -c_b.getResolved("autoCompleteShowBorder")and 1 or 0 end -local function daa(c_b)local d_b=c_b._autoCompleteFrame -local _ab=c_b._autoCompleteList;if not d_b or d_b._destroyed then return end -d_b:setBackground(c_b.getResolved("autoCompleteBackground")) -d_b:setForeground(c_b.getResolved("autoCompleteForeground")) -if _ab and not _ab._destroyed then -_ab:setBackground(c_b.getResolved("autoCompleteBackground")) -_ab:setForeground(c_b.getResolved("autoCompleteForeground")) -_ab:setSelectedBackground(c_b.getResolved("autoCompleteSelectedBackground")) -_ab:setSelectedForeground(c_b.getResolved("autoCompleteSelectedForeground"))_ab:updateRender()end;aaa(c_b)_aa(c_b)d_b:updateRender()end -local function _ba(c_b,d_b,_ab)local aab=c_b._autoCompleteList -if not aab or aab._destroyed then return end;local bab=aab.get("items")local cab=#bab;if cab==0 then return end -if d_b<1 then d_b=1 end;if d_b>cab then d_b=cab end;c_b._autoCompleteIndex=d_b -for abb,bbb in ipairs(bab)do if -type(bbb)=="table"then bbb.selected=(abb==d_b)end end;local dab=aab.get("height")or 0 -local _bb=aab.get("offset")or 0;if not _ab and dab>0 then -if d_b>_bb+dab then -aab:setOffset(math.max(0,d_b-dab))elseif d_b<=_bb then aab:setOffset(math.max(0,d_b-1))end end -aab:updateRender()end -local function aba(c_b,d_b)if baa(c_b)then -c_b._autoCompleteFrame:setVisible(false) -if not d_b then c_b:fireEvent("auto_complete_close")end end -c_b._autoCompleteIndex=nil;c_b._autoCompleteSuggestions=nil;c_b._autoCompleteToken=nil -c_b._autoCompleteTokenStart=nil;c_b._autoCompletePopupWidth=nil end -local function bba(c_b,d_b)local _ab=c_b._autoCompleteSuggestions or{} -local aab=c_b._autoCompleteIndex or 1;local bab=d_b or _ab[aab]if not bab then return end -local cab=bab.insert or bab.text or""if cab==""then return end;local dab=c_b.getResolved("lines") -local _bb=c_b.getResolved("cursorY")local abb=c_b.getResolved("cursorX")local bbb=dab[_bb]or""local cbb= -c_b._autoCompleteTokenStart or abb;if cbb<1 then cbb=1 end -local dbb=bbb:sub(1,cbb-1)local _cb=bbb:sub(abb)dab[_bb]=dbb..cab.._cb -c_b.set("cursorX",cbb+#cab)c_b:updateViewport()c_b:updateRender() -aba(c_b,true) -c_b:fireEvent("auto_complete_accept",cab,bab.source or bab)end -local function cba(c_b) -if not c_b.getResolved("autoCompleteEnabled")then return nil end;local d_b=c_b._autoCompleteFrame;if d_b and not d_b._destroyed then -return c_b._autoCompleteList end;local _ab=c_b:getBaseFrame()if not _ab or not -_ab.addFrame then return nil end -d_b=_ab:addFrame({width=c_b.getResolved("width"),height=1,x=1,y=1,visible=false,background=c_b.getResolved("autoCompleteBackground"),foreground=c_b.getResolved("autoCompleteForeground"),ignoreOffset=true,z= -c_b.getResolved("z")+c_b.getResolved("autoCompleteZOffset")})d_b:setIgnoreOffset(true)d_b:setVisible(false) -local aab=caa(c_b) -local bab=d_b:addList({x=aab+1,y=aab+1,width=math.max(1,d_b.get("width")-aab*2),height=math.max(1, -d_b.get("height")-aab*2),selectable=true,multiSelection=false,background=c_b.getResolved("autoCompleteBackground"),foreground=c_b.getResolved("autoCompleteForeground")}) -bab:setSelectedBackground(c_b.getResolved("autoCompleteSelectedBackground")) -bab:setSelectedForeground(c_b.getResolved("autoCompleteSelectedForeground"))bab:setOffset(0) -bab:onSelect(function(cab,dab,_bb)if not baa(c_b)then return end -_ba(c_b,dab) -if c_b.getResolved("autoCompleteAcceptOnClick")then bba(c_b,_bb)end end)c_b._autoCompleteFrame=d_b;c_b._autoCompleteList=bab;daa(c_b)return bab end -aaa=function(c_b,d_b,_ab)local aab=c_b._autoCompleteFrame;local bab=c_b._autoCompleteList -if -not aab or aab._destroyed or not bab or bab._destroyed then return end;local cab=caa(c_b) -local dab= -tonumber(d_b)or rawget(c_b,"_autoCompletePopupWidth")or bab.get("width")or aab.get("width") -local _bb=tonumber(_ab)or(bab.get and bab.get("height"))or(# ( -rawget(c_b,"_autoCompleteSuggestions")or{}))dab=math.max(1,dab or 1)_bb=math.max(1,_bb or 1)local abb= -aab.get and aab.get("width")or dab;local bbb= -aab.get and aab.get("height")or _bb;local cbb=math.max(1, -abb-cab*2) -local dbb=math.max(1,bbb-cab*2)if dab>cbb then dab=cbb end;if _bb>dbb then _bb=dbb end -bab:setPosition(cab+1,cab+1)bab:setWidth(math.max(1,dab)) -bab:setHeight(math.max(1,_bb))end -_aa=function(c_b)local d_b=c_b._autoCompleteFrame -if not d_b or d_b._destroyed then return end;local _ab=d_b.get and d_b.get("canvas") -if not _ab then return end;_ab:setType("post") -if d_b._autoCompleteBorderCommand then -_ab:removeCommand(d_b._autoCompleteBorderCommand)d_b._autoCompleteBorderCommand=nil end;if not c_b.getResolved("autoCompleteShowBorder")then -d_b:updateRender()return end;local aab= -c_b.getResolved("autoCompleteBorderColor")or colors.black -local bab=_ab:addCommand(function(cab)local dab= -cab.get("width")or 0;local _bb=cab.get("height")or 0;if dab<1 or -_bb<1 then return end -local abb=cab.get("background")or colors.black;local bbb=c_a[abb]or c_a[colors.black]local cbb=c_a[aab]or -c_a[colors.black] -cab:textFg(1,1,("\131"):rep(dab),aab)cab:multiBlit(1,_bb,dab,1,"\143",bbb,cbb) -cab:multiBlit(1,1,1,_bb,"\149",cbb,bbb)cab:multiBlit(dab,1,1,_bb,"\149",bbb,cbb) -cab:blit(1,1,"\151",cbb,bbb)cab:blit(dab,1,"\148",bbb,cbb) -cab:blit(1,_bb,"\138",bbb,cbb)cab:blit(dab,_bb,"\133",bbb,cbb)end)d_b._autoCompleteBorderCommand=bab;d_b:updateRender()end -local function dba(c_b)local d_b=c_b.getResolved("lines") -local _ab=c_b.getResolved("cursorY")local aab=c_b.getResolved("cursorX")local bab=d_b[_ab]or""local cab=bab:sub(1,math.max( -aab-1,0))local dab= -c_b.getResolved("autoCompleteTokenPattern")or"[%w_]+"local _bb="" -if -dab~=""then _bb=cab:match("("..dab..")$")or""end;local abb=aab-#_bb;if abb<1 then abb=1 end;return _bb,abb end -local function _ca(c_b) -if type(c_b)=="string"then return{text=c_b,insert=c_b,source=c_b}elseif -type(c_b)=="table"then local d_b= -c_b.text or c_b.label or c_b.value or c_b.insert or c_b[1]if not d_b then return -nil end -local _ab={text=d_b,insert=c_b.insert or c_b.value or d_b,source=c_b}if c_b.foreground then _ab.foreground=c_b.foreground end;if -c_b.background then _ab.background=c_b.background end;if c_b.selectedForeground then -_ab.selectedForeground=c_b.selectedForeground end;if c_b.selectedBackground then -_ab.selectedBackground=c_b.selectedBackground end -if c_b.icon then _ab.icon=c_b.icon end;if c_b.info then _ab.info=c_b.info end;return _ab end end -local function aca(c_b,d_b)if type(c_b)~="table"then return end;local _ab=#c_b -if _ab>0 then for index=1,_ab do -d_b(c_b[index])end else for aab,bab in pairs(c_b)do d_b(bab)end end end -local function bca(c_b,d_b)local _ab=c_b.getResolved("autoCompleteProvider")local aab={} -if _ab then -local abb,bbb=pcall(_ab,c_b,d_b)if abb and type(bbb)=="table"then aab=bbb end else aab= -c_b.getResolved("autoCompleteItems")or{}end;local bab={} -local cab=c_b.getResolved("autoCompleteCaseInsensitive")local dab=cab and d_b:lower()or d_b -aca(aab,function(abb)local bbb=_ca(abb) -if bbb and -bbb.text then -local cbb=cab and bbb.text:lower()or bbb.text;if dab==""or cbb:find(dab,1,true)==1 then -table.insert(bab,bbb)end end end)local _bb=c_b.getResolved("autoCompleteMaxItems") -if#bab>_bb then while -#bab>_bb do table.remove(bab)end end;return bab end -local function cca(c_b,d_b)local _ab=0 -for _bb,abb in ipairs(d_b)do local bbb=abb;if type(abb)=="table"then -bbb= -abb.text or abb.label or abb.value or abb.insert or abb[1]end;if bbb~=nil then -local cbb=#tostring(bbb)if cbb>_ab then _ab=cbb end end end;local aab=c_b.getResolved("autoCompleteMaxWidth") -local bab=c_b.getResolved("width")if aab and aab>0 then bab=math.min(bab,aab)end -local cab=caa(c_b)local dab=c_b:getBaseFrame() -if dab and dab.get then -local _bb=dab.get("width")if _bb and _bb>0 then local abb=_bb-cab*2;if abb<1 then abb=1 end -bab=math.min(bab,abb)end end;_ab=math.min(_ab,bab)return math.max(1,_ab)end -local function dca(c_b,d_b,_ab)local aab=c_b._autoCompleteFrame;local bab=c_b._autoCompleteList;if -not aab or aab._destroyed then return end;local cab=caa(c_b) -local dab=math.max(1,_ab or c_b.getResolved("width"))local _bb=math.max(1,d_b or 1)local abb=c_b:getBaseFrame()if not abb then -return end;local bbb=abb.get and abb.get("width")local cbb=abb.get and -abb.get("height") -if bbb and bbb>0 then local bac=bbb-cab*2;if bac<1 then -bac=1 end;if dab>bac then dab=bac end end;if cbb and cbb>0 then local bac=cbb-cab*2;if bac<1 then bac=1 end -if _bb>bac then _bb=bac end end;local dbb=dab+cab*2 -local _cb=_bb+cab*2;local acb,bcb=c_b:calculatePosition() -local ccb=c_b.getResolved("scrollX")or 0;local dcb=c_b.getResolved("scrollY")or 0 -local _db=( -c_b._autoCompleteTokenStart or c_b.getResolved("cursorX"))local adb=_db-ccb -adb=math.max(1,math.min(c_b.getResolved("width"),adb))local bdb=c_b.getResolved("cursorY")-dcb -bdb=math.max(1,math.min(c_b.getResolved("height"),bdb))local cdb=c_b.getResolved("autoCompleteOffsetX") -local ddb=c_b.getResolved("autoCompleteOffsetY")local __c=acb+adb-1 +cdb;local a_c=__c-cab -if cab>0 then a_c=a_c+1 end;local b_c=bcb+bdb+ddb;local c_c=bcb+bdb-ddb-1;local d_c=b_c-cab;local _ac= -c_c-_bb+1 -cab;local aac=d_c -if bbb and bbb>0 then if dbb>bbb then dbb=bbb -dab=math.max(1,dbb-cab*2)end;if a_c+dbb-1 >bbb then -a_c=math.max(1,bbb-dbb+1)end;if a_c<1 then a_c=1 end else if a_c<1 then a_c=1 end end -if cbb and cbb>0 then -if aac+_cb-1 >cbb then aac=_ac;if cab>0 then aac=aac-cab end;if -aac<1 then aac=math.max(1,cbb-_cb+1)end end;if aac<1 then aac=1 end else if aac<1 then aac=1 end;if aac==_ac and cab>0 then -aac=math.max(1,aac-cab)end end;aab:setPosition(a_c,aac)aab:setWidth(dbb) -aab:setHeight(_cb) -aab:setZ(c_b.getResolved("z")+c_b.getResolved("autoCompleteZOffset"))aaa(c_b,dab,_bb)if bab and not bab._destroyed then -bab:updateRender()end;aab:updateRender()end -local function _da(c_b)if not c_b.getResolved("autoCompleteEnabled")then -aba(c_b,true)return end;if -not c_b:hasState("focused")then aba(c_b,true)return end;local d_b,_ab=dba(c_b) -c_b._autoCompleteToken=d_b;c_b._autoCompleteTokenStart=_ab;if -#d_b=1 and bbb<=dab then -_ba(c_b,bbb)else _ab:updateRender()end;return true end -function d_a.new()local c_b=setmetatable({},d_a):__init() -c_b.class=d_a;c_b.set("width",20)c_b.set("height",10)return c_b end -function d_a:init(c_b,d_b)b_a.init(self,c_b,d_b) -self.set("type","TextBox") -local function _ab()if -self.getResolved("autoCompleteEnabled")and self:hasState("focused")then _da(self)end end;local function aab()daa(self)end -local function bab() -if baa(self)then -local cab=rawget(self,"_autoCompleteSuggestions")or{} -dca(self,math.max(#cab,1),rawget(self,"_autoCompletePopupWidth")or -self.getResolved("width"))end end -self:observe("autoCompleteEnabled",function(cab,dab)if not dab then aba(self,true)elseif self:hasState("focused")then -_da(self)end end)self:observe("foreground",aab) -self:observe("background",aab)self:observe("autoCompleteBackground",aab) -self:observe("autoCompleteForeground",aab) -self:observe("autoCompleteSelectedBackground",aab) -self:observe("autoCompleteSelectedForeground",aab)self:observe("autoCompleteBorderColor",aab) -self:observe("autoCompleteZOffset",function() -if - -self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then -self._autoCompleteFrame:setZ(self.getResolved("z")+ -self.getResolved("autoCompleteZOffset"))end end) -self:observe("z",function() -if -self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then -self._autoCompleteFrame:setZ(self.getResolved("z")+ -self.getResolved("autoCompleteZOffset"))end end) -self:observe("autoCompleteShowBorder",function()aab()bab()end) -for cab,dab in -ipairs({"autoCompleteItems","autoCompleteProvider","autoCompleteMinChars","autoCompleteMaxItems","autoCompleteCaseInsensitive","autoCompleteTokenPattern","autoCompleteOffsetX","autoCompleteOffsetY"})do self:observe(dab,_ab)end;self:observe("x",bab)self:observe("y",bab)self:observe("width",function() -bab()_ab()end) -self:observe("height",bab)self:observe("cursorX",bab) -self:observe("cursorY",bab)self:observe("scrollX",bab) -self:observe("scrollY",bab)self:observe("autoCompleteOffsetX",bab) -self:observe("autoCompleteOffsetY",bab) -self:observe("autoCompleteMaxWidth",function() -if baa(self)then -local cab=rawget(self,"_autoCompleteSuggestions")or{}if#cab>0 then local dab=cca(self,cab)self._autoCompletePopupWidth=dab -dca(self,math.max(#cab,1),dab)end end end)return self end;function d_a:addSyntaxPattern(c_b,d_b) -table.insert(self.getResolved("syntaxPatterns"),{pattern=c_b,color=d_b})return self end -function d_a:removeSyntaxPattern(c_b)local d_b= -self.getResolved("syntaxPatterns")or{} -if type(c_b)~="number"then return self end -if c_b>=1 and c_b<=#d_b then table.remove(d_b,c_b) -self.set("syntaxPatterns",d_b)self:updateRender()end;return self end;function d_a:clearSyntaxPatterns()self.set("syntaxPatterns",{}) -self:updateRender()return self end -local function cda(c_b,d_b) -local _ab=c_b.getResolved("lines")local aab=c_b.getResolved("cursorX") -local bab=c_b.getResolved("cursorY")local cab=_ab[bab] -_ab[bab]=cab:sub(1,aab-1)..d_b..cab:sub(aab)c_b.set("cursorX",aab+1)c_b:updateViewport() -c_b:updateRender()end -local function dda(c_b,d_b)for i=1,#d_b do cda(c_b,d_b:sub(i,i))end end -local function __b(c_b)local d_b=c_b.getResolved("lines") -local _ab=c_b.getResolved("cursorX")local aab=c_b.getResolved("cursorY")local bab=d_b[aab] -local cab=bab:sub(_ab)d_b[aab]=bab:sub(1,_ab-1) -table.insert(d_b,aab+1,cab)c_b.set("cursorX",1)c_b.set("cursorY",aab+1) -c_b:updateViewport()c_b:updateRender()end -local function a_b(c_b)local d_b=c_b.getResolved("lines") -local _ab=c_b.getResolved("cursorX")local aab=c_b.getResolved("cursorY")local bab=d_b[aab] -if _ab>1 then d_b[aab]=bab:sub(1, -_ab-2)..bab:sub(_ab) -c_b.set("cursorX",_ab-1)elseif aab>1 then local cab=d_b[aab-1]c_b.set("cursorX",#cab+1)c_b.set("cursorY", -aab-1)d_b[aab-1]=cab..bab -table.remove(d_b,aab)end;c_b:updateViewport()c_b:updateRender()end -function d_a:updateViewport()local c_b=self.getResolved("cursorX") -local d_b=self.getResolved("cursorY")local _ab=self.getResolved("scrollX") -local aab=self.getResolved("scrollY")local bab=self.getResolved("width") -local cab=self.getResolved("height") -if c_b-_ab>bab then self.set("scrollX",c_b-bab)elseif c_b-_ab<1 then self.set("scrollX", -c_b-1)end -if d_b-aab>cab then self.set("scrollY",d_b-cab)elseif d_b-aab<1 then self.set("scrollY", -d_b-1)end;return self end -function d_a:char(c_b) -if not self.getResolved("editable")or not -self:hasState("focused")then return false end;local d_b=self.getResolved("autoPairEnabled") -if d_b and#c_b==1 then local _ab= -self.getResolved("autoPairCharacters")or{} -local aab=self.getResolved("lines")local bab=self.getResolved("cursorX") -local cab=self.getResolved("cursorY")local dab=aab[cab]or""local _bb=dab:sub(bab,bab)local abb=_ab[c_b] -if abb then -cda(self,c_b) -if self.getResolved("autoPairSkipClosing")then -if _bb~=abb then cda(self,abb)self.set("cursorX", -self.getResolved("cursorX")-1)end else cda(self,abb) -self.set("cursorX",self.getResolved("cursorX")-1)end;_da(self)return true end -if self.getResolved("autoPairOverType")then for bbb,cbb in pairs(_ab)do -if c_b==cbb and _bb==cbb then self.set("cursorX", -bab+1)_da(self)return true end end end end;cda(self,c_b)_da(self)return true end -function d_a:key(c_b) -if not self.getResolved("editable")or -not self:hasState("focused")then return false end;if ada(self,c_b)then return true end -local d_b=self.getResolved("lines")local _ab=self.getResolved("cursorX") -local aab=self.getResolved("cursorY") -if c_b==keys.enter then -if self.getResolved("autoPairEnabled")and -self.getResolved("autoPairNewlineIndent")then -local bab=self.getResolved("lines")local cab=self.getResolved("cursorX") -local dab=self.getResolved("cursorY")local _bb=bab[dab]or""local abb=_bb:sub(1,cab-1) -local bbb=_bb:sub(cab) -local cbb=self.getResolved("autoPairCharacters")or{}local dbb={}for bcb,ccb in pairs(cbb)do dbb[ccb]=bcb end;local _cb=abb:sub(-1) -local acb=bbb:sub(1,1) -if _cb~=""and acb~=""and cbb[_cb]==acb then bab[dab]=abb;table.insert(bab, -dab+1,"")table.insert(bab,dab+2,bbb)self.set("cursorY", -dab+1)self.set("cursorX",1) -self:updateViewport()self:updateRender()_da(self)return true end end;__b(self)elseif c_b==keys.backspace then a_b(self)elseif c_b==keys.left then -if _ab>1 then self.set("cursorX", -_ab-1)elseif aab>1 then self.set("cursorY",aab-1)self.set("cursorX", -#d_b[aab-1]+1)end elseif c_b==keys.right then if _ab<=#d_b[aab]then -self.set("cursorX",_ab+1)elseif aab<#d_b then self.set("cursorY",aab+1) -self.set("cursorX",1)end elseif c_b==keys.up and -aab>1 then self.set("cursorY",aab-1) -self.set("cursorX",math.min(_ab,# -d_b[aab-1]+1))elseif c_b==keys.down and aab<#d_b then -self.set("cursorY",aab+1) -self.set("cursorX",math.min(_ab,#d_b[aab+1]+1))end;self:updateRender()self:updateViewport()_da(self)return -true end -function d_a:mouse_scroll(c_b,d_b,_ab)if bda(self,c_b)then return true end -if self:isInBounds(d_b,_ab)then -local aab=self.getResolved("scrollY")local bab=self.getResolved("height") -local cab=self.getResolved("lines")local dab=math.max(0,#cab-bab+2) -local _bb=math.max(0,math.min(dab,aab+c_b))self.set("scrollY",_bb)self:updateRender()return true end;return false end -function d_a:mouse_click(c_b,d_b,_ab) -if b_a.mouse_click(self,c_b,d_b,_ab)then -local aab,bab=self:getRelativePosition(d_b,_ab)local cab=self.getResolved("scrollX") -local dab=self.getResolved("scrollY")local _bb=(bab or 0)+ (dab or 0) -local abb=self.getResolved("lines")or{}if _bb<1 then _bb=1 end -if _bb<=#abb and abb[_bb]~=nil then -self.set("cursorY",_bb)local bbb=#tostring(abb[_bb]) -self.set("cursorX",math.min((aab or 1)+ (cab or 0), -bbb+1))end;self:updateRender()_da(self)return true end -if baa(self)then local aab=self._autoCompleteFrame;if -not -(aab and aab:isInBounds(d_b,_ab))and not self:isInBounds(d_b,_ab)then aba(self)end end;return false end -function d_a:paste(c_b) -if not self.getResolved("editable")or not -self:hasState("focused")then return false end;for d_b in c_b:gmatch(".")do -if d_b=="\n"then __b(self)else cda(self,d_b)end end;_da(self)return true end -function d_a:setText(c_b)local d_b={} -if c_b==""then d_b={""}else for _ab in(c_b.."\n"):gmatch("([^\n]*)\n")do -table.insert(d_b,_ab)end end;self.set("lines",d_b)aba(self,true)return self end;function d_a:getText() -return table.concat(self.getResolved("lines"),"\n")end -local function b_b(c_b,d_b)local _ab=d_b;local aab=string.rep(c_a[c_b.getResolved("foreground")], -#_ab) -local bab=c_b.getResolved("syntaxPatterns") -for cab,dab in ipairs(bab)do local _bb=1 -while true do local abb,bbb=_ab:find(dab.pattern,_bb) -if not abb then break end;local cbb=bbb-abb+1 -if cbb<=0 then -aab=aab:sub(1,abb-1).. -string.rep(c_a[dab.color],1)..aab:sub(abb+1)_bb=abb+1 else -aab=aab:sub(1,abb-1)..string.rep(c_a[dab.color],cbb)..aab:sub( -bbb+1)_bb=bbb+1 end end end;return _ab,aab end -function d_a:render()b_a.render(self)local c_b=self.getResolved("lines") -local d_b=self.getResolved("scrollX")local _ab=self.getResolved("scrollY") -local aab=self.getResolved("width")local bab=self.getResolved("height") -local cab=self.getResolved("foreground")local dab=self.getResolved("background")local _bb=c_a[cab] -local abb=c_a[dab] -for y=1,bab do local bbb=y+_ab;local cbb=c_b[bbb]or""local dbb,_cb=b_b(self,cbb)local acb=dbb:sub(d_b+1,d_b+ -aab)local bcb=_cb:sub(d_b+1,d_b+aab)local ccb= -aab-#acb;if ccb>0 then acb=acb..string.rep(" ",ccb)bcb=bcb.. -string.rep(c_a[cab],ccb)end;self:blit(1,y,acb,bcb,string.rep(abb, -#acb))end -if self:hasState("focused")then -local bbb=self.getResolved("cursorX")-d_b;local cbb=self.getResolved("cursorY")-_ab;if bbb>=1 and -bbb<=aab and cbb>=1 and cbb<=bab then -self:setCursor(bbb,cbb,true, -self.getResolved("cursorColor")or cab)end end end -function d_a:destroy() -if -self._autoCompleteFrame and not self._autoCompleteFrame._destroyed then self._autoCompleteFrame:destroy()end;self._autoCompleteFrame=nil;self._autoCompleteList=nil -self._autoCompletePopupWidth=nil;b_a.destroy(self)end;return d_a end -project["elements/Button.lua"] = function(...) local _a=require("elementManager") -local aa=_a.getElement("VisualElement") -local ba=require("libraries/utils").getCenteredPosition;local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"text",{default="Button",type="string",canTriggerRender=true})ca.defineEvent(ca,"mouse_click") -ca.defineEvent(ca,"mouse_up")function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;da.set("width",10)da.set("height",3)da.set("z",5) -return da end;function ca:init(da,_b) -aa.init(self,da,_b)self.set("type","Button")end -function ca:render() -aa.render(self)local da=self.getResolved("text") -da=da:sub(1,self.getResolved("width")) -local _b,ab=ba(da,self.getResolved("width"),self.getResolved("height")) -self:textFg(_b,ab,da,self.getResolved("foreground"))end;return ca end -project["elements/Label.lua"] = function(...) local _a=require("elementManager") -local aa=_a.getElement("VisualElement")local ba=require("libraries/utils").wrapText -local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"text",{default="Label",type="string",canTriggerRender=true,setter=function(da,_b) -if(type(_b)=="function")then _b=_b()end -if(da.getResolved("autoSize"))then da.set("width",#_b)else da.set("height",# -ba(_b,da.getResolved("width")))end;return _b end}) -ca.defineProperty(ca,"autoSize",{default=true,type="boolean",canTriggerRender=true,setter=function(da,_b)if(_b)then -da.set("width",#da.getResolved("text"))else -da.set("height",# -ba(da.getResolved("text"),da.getResolved("width")))end;return _b end}) -function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;da.set("z",3)da.set("backgroundEnabled",false)return da end;function ca:init(da,_b)aa.init(self,da,_b)self.set("type","Label") -return self end -function ca:getWrappedText() -local da=self.getResolved("text")local _b=ba(da,self.getResolved("width"))return _b end -function ca:render()aa.render(self)local da=self.getResolved("text") -if -(self.getResolved("autoSize"))then -self:textFg(1,1,da,self.getResolved("foreground"))else local _b=ba(da,self.getResolved("width"))for ab,bb in ipairs(_b)do -self:textFg(1,ab,bb,self.getResolved("foreground"))end end end;return ca end -project["elements/SideNav.lua"] = function(...) local aa=require("elementManager") -local ba=require("elements/VisualElement")local ca=aa.getElement("Container") -local da=require("libraries/colorHex")local _b=setmetatable({},ca)_b.__index=_b -_b.defineProperty(_b,"activeTab",{default=nil,type="number",allowNil=true,canTriggerRender=true,setter=function(ab,bb) -return bb end}) -_b.defineProperty(_b,"sidebarWidth",{default=12,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"tabs",{default={},type="table"}) -_b.defineProperty(_b,"sidebarBackground",{default=colors.gray,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"activeTabBackground",{default=colors.white,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"activeTabTextColor",{default=colors.black,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"sidebarScrollOffset",{default=0,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"sidebarPosition",{default="left",type="string",canTriggerRender=true})_b.defineEvent(_b,"mouse_click") -_b.defineEvent(_b,"mouse_up")_b.defineEvent(_b,"mouse_scroll")function _b.new() -local ab=setmetatable({},_b):__init()ab.class=_b;ab.set("width",30)ab.set("height",15) -ab.set("z",10)return ab end -function _b:init(ab,bb) -ca.init(self,ab,bb)self.set("type","SideNav")end -function _b:newTab(ab)local bb=self.getResolved("tabs")or{} -local cb=#bb+1 -table.insert(bb,{id=cb,title=tostring(ab or("Item "..cb))})self.set("tabs",bb)if -not self.getResolved("activeTab")then self.set("activeTab",cb)end -self:updateTabVisibility()local db=self;local _c={} -setmetatable(_c,{__index=function(ac,bc) -if -type(bc)=="string"and bc:sub(1,3)=="add"and type(db[bc])=="function"then -return -function(dc,...) -local _d=db[bc](db,...) -if _d then _d._tabId=cb;db.set("childrenSorted",false) -db.set("childrenEventsSorted",false)db:updateRender()end;return _d end end;local cc=db[bc]if type(cc)=="function"then -return function(dc,...)return cc(db,...)end end;return cc end})return _c end;_b.addTab=_b.newTab;function _b:setTab(ab,bb)ab._tabId=bb -self:updateTabVisibility()return self end -function _b:addElement(ab,bb) -local cb=ca.addElement(self,ab)local db=bb or self.getResolved("activeTab")if db then -cb._tabId=db;self:updateTabVisibility()end;return cb end -function _b:addChild(ab)ca.addChild(self,ab)if not ab._tabId then -local bb=self.getResolved("tabs")or{} -if#bb>0 then ab._tabId=1;self:updateTabVisibility()end end;return self end;function _b:updateTabVisibility()self.set("childrenSorted",false) -self.set("childrenEventsSorted",false)end -function _b:setActiveTab(ab) -local bb=self.getResolved("activeTab")if bb==ab then return self end;self.set("activeTab",ab) -self:updateTabVisibility()self:dispatchEvent("tabChanged",ab,bb)return self end -function _b:isChildVisible(ab) -if not ca.isChildVisible(self,ab)then return false end;if ab._tabId then -return ab._tabId==self.getResolved("activeTab")end;return true end -function _b:getContentXOffset()local ab=self:_getSidebarMetrics()return ab.sidebarWidth end -function _b:_getSidebarMetrics()local ab=self.getResolved("tabs")or{}local bb= -self.getResolved("height")or 1 -local cb=self.getResolved("sidebarWidth")or 12 -local db=self.getResolved("sidebarScrollOffset")or 0 -local _c=self.getResolved("sidebarPosition")or"left"local ac={}local bc=1;local cc=#ab -for dc,_d in ipairs(ab)do local ad=1;local bd=bc-db;local cd=0;local dd=0 -if bd<1 then cd=1 -bd end;if bd+ad-1 >bb then dd=(bd+ad-1)-bb end -if -bd+ad>1 and bd<=bb then local __a=math.max(1,bd)local a_a=ad-cd-dd -table.insert(ac,{id=_d.id,title=_d.title,y1=__a,y2=__a+a_a-1,height=ad,displayHeight=a_a,actualY=bc,startClip=cd,endClip=dd})end;bc=bc+ad end;return -{sidebarWidth=cb,sidebarPosition=_c,positions=ac,totalHeight=cc,scrollOffset=db,maxScroll=math.max(0,cc-bb)}end -function _b:mouse_click(ab,bb,cb) -if not ba.mouse_click(self,ab,bb,cb)then return false end;local db,_c=ba.getRelativePosition(self,bb,cb) -local ac=self:_getSidebarMetrics()local bc=self.getResolved("width")or 1;local cc=false;if -ac.sidebarPosition=="right"then cc=db> (bc-ac.sidebarWidth)else -cc=db<=ac.sidebarWidth end -if cc then if#ac.positions==0 then -return true end;for dc,_d in ipairs(ac.positions)do -if _c>=_d.y1 and _c<=_d.y2 then -self:setActiveTab(_d.id)self.set("focusedChild",nil)return true end end -return true end;return ca.mouse_click(self,ab,bb,cb)end -function _b:getRelativePosition(ab,bb)local cb=self:_getSidebarMetrics()local db= -self.getResolved("width")or 1 -if ab==nil or bb==nil then return -ba.getRelativePosition(self)else local _c,ac=ba.getRelativePosition(self,ab,bb) -if -cb.sidebarPosition=="right"then return _c,ac else return _c-cb.sidebarWidth,ac end end end -function _b:multiBlit(ab,bb,cb,db,_c,ac,bc)local cc=self:_getSidebarMetrics() -if -cc.sidebarPosition=="right"then return ca.multiBlit(self,ab,bb,cb,db,_c,ac,bc)else -return ca.multiBlit(self,(ab or 1)+ -cc.sidebarWidth,bb,cb,db,_c,ac,bc)end end -function _b:textFg(ab,bb,cb,db)local _c=self:_getSidebarMetrics() -if -_c.sidebarPosition=="right"then return ca.textFg(self,ab,bb,cb,db)else return -ca.textFg(self,(ab or 1)+_c.sidebarWidth,bb,cb,db)end end -function _b:textBg(ab,bb,cb,db)local _c=self:_getSidebarMetrics() -if -_c.sidebarPosition=="right"then return ca.textBg(self,ab,bb,cb,db)else return -ca.textBg(self,(ab or 1)+_c.sidebarWidth,bb,cb,db)end end -function _b:drawText(ab,bb,cb)local db=self:_getSidebarMetrics() -if -db.sidebarPosition=="right"then return ca.drawText(self,ab,bb,cb)else return -ca.drawText(self,(ab or 1)+db.sidebarWidth,bb,cb)end end -function _b:drawFg(ab,bb,cb)local db=self:_getSidebarMetrics() -if -db.sidebarPosition=="right"then return ca.drawFg(self,ab,bb,cb)else return -ca.drawFg(self,(ab or 1)+db.sidebarWidth,bb,cb)end end -function _b:drawBg(ab,bb,cb)local db=self:_getSidebarMetrics() -if -db.sidebarPosition=="right"then return ca.drawBg(self,ab,bb,cb)else return -ca.drawBg(self,(ab or 1)+db.sidebarWidth,bb,cb)end end -function _b:blit(ab,bb,cb,db,_c)local ac=self:_getSidebarMetrics() -if ac.sidebarPosition=="right"then return -ca.blit(self,ab,bb,cb,db,_c)else return -ca.blit(self,(ab or 1)+ac.sidebarWidth,bb,cb,db,_c)end end -function _b:mouse_up(ab,bb,cb) -if not ba.mouse_up(self,ab,bb,cb)then return false end;local db,_c=ba.getRelativePosition(self,bb,cb) -local ac=self:_getSidebarMetrics()local bc=self.getResolved("width")or 1;local cc=false;if -ac.sidebarPosition=="right"then cc=db> (bc-ac.sidebarWidth)else -cc=db<=ac.sidebarWidth end;if cc then return true end;return -ca.mouse_up(self,ab,bb,cb)end -function _b:mouse_release(ab,bb,cb)ba.mouse_release(self,ab,bb,cb) -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getSidebarMetrics() -local bc=self.getResolved("width")or 1;local cc=false -if ac.sidebarPosition=="right"then -cc=db> (bc-ac.sidebarWidth)else cc=db<=ac.sidebarWidth end;if cc then return end;return ca.mouse_release(self,ab,bb,cb)end -function _b:mouse_move(ab,bb,cb) -if ba.mouse_move(self,ab,bb,cb)then -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getSidebarMetrics() -local bc=self.getResolved("width")or 1;local cc=false -if ac.sidebarPosition=="right"then -cc=db> (bc-ac.sidebarWidth)else cc=db<=ac.sidebarWidth end;if cc then return true end -local dc={self:getRelativePosition(bb,cb)} -local _d,ad=self:callChildrenEvent(true,"mouse_move",table.unpack(dc))if _d then return true end end;return false end -function _b:mouse_drag(ab,bb,cb) -if ba.mouse_drag(self,ab,bb,cb)then -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getSidebarMetrics() -local bc=self.getResolved("width")or 1;local cc=false -if ac.sidebarPosition=="right"then -cc=db> (bc-ac.sidebarWidth)else cc=db<=ac.sidebarWidth end;if cc then return true end;return ca.mouse_drag(self,ab,bb,cb)end;return false end -function _b:scrollSidebar(ab)local bb=self:_getSidebarMetrics()local cb= -self.getResolved("sidebarScrollOffset")or 0 -local db=bb.maxScroll or 0;local _c=cb+ (ab*2)_c=math.max(0,math.min(db,_c)) -self.set("sidebarScrollOffset",_c)return self end -function _b:mouse_scroll(ab,bb,cb) -if ba.mouse_scroll(self,ab,bb,cb)then -local db,_c=ba.getRelativePosition(self,bb,cb)local ac=self:_getSidebarMetrics() -local bc=self.getResolved("width")or 1;local cc=false -if ac.sidebarPosition=="right"then -cc=db> (bc-ac.sidebarWidth)else cc=db<=ac.sidebarWidth end;if cc then self:scrollSidebar(ab)return true end;return -ca.mouse_scroll(self,ab,bb,cb)end;return false end -function _b:setCursor(ab,bb,cb,db)local _c=self:_getSidebarMetrics() -if self.parent then -local ac,bc=self:calculatePosition()local cc,dc -if _c.sidebarPosition=="right"then cc=ab+ac-1;dc=bb+bc-1 else cc=ab+ac-1 + -_c.sidebarWidth;dc=bb+bc-1 end -if - -(cc<1)or(cc>self.parent.get("width"))or(dc<1)or(dc>self.parent.get("height"))then return self.parent:setCursor(cc,dc,false)end;return self.parent:setCursor(cc,dc,cb,db)end;return self end -function _b:render()ba.render(self)local ab=self.getResolved("height") -local bb=self.getResolved("foreground")local cb=self.getResolved("sidebarBackground") -local db=self:_getSidebarMetrics()local _c=db.sidebarWidth or 12;for y=1,ab do -ba.multiBlit(self,1,y,_c,1," ",da[bb],da[cb])end -local ac=self.getResolved("activeTab") -for bc,cc in ipairs(db.positions)do local dc= -(cc.id==ac)and self.getResolved("activeTabBackground")or cb -local _d= -(cc.id==ac)and self.getResolved("activeTabTextColor")or bb;local ad=cc.displayHeight or(cc.y2 -cc.y1 +1)for dy=0,ad-1 -do -ba.multiBlit(self,1,cc.y1 +dy,_c,1," ",da[bb],da[dc])end;local bd=cc.title;if#bd>_c-2 then -bd=bd:sub(1,_c-2)end;ba.textFg(self,2,cc.y1,bd,_d)end;if not self.getResolved("childrenSorted")then -self:sortChildren()end -if -not self.getResolved("childrenEventsSorted")then for bc in pairs(self._values.childrenEvents or{})do -self:sortChildrenEvents(bc)end end -for bc,cc in -ipairs(self.getResolved("visibleChildren")or{})do -if cc==self then error("CIRCULAR REFERENCE DETECTED!")return end;cc:render()cc:postRender()end end -function _b:sortChildrenEvents(ab) -local bb=self._values.childrenEvents and self._values.childrenEvents[ab] -if bb then local cb={}for db,_c in ipairs(bb)do -if self:isChildVisible(_c)then table.insert(cb,_c)end end -for i=2,#cb do local db=cb[i] -local _c=db.get("z")local ac=i-1 -while ac>0 do local bc=cb[ac].get("z")if bc>_c then cb[ac+1]=cb[ac] -ac=ac-1 else break end end;cb[ac+1]=db end -self._values.visibleChildrenEvents=self._values.visibleChildrenEvents or{}self._values.visibleChildrenEvents[ab]=cb end;self.set("childrenEventsSorted",true)return self end;return _b end -project["elements/Input.lua"] = function(...) local d=require("elements/VisualElement") -local _a=require("libraries/colorHex")local aa=setmetatable({},d)aa.__index=aa -aa.defineProperty(aa,"text",{default="",type="string",canTriggerRender=true,setter=function(ba,ca) -ba.set("cursorPos",math.min( -#ca+1,ba.getResolved("cursorPos")))ba:updateViewport()return ca end}) -aa.defineProperty(aa,"cursorPos",{default=1,type="number"}) -aa.defineProperty(aa,"viewOffset",{default=0,type="number",canTriggerRender=true}) -aa.defineProperty(aa,"maxLength",{default=nil,type="number"}) -aa.defineProperty(aa,"placeholder",{default="...",type="string"}) -aa.defineProperty(aa,"placeholderColor",{default=colors.gray,type="color"}) -aa.defineProperty(aa,"pattern",{default=nil,type="string"}) -aa.defineProperty(aa,"cursorColor",{default=nil,type="number"}) -aa.defineProperty(aa,"replaceChar",{default=nil,type="string",canTriggerRender=true})aa.defineEvent(aa,"mouse_click") -aa.defineEvent(aa,"mouse_up")aa.defineEvent(aa,"key")aa.defineEvent(aa,"char") -aa.defineEvent(aa,"paste") -function aa.new()local ba=setmetatable({},aa):__init() -ba.class=aa;ba.set("width",8)ba.set("z",3)return ba end -function aa:init(ba,ca)d.init(self,ba,ca)self.set("type","Input")return self end -function aa:setCursor(ba,ca,da,_b) -ba=math.min(self.getResolved("width"),math.max(1,ba))return d.setCursor(self,ba,ca,da,_b)end -function aa:char(ba) -if not self:hasState("focused")then return false end;local ca=self.getResolved("text") -local da=self.getResolved("cursorPos")local _b=self.getResolved("maxLength") -local ab=self.getResolved("pattern")if _b and#ca>=_b then return false end;if ab and not ba:match(ab)then return -false end -self.set("text",ca:sub(1,da-1)..ba..ca:sub(da))self.set("cursorPos",da+1)self:updateViewport() -local bb= -self.getResolved("cursorPos")-self.getResolved("viewOffset") -self:setCursor(bb,1,true,self.getResolved("cursorColor")or -self.getResolved("foreground"))d.char(self,ba)return true end -function aa:key(ba,ca) -if not self:hasState("focused")then return false end;local da=self.getResolved("cursorPos") -local _b=self.getResolved("text")local ab=self.getResolved("viewOffset") -local bb=self.getResolved("width") -if ba==keys.left then if da>1 then self.set("cursorPos",da-1) -if da-1 <=ab then self.set("viewOffset",math.max(0, -da-2))end end elseif ba==keys.right then if da<=#_b then self.set("cursorPos", -da+1)if da-ab>=bb then -self.set("viewOffset",da-bb+1)end end elseif -ba==keys.backspace then if da>1 then -self.set("text",_b:sub(1,da-2).._b:sub(da))self.set("cursorPos",da-1)self:updateRender() -self:updateViewport()end elseif -ba==keys.delete then -if da<=#_b then -self.set("text",_b:sub(1,da-1).._b:sub(da+1))self:updateRender()self:updateViewport()end elseif ba==keys.home then self.set("cursorPos",1) -self.set("viewOffset",0)elseif ba==keys["end"]then self.set("cursorPos",#_b+1)self:set("viewOffset",math.max(0, -#_b-bb+1))elseif -ba==keys.enter then -self:fireEvent("submit",self.getResolved("text"))end -local cb=self.getResolved("cursorPos")-self.getResolved("viewOffset") -self:setCursor(cb,1,true,self.getResolved("cursorColor")or -self.getResolved("foreground"))d.key(self,ba,ca)return true end -function aa:mouse_click(ba,ca,da) -if d.mouse_click(self,ba,ca,da)then -local _b,ab=self:getRelativePosition(ca,da)local bb=self.getResolved("text") -local cb=self.getResolved("viewOffset")local db=#bb+1;local _c=math.min(db,cb+_b) -self.set("cursorPos",_c)local ac=_c-cb -self:setCursor(ac,1,true,self.getResolved("cursorColor")or -self.getResolved("foreground"))return true end;return false end -function aa:updateViewport()local ba=self.getResolved("width") -local ca=self.getResolved("cursorPos")local da=self.getResolved("viewOffset")local _b=# -self.getResolved("text") -if ca-da>=ba then -self.set("viewOffset",ca-ba+1)elseif ca<=da then self.set("viewOffset",ca-1)end -self.set("viewOffset",math.max(0,math.min(self.getResolved("viewOffset"),_b-ba+1)))return self end -function aa:onSubmit(ba)self:registerCallback("submit",ba)return self end -function aa:focus()d.focus(self) -self:setCursor(self.getResolved("cursorPos")- -self.getResolved("viewOffset"),1,true,self.getResolved("cursorColor")or -self.getResolved("foreground"))self:updateRender()end -function aa:blur()d.blur(self) -self:setCursor(1,1,false,self.getResolved("cursorColor")or -self.getResolved("foreground"))self:updateRender()end -function aa:paste(ba) -if not self:hasState("focused")then return false end;local ca=self.getResolved("text") -local da=self.getResolved("cursorPos")local _b=self.getResolved("maxLength") -local ab=self.getResolved("pattern")local bb=ca:sub(1,da-1)..ba..ca:sub(da)if -_b and#bb>_b then bb=bb:sub(1,_b)end;if ab and not bb:match(ab)then -return false end;self.set("text",bb) -self.set("cursorPos",da+#ba)self:updateViewport()end -function aa:render()local ba=self.getResolved("text") -local ca=self.getResolved("viewOffset")local da=self.getResolved("placeholder") -local _b=self:hasState("focused") -local ab,bb=self.getResolved("width"),self.getResolved("height")local cb=self.getResolved("replaceChar") -self:multiBlit(1,1,ab,bb," ",_a[self.getResolved("foreground")],_a[self.getResolved("background")])if#ba==0 and#da~=0 and not _b then -self:textFg(1,1,da:sub(1,ab),self.getResolved("placeholderColor"))return end;if(_b)then -self:setCursor( -self.getResolved("cursorPos")-ca,1,true,self.getResolved("cursorColor")or -self.getResolved("foreground"))end;local db=ba:sub(ca+1, -ca+ab) -if cb and#cb>0 then db=cb:rep(#db)end -self:textFg(1,1,db,self.getResolved("foreground"))end;return aa end -project["elements/Toast.lua"] = function(...) local d=require("elementManager") -local _a=d.getElement("VisualElement")local aa=setmetatable({},_a)aa.__index=aa -aa.defineProperty(aa,"title",{default="",type="string",canTriggerRender=true}) -aa.defineProperty(aa,"message",{default="",type="string",canTriggerRender=true}) -aa.defineProperty(aa,"duration",{default=3,type="number"}) -aa.defineProperty(aa,"toastType",{default="default",type="string",canTriggerRender=true}) -aa.defineProperty(aa,"callback",{default=nil,type="function"}) -aa.defineProperty(aa,"autoHide",{default=true,type="boolean"}) -aa.defineProperty(aa,"active",{default=false,type="boolean",canTriggerRender=true}) -aa.defineProperty(aa,"colorMap",{default={success=colors.green,error=colors.red,warning=colors.orange,info=colors.lightBlue,default=colors.gray},type="table"})aa.defineEvent(aa,"timer")function aa.new() -local ba=setmetatable({},aa):__init()ba.class=aa;ba.set("width",30)ba.set("height",3) -ba.set("z",100)return ba end;function aa:init(ba,ca) -_a.init(self,ba,ca)return self end -function aa:show(ba,ca,da,_b)local ab,bb,cb -if type(ca)=="string"then ab=ba -bb=ca;cb=da or self.getResolved("duration")elseif type(ca)== -"number"then ab=""bb=ba;cb=ca else ab=""bb=ba -cb=self.getResolved("duration")end;self.set("title",ab)self.set("message",bb) -self.set("active",true)self.set("callback",_b)if self._hideTimerId then -os.cancelTimer(self._hideTimerId)self._hideTimerId=nil end -if -self.getResolved("autoHide")and cb>0 then self._hideTimerId=os.startTimer(cb)end;return self end -function aa:hide()self.set("active",false)self.set("title","") -self.set("message","")if self._hideTimerId then os.cancelTimer(self._hideTimerId) -self._hideTimerId=nil end;return self end;function aa:success(ba,ca,da,_b)self.set("toastType","success") -return self:show(ba,ca,da,_b)end -function aa:error(ba,ca,da,_b) -self.set("toastType","error")return self:show(ba,ca,da,_b)end;function aa:warning(ba,ca,da,_b)self.set("toastType","warning") -return self:show(ba,ca,da,_b)end -function aa:info(ba,ca,da,_b) -self.set("toastType","info")return self:show(ba,ca,da,_b)end -function aa:dispatchEvent(ba,...)_a.dispatchEvent(self,ba,...) -if ba=="timer"then -local ca=select(1,...) -if ca==self._hideTimerId then self._hideTimerId=nil -local da=self.getResolved("callback")if da then da(self)end;self:hide()end end end -function aa:render()_a.render(self) -if not self.getResolved("active")then return end;local ba=self.getResolved("width") -local ca=self.getResolved("height")local da=self.getResolved("title") -local _b=self.getResolved("message")local ab=self.getResolved("toastType") -local bb=self.getResolved("colorMap")local cb=bb[ab]or bb.default -local db=self.getResolved("foreground")local _c=1;local ac=1;if da~=""then local bc=da:sub(1,ba-_c+1) -self:textFg(_c,ac,bc,cb)ac=ac+1 end -if _b~=""and ac<=ca then -local bc=ba-_c+1;local cc={} -for _d in _b:gmatch("%S+")do table.insert(cc,_d)end;local dc="" -for _d,ad in ipairs(cc)do if#dc+#ad+1 >bc then if ac<=ca then -self:textFg(_c,ac,dc,db)ac=ac+1;dc=ad else break end else -dc=dc==""and ad or dc.." "..ad end end -if dc~=""and ac<=ca then self:textFg(_c,ac,dc,db)end end end;return aa end -project["elements/BigFont.lua"] = function(...) local _b=require("libraries/colorHex") -local ab={{"\32\32\32\137\156\148\158\159\148\135\135\144\159\139\32\136\157\32\159\139\32\32\143\32\32\143\32\32\32\32\32\32\32\32\147\148\150\131\148\32\32\32\151\140\148\151\140\147","\32\32\32\149\132\149\136\156\149\144\32\133\139\159\129\143\159\133\143\159\133\138\32\133\138\32\133\32\32\32\32\32\32\150\150\129\137\156\129\32\32\32\133\131\129\133\131\132","\32\32\32\130\131\32\130\131\32\32\129\32\32\32\32\130\131\32\130\131\32\32\32\32\143\143\143\32\32\32\32\32\32\130\129\32\130\135\32\32\32\32\131\32\32\131\32\131","\139\144\32\32\143\148\135\130\144\149\32\149\150\151\149\158\140\129\32\32\32\135\130\144\135\130\144\32\149\32\32\139\32\159\148\32\32\32\32\159\32\144\32\148\32\147\131\132","\159\135\129\131\143\149\143\138\144\138\32\133\130\149\149\137\155\149\159\143\144\147\130\132\32\149\32\147\130\132\131\159\129\139\151\129\148\32\32\139\131\135\133\32\144\130\151\32","\32\32\32\32\32\32\130\135\32\130\32\129\32\129\129\131\131\32\130\131\129\140\141\132\32\129\32\32\129\32\32\32\32\32\32\32\131\131\129\32\32\32\32\32\32\32\32\32","\32\32\32\32\149\32\159\154\133\133\133\144\152\141\132\133\151\129\136\153\32\32\154\32\159\134\129\130\137\144\159\32\144\32\148\32\32\32\32\32\32\32\32\32\32\32\151\129","\32\32\32\32\133\32\32\32\32\145\145\132\141\140\132\151\129\144\150\146\129\32\32\32\138\144\32\32\159\133\136\131\132\131\151\129\32\144\32\131\131\129\32\144\32\151\129\32","\32\32\32\32\129\32\32\32\32\130\130\32\32\129\32\129\32\129\130\129\129\32\32\32\32\130\129\130\129\32\32\32\32\32\32\32\32\133\32\32\32\32\32\129\32\129\32\32","\150\156\148\136\149\32\134\131\148\134\131\148\159\134\149\136\140\129\152\131\32\135\131\149\150\131\148\150\131\148\32\148\32\32\148\32\32\152\129\143\143\144\130\155\32\134\131\148","\157\129\149\32\149\32\152\131\144\144\131\148\141\140\149\144\32\149\151\131\148\32\150\32\150\131\148\130\156\133\32\144\32\32\144\32\130\155\32\143\143\144\32\152\129\32\134\32","\130\131\32\131\131\129\131\131\129\130\131\32\32\32\129\130\131\32\130\131\32\32\129\32\130\131\32\130\129\32\32\129\32\32\133\32\32\32\129\32\32\32\130\32\32\32\129\32","\150\140\150\137\140\148\136\140\132\150\131\132\151\131\148\136\147\129\136\147\129\150\156\145\138\143\149\130\151\32\32\32\149\138\152\129\149\32\32\157\152\149\157\144\149\150\131\148","\149\143\142\149\32\149\149\32\149\149\32\144\149\32\149\149\32\32\149\32\32\149\32\149\149\32\149\32\149\32\144\32\149\149\130\148\149\32\32\149\32\149\149\130\149\149\32\149","\130\131\129\129\32\129\131\131\32\130\131\32\131\131\32\131\131\129\129\32\32\130\131\32\129\32\129\130\131\32\130\131\32\129\32\129\131\131\129\129\32\129\129\32\129\130\131\32","\136\140\132\150\131\148\136\140\132\153\140\129\131\151\129\149\32\149\149\32\149\149\32\149\137\152\129\137\152\129\131\156\133\149\131\32\150\32\32\130\148\32\152\137\144\32\32\32","\149\32\32\149\159\133\149\32\149\144\32\149\32\149\32\149\32\149\150\151\129\138\155\149\150\130\148\32\149\32\152\129\32\149\32\32\32\150\32\32\149\32\32\32\32\32\32\32","\129\32\32\130\129\129\129\32\129\130\131\32\32\129\32\130\131\32\32\129\32\129\32\129\129\32\129\32\129\32\131\131\129\130\131\32\32\32\129\130\131\32\32\32\32\140\140\132","\32\154\32\159\143\32\149\143\32\159\143\32\159\144\149\159\143\32\159\137\145\159\143\144\149\143\32\32\145\32\32\32\145\149\32\144\32\149\32\143\159\32\143\143\32\159\143\32","\32\32\32\152\140\149\151\32\149\149\32\145\149\130\149\157\140\133\32\149\32\154\143\149\151\32\149\32\149\32\144\32\149\149\153\32\32\149\32\149\133\149\149\32\149\149\32\149","\32\32\32\130\131\129\131\131\32\130\131\32\130\131\129\130\131\129\32\129\32\140\140\129\129\32\129\32\129\32\137\140\129\130\32\129\32\130\32\129\32\129\129\32\129\130\131\32","\144\143\32\159\144\144\144\143\32\159\143\144\159\138\32\144\32\144\144\32\144\144\32\144\144\32\144\144\32\144\143\143\144\32\150\129\32\149\32\130\150\32\134\137\134\134\131\148","\136\143\133\154\141\149\151\32\129\137\140\144\32\149\32\149\32\149\154\159\133\149\148\149\157\153\32\154\143\149\159\134\32\130\148\32\32\149\32\32\151\129\32\32\32\32\134\32","\133\32\32\32\32\133\129\32\32\131\131\32\32\130\32\130\131\129\32\129\32\130\131\129\129\32\129\140\140\129\131\131\129\32\130\129\32\129\32\130\129\32\32\32\32\32\129\32","\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32","\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32","\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32","\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32","\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32","\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32","\32\32\32\32\145\32\159\139\32\151\131\132\155\143\132\134\135\145\32\149\32\158\140\129\130\130\32\152\147\155\157\134\32\32\144\144\32\32\32\32\32\32\152\131\155\131\131\129","\32\32\32\32\149\32\149\32\145\148\131\32\149\32\149\140\157\132\32\148\32\137\155\149\32\32\32\149\154\149\137\142\32\153\153\32\131\131\149\131\131\129\149\135\145\32\32\32","\32\32\32\32\129\32\130\135\32\131\131\129\134\131\132\32\129\32\32\129\32\131\131\32\32\32\32\130\131\129\32\32\32\32\129\129\32\32\32\32\32\32\130\131\129\32\32\32","\150\150\32\32\148\32\134\32\32\132\32\32\134\32\32\144\32\144\150\151\149\32\32\32\32\32\32\145\32\32\152\140\144\144\144\32\133\151\129\133\151\129\132\151\129\32\145\32","\130\129\32\131\151\129\141\32\32\142\32\32\32\32\32\149\32\149\130\149\149\32\143\32\32\32\32\142\132\32\154\143\133\157\153\132\151\150\148\151\158\132\151\150\148\144\130\148","\32\32\32\140\140\132\32\32\32\32\32\32\32\32\32\151\131\32\32\129\129\32\32\32\32\134\32\32\32\32\32\32\32\129\129\32\129\32\129\129\130\129\129\32\129\130\131\32","\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\150\151\129\150\131\132\140\143\144\143\141\145\137\140\148\141\141\144\157\142\32\159\140\32\151\134\32\157\141\32","\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\151\151\32\154\143\132\157\140\32\157\140\32\157\140\32\157\140\32\32\149\32\32\149\32\32\149\32\32\149\32","\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\131\129\32\134\32\131\131\129\131\131\129\131\131\129\131\131\129\130\131\32\130\131\32\130\131\32\130\131\32","\151\131\148\152\137\145\155\140\144\152\142\145\153\140\132\153\137\32\154\142\144\155\159\132\150\156\148\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\136\32\151\140\132","\151\32\149\151\155\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\152\137\144\157\129\149\149\32\149\149\32\149\149\32\149\149\32\149\130\150\32\32\157\129\149\32\149","\131\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\129\32\130\131\32\133\131\32","\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\159\159\144\152\140\144\156\143\32\159\141\129\153\140\132\157\141\32\130\145\32\32\147\32\136\153\32\130\146\32","\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\149\157\134\154\143\132\157\140\133\157\140\133\157\140\133\157\140\133\32\149\32\32\149\32\32\149\32\32\149\32","\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\130\131\32\134\32\130\131\129\130\131\129\130\131\129\130\131\129\32\129\32\32\129\32\32\129\32\32\129\32","\159\134\144\137\137\32\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\32\132\32\159\143\32\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\138\32\146\130\144","\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\131\147\129\138\134\149\149\32\149\149\32\149\149\32\149\149\32\149\154\143\149\32\157\129\154\143\149","\130\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\129\130\131\129\130\131\129\130\131\129\140\140\129\130\131\32\140\140\129"},{"000110000110110000110010101000000010000000100101","000000110110000000000010101000000010000000100101","000000000000000000000000000000000000000000000000","100010110100000010000110110000010100000100000110","000000110000000010110110000110000000000000110000","000000000000000000000000000000000000000000000000","000000110110000010000000100000100000000000000010","000000000110110100010000000010000000000000000100","000000000000000000000000000000000000000000000000","010000000000100110000000000000000000000110010000","000000000000000000000000000010000000010110000000","000000000000000000000000000000000000000000000000","011110110000000100100010110000000100000000000000","000000000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000","110000110110000000000000000000010100100010000000","000010000000000000110110000000000100010010000000","000000000000000000000000000000000000000000000000","010110010110100110110110010000000100000110110110","000000000000000000000110000000000110000000000000","000000000000000000000000000000000000000000000000","010100010110110000000000000000110000000010000000","110110000000000000110000110110100000000010000000","000000000000000000000000000000000000000000000000","000100011111000100011111000100011111000100011111","000000000000100100100100011011011011111111111111","000000000000000000000000000000000000000000000000","000100011111000100011111000100011111000100011111","000000000000100100100100011011011011111111111111","100100100100100100100100100100100100100100100100","000000110100110110000010000011110000000000011000","000000000100000000000010000011000110000000001000","000000000000000000000000000000000000000000000000","010000100100000000000000000100000000010010110000","000000000000000000000000000000110110110110110000","000000000000000000000000000000000000000000000000","110110110110110110000000110110110110110110110110","000000000000000000000110000000000000000000000000","000000000000000000000000000000000000000000000000","000000000000110110000110010000000000000000010010","000010000000000000000000000000000000000000000000","000000000000000000000000000000000000000000000000","110110110110110110110000110110110110000000000000","000000000000000000000110000000000000000000000000","000000000000000000000000000000000000000000000000","110110110110110110110000110000000000000000010000","000000000000000000000000100000000000000110000110","000000000000000000000000000000000000000000000000"}}local bb={}local cb={} -do local dc=0;local _d=#ab[1]local ad=#ab[1][1] -for i=1,_d,3 do -for j=1,ad,3 do -local bd=string.char(dc)local cd={}cd[1]=ab[1][i]:sub(j,j+2) -cd[2]=ab[1][i+1]:sub(j,j+2)cd[3]=ab[1][i+2]:sub(j,j+2)local dd={}dd[1]=ab[2][i]:sub(j, -j+2)dd[2]=ab[2][i+1]:sub(j,j+2)dd[3]=ab[2][ -i+2]:sub(j,j+2)cb[bd]={cd,dd}dc=dc+1 end end;bb[1]=cb end -local function db(dc,_d)local ad={["0"]="1",["1"]="0"}if dc<=#bb then return true end -for f=#bb+1,dc do local bd={}local cd=bb[ -f-1] -for char=0,255 do local dd=string.char(char)local __a={}local a_a={} -local b_a=cd[dd][1]local c_a=cd[dd][2] -for i=1,#b_a do local d_a,_aa,aaa,baa,caa,daa={},{},{},{},{},{} -for j=1,#b_a[1]do -local _ba=cb[b_a[i]:sub(j,j)][1]table.insert(d_a,_ba[1]) -table.insert(_aa,_ba[2])table.insert(aaa,_ba[3]) -local aba=cb[b_a[i]:sub(j,j)][2] -if c_a[i]:sub(j,j)=="1"then -table.insert(baa,(aba[1]:gsub("[01]",ad))) -table.insert(caa,(aba[2]:gsub("[01]",ad))) -table.insert(daa,(aba[3]:gsub("[01]",ad)))else table.insert(baa,aba[1]) -table.insert(caa,aba[2])table.insert(daa,aba[3])end end;table.insert(__a,table.concat(d_a)) -table.insert(__a,table.concat(_aa))table.insert(__a,table.concat(aaa)) -table.insert(a_a,table.concat(baa))table.insert(a_a,table.concat(caa)) -table.insert(a_a,table.concat(daa))end;bd[dd]={__a,a_a}if _d then _d="Font"..f.."Yeld"..char -os.queueEvent(_d)os.pullEvent(_d)end end;bb[f]=bd end;return true end -local function _c(dc,_d,ad,bd,cd) -if not type(_d)=="string"then error("Not a String",3)end -local dd=type(ad)=="string"and ad:sub(1,1)or _b[ad]or -error("Wrong Front Color",3) -local __a=type(bd)=="string"and bd:sub(1,1)or _b[bd]or -error("Wrong Back Color",3)if(bb[dc]==nil)then db(3,false)end;local a_a=bb[dc]or -error("Wrong font size selected",3)if _d==""then -return{{""},{""},{""}}end;local b_a={} -for daa in _d:gmatch('.')do table.insert(b_a,daa)end;local c_a={}local d_a=#a_a[b_a[1]][1] -for nLine=1,d_a do local daa={}for i=1,#b_a do -daa[i]= -a_a[b_a[i]]and a_a[b_a[i]][1][nLine]or""end;c_a[nLine]=table.concat(daa)end;local _aa={}local aaa={}local baa={["0"]=dd,["1"]=__a}local caa={["0"]=__a,["1"]=dd} -for nLine=1,d_a -do local daa={}local _ba={} -for i=1,#b_a do local aba= -a_a[b_a[i]]and a_a[b_a[i]][2][nLine]or"" -daa[i]=aba:gsub("[01]",cd and -{["0"]=ad:sub(i,i),["1"]=bd:sub(i,i)}or baa) -_ba[i]=aba:gsub("[01]", -cd and{["0"]=bd:sub(i,i),["1"]=ad:sub(i,i)}or caa)end;_aa[nLine]=table.concat(daa) -aaa[nLine]=table.concat(_ba)end;return{c_a,_aa,aaa}end;local ac=require("elementManager") -local bc=ac.getElement("VisualElement")local cc=setmetatable({},bc)cc.__index=cc -cc.defineProperty(cc,"text",{default="BigFont",type="string",canTriggerRender=true,setter=function(dc,_d) -dc.bigfontText=_c(dc.getResolved("fontSize"),_d,dc.getResolved("foreground"),dc.getResolved("background"))return _d end}) -cc.defineProperty(cc,"fontSize",{default=1,type="number",canTriggerRender=true,setter=function(dc,_d) -dc.bigfontText=_c(_d,dc.getResolved("text"),dc.getResolved("foreground"),dc.getResolved("background"))return _d end})function cc.new()local dc=setmetatable({},cc):__init() -dc.class=cc;dc.set("width",16)dc.set("height",3)dc.set("z",5) -return dc end -function cc:init(dc,_d) -bc.init(self,dc,_d)self.set("type","BigFont") -self:observe("background",function(ad,bd) -ad.bigfontText=_c(ad.getResolved("fontSize"),ad.getResolved("text"),ad.getResolved("foreground"),bd)end) -self:observe("foreground",function(ad,bd) -ad.bigfontText=_c(ad.getResolved("fontSize"),ad.getResolved("text"),bd,ad.getResolved("background"))end)end -function cc:render()bc.render(self) -if(self.bigfontText)then -local dc,_d=self.getResolved("x"),self.getResolved("y")local ad=self.getResolved("width") -for i=1,#self.bigfontText[1]do -local bd=self.bigfontText[1][i]:sub(1,ad) -local cd=self.bigfontText[2][i]:sub(1,ad) -local dd=self.bigfontText[3][i]:sub(1,ad)self:blit(dc,_d+i-1,bd,cd,dd)end end end;return cc end -project["elements/Switch.lua"] = function(...) local _a=require("elementManager") -local aa=_a.getElement("VisualElement")local ba=require("libraries/colorHex") -local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"checked",{default=false,type="boolean",canTriggerRender=true}) -ca.defineProperty(ca,"text",{default="",type="string",canTriggerRender=true}) -ca.defineProperty(ca,"autoSize",{default=false,type="boolean"}) -ca.defineProperty(ca,"onBackground",{default=colors.green,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"offBackground",{default=colors.red,type="number",canTriggerRender=true})ca.defineEvent(ca,"mouse_click") -ca.defineEvent(ca,"mouse_up") -function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;da.set("width",2)da.set("height",1)da.set("z",5) -da.set("backgroundEnabled",true)return da end -function ca:init(da,_b)aa.init(self,da,_b)self.set("type","Switch")end -function ca:mouse_click(da,_b,ab)if aa.mouse_click(self,da,_b,ab)then -self.set("checked",not self.getResolved("checked"))return true end;return false end -function ca:render()local da=self.getResolved("checked") -local _b=self.getResolved("text")local ab=self.getResolved("width") -local bb=self.getResolved("height")local cb=self.getResolved("foreground") -local db=da and -self.getResolved("onBackground")or self.getResolved("offBackground")self:multiBlit(1,1,ab,bb," ",ba[cb],ba[db]) -local _c=math.floor(ab/2)local ac=da and(ab-_c+1)or 1 -self:multiBlit(ac,1,_c,bb," ",ba[cb],ba[self.getResolved("background")])if _b~=""then self:textFg(ab+2,1,_b,cb)end end;return ca end -project["elements/Frame.lua"] = function(...) local aa=require("elementManager") -local ba=aa.getElement("VisualElement")local ca=aa.getElement("Container")local da=setmetatable({},ca) -da.__index=da -da.defineProperty(da,"draggable",{default=false,type="boolean"}) -da.defineProperty(da,"draggingMap",{default={{x=1,y=1,width="width",height=1}},type="table"}) -da.defineProperty(da,"scrollable",{default=false,type="boolean"})da.defineEvent(da,"mouse_click") -da.defineEvent(da,"mouse_drag")da.defineEvent(da,"mouse_up") -da.defineEvent(da,"mouse_scroll")function da.new()local ab=setmetatable({},da):__init() -ab.class=da;ab.set("width",12)ab.set("height",6)ab.set("z",10) -return ab end -function da:init(ab,bb) -ca.init(self,ab,bb)self.set("type","Frame")return self end -function da:mouse_click(ab,bb,cb) -if self:isInBounds(bb,cb)then -if self.getResolved("draggable")then -local db,_c=self:getRelativePosition(bb,cb)local ac=self.getResolved("draggingMap") -for bc,cc in ipairs(ac)do -local dc=cc.width or 1;local _d=cc.height or 1 -if type(dc)=="string"and dc=="width"then -dc=self.getResolved("width")elseif type(dc)=="function"then dc=dc(self)end -if type(_d)=="string"and _d=="height"then -_d=self.getResolved("height")elseif type(_d)=="function"then _d=_d(self)end;local ad=cc.y or 1 -if -db>=cc.x and db<=cc.x+dc-1 and _c>=ad and _c<=ad+_d-1 then -self.dragStartX=bb-self.getResolved("x")self.dragStartY=cb-self.getResolved("y") -self.dragging=true;return true end end end;return ca.mouse_click(self,ab,bb,cb)end;return false end -function da:mouse_up(ab,bb,cb)if self.dragging then self.dragging=false;self.dragStartX=nil -self.dragStartY=nil;return true end;return -ca.mouse_up(self,ab,bb,cb)end -function da:mouse_drag(ab,bb,cb) -if self.dragging then local db=bb-self.dragStartX -local _c=cb-self.dragStartY;self.set("x",db)self.set("y",_c)return true end;return ca.mouse_drag(self,ab,bb,cb)end -function da:getChildrenHeight()local ab=0;local bb=self.getResolved("children") -for cb,db in ipairs(bb)do if -db.get("visible")then local _c=db.get("y")local ac=db.get("height")local bc=_c+ac-1 -if bc>ab then ab=bc end end end;return ab end -local function _b(ab,bb,...)local cb={...} -if bb and bb:find("mouse_")then local db,_c,ac=... -local bc,cc=ab.getResolved("offsetX"),ab.getResolved("offsetY")local dc,_d=ab:getRelativePosition(_c+bc,ac+cc) -cb={db,dc,_d}end;return cb end -function da:mouse_scroll(ab,bb,cb) -if(ba.mouse_scroll(self,ab,bb,cb))then -local db=_b(self,"mouse_scroll",ab,bb,cb) -local _c,ac=self:callChildrenEvent(true,"mouse_scroll",table.unpack(db))if _c then return true end -if self.getResolved("scrollable")then -local bc=self.getResolved("height")local cc=self:getChildrenHeight() -local dc=self.getResolved("offsetY")local _d=math.max(0,cc-bc)local ad=dc+ab -ad=math.max(0,math.min(_d,ad))self.set("offsetY",ad)return true end end;return false end;return da end -project["elements/Container.lua"] = function(...) local _b=require("elementManager") -local ab=require("errorManager")local bb=_b.getElement("VisualElement") -local cb=require("layoutManager")local db=require("libraries/expect") -local _c=require("libraries/utils").split;local ac=setmetatable({},bb)ac.__index=ac -ac.defineProperty(ac,"children",{default={},type="table"}) -ac.defineProperty(ac,"childrenSorted",{default=true,type="boolean"}) -ac.defineProperty(ac,"childrenEventsSorted",{default=true,type="boolean"}) -ac.defineProperty(ac,"childrenEvents",{default={},type="table"}) -ac.defineProperty(ac,"eventListenerCount",{default={},type="table"}) -ac.defineProperty(ac,"focusedChild",{default=nil,type="table",allowNil=true,setter=function(dc,_d,ad)local bd=dc._values.focusedChild -if _d==bd then return _d end -if bd then -if bd:isType("Container")then bd.set("focusedChild",nil,true)end;bd:setFocused(false,true)end;if _d and not ad then _d:setFocused(true,true)if dc.parent then -dc.parent:setFocusedChild(dc)end end;return -_d end}) -ac.defineProperty(ac,"visibleChildren",{default={},type="table"}) -ac.defineProperty(ac,"visibleChildrenEvents",{default={},type="table"}) -ac.defineProperty(ac,"offsetX",{default=0,type="number",canTriggerRender=true,setter=function(dc,_d)dc.set("childrenSorted",false) -dc.set("childrenEventsSorted",false)return _d end}) -ac.defineProperty(ac,"offsetY",{default=0,type="number",canTriggerRender=true,setter=function(dc,_d)dc.set("childrenSorted",false) -dc.set("childrenEventsSorted",false)return _d end}) -ac.combineProperties(ac,"offset","offsetX","offsetY") -for dc,_d in pairs(_b:getElementList())do -local ad=dc:sub(1,1):upper()..dc:sub(2) -if ad~="BaseFrame"then -ac["add"..ad]=function(bd,...)db(1,bd,"table") -local cd=bd.basalt.create(dc,...)bd:addChild(cd)return cd end -ac["addDelayed"..ad]=function(bd,cd)db(1,bd,"table") -local dd=bd.basalt.create(dc,cd,true,bd)return dd end end end;function ac.new()local dc=setmetatable({},ac):__init() -dc.class=ac;return dc end -function ac:init(dc,_d) -bb.init(self,dc,_d)self.set("type","Container") -self:observe("width",function() -self.set("childrenSorted",false)self.set("childrenEventsSorted",false) -self:updateRender()end) -self:observe("height",function()self.set("childrenSorted",false) -self.set("childrenEventsSorted",false)self:updateRender()end)end -function ac:isChildVisible(dc) -if not dc:isType("VisualElement")then return false end;if(dc.get("visible")==false)then return false end;if(dc._destroyed)then return -false end -local _d,ad=self.getResolved("width"),self.getResolved("height") -local bd,cd=self.getResolved("offsetX"),self.getResolved("offsetY")local dd,__a=dc.get("x"),dc.get("y") -local a_a,b_a=dc.get("width"),dc.get("height")local c_a;local d_a -if(dc.get("ignoreOffset"))then c_a=dd;d_a=__a else c_a=dd-bd;d_a=__a-cd end;return -(c_a+a_a>0)and(c_a<=_d)and(d_a+b_a>0)and(d_a<=ad)end -function ac:addChild(dc) -if dc==self then error("Cannot add container to itself")end;if(dc~=nil)then table.insert(self._values.children,dc) -dc.parent=self;dc:postInit()self.set("childrenSorted",false) -self:registerChildrenEvents(dc)end;return -self end -local function bc(dc,_d)local ad={}for bd,cd in ipairs(_d)do -if dc:isChildVisible(cd)and cd.get("visible")and not -cd._destroyed then table.insert(ad,cd)end end -for i=2,#ad do -local bd=ad[i]local cd=bd.get("z")local dd=i-1 -while dd>0 do local __a=ad[dd].get("z")if __a>cd then -ad[dd+1]=ad[dd]dd=dd-1 else break end end;ad[dd+1]=bd end;return ad end -function ac:clear()self.set("children",{}) -self.set("childrenEvents",{})self.set("visibleChildren",{}) -self.set("visibleChildrenEvents",{})self.set("childrenSorted",true) -self.set("childrenEventsSorted",true)return self end -function ac:sortChildren()self.set("childrenSorted",true)if self._layoutInstance then -self:updateLayout()end -self.set("visibleChildren",bc(self,self._values.children))return self end -function ac:sortChildrenEvents(dc)if self._values.childrenEvents[dc]then -self._values.visibleChildrenEvents[dc]=bc(self,self._values.childrenEvents[dc])end -self.set("childrenEventsSorted",true)return self end -function ac:registerChildrenEvents(dc)if(dc._registeredEvents==nil)then return end -for _d in -pairs(dc._registeredEvents)do self:registerChildEvent(dc,_d)end;return self end -function ac:registerChildEvent(dc,_d) -if not self._values.childrenEvents[_d]then -self._values.childrenEvents[_d]={}self._values.eventListenerCount[_d]=0;if self.parent then -self.parent:registerChildEvent(self,_d)end end -for ad,bd in ipairs(self._values.childrenEvents[_d])do if bd.get("id")== -dc.get("id")then return self end end;self.set("childrenEventsSorted",false) -table.insert(self._values.childrenEvents[_d],dc)self._values.eventListenerCount[_d]= -self._values.eventListenerCount[_d]+1;return self end -function ac:removeChildrenEvents(dc) -if dc~=nil then -if(dc._registeredEvents==nil)then return self end;for _d in pairs(dc._registeredEvents)do -self:unregisterChildEvent(dc,_d)end end;return self end -function ac:unregisterChildEvent(dc,_d) -if self._values.childrenEvents[_d]then -for ad,bd in -ipairs(self._values.childrenEvents[_d])do -if bd.get("id")==dc.get("id")then -table.remove(self._values.childrenEvents[_d],ad)self._values.eventListenerCount[_d]= -self._values.eventListenerCount[_d]-1 -if -self._values.eventListenerCount[_d]<=0 then -self._values.childrenEvents[_d]=nil;self._values.eventListenerCount[_d]=nil;if self.parent then -self.parent:unregisterChildEvent(self,_d)end end;self.set("childrenEventsSorted",false)break end end end;return self end -function ac:removeChild(dc)if dc==nil then return self end -for _d,ad in ipairs(self._values.children)do if -ad.get("id")==dc.get("id")then -table.remove(self._values.children,_d)dc.parent=nil;break end end;self:removeChildrenEvents(dc)self:updateRender() -self.set("childrenSorted",false)return self end -function ac:getChild(dc) -if type(dc)=="string"then local _d=_c(dc,"/") -for ad,bd in -pairs(self._values.children)do if bd.get("name")==_d[1]then -if#_d==1 then return bd else if(bd:isType("Container"))then return -bd:find(table.concat(_d,"/",2))end end end end end;return nil end -local function cc(dc,_d,...)local ad={...} -if _d and _d:find("mouse_")then local bd,cd,dd=... -local __a,a_a=dc.getResolved("offsetX"),dc.getResolved("offsetY")local b_a,c_a=dc:getRelativePosition(cd+__a,dd+a_a) -ad={bd,b_a,c_a}end;return ad end -function ac:callChildrenEvent(dc,_d,...) -if -dc and not self.getResolved("childrenEventsSorted")then for bd in pairs(self._values.childrenEvents)do -self:sortChildrenEvents(bd)end end -local ad=dc and self.getResolved("visibleChildrenEvents")or -self.getResolved("childrenEvents") -if ad[_d]then local bd=ad[_d]for i=#bd,1,-1 do local cd=bd[i] -if(cd:dispatchEvent(_d,...))then return true,cd end end end -if(ad["*"])then local bd=ad["*"]for i=#bd,1,-1 do local cd=bd[i] -if(cd:dispatchEvent(_d,...))then return true,cd end end end;return false end -function ac:handleEvent(dc,...)bb.handleEvent(self,dc,...)local _d=cc(self,dc,...)return -self:callChildrenEvent(false,dc,table.unpack(_d))end -function ac:mouse_click(dc,_d,ad) -if bb.mouse_click(self,dc,_d,ad)then -local bd=cc(self,"mouse_click",dc,_d,ad) -local cd,dd=self:callChildrenEvent(true,"mouse_click",table.unpack(bd)) -if(cd)then self.set("focusedChild",dd)return true end;self.set("focusedChild",nil)return true end;return false end -function ac:mouse_up(dc,_d,ad)self:mouse_release(dc,_d,ad) -if bb.mouse_up(self,dc,_d,ad)then -local bd=cc(self,"mouse_up",dc,_d,ad) -local cd,dd=self:callChildrenEvent(true,"mouse_up",table.unpack(bd))if(cd)then return true end end;return false end -function ac:mouse_release(dc,_d,ad)bb.mouse_release(self,dc,_d,ad) -local bd=cc(self,"mouse_release",dc,_d,ad) -self:callChildrenEvent(false,"mouse_release",table.unpack(bd))end -function ac:mouse_move(dc,_d,ad) -if bb.mouse_move(self,dc,_d,ad)then -local bd=cc(self,"mouse_move",dc,_d,ad) -local cd,dd=self:callChildrenEvent(true,"mouse_move",table.unpack(bd))if(cd)then return true end end;return false end -function ac:mouse_drag(dc,_d,ad) -if bb.mouse_drag(self,dc,_d,ad)then -local bd=cc(self,"mouse_drag",dc,_d,ad) -local cd,dd=self:callChildrenEvent(true,"mouse_drag",table.unpack(bd))if(cd)then return true end end;return false end -function ac:mouse_scroll(dc,_d,ad) -if(bb.mouse_scroll(self,dc,_d,ad))then -local bd=cc(self,"mouse_scroll",dc,_d,ad) -local cd,dd=self:callChildrenEvent(true,"mouse_scroll",table.unpack(bd))return true end;return false end -function ac:key(dc) -if self.getResolved("focusedChild")then return -self.getResolved("focusedChild"):dispatchEvent("key",dc)end;return true end -function ac:char(dc) -if self.getResolved("focusedChild")then return -self.getResolved("focusedChild"):dispatchEvent("char",dc)end;return true end -function ac:key_up(dc) -if self.getResolved("focusedChild")then return -self.getResolved("focusedChild"):dispatchEvent("key_up",dc)end;return true end -function ac:multiBlit(dc,_d,ad,bd,cd,dd,__a) -local a_a,b_a=self.getResolved("width"),self.getResolved("height")ad=dc<1 and math.min(ad+dc-1,a_a)or -math.min(ad,math.max(0,a_a-dc+1))bd=_d<1 and math.min( -bd+_d-1,b_a)or -math.min(bd,math.max(0,b_a-_d+1))if ad<=0 or -bd<=0 then return self end -bb.multiBlit(self,math.max(1,dc),math.max(1,_d),ad,bd,cd,dd,__a)return self end -function ac:textFg(dc,_d,ad,bd) -local cd,dd=self.getResolved("width"),self.getResolved("height")if _d<1 or _d>dd then return self end -local __a=dc<1 and(2 -dc)or 1 -local a_a=math.min(#ad-__a+1,cd-math.max(1,dc)+1)if a_a<=0 then return self end -bb.textFg(self,math.max(1,dc),math.max(1,_d),ad:sub(__a, -__a+a_a-1),bd)return self end -function ac:textBg(dc,_d,ad,bd) -local cd,dd=self.getResolved("width"),self.getResolved("height")if _d<1 or _d>dd then return self end -local __a=dc<1 and(2 -dc)or 1 -local a_a=math.min(#ad-__a+1,cd-math.max(1,dc)+1)if a_a<=0 then return self end -bb.textBg(self,math.max(1,dc),math.max(1,_d),ad:sub(__a, -__a+a_a-1),bd)return self end -function ac:drawText(dc,_d,ad) -local bd,cd=self.getResolved("width"),self.getResolved("height")if _d<1 or _d>cd then return self end -local dd=dc<1 and(2 -dc)or 1 -local __a=math.min(#ad-dd+1,bd-math.max(1,dc)+1)if __a<=0 then return self end -bb.drawText(self,math.max(1,dc),math.max(1,_d),ad:sub(dd, -dd+__a-1))return self end -function ac:drawFg(dc,_d,ad) -local bd,cd=self.getResolved("width"),self.getResolved("height")if _d<1 or _d>cd then return self end -local dd=dc<1 and(2 -dc)or 1 -local __a=math.min(#ad-dd+1,bd-math.max(1,dc)+1)if __a<=0 then return self end -bb.drawFg(self,math.max(1,dc),math.max(1,_d),ad:sub(dd,dd+__a-1))return self end -function ac:drawBg(dc,_d,ad) -local bd,cd=self.getResolved("width"),self.getResolved("height")if _d<1 or _d>cd then return self end -local dd=dc<1 and(2 -dc)or 1 -local __a=math.min(#ad-dd+1,bd-math.max(1,dc)+1)if __a<=0 then return self end -bb.drawBg(self,math.max(1,dc),math.max(1,_d),ad:sub(dd,dd+__a-1))return self end -function ac:blit(dc,_d,ad,bd,cd) -local dd,__a=self.getResolved("width"),self.getResolved("height")if _d<1 or _d>__a then return self end -local a_a=dc<1 and(2 -dc)or 1 -local b_a=math.min(#ad-a_a+1,dd-math.max(1,dc)+1) -local c_a=math.min(#bd-a_a+1,dd-math.max(1,dc)+1) -local d_a=math.min(#cd-a_a+1,dd-math.max(1,dc)+1)if b_a<=0 then return self end;local _aa=ad:sub(a_a,a_a+b_a-1)local aaa=bd:sub(a_a, -a_a+c_a-1) -local baa=cd:sub(a_a,a_a+d_a-1) -bb.blit(self,math.max(1,dc),math.max(1,_d),_aa,aaa,baa)return self end -function ac:render()bb.render(self)if -not self.getResolved("childrenSorted")then self:sortChildren()end -if not -self.getResolved("childrenEventsSorted")then for dc in pairs(self._values.childrenEvents)do -self:sortChildrenEvents(dc)end end -for dc,_d in ipairs(self.getResolved("visibleChildren"))do if _d==self then -ab.error("CIRCULAR REFERENCE DETECTED!")return end;_d:render() -_d:postRender()end end -function ac:applyLayout(dc,_d) -if self._layoutInstance then cb.destroy(self._layoutInstance)end;self._layoutInstance=cb.apply(self,dc)if _d then -self._layoutInstance.options=_d end;return self end;function ac:updateLayout() -if self._layoutInstance then cb.update(self._layoutInstance)end;return self end -function ac:clearLayout() -if -self._layoutInstance then local dc=require("layoutManager") -dc.destroy(self._layoutInstance)self._layoutInstance=nil end;return self end -function ac:destroy() -if not self:isType("BaseFrame")then -for dc,_d in -ipairs(self._values.children)do if _d.destroy then _d:destroy()end end;self:removeAllObservers()bb.destroy(self)return self else -ab.header="Basalt Error"ab.error("Cannot destroy a BaseFrame.")end end;return ac end -project["elements/Dialog.lua"] = function(...) local d=require("elementManager") -local _a=d.getElement("Frame")local aa=setmetatable({},_a)aa.__index=aa -aa.defineProperty(aa,"title",{default="",type="string",canTriggerRender=true}) -aa.defineProperty(aa,"primaryColor",{default=colors.lime,type="color"}) -aa.defineProperty(aa,"secondaryColor",{default=colors.lightGray,type="color"}) -aa.defineProperty(aa,"buttonForeground",{default=colors.black,type="color"}) -aa.defineProperty(aa,"modal",{default=true,type="boolean"})aa.defineEvent(aa,"mouse_click") -aa.defineEvent(aa,"close") -function aa.new()local ba=setmetatable({},aa):__init() -ba.class=aa;ba.set("z",100)ba.set("width",30) -ba.set("height",10)ba.set("background",colors.gray) -ba.set("foreground",colors.white)ba.set("borderColor",colors.cyan)return ba end -function aa:init(ba,ca)_a.init(self,ba,ca) -self:addBorder({left=true,right=true,top=true,bottom=true})self.set("type","Dialog")return self end -function aa:show()self:center()self.set("visible",true)if -self.getResolved("modal")then self:setFocused(true)end;return self end;function aa:close()self.set("visible",false) -self:fireEvent("close")return self end -function aa:alert(ba,ca,da)self:clear() -self.set("title",ba)self.set("height",8) -self:addLabel({text=ca,x=2,y=3,width=self.getResolved("width")-3,height=3,foreground=colors.white})local _b=10;local ab= -math.floor((self.getResolved("width")-_b)/2)+1 -self:addButton({text="OK",x=ab,y= -self.getResolved("height")-2,width=_b,height=1,background=self.getResolved("primaryColor"),foreground=self.getResolved("buttonForeground")}):onClick(function()if -da then da()end;self:close()end)return self:show()end -function aa:confirm(ba,ca,da)self:clear()self.set("title",ba) -self.set("height",8) -self:addLabel({text=ca,x=2,y=3,width=self.getResolved("width")-3,height=3,foreground=colors.white})local _b=10;local ab=2;local bb=_b*2 +ab -local cb=math.floor( -(self.getResolved("width")-bb)/2)+1 -self:addButton({text="Cancel",x=cb,y=self.getResolved("height")-2,width=_b,height=1,background=self.getResolved("secondaryColor"),foreground=self.getResolved("buttonForeground")}):onClick(function()if -da then da(false)end;self:close()end) -self:addButton({text="OK",x=cb+_b+ab,y=self.getResolved("height")-2,width=_b,height=1,background=self.getResolved("primaryColor"),foreground=self.getResolved("buttonForeground")}):onClick(function()if -da then da(true)end;self:close()end)return self:show()end -function aa:prompt(ba,ca,da,_b)self:clear()self.set("title",ba) -self.set("height",11) -self:addLabel({text=ca,x=2,y=3,foreground=colors.white}) -local ab=self:addInput({x=2,y=5,width=self.getResolved("width")-3,height=1,defaultText=da or"",background=colors.white,foreground=colors.black})local bb=10;local cb=2;local db=bb*2 +cb -local _c=math.floor( -(self.getResolved("width")-db)/2)+1 -self:addButton({text="Cancel",x=_c,y=self.getResolved("height")-2,width=bb,height=1,background=self.getResolved("secondaryColor"),foreground=self.getResolved("buttonForeground")}):onClick(function()if -_b then _b(nil)end;self:close()end) -self:addButton({text="OK",x=_c+bb+cb,y=self.getResolved("height")-2,width=bb,height=1,background=self.getResolved("primaryColor"),foreground=self.getResolved("buttonForeground")}):onClick(function()if -_b then _b(ab.get("text")or"")end -self:close()end)return self:show()end -function aa:render()_a.render(self)local ba=self.getResolved("title")if -ba~=""then local ca=self.getResolved("width")local da=ba:sub(1,ca-4) -self:textFg(2,2,da,colors.white)end end -function aa:mouse_click(ba,ca,da) -if self.getResolved("modal")then if self:isInBounds(ca,da)then return -_a.mouse_click(self,ba,ca,da)end;return true end;return _a.mouse_click(self,ba,ca,da)end -function aa:mouse_drag(ba,ca,da) -if self.getResolved("modal")then if self:isInBounds(ca,da)then -return _a.mouse_drag and -_a.mouse_drag(self,ba,ca,da)or false end;return true end;return -_a.mouse_drag and _a.mouse_drag(self,ba,ca,da)or false end -function aa:mouse_up(ba,ca,da) -if self.getResolved("modal")then if self:isInBounds(ca,da)then -return _a.mouse_up and -_a.mouse_up(self,ba,ca,da)or false end;return true end;return -_a.mouse_up and _a.mouse_up(self,ba,ca,da)or false end -function aa:mouse_scroll(ba,ca,da) -if self.getResolved("modal")then if self:isInBounds(ca,da)then -return _a.mouse_scroll and -_a.mouse_scroll(self,ba,ca,da)or false end;return true end;return -_a.mouse_scroll and _a.mouse_scroll(self,ba,ca,da)or false end;return aa end -project["elements/Tree.lua"] = function(...) local aa=require("elements/VisualElement")local ba=string.sub -local ca=require("libraries/colorHex") -local function da(ab,bb,cb,db)db=db or{}cb=cb or 0;for _c,ac in ipairs(ab)do -table.insert(db,{node=ac,level=cb}) -if bb[ac]and ac.children then da(ac.children,bb,cb+1,db)end end;return db end;local _b=setmetatable({},aa)_b.__index=_b -_b.defineProperty(_b,"nodes",{default={},type="table",canTriggerRender=true,setter=function(ab,bb)if#bb>0 then -ab.getResolved("expandedNodes")[bb[1]]=true end;return bb end}) -_b.defineProperty(_b,"selectedNode",{default=nil,type="table",canTriggerRender=true}) -_b.defineProperty(_b,"expandedNodes",{default={},type="table",canTriggerRender=true}) -_b.defineProperty(_b,"offset",{default=0,type="number",canTriggerRender=true,setter=function(ab,bb)return math.max(0,bb)end}) -_b.defineProperty(_b,"horizontalOffset",{default=0,type="number",canTriggerRender=true,setter=function(ab,bb)return math.max(0,bb)end}) -_b.defineProperty(_b,"selectedForegroundColor",{default=colors.white,type="color"}) -_b.defineProperty(_b,"selectedBackgroundColor",{default=colors.lightBlue,type="color"}) -_b.defineProperty(_b,"showScrollBar",{default=true,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"scrollBarSymbol",{default=" ",type="string",canTriggerRender=true}) -_b.defineProperty(_b,"scrollBarBackground",{default="\127",type="string",canTriggerRender=true}) -_b.defineProperty(_b,"scrollBarColor",{default=colors.lightGray,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"scrollBarBackgroundColor",{default=colors.gray,type="color",canTriggerRender=true})_b.defineEvent(_b,"mouse_click") -_b.defineEvent(_b,"mouse_drag")_b.defineEvent(_b,"mouse_up") -_b.defineEvent(_b,"mouse_scroll")function _b.new()local ab=setmetatable({},_b):__init() -ab.class=_b;ab.set("width",30)ab.set("height",10)ab.set("z",5) -return ab end -function _b:init(ab,bb) -aa.init(self,ab,bb)self.set("type","Tree")return self end -function _b:expandNode(ab) -self.getResolved("expandedNodes")[ab]=true;self:updateRender()return self end -function _b:collapseNode(ab) -self.getResolved("expandedNodes")[ab]=nil;self:updateRender()return self end -function _b:toggleNode(ab) -if self.getResolved("expandedNodes")[ab]then -self:collapseNode(ab)else self:expandNode(ab)end;return self end -function _b:mouse_click(ab,bb,cb) -if aa.mouse_click(self,ab,bb,cb)then -local db,_c=self:getRelativePosition(bb,cb)local ac=self.getResolved("width") -local bc=self.getResolved("height") -local cc=da(self.getResolved("nodes"),self.getResolved("expandedNodes"))local dc=self.getResolved("showScrollBar") -local _d,ad=self:getNodeSize()local bd=dc and _d>ac;local cd=bd and bc-1 or bc -local dd=dc and#cc>cd -if dd and db==ac and(not bd or _c0 and -(self.getResolved("offset")/c_a*100)or 0 -local _aa=math.floor((d_a/100)* (a_a-b_a))+1 -if _c>=_aa and _c<_aa+b_a then self._scrollBarDragging=true;self._scrollBarDragOffset=_c- -_aa else local aaa=( (_c-1)/ (a_a-b_a))*100;local baa=math.floor(( -aaa/100)*c_a+0.5) -self.set("offset",math.max(0,math.min(c_a,baa)))end;return true end -if bd and _c==bc and(not dd or db0 and( -self.getResolved("horizontalOffset")/c_a*100)or 0 -local _aa=math.floor((d_a/100)* (a_a-b_a))+1 -if db>=_aa and db<_aa+b_a then self._hScrollBarDragging=true;self._hScrollBarDragOffset= -db-_aa else local aaa=( (db-1)/ (a_a-b_a))*100;local baa=math.floor(( -aaa/100)*c_a+0.5) -self.set("horizontalOffset",math.max(0,math.min(c_a,baa)))end;return true end;local __a=_c+self.getResolved("offset") -if cc[__a]then -local a_a=cc[__a]local b_a=a_a.node -if db<=a_a.level*2 +2 then self:toggleNode(b_a)end;self.set("selectedNode",b_a) -self:fireEvent("node_select",b_a)end;return true end;return false end -function _b:onSelect(ab)self:registerCallback("node_select",ab)return self end -function _b:mouse_drag(ab,bb,cb) -if self._scrollBarDragging then local db,_c=self:getRelativePosition(bb,cb) -local ac=da(self.getResolved("nodes"),self.getResolved("expandedNodes"))local bc=self.getResolved("height") -local cc,dc=self:getNodeSize() -local _d=self.getResolved("showScrollBar")and cc>self.getResolved("width")local ad=_d and bc-1 or bc;local bd=ad -local cd=math.max(1,math.floor((ad/#ac)*bd))local dd=#ac-ad;_c=math.max(1,math.min(bd,_c))local __a=_c- ( -self._scrollBarDragOffset or 0)local a_a= -( (__a-1)/ (bd-cd))*100 -local b_a=math.floor((a_a/100)*dd+0.5) -self.set("offset",math.max(0,math.min(dd,b_a)))return true end -if self._hScrollBarDragging then local db,_c=self:getRelativePosition(bb,cb) -local ac=self.getResolved("width")local bc,cc=self:getNodeSize() -local dc=da(self.getResolved("nodes"),self.getResolved("expandedNodes"))local _d=self.getResolved("height")local ad= -self.getResolved("showScrollBar")and bc>ac -local bd=ad and _d-1 or _d -local cd=self.getResolved("showScrollBar")and#dc>bd;local dd=cd and ac-1 or ac -local __a=math.max(1,math.floor((dd/bc)*dd))local a_a=bc-dd;db=math.max(1,math.min(dd,db))local b_a=db- ( -self._hScrollBarDragOffset or 0)local c_a= -( (b_a-1)/ (dd-__a))*100 -local d_a=math.floor((c_a/100)*a_a+0.5) -self.set("horizontalOffset",math.max(0,math.min(a_a,d_a)))return true end;return -aa.mouse_drag and aa.mouse_drag(self,ab,bb,cb)or false end -function _b:mouse_up(ab,bb,cb)if self._scrollBarDragging then self._scrollBarDragging=false -self._scrollBarDragOffset=nil;return true end -if self._hScrollBarDragging then -self._hScrollBarDragging=false;self._hScrollBarDragOffset=nil;return true end;return -aa.mouse_up and aa.mouse_up(self,ab,bb,cb)or false end -function _b:mouse_scroll(ab,bb,cb) -if aa.mouse_scroll(self,ab,bb,cb)then -local db=da(self.getResolved("nodes"),self.getResolved("expandedNodes"))local _c=self.getResolved("height") -local ac=self.getResolved("width")local bc=self.getResolved("showScrollBar") -local cc,dc=self:getNodeSize()local _d=bc and cc>ac;local ad=_d and _c-1 or _c -local bd=math.max(0,#db-ad) -local cd=math.min(bd,math.max(0,self.getResolved("offset")+ab))self.set("offset",cd)return true end;return false end -function _b:getNodeSize()local ab,bb=0,0 -local cb=da(self.getResolved("nodes"),self.getResolved("expandedNodes"))local db=self.getResolved("expandedNodes") -for _c,ac in ipairs(cb)do -local bc=ac.node;local cc=ac.level;local dc=string.rep(" ",cc)local _d=" " -if bc.children and -#bc.children>0 then _d=db[bc]and"\31"or"\16"end -local ad=dc.._d.." ".. (bc.text or"Node")ab=math.max(ab,#ad)end;bb=#cb;return ab,bb end -function _b:render()aa.render(self) -local ab=da(self.getResolved("nodes"),self.getResolved("expandedNodes"))local bb=self.getResolved("height") -local cb=self.getResolved("width")local db=self.getResolved("selectedNode") -local _c=self.getResolved("expandedNodes")local ac=self.getResolved("offset") -local bc=self.getResolved("horizontalOffset")local cc=self.getResolved("showScrollBar") -local dc,_d=self:getNodeSize()local ad=cc and dc>cb;local bd=ad and bb-1 or bb -local cd=cc and#ab>bd;local dd=cd and cb-1 or cb -for y=1,bd do local _aa=ab[y+ac] -if _aa then local aaa=_aa.node -local baa=_aa.level;local caa=string.rep(" ",baa)local daa=" " -if -aaa.children and#aaa.children>0 then daa=_c[aaa]and"\31"or"\16"end;local _ba=aaa==db -local aba= -_ba and self.getResolved("selectedBackgroundColor")or -(aaa.background or aaa.bg or self.getResolved("background")) -local bba=_ba and self.getResolved("selectedForegroundColor")or -( -aaa.foreground or aaa.fg or self.getResolved("foreground")) -local cba=caa..daa.." ".. (aaa.text or"Node")local dba=ba(cba,bc+1,bc+dd) -local _ca=dba..string.rep(" ",dd-#dba) -local aca=ca[aba]:rep(#_ca)or ca[colors.black]:rep(#_ca) -local bca=ca[bba]:rep(#_ca)or ca[colors.white]:rep(#_ca)self:blit(1,y,_ca,bca,aca)else -self:blit(1,y,string.rep(" ",dd),ca[self.getResolved("foreground")]:rep(dd),ca[self.getResolved("background")]:rep(dd))end end;local __a=self.getResolved("scrollBarSymbol") -local a_a=self.getResolved("scrollBarBackground")local b_a=self.getResolved("scrollBarColor") -local c_a=self.getResolved("scrollBarBackgroundColor")local d_a=self.getResolved("foreground") -if cd then -local _aa=ad and bb-1 or bb -local aaa=math.max(1,math.floor((bd/#ab)*_aa))local baa=#ab-bd;local caa=baa>0 and(ac/baa*100)or 0 -local daa=math.floor(( -caa/100)* (_aa-aaa))+1 -for i=1,_aa do self:blit(cb,i,a_a,ca[d_a],ca[c_a])end;for i=daa,math.min(_aa,daa+aaa-1)do -self:blit(cb,i,__a,ca[b_a],ca[c_a])end end -if ad then local _aa=cd and cb-1 or cb -local aaa=math.max(1,math.floor((_aa/dc)*_aa))local baa=dc-dd;local caa=baa>0 and(bc/baa*100)or 0 -local daa=math.floor(( -caa/100)* (_aa-aaa))+1 -for i=1,_aa do self:blit(i,bb,a_a,ca[d_a],ca[c_a])end;for i=daa,math.min(_aa,daa+aaa-1)do -self:blit(i,bb,__a,ca[b_a],ca[c_a])end end;if cd and ad then -self:blit(cb,bb," ",ca[d_a],ca[self.getResolved("background")])end end;return _b end -project["elements/DropDown.lua"] = function(...) local _a=require("elements/VisualElement") -local aa=require("elements/List")local ba=require("libraries/colorHex") -local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"dropdownHeight",{default=5,type="number"}) -ca.defineProperty(ca,"selectedText",{default="",type="string"}) -ca.defineProperty(ca,"dropSymbol",{default="\31",type="string"}) -ca.defineProperty(ca,"undropSymbol",{default="\17",type="string"})function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;da.set("width",16)da.set("height",1)da.set("z",8) -return da end;function ca:init(da,_b) -aa.init(self,da,_b)self.set("type","DropDown") -self:registerState("opened",nil,200)return self end -function ca:mouse_click(da,_b,ab) -if -not _a.mouse_click(self,da,_b,ab)then return false end;local bb,cb=self:getRelativePosition(_b,ab) -local db=self:hasState("opened") -if cb==1 then -if db then self.set("height",1)self:unsetState("opened")else -self.set("height", -1 + -math.min(self.getResolved("dropdownHeight"),#self.getResolved("items")))self:setState("opened")end;return true elseif db and cb>1 then return aa.mouse_click(self,da,_b,ab-1)end;return false end -function ca:mouse_drag(da,_b,ab)if self:hasState("opened")then -return aa.mouse_drag(self,da,_b,ab-1)end;return -_a.mouse_drag and _a.mouse_drag(self,da,_b,ab)or false end -function ca:mouse_up(da,_b,ab) -if self:hasState("opened")then -local bb,cb=self:getRelativePosition(_b,ab) -if cb>1 and self.getResolved("selectable")and -not self._scrollBarDragging then -local db=(cb-1)+self.getResolved("offset")local _c=self.getResolved("items") -if db<=#_c then local ac=_c[db]if -type(ac)=="string"then ac={text=ac}_c[db]=ac end -if not -self.getResolved("multiSelection")then for bc,cc in ipairs(_c)do -if type(cc)=="table"then cc.selected=false end end end;ac.selected=not ac.selected -if ac.callback then ac.callback(self)end;self:fireEvent("select",db,ac) -self:unsetState("opened")self:unsetState("clicked")self.set("height",1) -self:updateRender()return true end end;aa.mouse_up(self,da,_b,ab-1) -self:unsetState("clicked")return true end;return -_a.mouse_up and _a.mouse_up(self,da,_b,ab)or false end -function ca:render()_a.render(self)local da=self.getResolved("width") -local _b=self.getResolved("height")local ab=self.getResolved("selectedText") -local bb=self:hasState("opened")local cb=self:getSelectedItems()if#cb>0 then local db=cb[1] -ab=db.text or""ab=ab:sub(1,da-2)end -if bb then local db=_b -local _c=math.min(self.getResolved("dropdownHeight"), -#self.getResolved("items"))self.set("height",_c)aa.render(self,1) -self.set("height",db)end -self:blit(1,1,ab.. -string.rep(" ",da-#ab-1).. ( -bb and self.getResolved("dropSymbol")or self.getResolved("undropSymbol")),string.rep(ba[self.getResolved("foreground")],da),string.rep(ba[self.getResolved("background")],da))end;function ca:focus()_a.focus(self)self:prioritize() -self:setState("opened")end -function ca:blur()_a.blur(self) -self:unsetState("opened")self.set("height",1)self:updateRender()end;return ca end -project["elements/Display.lua"] = function(...) local d=require("elementManager") -local _a=d.getElement("VisualElement")local aa=setmetatable({},_a)aa.__index=aa;function aa.new() -local ba=setmetatable({},aa):__init()ba.class=aa;ba.set("width",25)ba.set("height",8) -ba.set("z",5)return ba end -function aa:init(ba,ca) -_a.init(self,ba,ca)self.set("type","Display") -self._window=window.create(ca.getActiveFrame():getTerm(),1,1,self.getResolved("width"),self.getResolved("height"),false)local da=self._window.reposition;local _b=self._window.blit -local ab=self._window.write -self._window.reposition=function(bb,cb,db,_c)self.set("x",bb)self.set("y",cb) -self.set("width",db)self.set("height",_c)da(1,1,db,_c)end;self._window.getPosition=function(bb) -return bb.getResolved("x"),bb.getResolved("y")end;self._window.setVisible=function(bb) -self.set("visible",bb)end;self._window.isVisible=function(bb)return -bb.getResolved("visible")end -self._window.blit=function(bb,cb,db,_c,ac) -_b(bb,cb,db,_c,ac)self:updateRender()end -self._window.write=function(bb,cb,db)ab(bb,cb,db)self:updateRender()end -self:observe("width",function(bb,cb)local db=bb._window;if db then -db.reposition(1,1,cb,bb.getResolved("height"))end end) -self:observe("height",function(bb,cb)local db=bb._window;if db then -db.reposition(1,1,bb.getResolved("width"),cb)end end)end;function aa:getWindow()return self._window end -function aa:write(ba,ca,da,_b,ab)local bb=self._window -if bb then if _b then -bb.setTextColor(_b)end;if ab then bb.setBackgroundColor(ab)end -bb.setCursorPos(ba,ca)bb.write(da)end;self:updateRender()return self end -function aa:render()_a.render(self)local ba=self._window;local ca,da=ba.getSize() -if ba then for y=1,da do -local _b,ab,bb=ba.getLine(y)self:blit(1,y,_b,ab,bb)end end end;return aa end -project["elements/Breadcrumb.lua"] = function(...) local d=require("elementManager") -local _a=d.getElement("VisualElement")local aa=setmetatable({},_a)aa.__index=aa -aa.defineProperty(aa,"path",{default={},type="table",canTriggerRender=true}) -aa.defineProperty(aa,"separator",{default=" > ",type="string",canTriggerRender=true}) -aa.defineProperty(aa,"clickable",{default=true,type="boolean"}) -aa.defineProperty(aa,"autoSize",{default=true,type="boolean"})aa.defineEvent(aa,"mouse_click") -aa.defineEvent(aa,"mouse_up") -function aa.new()local ba=setmetatable({},aa):__init() -ba.class=aa;ba.set("z",5)ba.set("height",1) -ba.set("backgroundEnabled",false)return ba end;function aa:init(ba,ca)_a.init(self,ba,ca) -self.set("type","Breadcrumb")end -function aa:mouse_click(ba,ca,da)if not -self.getResolved("clickable")then return false end -if -_a.mouse_click(self,ba,ca,da)then local _b=self.getResolved("path") -local ab=self.getResolved("separator")local bb=1 -for cb,db in ipairs(_b)do local _c=#db;if ca>=bb and caab then local _c="... > " -local ac=ab-#_c -if ac>0 then bb=_c..bb:sub(-ac)else bb=_c:sub(1,ab)end end end;local cb=1;local db -for _c in bb:gmatch("[^"..ca.."]+")do db=da -self:textFg(cb,1,_c,db)cb=cb+#_c;local ac=bb:find(ca,cb,true)if ac then -self:textFg(cb,1,ca,_b and colors.gray or -colors.lightGray)cb=cb+#ca end end end;return aa end -project["elements/LineChart.lua"] = function(...) local ba=require("elementManager") -local ca=ba.getElement("VisualElement")local da=ba.getElement("Graph") -local _b=require("libraries/colorHex")local ab=setmetatable({},da)ab.__index=ab;function ab.new() -local cb=setmetatable({},ab):__init()cb.class=ab;return cb end -function ab:init(cb,db) -da.init(self,cb,db)self.set("type","LineChart")return self end -local function bb(cb,db,_c,ac,bc,cc,dc,_d)local ad=ac-db;local bd=bc-_c -local cd=math.max(math.abs(ad),math.abs(bd)) -for i=0,cd do local dd=cd==0 and 0 or i/cd -local __a=math.floor(db+ad*dd)local a_a=math.floor(_c+bd*dd)if __a>=1 and -__a<=cb.getResolved("width")and a_a>=1 and -a_a<=cb.getResolved("height")then -cb:blit(__a,a_a,cc,_b[dc],_b[_d])end end end -function ab:render()ca.render(self)local cb=self.getResolved("width") -local db=self.getResolved("height")local _c=self.getResolved("minValue") -local ac=self.getResolved("maxValue")local bc=self.getResolved("series") -for cc,dc in pairs(bc)do -if(dc.visible)then local _d,ad -local bd=#dc.data;local cd=(cb-1)/math.max((bd-1),1) -for dd,__a in ipairs(dc.data)do local a_a=math.floor(( ( -dd-1)*cd)+1) -local b_a=(__a-_c)/ (ac-_c)local c_a=math.floor(db- (b_a* (db-1))) -c_a=math.max(1,math.min(c_a,db))if _d then -bb(self,_d,ad,a_a,c_a,dc.symbol,dc.bgColor,dc.fgColor)end;_d,ad=a_a,c_a end end end end;return ab end -project["elements/Graph.lua"] = function(...) local _a=require("elementManager") -local aa=_a.getElement("VisualElement")local ba=require("libraries/colorHex") -local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"minValue",{default=0,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"maxValue",{default=100,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"series",{default={},type="table",canTriggerRender=true})function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;return da end;function ca:init(da,_b) -aa.init(self,da,_b)self.set("type","Graph")self.set("width",20) -self.set("height",10)return self end -function ca:addSeries(da,_b,ab,bb,cb) -local db=self.getResolved("series") -table.insert(db,{name=da,symbol=_b or" ",bgColor=ab or colors.white,fgColor=bb or colors.black,pointCount=cb or -self.getResolved("width"),data={},visible=true})self:updateRender()return self end -function ca:removeSeries(da)local _b=self.getResolved("series")for ab,bb in ipairs(_b)do if bb.name==da then -table.remove(_b,ab)break end end -self:updateRender()return self end -function ca:getSeries(da)local _b=self.getResolved("series")for ab,bb in ipairs(_b)do if bb.name==da then -return bb end end;return nil end -function ca:changeSeriesVisibility(da,_b)local ab=self.getResolved("series")for bb,cb in ipairs(ab)do if -cb.name==da then cb.visible=_b;break end end -self:updateRender()return self end -function ca:addPoint(da,_b)local ab=self.getResolved("series") -for bb,cb in ipairs(ab)do if cb.name==da then -table.insert(cb.data,_b) -while#cb.data>cb.pointCount do table.remove(cb.data,1)end;break end end;self:updateRender()return self end -function ca:focusSeries(da)local _b=self.getResolved("series") -for ab,bb in ipairs(_b)do if bb.name==da then -table.remove(_b,ab)table.insert(_b,bb)break end end;self:updateRender()return self end -function ca:setSeriesPointCount(da,_b)local ab=self.getResolved("series") -for bb,cb in ipairs(ab)do if -cb.name==da then cb.pointCount=_b -while#cb.data>_b do table.remove(cb.data,1)end;break end end;self:updateRender()return self end -function ca:clear(da)local _b=self.getResolved("series") -if da then for ab,bb in ipairs(_b)do if bb.name==da then -bb.data={}break end end else for ab,bb in ipairs(_b)do -bb.data={}end end;return self end -function ca:render()aa.render(self)local da=self.getResolved("width") -local _b=self.getResolved("height")local ab=self.getResolved("minValue") -local bb=self.getResolved("maxValue")local cb=self.getResolved("series") -for db,_c in pairs(cb)do -if(_c.visible)then -local ac=#_c.data;local bc=(da-1)/math.max((ac-1),1) -for cc,dc in ipairs(_c.data)do local _d=math.floor(( ( -cc-1)*bc)+1) -local ad=(dc-ab)/ (bb-ab)local bd=math.floor(_b- (ad* (_b-1))) -bd=math.max(1,math.min(bd,_b)) -self:blit(_d,bd,_c.symbol,ba[_c.bgColor],ba[_c.fgColor])end end end end;return ca end -project["elements/Table.lua"] = function(...) local _a=require("elements/Collection") -local aa=require("libraries/colorHex")local ba=setmetatable({},_a)ba.__index=ba -ba.defineProperty(ba,"columns",{default={},type="table",canTriggerRender=true,setter=function(da,_b)local ab={} -for bb,cb in -ipairs(_b)do -if type(cb)=="string"then ab[bb]={name=cb,width=#cb+1}elseif type(cb)=="table"then -ab[bb]={name= -cb.name or"",width=cb.width,minWidth=cb.minWidth or 3,maxWidth=cb.maxWidth or nil}end end;return ab end}) -ba.defineProperty(ba,"headerColor",{default=colors.blue,type="color"}) -ba.defineProperty(ba,"gridColor",{default=colors.gray,type="color"}) -ba.defineProperty(ba,"sortColumn",{default=nil,type="number",canTriggerRender=true}) -ba.defineProperty(ba,"sortDirection",{default="asc",type="string",canTriggerRender=true}) -ba.defineProperty(ba,"customSortFunction",{default={},type="table"}) -ba.defineProperty(ba,"offset",{default=0,type="number",canTriggerRender=true,setter=function(da,_b) -local ab=math.max(0,#da.getResolved("items")- ( -da.getResolved("height")-1))return math.min(ab,math.max(0,_b))end}) -ba.defineProperty(ba,"showScrollBar",{default=true,type="boolean",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarSymbol",{default=" ",type="string",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarBackground",{default="\127",type="string",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarColor",{default=colors.lightGray,type="color",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarBackgroundColor",{default=colors.gray,type="color",canTriggerRender=true})ba.defineEvent(ba,"mouse_click") -ba.defineEvent(ba,"mouse_drag")ba.defineEvent(ba,"mouse_up") -ba.defineEvent(ba,"mouse_scroll") -local ca={cells={type="table",default={}},_sortValues={type="table",default={}},selected={type="boolean",default=false},text={type="string",default=""}}function ba.new()local da=setmetatable({},ba):__init() -da.class=ba;da.set("width",30)da.set("height",10)da.set("z",5) -return da end -function ba:init(da,_b) -_a.init(self,da,_b)self._entrySchema=ca;self.set("type","Table") -self:observe("sortColumn",function()if -self.getResolved("sortColumn")then -self:sortByColumn(self.getResolved("sortColumn"))end end)return self end;function ba:addRow(...)local da={...} -_a.addItem(self,{cells=da,_sortValues=da,text=table.concat(da," ")})return self end;function ba:removeRow(da) -local _b=self.getResolved("items") -if _b[da]then table.remove(_b,da)self.set("items",_b)end;return self end;function ba:getRow(da) -local _b=self.getResolved("items")return _b[da]end -function ba:updateCell(da,_b,ab) -local bb=self.getResolved("items")if bb[da]and bb[da].cells then bb[da].cells[_b]=ab -self.set("items",bb)end;return self end -function ba:getSelectedRow()local da=self.getResolved("items")for _b,ab in ipairs(da)do local bb=ab._data and -ab._data.selected or ab.selected;if bb then -return ab end end;return nil end;function ba:clearData()self.set("items",{})return self end -function ba:addColumn(da,_b) -local ab=self.getResolved("columns")table.insert(ab,{name=da,width=_b}) -self.set("columns",ab)return self end -function ba:setColumnSortFunction(da,_b) -local ab=self.getResolved("customSortFunction")ab[da]=_b;self.set("customSortFunction",ab)return self end -function ba:setData(da,_b)self:clearData() -for ab,bb in ipairs(da)do local cb={}local db={} -for _c,ac in ipairs(bb)do db[_c]=ac;if _b and -_b[_c]then cb[_c]=_b[_c](ac)else cb[_c]=ac end end -_a.addItem(self,{cells=cb,_sortValues=db,text=table.concat(cb," ")})end;return self end -function ba:getData()local da=self.getResolved("items")local _b={}for ab,bb in ipairs(da)do local cb=bb._data and -bb._data.cells or bb.cells;if cb then -table.insert(_b,cb)end end -return _b end -function ba:calculateColumnWidths(da,_b)local ab={}local bb=_b;local cb={}local db=0 -for ac,bc in ipairs(da)do -ab[ac]={name=bc.name,width=bc.width,minWidth=bc.minWidth or 3,maxWidth=bc.maxWidth} -if type(bc.width)=="number"then -ab[ac].visibleWidth=math.max(bc.width,ab[ac].minWidth)if ab[ac].maxWidth then -ab[ac].visibleWidth=math.min(ab[ac].visibleWidth,ab[ac].maxWidth)end -bb=bb-ab[ac].visibleWidth;db=db+ab[ac].visibleWidth elseif type(bc.width)=="string"and -bc.width:match("%%$")then -local cc=tonumber(bc.width:match("(%d+)%%")) -if cc then ab[ac].visibleWidth=math.floor(_b*cc/100) -ab[ac].visibleWidth=math.max(ab[ac].visibleWidth,ab[ac].minWidth)if ab[ac].maxWidth then -ab[ac].visibleWidth=math.min(ab[ac].visibleWidth,ab[ac].maxWidth)end -bb=bb-ab[ac].visibleWidth;db=db+ab[ac].visibleWidth else table.insert(cb,ac)end else table.insert(cb,ac)end end -if#cb>0 and bb>0 then local ac=math.floor(bb/#cb) -for bc,cc in ipairs(cb)do -ab[cc].visibleWidth=math.max(ac,ab[cc].minWidth)if ab[cc].maxWidth then -ab[cc].visibleWidth=math.min(ab[cc].visibleWidth,ab[cc].maxWidth)end end end;local _c=0 -for ac,bc in ipairs(ab)do _c=_c+ (bc.visibleWidth or 0)end;if _c>_b then local ac=_b/_c -for bc,cc in ipairs(ab)do if cc.visibleWidth then -cc.visibleWidth=math.max(1,math.floor(cc.visibleWidth*ac))end end end -return ab end -function ba:sortByColumn(da,_b)local ab=self.getResolved("items") -local bb=self.getResolved("sortDirection")local cb=self.getResolved("customSortFunction") -local db=_b or cb[da] -if db then -table.sort(ab,function(_c,ac)return db(_c,ac,bb)end)else -table.sort(ab,function(_c,ac) -local bc=_c._data and _c._data.cells or _c.cells -local cc=ac._data and ac._data.cells or ac.cells -local dc=_c._data and _c._data._sortValues or _c._sortValues -local _d=ac._data and ac._data._sortValues or ac._sortValues -if not _c or not ac or not bc or not cc then return false end;local ad,bd;if dc and dc[da]then ad=dc[da]else ad=bc[da]end;if _d and _d[da]then -bd=_d[da]else bd=cc[da]end -if -type(ad)=="number"and type(bd)=="number"then if bb=="asc"then return adbd end else -local cd=tostring(ad or"")local dd=tostring(bd or"") -if bb=="asc"then return cd
dd end end end)end;self.set("items",ab)return self end -function ba:onRowSelect(da)self:registerCallback("rowSelect",da)return self end -function ba:mouse_click(da,_b,ab) -if not _a.mouse_click(self,da,_b,ab)then return false end;local bb,cb=self:getRelativePosition(_b,ab) -local db=self.getResolved("width")local _c=self.getResolved("height") -local ac=self.getResolved("items")local bc=self.getResolved("showScrollBar")local cc=_c-1 -if -bc and#ac>cc and bb==db and cb>1 then local dc=_c-1;local _d=#ac-cc;local ad=math.max(1,math.floor(( -cc/#ac)*dc)) -local bd=_d>0 and( -self.getResolved("offset")/_d*100)or 0 -local cd=math.floor((bd/100)* (dc-ad))+1;local dd=cb-1 -if dd>=cd and dd=ad and bb1 then local dc=cb-2 +self.getResolved("offset") -if dc>=0 and -dc<#ac then local _d=dc+1 -for ad,bd in ipairs(ac)do if bd._data then bd._data.selected=false else -bd.selected=false end end -if ac[_d]then if ac[_d]._data then ac[_d]._data.selected=true else -ac[_d].selected=true end -self:fireEvent("rowSelect",_d,ac[_d])self:updateRender()end end;return true end;return true end -function ba:mouse_drag(da,_b,ab) -if self._scrollBarDragging then local bb,cb=self:getRelativePosition(_b,ab) -local db=self.getResolved("items")local _c=self.getResolved("height")local ac=_c-1;local bc=_c-1;local cc=math.max(1,math.floor(( -ac/#db)*bc)) -local dc=#db-ac;local _d=cb-1;_d=math.max(1,math.min(bc,_d))local ad=_d- ( -self._scrollBarDragOffset or 0)local bd= -( (ad-1)/ (bc-cc))*100 -local cd=math.floor((bd/100)*dc+0.5) -self.set("offset",math.max(0,math.min(dc,cd)))return true end;return -_a.mouse_drag and _a.mouse_drag(self,da,_b,ab)or false end -function ba:mouse_up(da,_b,ab)if self._scrollBarDragging then self._scrollBarDragging=false -self._scrollBarDragOffset=nil;return true end -return _a.mouse_up and -_a.mouse_up(self,da,_b,ab)or false end -function ba:mouse_scroll(da,_b,ab) -if _a.mouse_scroll(self,da,_b,ab)then -local bb=self.getResolved("items")local cb=self.getResolved("height")local db=cb-1 -local _c=math.max(0,#bb-db) -local ac=math.min(_c,math.max(0,self.getResolved("offset")+da))self.set("offset",ac)self:updateRender()return true end;return false end -function ba:render()_a.render(self)local da=self.getResolved("columns") -local _b=self.getResolved("items")local ab=self.getResolved("sortColumn") -local bb=self.getResolved("offset")local cb=self.getResolved("height") -local db=self.getResolved("width")local _c=self.getResolved("showScrollBar") -local ac=self.getResolved("background")local bc=self.getResolved("foreground")local cc=cb-1 -local dc=_c and#_b>cc;local _d=dc and db-1 or db -local ad=self:calculateColumnWidths(da,_d)local bd=0;local cd=#ad -for __a,a_a in ipairs(ad)do -if bd+a_a.visibleWidth>_d then cd=__a-1;break end;bd=bd+a_a.visibleWidth end;local dd=1 -for __a,a_a in ipairs(ad)do if __a>cd then break end;local b_a=a_a.name -if __a==ab then b_a=b_a.. -( -self.getResolved("sortDirection")=="asc"and"\30"or"\31")end -self:textFg(dd,1,b_a:sub(1,a_a.visibleWidth),self.getResolved("headerColor"))dd=dd+a_a.visibleWidth end;if dd<=_d then -self:textBg(dd,1,string.rep(" ",_d-dd+1),ac)end -for y=2,cb do local __a=y-2 +bb;local a_a=_b[__a+1] -if a_a then local b_a=a_a._data and -a_a._data.cells or a_a.cells -local c_a=a_a._data and -a_a._data.selected or a_a.selected -if b_a then dd=1 -local d_a=c_a and self.getResolved("selectedBackground")or ac -for _aa,aaa in ipairs(ad)do if _aa>cd then break end;local baa=tostring(b_a[_aa]or"") -local caa= -baa..string.rep(" ",aaa.visibleWidth-#baa)if _aa0 and(bb/b_a*100)or 0 -local d_a=math.floor(( -c_a/100)* (__a-a_a))+1;local _aa=self.getResolved("scrollBarSymbol") -local aaa=self.getResolved("scrollBarBackground")local baa=self.getResolved("scrollBarColor") -local caa=self.getResolved("scrollBarBackgroundColor") -for i=2,cb do self:blit(db,i,aaa,aa[bc],aa[caa])end;for i=d_a,math.min(__a,d_a+a_a-1)do -self:blit(db,i+1,_aa,aa[baa],aa[caa])end end end;return ba end -project["elements/Menu.lua"] = function(...) local aa=require("elements/VisualElement") -local ba=require("elements/List")local ca=require("libraries/colorHex") -local da=setmetatable({},ba)da.__index=da -da.defineProperty(da,"separatorColor",{default=colors.gray,type="color"}) -da.defineProperty(da,"spacing",{default=1,type="number",canTriggerRender=true}) -da.defineProperty(da,"openDropdown",{default=nil,type="table",allowNil=true,canTriggerRender=true}) -da.defineProperty(da,"dropdownBackground",{default=colors.black,type="color",canTriggerRender=true}) -da.defineProperty(da,"dropdownForeground",{default=colors.white,type="color",canTriggerRender=true}) -da.defineProperty(da,"horizontalOffset",{default=0,type="number",canTriggerRender=true,setter=function(ab,bb) -local cb=math.max(0,ab:getTotalWidth()-ab.getResolved("width"))return math.min(cb,math.max(0,bb))end}) -da.defineProperty(da,"maxWidth",{default=nil,type="number",canTriggerRender=true}) -local _b={text={type="string",default="Entry"},bg={type="number",default=nil},fg={type="number",default=nil},selectedBg={type="number",default=nil},selectedFg={type="number",default= -nil},callback={type="function",default=nil},dropdown={type="table",default=nil}}function da.new()local ab=setmetatable({},da):__init() -ab.class=da;ab.set("width",30)ab.set("height",1)ab.set("z",8) -return ab end -function da:init(ab,bb) -ba.init(self,ab,bb)self._entrySchema=_b;self.set("type","Menu") -self:observe("items",function() -local cb=self.getResolved("maxWidth")if cb then -self.set("width",math.min(cb,self:getTotalWidth()),true)else -self.set("width",self:getTotalWidth(),true)end end)return self end -function da:getTotalWidth()local ab=self.getResolved("items") -local bb=self.getResolved("spacing")local cb=0 -for db,_c in ipairs(ab)do if type(_c)=="table"then cb=cb+#_c.text else cb=cb+ -#tostring(_c)+2 end;if db<#ab then -cb=cb+bb end end;return cb end -function da:render()aa.render(self)local ab=self.getResolved("width") -local bb=self.getResolved("spacing")local cb=self.getResolved("horizontalOffset") -local db=self.getResolved("items")local _c={}local ac=1 -for cc,dc in ipairs(db)do if type(dc)=="string"then -dc={text=" "..dc.." "}db[cc]=dc end -_c[cc]={startX=ac,endX=ac+#dc.text-1,text=dc.text,item=dc}ac=ac+#dc.text;if cc<#db and bb>0 then ac=ac+bb end end -for cc,dc in ipairs(_c)do local _d=dc.item;local ad=dc.startX-cb;local bd=dc.endX-cb -if ad>ab then break end -if bd>=1 then local cd=math.max(1,ad)local dd=math.min(ab,bd) -local __a=math.max(1,1 -ad+1) -local a_a=math.min(#dc.text,#dc.text- (bd-ab))local b_a=dc.text:sub(__a,a_a) -if#b_a>0 then local c_a=_d.selected -local d_a= -_d.selectable==false and self.getResolved("separatorColor")or -( -c_a and( -_d.selectedForeground or self.getResolved("selectedForeground"))or(_d.foreground or self.getResolved("foreground"))) -local _aa=c_a and -(_d.selectedBackground or self.getResolved("selectedBackground"))or -(_d.background or self.getResolved("background")) -self:blit(cd,1,b_a,string.rep(ca[d_a],#b_a),string.rep(ca[_aa],#b_a))end -if cc<#db and bb>0 then local c_a=dc.endX+1 -cb;local d_a=c_a+bb-1 -if d_a>=1 and -c_a<=ab then local _aa=math.max(1,c_a)local aaa=math.min(ab,d_a) -local baa=aaa-_aa+1 -if baa>0 then local caa=string.rep(" ",baa) -self:blit(_aa,1,caa,string.rep(ca[self.getResolved("foreground")],baa),string.rep(ca[self.getResolved("background")],baa))end end end end end;local bc=self.getResolved("openDropdown")if bc then -self:renderDropdown(bc)end end -function da:renderDropdown(ab)local bb=self.getResolved("dropdownBackground") -local cb=self.getResolved("dropdownForeground") -for db,_c in ipairs(ab.items)do local ac=ab.y+db-1 -local bc=_c.text or _c.label or""local cc=bc=="---"local dc=ca[_c.background or bb] -local _d=ca[_c.foreground or cb]local ad=string.rep(" ",ab.width) -self:blit(ab.x,ac,ad,string.rep(_d,ab.width),string.rep(dc,ab.width)) -if cc then local bd=string.rep("-",ab.width) -self:blit(ab.x,ac,bd,string.rep(ca[colors.gray],ab.width),string.rep(dc,ab.width))else -if#bc>ab.width-2 then bc=bc:sub(1,ab.width-2)end -self:textFg(ab.x+1,ac,bc,_c.foreground or cb)end end end -function da:mouse_click(ab,bb,cb)local db=self.getResolved("openDropdown") -if db then -local ad,bd=self:getRelativePosition(bb,cb) -if self:isInsideDropdown(ad,bd,db)then -return self:handleDropdownClick(ad,bd,db)else self:hideDropdown()end end -if not aa.mouse_click(self,ab,bb,cb)then return false end -if(self.getResolved("selectable")==false)then return false end -local _c=select(1,self:getRelativePosition(bb,cb))local ac=self.getResolved("horizontalOffset") -local bc=self.getResolved("spacing")local cc=self.getResolved("items")local dc=_c+ac;local _d=1 -for ad,bd in ipairs(cc)do -local cd=#bd.text -if dc>=_d and dc<_d+cd then -if bd.selectable~=false then if type(bd)=="string"then -bd={text=bd}cc[ad]=bd end -if -bd.dropdown and#bd.dropdown>0 then self:showDropdown(ad,bd,_d-ac)return true end;if not self.getResolved("multiSelection")then -for dd,__a in ipairs(cc)do if -type(__a)=="table"then __a.selected=false end end end;bd.selected=not -bd.selected;if bd.callback then bd.callback(self)end -self:fireEvent("select",ad,bd)end;return true end;_d=_d+cd;if ad<#cc and bc>0 then _d=_d+bc end end;return false end -function da:mouse_scroll(ab,bb,cb) -if aa.mouse_scroll(self,ab,bb,cb)then -local db=self.getResolved("horizontalOffset") -local _c=math.max(0,self:getTotalWidth()-self.getResolved("width"))db=math.min(_c,math.max(0,db+ (ab*3))) -self.set("horizontalOffset",db)return true end;return false end -function da:showDropdown(ab,bb,cb)local db=bb.dropdown;if not db or#db==0 then return end;local _c=8;for cc,dc in -ipairs(db)do local _d=dc.text or dc.label or"" -if#_d+2 >_c then _c=#_d+2 end end;local ac=#db -local bc=self.getResolved("height") -self.set("openDropdown",{index=ab,items=db,x=cb,y=bc+1,width=_c,height=ac})self:updateRender()end;function da:hideDropdown()self.set("openDropdown",nil) -self:updateRender()end -function da:isInsideDropdown(ab,bb,cb)return -ab>=cb.x and -ab=cb.y and bb=1 and db<=#cb.items then -local _c=cb.items[db]if _c.text=="---"or _c.label=="---"or _c.disabled then return -true end -if _c.callback then -_c.callback(self,_c)elseif _c.onClick then _c.onClick(self,_c)end;self:hideDropdown()return true end;return false end;return da end -project["elements/Slider.lua"] = function(...) local c=require("elements/VisualElement") -local d=setmetatable({},c)d.__index=d -d.defineProperty(d,"step",{default=1,type="number",canTriggerRender=true}) -d.defineProperty(d,"max",{default=100,type="number"}) -d.defineProperty(d,"horizontal",{default=true,type="boolean",canTriggerRender=true,setter=function(_a,aa)if aa then _a.set("backgroundEnabled",false)else -_a.set("backgroundEnabled",true)end end}) -d.defineProperty(d,"barColor",{default=colors.gray,type="color",canTriggerRender=true}) -d.defineProperty(d,"sliderColor",{default=colors.blue,type="color",canTriggerRender=true})d.defineEvent(d,"mouse_click") -d.defineEvent(d,"mouse_drag")d.defineEvent(d,"mouse_up") -d.defineEvent(d,"mouse_scroll") -function d.new()local _a=setmetatable({},d):__init()_a.class=d -_a.set("width",8)_a.set("height",1) -_a.set("backgroundEnabled",false)return _a end -function d:init(_a,aa)c.init(self,_a,aa)self.set("type","Slider")end -function d:getValue()local _a=self.getResolved("step") -local aa=self.getResolved("max") -local ba= -self.getResolved("horizontal")and self.getResolved("width")or self.getResolved("height")return math.floor((_a-1)* (aa/ (ba-1)))end -function d:mouse_click(_a,aa,ba) -if self:isInBounds(aa,ba)then -local ca,da=self:getRelativePosition(aa,ba) -local _b=self.getResolved("horizontal")and ca or da -local ab= -self.getResolved("horizontal")and self.getResolved("width")or self.getResolved("height") -self.set("step",math.min(ab,math.max(1,_b)))self:updateRender()return true end;return false end;d.mouse_drag=d.mouse_click -function d:mouse_scroll(_a,aa,ba) -if self:isInBounds(aa,ba)then -local ca=self.getResolved("step") -local da= -self.getResolved("horizontal")and self.getResolved("width")or self.getResolved("height") -self.set("step",math.min(da,math.max(1,ca+_a)))self:updateRender()return true end;return false end -function d:render()c.render(self)local _a=self.getResolved("width") -local aa=self.getResolved("height")local ba=self.getResolved("horizontal") -local ca=self.getResolved("step")local da=ba and"\140"or" " -local _b=string.rep(da,ba and _a or aa) -if ba then -self:textFg(1,1,_b,self.getResolved("barColor")) -self:textBg(ca,1," ",self.getResolved("sliderColor"))else local ab=self.getResolved("background")for y=1,aa do -self:textBg(1,y," ",ab)end -self:textBg(1,ca," ",self.getResolved("sliderColor"))end end;return d end -project["elements/ScrollBar.lua"] = function(...) local aa=require("elements/VisualElement") -local ba=require("libraries/colorHex")local ca=setmetatable({},aa)ca.__index=ca -ca.defineProperty(ca,"value",{default=0,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"min",{default=0,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"max",{default=100,type="number",canTriggerRender=true}) -ca.defineProperty(ca,"step",{default=10,type="number"}) -ca.defineProperty(ca,"dragMultiplier",{default=1,type="number"}) -ca.defineProperty(ca,"symbol",{default=" ",type="string",canTriggerRender=true}) -ca.defineProperty(ca,"symbolColor",{default=colors.gray,type="color",canTriggerRender=true}) -ca.defineProperty(ca,"symbolBackgroundColor",{default=colors.black,type="color",canTriggerRender=true}) -ca.defineProperty(ca,"backgroundSymbol",{default="\127",type="string",canTriggerRender=true}) -ca.defineProperty(ca,"attachedElement",{default=nil,type="table"}) -ca.defineProperty(ca,"attachedProperty",{default=nil,type="string"}) -ca.defineProperty(ca,"minValue",{default=0,type="number"}) -ca.defineProperty(ca,"maxValue",{default=100,type="number"}) -ca.defineProperty(ca,"orientation",{default="vertical",type="string",canTriggerRender=true}) -ca.defineProperty(ca,"handleSize",{default=2,type="number",canTriggerRender=true})ca.defineEvent(ca,"mouse_click") -ca.defineEvent(ca,"mouse_release")ca.defineEvent(ca,"mouse_drag") -ca.defineEvent(ca,"mouse_scroll") -function ca.new()local ab=setmetatable({},ca):__init() -ab.class=ca;ab.set("width",1)ab.set("height",10)return ab end;function ca:init(ab,bb)aa.init(self,ab,bb)self.set("type","ScrollBar")return -self end -function ca:attach(ab,bb) -self.set("attachedElement",ab)self.set("attachedProperty",bb.property)self.set("minValue", -bb.min or 0) -self.set("maxValue",bb.max or 100) -ab:observe(bb.property,function(cb,db) -if db then local _c=self.getResolved("minValue") -local ac=self.getResolved("maxValue")if _c==ac then return end -self.set("value",math.floor((db-_c)/ (ac-_c)*100 +0.5))end end)return self end -function ca:updateAttachedElement()local ab=self.getResolved("attachedElement")if not ab then -return end;local bb=self.getResolved("value") -local cb=self.getResolved("minValue")local db=self.getResolved("maxValue")if type(cb)=="function"then -cb=cb()end;if type(db)=="function"then db=db()end;local _c=cb+ (bb/100)* ( -db-cb) -ab.set(self.getResolved("attachedProperty"),math.floor( -_c+0.5))return self end -local function da(ab)return -ab.getResolved("orientation")=="vertical"and -ab.getResolved("height")or ab.getResolved("width")end -local function _b(ab,bb,cb)local db,_c=ab:getRelativePosition(bb,cb)return - -ab.getResolved("orientation")=="vertical"and _c or db end -function ca:mouse_click(ab,bb,cb) -if aa.mouse_click(self,ab,bb,cb)then local db=da(self) -local _c=self.getResolved("value")local ac=self.getResolved("handleSize")local bc= -math.floor((_c/100)* (db-ac))+1;local cc=_b(self,bb,cb) -if -cc>=bc and cc0 and-1 or 1;local db=self.getResolved("step") -local _c=self.getResolved("value")local ac=_c-ab*db -self.set("value",math.min(100,math.max(0,ac)))self:updateAttachedElement()return true end -function ca:render()aa.render(self)local ab=da(self) -local bb=self.getResolved("value")local cb=self.getResolved("handleSize") -local db=self.getResolved("symbol")local _c=self.getResolved("symbolColor") -local ac=self.getResolved("symbolBackgroundColor")local bc=self.getResolved("backgroundSymbol")local cc= -self.getResolved("orientation")=="vertical" -local dc=self.getResolved("foreground")local _d=self.getResolved("background")local ad= -math.floor((bb/100)* (ab-cb))+1;for i=1,ab do -if cc then -self:blit(1,i,bc,ba[dc],ba[_d])else self:blit(i,1,bc,ba[dc],ba[_d])end end;for i=ad,ad+cb-1 do -if cc then -self:blit(1,i,db,ba[_c],ba[ac])else self:blit(i,1,db,ba[_c],ba[ac])end end end;return ca end -project["elements/VisualElement.lua"] = function(...) local ba=require("elementManager") -local ca=ba.getElement("BaseElement")local da=require("libraries/colorHex") -local _b=setmetatable({},ca)_b.__index=_b -_b.defineProperty(_b,"x",{default=1,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"y",{default=1,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"z",{default=1,type="number",canTriggerRender=true,setter=function(cb,db) -if cb.parent then cb.parent:sortChildren()end;return db end}) -_b.defineProperty(_b,"constraints",{default={},type="table"}) -_b.defineProperty(_b,"width",{default=1,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"height",{default=1,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"background",{default=colors.black,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"foreground",{default=colors.white,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"backgroundEnabled",{default=true,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"borderTop",{default=false,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"borderBottom",{default=false,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"borderLeft",{default=false,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"borderRight",{default=false,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"borderColor",{default=colors.white,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"visible",{default=true,type="boolean",canTriggerRender=true,setter=function(cb,db) -if(cb.parent~=nil)then -cb.parent.set("childrenSorted",false)cb.parent.set("childrenEventsSorted",false)end;if(db==false)then cb:unsetState("clicked")end;return db end}) -_b.defineProperty(_b,"ignoreOffset",{default=false,type="boolean"}) -_b.defineProperty(_b,"layoutConfig",{default={},type="table"})_b.combineProperties(_b,"position","x","y") -_b.combineProperties(_b,"size","width","height") -_b.combineProperties(_b,"color","foreground","background")_b.defineEvent(_b,"focus") -_b.defineEvent(_b,"blur") -_b.registerEventCallback(_b,"Click","mouse_click","mouse_up") -_b.registerEventCallback(_b,"ClickUp","mouse_up","mouse_click") -_b.registerEventCallback(_b,"Drag","mouse_drag","mouse_click","mouse_up") -_b.registerEventCallback(_b,"Scroll","mouse_scroll") -_b.registerEventCallback(_b,"Enter","mouse_enter","mouse_move") -_b.registerEventCallback(_b,"LeEave","mouse_leave","mouse_move")_b.registerEventCallback(_b,"Focus","focus","blur") -_b.registerEventCallback(_b,"Blur","blur","focus")_b.registerEventCallback(_b,"Key","key","key_up") -_b.registerEventCallback(_b,"Char","char")_b.registerEventCallback(_b,"KeyUp","key_up","key") -local ab,bb=math.max,math.min;function _b.new()local cb=setmetatable({},_b):__init() -cb.class=_b;return cb end -function _b:init(cb,db) -ca.init(self,cb,db)self.set("type","VisualElement") -self:registerState("disabled",nil,1000)self:registerState("clicked",nil,500) -self:registerState("hover",nil,400)self:registerState("focused",nil,300) -self:registerState("dragging",nil,600) -self:observe("x",function()if self.parent then -self.parent.set("childrenSorted",false)end end) -self:observe("y",function()if self.parent then -self.parent.set("childrenSorted",false)end end) -self:observe("width",function()if self.parent then -self.parent.set("childrenSorted",false)end end) -self:observe("height",function()if self.parent then -self.parent.set("childrenSorted",false)end end) -self:observe("visible",function()if self.parent then -self.parent.set("childrenSorted",false)end end)end -function _b:setConstraint(cb,db,_c,ac)local bc=self.get("constraints")if bc[cb]then -self:_removeConstraintObservers(cb,bc[cb])end -bc[cb]={element=db,property=_c,offset=ac or 0}self.set("constraints",bc) -self:_addConstraintObservers(cb,bc[cb])self._constraintsDirty=true;self:updateRender()return self end -function _b:setLayoutConfigProperty(cb,db)local _c=self.getResolved("layoutConfig") -_c[cb]=db;self.set("layoutConfig",_c)return self end;function _b:getLayoutConfigProperty(cb)local db=self.getResolved("layoutConfig") -return db[cb]end -function _b:resolveAllConstraints()if -not self._constraintsDirty then return self end -local cb=self.getResolved("constraints")if not cb or not next(cb)then return self end -local db={"width","height","left","right","top","bottom","x","y","centerX","centerY"} -for _c,ac in ipairs(db)do if cb[ac]then local bc=self:_resolveConstraint(ac,cb[ac]) -self:_applyConstraintValue(ac,bc,cb)end end;self._constraintsDirty=false;return self end -function _b:_applyConstraintValue(cb,db,_c) -if cb=="x"or cb=="left"then self.set("x",db)elseif cb=="y"or -cb=="top"then self.set("y",db)elseif cb=="right"then -if _c.left then -local ac=self:_resolveConstraint("left",_c.left)local bc=db-ac+1;self.set("width",bc)self.set("x",ac)else -local ac=self.getResolved("width")self.set("x",db-ac+1)end elseif cb=="bottom"then -if _c.top then -local ac=self:_resolveConstraint("top",_c.top)local bc=db-ac+1;self.set("height",bc)self.set("y",ac)else -local ac=self.getResolved("height")self.set("y",db-ac+1)end elseif cb=="centerX"then local ac=self.getResolved("width")self.set("x",db- -math.floor(ac/2))elseif cb=="centerY"then -local ac=self.getResolved("height")self.set("y",db-math.floor(ac/2))elseif -cb=="width"then self.set("width",db)elseif cb=="height"then self.set("height",db)end end -function _b:_addConstraintObservers(cb,db)local _c=db.element;local ac=db.property -if _c=="parent"then _c=self.parent end;if not _c then return end -local bc=function()self._constraintsDirty=true -self:resolveAllConstraints()self:updateRender()end -if not self._constraintObserverCallbacks then self._constraintObserverCallbacks={}end;if not self._constraintObserverCallbacks[cb]then -self._constraintObserverCallbacks[cb]={}end;local cc={} -if -ac=="left"or ac=="x"then cc={"x"}elseif ac=="right"then cc={"x","width"}elseif ac=="top"or ac=="y"then cc={"y"}elseif ac== -"bottom"then cc={"y","height"}elseif ac=="centerX"then cc={"x","width"}elseif ac=="centerY"then -cc={"y","height"}elseif ac=="width"then cc={"width"}elseif ac=="height"then cc={"height"}end;for dc,_d in ipairs(cc)do _c:observe(_d,bc) -table.insert(self._constraintObserverCallbacks[cb],{element=_c,property=_d,callback=bc})end end -function _b:_removeConstraintObservers(cb,db) -if not self._constraintObserverCallbacks or not -self._constraintObserverCallbacks[cb]then return end;for _c,ac in ipairs(self._constraintObserverCallbacks[cb])do -ac.element:removeObserver(ac.property,ac.callback)end;self._constraintObserverCallbacks[cb]= -nil end -function _b:_removeAllConstraintObservers() -if not self._constraintObserverCallbacks then return end -for cb,db in pairs(self._constraintObserverCallbacks)do for _c,ac in ipairs(db)do -ac.element:removeObserver(ac.property,ac.callback)end end;self._constraintObserverCallbacks=nil end -function _b:removeConstraint(cb)local db=self.getResolved("constraints")db[cb]=nil -self.set("constraints",db)self:updateConstraints()return self end -function _b:updateConstraints()local cb=self.getResolved("constraints") -for db,_c in pairs(cb)do -local ac=self:_resolveConstraint(db,_c) -if db=="x"or db=="left"then self.set("x",ac)elseif db=="y"or db=="top"then -self.set("y",ac)elseif db=="right"then local bc=self.getResolved("width") -self.set("x",ac-bc+1)elseif db=="bottom"then local bc=self.getResolved("height") -self.set("y",ac-bc+1)elseif db=="centerX"then local bc=self.getResolved("width")self.set("x",ac- -math.floor(bc/2))elseif db=="centerY"then -local bc=self.getResolved("height")self.set("y",ac-math.floor(bc/2))elseif -db=="width"then self.set("width",ac)elseif db=="height"then self.set("height",ac)end end end -function _b:_resolveConstraint(cb,db)local _c=db.element;local ac=db.property;local bc=db.offset;if _c=="parent"then -_c=self.parent end -if not _c then return self.getResolved(cb)or 1 end;local cc -if ac=="left"or ac=="x"then cc=_c.get("x")elseif ac=="right"then cc=_c.get("x")+ -_c.get("width")-1 elseif ac=="top"or ac=="y"then -cc=_c.get("y")elseif ac=="bottom"then -cc=_c.get("y")+_c.get("height")-1 elseif ac=="centerX"then -cc=_c.get("x")+math.floor(_c.get("width")/2)elseif ac=="centerY"then -cc=_c.get("y")+math.floor(_c.get("height")/2)elseif ac=="width"then cc=_c.get("width")elseif ac=="height"then cc=_c.get("height")end -if type(bc)=="number"then if bc>-1 and bc<1 and bc~=0 then -return math.floor(cc*bc)else return cc+bc end end;return cc end;function _b:alignRight(cb,db)db=db or 0 -return self:setConstraint("right",cb,"right",db)end;function _b:alignLeft(cb,db)db=db or 0;return -self:setConstraint("left",cb,"left",db)end -function _b:alignTop(cb,db)db= -db or 0;return self:setConstraint("top",cb,"top",db)end;function _b:alignBottom(cb,db)db=db or 0 -return self:setConstraint("bottom",cb,"bottom",db)end -function _b:centerHorizontal(cb,db)db=db or 0;return -self:setConstraint("centerX",cb,"centerX",db)end;function _b:centerVertical(cb,db)db=db or 0 -return self:setConstraint("centerY",cb,"centerY",db)end;function _b:centerIn(cb)return -self:centerHorizontal(cb):centerVertical(cb)end -function _b:rightOf(cb,db) -db=db or 0;return self:setConstraint("left",cb,"right",db)end;function _b:leftOf(cb,db)db=db or 0 -return self:setConstraint("right",cb,"left",-db)end;function _b:below(cb,db)db=db or 0;return -self:setConstraint("top",cb,"bottom",db)end -function _b:above(cb,db) -db=db or 0;return self:setConstraint("bottom",cb,"top",-db)end;function _b:stretchWidth(cb,db)db=db or 0 -return self:setConstraint("left",cb,"left",db):setConstraint("right",cb,"right", --db)end;function _b:stretchHeight(cb,db)db= -db or 0 -return self:setConstraint("top",cb,"top",db):setConstraint("bottom",cb,"bottom", --db)end;function _b:stretch(cb,db)return -self:stretchWidth(cb,db):stretchHeight(cb,db)end -function _b:widthPercent(cb,db)return self:setConstraint("width",cb,"width", -db/100)end;function _b:heightPercent(cb,db) -return self:setConstraint("height",cb,"height",db/100)end;function _b:matchWidth(cb,db)db=db or 0;return -self:setConstraint("width",cb,"width",db)end;function _b:matchHeight(cb,db)db= -db or 0 -return self:setConstraint("height",cb,"height",db)end;function _b:fillParent(cb)return -self:stretch("parent",cb)end;function _b:fillWidth(cb)return -self:stretchWidth("parent",cb)end;function _b:fillHeight(cb)return -self:stretchHeight("parent",cb)end;function _b:center()return -self:centerIn("parent")end;function _b:toRight(cb)return -self:alignRight("parent",- (cb or 0))end;function _b:toLeft(cb)return self:alignLeft("parent", -cb or 0)end;function _b:toTop(cb)return self:alignTop("parent", -cb or 0)end -function _b:toBottom(cb)return self:alignBottom("parent", -- (cb or 0))end -function _b:multiBlit(cb,db,_c,ac,bc,cc,dc)local _d,ad=self:calculatePosition()cb=cb+_d-1 -db=db+ad-1;self.parent:multiBlit(cb,db,_c,ac,bc,cc,dc)end -function _b:textFg(cb,db,_c,ac)local bc,cc=self:calculatePosition()cb=cb+bc-1 -db=db+cc-1;self.parent:textFg(cb,db,_c,ac)end -function _b:textBg(cb,db,_c,ac)local bc,cc=self:calculatePosition()cb=cb+bc-1 -db=db+cc-1;self.parent:textBg(cb,db,_c,ac)end -function _b:drawText(cb,db,_c)local ac,bc=self:calculatePosition()cb=cb+ac-1 -db=db+bc-1;self.parent:drawText(cb,db,_c)end -function _b:drawFg(cb,db,_c)local ac,bc=self:calculatePosition()cb=cb+ac-1 -db=db+bc-1;self.parent:drawFg(cb,db,_c)end -function _b:drawBg(cb,db,_c)local ac,bc=self:calculatePosition()cb=cb+ac-1 -db=db+bc-1;self.parent:drawBg(cb,db,_c)end -function _b:blit(cb,db,_c,ac,bc)local cc,dc=self:calculatePosition()cb=cb+cc-1 -db=db+dc-1;self.parent:blit(cb,db,_c,ac,bc)end -function _b:isInBounds(cb,db) -local _c,ac=self.getResolved("x"),self.getResolved("y") -local bc,cc=self.getResolved("width"),self.getResolved("height") -if(self.getResolved("ignoreOffset"))then if(self.parent)then cb=cb- -self.parent.get("offsetX") -db=db-self.parent.get("offsetY")end end;return -cb>=_c and cb<=_c+bc-1 and db>=ac and db<=ac+cc-1 end -function _b:mouse_click(cb,db,_c)if self:isInBounds(db,_c)then self:setState("clicked") -self:fireEvent("mouse_click",cb,self:getRelativePosition(db,_c))return true end;return -false end -function _b:mouse_up(cb,db,_c) -if self:isInBounds(db,_c)then self:unsetState("clicked") -self:unsetState("dragging") -self:fireEvent("mouse_up",cb,self:getRelativePosition(db,_c))return true end;return false end -function _b:mouse_release(cb,db,_c) -self:fireEvent("mouse_release",cb,self:getRelativePosition(db,_c))self:unsetState("clicked") -self:unsetState("dragging")end -function _b:mouse_move(cb,db,_c)if(db==nil)or(_c==nil)then return false end -local ac=self.getResolved("hover") -if(self:isInBounds(db,_c))then if(not ac)then self.set("hover",true) -self:fireEvent("mouse_enter",self:getRelativePosition(db,_c))end;return true else if(ac)then -self.set("hover",false) -self:fireEvent("mouse_leave",self:getRelativePosition(db,_c))end end;return false end -function _b:mouse_scroll(cb,db,_c)if(self:isInBounds(db,_c))then -self:fireEvent("mouse_scroll",cb,self:getRelativePosition(db,_c))return true end;return false end -function _b:mouse_drag(cb,db,_c)if(self:hasState("clicked"))then -self:fireEvent("mouse_drag",cb,self:getRelativePosition(db,_c))return true end;return false end -function _b:setFocused(cb,db)local _c=self:hasState("focused") -if cb==_c then return self end -if cb then self:setState("focused")self:focus() -if -not db and self.parent then self.parent:setFocusedChild(self)end else self:unsetState("focused")self:blur() -if -not db and self.parent then self.parent:setFocusedChild(nil)end end;return self end -function _b:isFocused()return self:hasState("focused")end;function _b:focus()self:fireEvent("focus")end;function _b:blur() -self:fireEvent("blur") -pcall(function() -self:setCursor(1,1,false,self.get and self.getResolved("foreground"))end)end;function _b:isFocused()return -self:hasState("focused")end -function _b:addBorder(cb,db)local _c=nil;local ac=nil -if -type(cb)== -"table"and(cb.color or cb.top~=nil or cb.left~=nil)then _c=cb.color;ac=cb else _c=cb;ac=db end -if ac then -if ac.top~=nil then self.set("borderTop",ac.top)end -if ac.bottom~=nil then self.set("borderBottom",ac.bottom)end -if ac.left~=nil then self.set("borderLeft",ac.left)end -if ac.right~=nil then self.set("borderRight",ac.right)end else self.set("borderTop",true) -self.set("borderBottom",true)self.set("borderLeft",true) -self.set("borderRight",true)end;if _c then self.set("borderColor",_c)end;return self end -function _b:removeBorder()self.set("borderTop",false) -self.set("borderBottom",false)self.set("borderLeft",false) -self.set("borderRight",false)return self end;function _b:key(cb,db) -if(self:hasState("focused"))then self:fireEvent("key",cb,db)end end -function _b:key_up(cb)if -(self:hasState("focused"))then self:fireEvent("key_up",cb)end end;function _b:char(cb) -if(self:hasState("focused"))then self:fireEvent("char",cb)end end -function _b:calculatePosition() -self:resolveAllConstraints()local cb,db=self.getResolved("x"),self.getResolved("y") -if not -self.getResolved("ignoreOffset")then if self.parent~=nil then -local _c,ac=self.parent.get("offsetX"),self.parent.get("offsetY")cb=cb-_c;db=db-ac end end;return cb,db end -function _b:getAbsolutePosition(cb,db) -local _c,ac=self.getResolved("x"),self.getResolved("y")if(cb~=nil)then _c=_c+cb-1 end;if(db~=nil)then ac=ac+db-1 end -local bc=self.parent;while bc do local cc,dc=bc.get("x"),bc.get("y")_c=_c+cc-1;ac=ac+dc-1 -bc=bc.parent end;return _c,ac end -function _b:getRelativePosition(cb,db)if(cb==nil)or(db==nil)then -cb,db=self.getResolved("x"),self.getResolved("y")end;local _c,ac=1,1;if self.parent then -_c,ac=self.parent:getRelativePosition()end -local bc,cc=self.getResolved("x"),self.getResolved("y")return cb- (bc-1)- (_c-1),db- (cc-1)- (ac-1)end -function _b:setCursor(cb,db,_c,ac) -if self.parent then local bc,cc=self:calculatePosition() -if -(cb+bc-1 <1)or( -cb+bc-1 >self.parent.get("width"))or(db+cc-1 <1)or(db+cc-1 > -self.parent.get("height"))then return self.parent:setCursor( -cb+bc-1,db+cc-1,false)end -return self.parent:setCursor(cb+bc-1,db+cc-1,_c,ac)end;return self end -function _b:prioritize() -if(self.parent)then local cb=self.parent;cb:removeChild(self) -cb:addChild(self)self:updateRender()end;return self end -function _b:render() -if(not self.getResolved("backgroundEnabled"))then return end -local cb,db=self.getResolved("width"),self.getResolved("height")local _c=da[self.getResolved("foreground")] -local ac=da[self.getResolved("background")] -local bc,cc,dc,_d=self.getResolved("borderTop"),self.getResolved("borderBottom"),self.getResolved("borderLeft"),self.getResolved("borderRight")self:multiBlit(1,1,cb,db," ",_c,ac) -if -(bc or cc or dc or _d)then -local ad=self.getResolved("borderColor")or self.getResolved("foreground")local bd=da[ad]or _c;if bc then -self:textFg(1,1,("\131"):rep(cb),ad)end;if cc then -self:multiBlit(1,db,cb,1,"\143",ac,bd)end;if dc then -self:multiBlit(1,1,1,db,"\149",bd,ac)end;if _d then -self:multiBlit(cb,1,1,db,"\149",ac,bd)end -if bc and dc then self:blit(1,1,"\151",bd,ac)end;if bc and _d then self:blit(cb,1,"\148",ac,bd)end;if -cc and dc then self:blit(1,db,"\138",ac,bd)end;if cc and _d then -self:blit(cb,db,"\133",ac,bd)end end end;function _b:postRender()end -function _b:destroy() -self:_removeAllConstraintObservers()self.set("visible",false)ca.destroy(self)end;return _b end -project["elements/ProgressBar.lua"] = function(...) local d=require("elements/VisualElement") -local _a=require("libraries/colorHex")local aa=setmetatable({},d)aa.__index=aa -aa.defineProperty(aa,"progress",{default=0,type="number",canTriggerRender=true}) -aa.defineProperty(aa,"showPercentage",{default=false,type="boolean"}) -aa.defineProperty(aa,"progressColor",{default=colors.black,type="color"}) -aa.defineProperty(aa,"direction",{default="right",type="string"}) -function aa.new()local ba=setmetatable({},aa):__init() -ba.class=aa;ba.set("width",25)ba.set("height",3)return ba end;function aa:init(ba,ca)d.init(self,ba,ca) -self.set("type","ProgressBar")end -function aa:render()d.render(self) -local ba=self.getResolved("width")local ca=self.getResolved("height") -local da=math.min(100,math.max(0,self.getResolved("progress")))local _b=math.floor((ba*da)/100) -local ab=math.floor((ca*da)/100)local bb=self.getResolved("direction") -local cb=self.getResolved("progressColor")local db=self.getResolved("foreground") -if bb=="right"then -self:multiBlit(1,1,_b,ca," ",_a[db],_a[cb])elseif bb=="left"then -self:multiBlit(ba-_b+1,1,_b,ca," ",_a[db],_a[cb])elseif bb=="up"then -self:multiBlit(1,ca-ab+1,ba,ab," ",_a[db],_a[cb])elseif bb=="down"then -self:multiBlit(1,1,ba,ab," ",_a[db],_a[cb])end -if self.getResolved("showPercentage")then local _c=tostring(da).."%"local ac=math.floor(( -ba-#_c)/2)+1;local bc= -math.floor((ca-1)/2)+1;self:textFg(ac,bc,_c,db)end end;return aa end -project["elements/CheckBox.lua"] = function(...) local c=require("elements/VisualElement") -local d=setmetatable({},c)d.__index=d -d.defineProperty(d,"checked",{default=false,type="boolean",canTriggerRender=true}) -d.defineProperty(d,"text",{default=" ",type="string",canTriggerRender=true,setter=function(_a,aa)local ba=_a.getResolved("checkedText")local ca=math.max(#aa, -#ba)if(_a.getResolved("autoSize"))then -_a.set("width",ca)end;return aa end}) -d.defineProperty(d,"checkedText",{default="x",type="string",canTriggerRender=true,setter=function(_a,aa)local ba=_a.getResolved("text") -local ca=math.max(#aa,#ba) -if(_a.getResolved("autoSize"))then _a.set("width",ca)end;return aa end}) -d.defineProperty(d,"autoSize",{default=true,type="boolean"})d.defineEvent(d,"mouse_click") -d.defineEvent(d,"mouse_up")function d.new()local _a=setmetatable({},d):__init()_a.class=d -_a.set("backgroundEnabled",false)return _a end -function d:init(_a,aa) -c.init(self,_a,aa)self.set("type","CheckBox")end -function d:mouse_click(_a,aa,ba)if c.mouse_click(self,_a,aa,ba)then -self.set("checked",not self.getResolved("checked"))return true end;return false end -function d:render()c.render(self)local _a=self.getResolved("checked") -local aa=self.getResolved("text")local ba=self.getResolved("checkedText") -local ca=string.sub(_a and ba or aa,1,self.getResolved("width")) -self:textFg(1,1,ca,self.getResolved("foreground"))end;return d end -project["elements/BaseElement.lua"] = function(...) local _a=require("propertySystem") -local aa=require("libraries/utils").uuid;local ba=require("errorManager")local ca=setmetatable({},_a) -ca.__index=ca -ca.defineProperty(ca,"type",{default={"BaseElement"},type="string",setter=function(da,_b)if type(_b)=="string"then -table.insert(da._values.type,1,_b)return da._values.type end;return _b end,getter=function(da,_b,ab)if -ab~=nil and ab<1 then return da._values.type end;return da._values.type[ -ab or 1]end}) -ca.defineProperty(ca,"id",{default="",type="string",readonly=true}) -ca.defineProperty(ca,"name",{default="",type="string"}) -ca.defineProperty(ca,"eventCallbacks",{default={},type="table"}) -ca.defineProperty(ca,"enabled",{default=true,type="boolean"}) -ca.defineProperty(ca,"states",{default={},type="table",canTriggerRender=true}) -function ca.defineEvent(da,_b,ab) -if not rawget(da,'_eventConfigs')then da._eventConfigs={}end;da._eventConfigs[_b]={requires=ab and ab or _b}end -function ca.registerEventCallback(da,_b,...) -local ab=_b:match("^on")and _b or"on".._b;local bb={...}local cb=bb[1] -da[ab]=function(db,...) -for _c,ac in ipairs(bb)do if not db._registeredEvents[ac]then -db:listenEvent(ac,true)end end;db:registerCallback(cb,...)return db end end;function ca.new()local da=setmetatable({},ca):__init() -da.class=ca;return da end -function ca:init(da,_b) -if self._initialized then return self end;self._initialized=true;self._props=da;self._values.id=aa() -self.basalt=_b;self._registeredEvents={}self._registeredStates={} -self._cachedActiveStates=nil;local ab=getmetatable(self).__index;local bb={}ab=self.class -while ab do -if -type(ab)=="table"and ab._eventConfigs then for cb,db in pairs(ab._eventConfigs)do if not bb[cb]then -bb[cb]=db end end end -ab=getmetatable(ab)and getmetatable(ab).__index end -for cb,db in pairs(bb)do self._registeredEvents[db.requires]=true end;if self._callbacks then -for cb,db in pairs(self._callbacks)do self[db]=function(_c,...) -_c:registerCallback(cb,...)return _c end end end -return self end -function ca:postInit()if self._postInitialized then return self end -self._postInitialized=true;self._modifiedProperties={}if(self._props)then for da,_b in pairs(self._props)do -self.set(da,_b)end end -self._props=nil;return self end;function ca:isType(da) -for _b,ab in ipairs(self._values.type)do if ab==da then return true end end;return false end -function ca:listenEvent(da,_b)_b= -_b~=false -if -_b~= (self._registeredEvents[da]or false)then -if _b then self._registeredEvents[da]=true;if self.parent then -self.parent:registerChildEvent(self,da)end else self._registeredEvents[da]=nil -if -self.parent then self.parent:unregisterChildEvent(self,da)end end end;return self end -function ca:registerCallback(da,_b)if not self._registeredEvents[da]then -self:listenEvent(da,true)end -if -not self._values.eventCallbacks[da]then self._values.eventCallbacks[da]={}end -table.insert(self._values.eventCallbacks[da],_b)return self end;function ca:registerState(da,_b,ab) -self._registeredStates[da]={condition=_b,priority=ab or 0}return self end -function ca:setState(da,_b) -local ab=self.getResolved("states")if not _b and self._registeredStates[da]then -_b=self._registeredStates[da].priority end;ab[da]=_b or 0 -self.set("states",ab)self._cachedActiveStates=nil;return self end -function ca:unsetState(da)local _b=self.get("states")if _b[da]~=nil then _b[da]=nil -self.set("states",_b)self._cachedActiveStates=nil end -return self end -function ca:hasState(da)local _b=self.get("states")return _b[da]~=nil end -function ca:getCurrentState()local da=self.get("states")local _b=-math.huge;local ab=nil;for bb,cb in -pairs(da)do if cb>_b then _b=cb;ab=bb end end;return ab end -function ca:getActiveStates() -if self._cachedActiveStates then return self._cachedActiveStates end;local da=self.get("states")local _b={}for ab,bb in pairs(da)do -table.insert(_b,{name=ab,priority=bb})end -table.sort(_b,function(ab,bb) -return ab.priority>bb.priority end)self._cachedActiveStates=_b;return _b end -function ca:updateConditionalStates() -for da,_b in pairs(self._registeredStates)do -if _b.condition then -local ab=_b.condition(self)if ab then self:setState(da,_b.priority)else -self:unsetState(da)end end end;return self end -function ca:registerResponsiveState(da,_b,ab)local bb=100;local cb={} -if type(ab)=="number"then bb=ab elseif type(ab)=="table"then bb= -ab.priority or 100;cb=ab.observe or{}end;local db;local _c=type(_b)=="string" -if _c then -db=self:_parseResponsiveExpression(_b)local ac=self:_detectDependencies(_b)for bc,cc in ipairs(ac)do -table.insert(cb,cc)end else db=_b end;self:registerState(da,db,bb) -for ac,bc in ipairs(cb)do -local cc=bc.element or bc[1]local dc=bc.property or bc[2]if cc and dc then -cc:observe(dc,function() -self:updateConditionalStates()end)end end;self:updateConditionalStates()return self end -function ca:_parseResponsiveExpression(da)local _b={colors=true,math=true,clamp=true,round=true} -local ab={clamp=function(ac,bc,cc)return -math.min(math.max(ac,bc),cc)end,round=function(ac) -return math.floor(ac+0.5)end,floor=math.floor,ceil=math.ceil,abs=math.abs} -da=da:gsub("([%w_]+)%.([%w_]+)",function(ac,bc) -if _b[ac]or tonumber(ac)then return ac.."."..bc end -return string.format('__getProperty("%s", "%s")',ac,bc)end)local bb=self -local cb=setmetatable({colors=colors,math=math,tostring=tostring,tonumber=tonumber,__getProperty=function(ac,bc) -if ac=="self"then -if bb._properties[bc]then return bb.get(bc)end elseif ac=="parent"then if bb.parent and bb.parent._properties[bc]then return -bb.parent.get(bc)end else -local cc=bb:getBaseFrame():getChild(ac) -if cc and cc._properties[bc]then return cc.get(bc)end end;return nil end},{__index=ab})local db,_c=load("return "..da,"responsive","t",cb) -if not db then error( -"Invalid responsive expression: ".._c)end;return -function(ac)local bc,cc=pcall(db)return bc and cc or false end end -function ca:_detectDependencies(da)local _b={} -local ab={colors=true,math=true,clamp=true,round=true} -for bb,cb in da:gmatch("([%w_]+)%.([%w_]+)")do -if not ab[bb]and not tonumber(bb)then -local db;if bb=="self"then db=self elseif bb=="parent"then db=self.parent else -db=self:getBaseFrame():getChild(bb)end;if db then -table.insert(_b,{element=db,property=cb})end end end;return _b end;function ca:unregisterState(da)self._stateRegistry[da]=nil -self:unsetState(da)return self end -function ca:fireEvent(da,...) -if -self.getResolved("eventCallbacks")[da]then local _b;for ab,bb in -ipairs(self.getResolved("eventCallbacks")[da])do _b=bb(self,...)end;return _b end;return self end -function ca:dispatchEvent(da,...) -if self.getResolved("enabled")==false then return false end;if self[da]then return self[da](self,...)end;return -self:handleEvent(da,...)end;function ca:handleEvent(da,...)return false end;function ca:onChange(da,_b) -self:observe(da,_b)return self end -function ca:getBaseFrame()if self.parent then return -self.parent:getBaseFrame()end;return self end;function ca:destroy() -if(self.parent)then self.parent:removeChild(self)end;self._destroyed=true;self:removeAllObservers() -self:setFocused(false)end;function ca:updateRender() -if -(self.parent)then self.parent:updateRender()else self._renderUpdate=true end;return self end;return ca end -project["elements/ContextMenu.lua"] = function(...) local aa=require("elementManager") -local ba=require("elements/VisualElement")local ca=aa.getElement("Container") -local da=require("libraries/colorHex")local _b=setmetatable({},ca)_b.__index=_b -_b.defineProperty(_b,"items",{default={},type="table",canTriggerRender=true}) -_b.defineProperty(_b,"isOpen",{default=false,type="boolean",canTriggerRender=true}) -_b.defineProperty(_b,"openSubmenu",{default=nil,type="table",allowNil=true}) -_b.defineProperty(_b,"itemHeight",{default=1,type="number",canTriggerRender=true})_b.defineEvent(_b,"mouse_click") -function _b.new() -local ab=setmetatable({},_b):__init()ab.class=_b;ab.set("width",10)ab.set("height",10) -ab.set("visible",false)return ab end;function _b:init(ab,bb)ca.init(self,ab,bb) -self.set("type","ContextMenu")end -function _b:setItems(ab) -self.set("items",ab or{})self:calculateSize()return self end -function _b:calculateSize()local ab=self.getResolved("items") -local bb=self.getResolved("itemHeight") -if#ab==0 then self.set("width",10)self.set("height",2)return end;local cb=8 -for _c,ac in ipairs(ab)do if ac.label then local bc=#ac.label;local cc=bc+3 -if ac.submenu then cc=cc+1 end;if cc>cb then cb=cc end end end;local db=#ab*bb;self.set("width",cb) -self.set("height",db)end;function _b:open()self.set("isOpen",true) -self.set("visible",true)self:updateRender()self:dispatchEvent("opened") -return self end -function _b:close() -self.set("isOpen",false)self.set("visible",false) -local ab=self.getResolved("openSubmenu")if ab and ab.menu then ab.menu:close()end -self.set("openSubmenu",nil)self:updateRender()self:dispatchEvent("closed") -return self end;function _b:closeAll()local ab=self;while ab.parentMenu do ab=ab.parentMenu end -ab:close()return self end -function _b:getItemAt(ab) -local bb=self.getResolved("items")local cb=self.getResolved("itemHeight")local db= -math.floor((ab-1)/cb)+1 -if db>=1 and db<=#bb then return db,bb[db]end;return nil,nil end -function _b:createSubmenu(ab,bb)local cb=self.parent:addContextMenu() -cb:setItems(ab) -cb.set("background",self.getResolved("background")) -cb.set("foreground",self.getResolved("foreground"))cb.parentMenu=self;local db=self.getResolved("x") -local _c=self.getResolved("y")local ac=self.getResolved("width") -local bc=self.getResolved("itemHeight")local cc=bb._index or 1;cb.set("x",db+ac) -cb.set("y",_c+ (cc-1)*bc)cb.set("z",self.getResolved("z")+1)return -cb end -function _b:mouse_click(ab,bb,cb)if not ba.mouse_click(self,ab,bb,cb)then self:close() -return false end -local db,_c=ba.getRelativePosition(self,bb,cb)local ac,bc=self:getItemAt(_c) -if bc then if bc.disabled then return true end -if bc.submenu then -local cc=self.getResolved("openSubmenu") -if cc and cc.index==ac then cc.menu:close() -self.set("openSubmenu",nil)else if cc and cc.menu then cc.menu:close()end;bc._index=ac -local dc=self:createSubmenu(bc.submenu,bc)dc:open() -self.set("openSubmenu",{index=ac,menu=dc})end;return true end;if bc.onClick then bc.onClick(bc)end;self:closeAll() -return true end;return true end -function _b:render()local ab=self.getResolved("items") -local bb=self.getResolved("width")local cb=self.getResolved("height") -local db=self.getResolved("itemHeight")local _c=self.getResolved("background") -local ac=self.getResolved("foreground") -for bc,cc in ipairs(ab)do local dc=(bc-1)*db+1;local _d=cc.background or _c;local ad=cc.foreground or -ac;local bd=da[_d]local cd=da[ad] -local dd=string.rep(" ",bb)local __a=string.rep(bd,bb)local a_a=string.rep(cd,bb) -self:blit(1,dc,dd,a_a,__a)local b_a=cc.label or"" -if#b_a>bb-3 then b_a=b_a:sub(1,bb-3)end;self:textFg(2,dc,b_a,ad)if cc.submenu then -self:textFg(bb-1,dc,">",ad)end end;if not self.getResolved("childrenSorted")then -self:sortChildren()end -if -not self.getResolved("childrenEventsSorted")then for bc in pairs(self._values.childrenEvents or{})do -self:sortChildrenEvents(bc)end end -for bc,cc in -ipairs(self.getResolved("visibleChildren")or{})do -if cc==self then error("CIRCULAR REFERENCE DETECTED!")return end;cc:render()cc:postRender()end end;return _b end -project["elements/List.lua"] = function(...) local _a=require("elements/Collection") -local aa=require("libraries/colorHex")local ba=setmetatable({},_a)ba.__index=ba -ba.defineProperty(ba,"offset",{default=0,type="number",canTriggerRender=true,setter=function(da,_b) -local ab=math.max(0,# -da.getResolved("items")-da.getResolved("height"))return math.min(ab,math.max(0,_b))end}) -ba.defineProperty(ba,"emptyText",{default="No items",type="string",canTriggerRender=true}) -ba.defineProperty(ba,"showScrollBar",{default=true,type="boolean",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarSymbol",{default=" ",type="string",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarBackground",{default="\127",type="string",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarColor",{default=colors.lightGray,type="color",canTriggerRender=true}) -ba.defineProperty(ba,"scrollBarBackgroundColor",{default=colors.gray,type="color",canTriggerRender=true})ba.defineEvent(ba,"mouse_click") -ba.defineEvent(ba,"mouse_up")ba.defineEvent(ba,"mouse_drag") -ba.defineEvent(ba,"mouse_scroll")ba.defineEvent(ba,"key") -local ca={text={type="string",default="Entry"},bg={type="number",default=nil},fg={type="number",default= -nil},selectedBg={type="number",default=nil},selectedFg={type="number",default=nil},callback={type="function",default=nil}}function ba.new()local da=setmetatable({},ba):__init() -da.class=ba;da.set("width",16)da.set("height",8)da.set("z",5) -return da end -function ba:init(da,_b) -_a.init(self,da,_b)self._entrySchema=ca;self.set("type","List") -self:observe("items",function() -local ab=math.max(0,# -self.getResolved("items")-self.getResolved("height")) -if self.getResolved("offset")>ab then self.set("offset",ab)end end) -self:observe("height",function() -local ab=math.max(0,#self.getResolved("items")- -self.getResolved("height")) -if self.getResolved("offset")>ab then self.set("offset",ab)end end)return self end -function ba:mouse_click(da,_b,ab) -if _a.mouse_click(self,da,_b,ab)then -local bb,cb=self:getRelativePosition(_b,ab)local db=self.getResolved("width") -local _c=self.getResolved("items")local ac=self.getResolved("height") -local bc=self.getResolved("showScrollBar") -if bc and#_c>ac and bb==db then local cc=#_c-ac -local dc=math.max(1,math.floor((ac/#_c)*ac))local _d= -cc>0 and(self.getResolved("offset")/cc*100)or 0;local ad= -math.floor((_d/100)* (ac-dc))+1 -if cb>=ad and cb -ab+_b then self.set("offset",da-_b)end;return self end -function ba:key(da) -if _a.key(self,da)and self.getResolved("selectable")then -local _b=self.getResolved("items")local ab=self:getSelectedIndex() -if da==keys.up then -self:selectPrevious()if ab and ab>1 then self:scrollToItem(ab-1)end -return true elseif da==keys.down then self:selectNext()if ab and ab<#_b then -self:scrollToItem(ab+1)end;return true elseif da==keys.home then -self:clearItemSelection()self:selectItem(1)self:scrollToTop()return true elseif -da==keys["end"]then self:clearItemSelection()self:selectItem(#_b) -self:scrollToBottom()return true elseif da==keys.pageUp then local bb=self.getResolved("height")local cb=math.max(1,( -ab or 1)-bb) -self:clearItemSelection()self:selectItem(cb)self:scrollToItem(cb)return true elseif da== -keys.pageDown then local bb=self.getResolved("height") -local cb=math.min(#_b,(ab or 1)+bb)self:clearItemSelection()self:selectItem(cb) -self:scrollToItem(cb)return true end end;return false end -function ba:render(da)da=da or 0;_a.render(self) -local _b=self.getResolved("items")local ab=self.getResolved("height") -local bb=self.getResolved("offset")local cb=self.getResolved("width") -local db=self.getResolved("background")local _c=self.getResolved("foreground") -local ac=self.getResolved("showScrollBar")local bc=ac and#_b>ab;local cc=bc and cb-1 or cb -if#_b==0 then -local dc=self.getResolved("emptyText")local _d=math.floor(ab/2)+da;local ad=math.max(1, -math.floor((cb-#dc)/2)+1)for i=1,ab do -self:textBg(1,i,string.rep(" ",cb),db)end;if _d>=1 and _d<=ab then -self:textFg(ad,_d+da,dc,colors.gray)end;return end -for i=1,ab do local dc=i+bb;local _d=_b[dc] -if _d then -if _d.separator then -local ad=( -(_d.text or"-")~=""and _d.text or"-"):sub(1,1)local bd=string.rep(ad,cc)local cd=_d.fg or _c;local dd=_d.bg or db;self:textBg(1, -i+da,string.rep(" ",cc),dd)self:textFg(1,i+ -da,bd,cd)else local ad=_d.text or""local bd=_d.selected -local cd= -bd and( -_d.selectedBg or self.getResolved("selectedBackground"))or(_d.bg or db) -local dd=bd and -(_d.selectedFg or self.getResolved("selectedForeground"))or(_d.fg or _c)local __a=ad -if#__a>cc then __a=__a:sub(1,cc-3).."..."else __a=__a..string.rep(" ",cc- -#__a)end;self:textBg(1,i+da,string.rep(" ",cc),cd)self:textFg(1, -i+da,__a,dd)end else self:textBg(1,i+da,string.rep(" ",cc),db)end end -if bc then -local dc=math.max(1,math.floor((ab/#_b)*ab))local _d=#_b-ab;local ad=_d>0 and(bb/_d*100)or 0;local bd=math.floor((ad/ -100)* (ab-dc))+1 -local cd=self.getResolved("scrollBarSymbol")local dd=self.getResolved("scrollBarBackground") -local __a=self.getResolved("scrollBarColor")local a_a=self.getResolved("scrollBarBackgroundColor") -for i=1,ab do self:blit(cb, -i+da,dd,aa[_c],aa[a_a])end;for i=bd,math.min(ab,bd+dc-1)do -self:blit(cb,i+da,cd,aa[__a],aa[a_a])end end end;return ba end -project["elements/Collection.lua"] = function(...) local d=require("elements/VisualElement") -local _a=require("libraries/collectionentry")local aa=setmetatable({},d)aa.__index=aa -aa.defineProperty(aa,"items",{default={},type="table",canTriggerRender=true}) -aa.defineProperty(aa,"selectable",{default=true,type="boolean"}) -aa.defineProperty(aa,"multiSelection",{default=false,type="boolean"}) -aa.defineProperty(aa,"selectedBackground",{default=colors.blue,type="color",canTriggerRender=true}) -aa.defineProperty(aa,"selectedForeground",{default=colors.white,type="color",canTriggerRender=true}) -aa.combineProperties(aa,"selectionColor","selectedForeground","selectedBackground")function aa.new()local ba=setmetatable({},aa):__init() -ba.class=aa;return ba end -function aa:init(ba,ca) -d.init(self,ba,ca)self._entrySchema={}self.set("type","Collection")return self end -function aa:addItem(ba)if type(ba)=="string"then ba={text=ba}end;if ba.selected==nil then -ba.selected=false end -local ca=_a.new(self,ba,self._entrySchema) -table.insert(self.getResolved("items"),ca)self:updateRender()return ca end -function aa:removeItem(ba)local ca=self.getResolved("items")if type(ba)=="number"then -table.remove(ca,ba)else -for da,_b in pairs(ca)do if _b==ba then table.remove(ca,da)break end end end -self:updateRender()return self end -function aa:clear()self.set("items",{})self:updateRender()return self end -function aa:getSelectedItems()local ba={}for ca,da in ipairs(self.getResolved("items"))do -if -type(da)=="table"and da.selected then local _b=da;_b.index=ca;table.insert(ba,_b)end end -return ba end -function aa:getSelectedItem()local ba=self.getResolved("items") -for ca,da in ipairs(ba)do if -type(da)=="table"and da.selected then return da end end;return nil end -function aa:selectItem(ba)local ca=self.getResolved("items") -if type(ba)=="number"then -if -ca[ba]and type(ca[ba])=="table"then ca[ba].selected=true end else for da,_b in pairs(ca)do -if _b==ba then if type(_b)=="table"then _b.selected=true end;break end end end;self:updateRender()return self end -function aa:unselectItem(ba)local ca=self.getResolved("items") -if type(ba)=="number"then -if -ca[ba]and type(ca[ba])=="table"then ca[ba].selected=false end else -for da,_b in pairs(ca)do if _b==ba then -if type(ca[da])=="table"then ca[da].selected=false end;break end end end;self:updateRender()return self end -function aa:clearItemSelection()local ba=self.getResolved("items")for ca,da in ipairs(ba)do -da.selected=false end;self:updateRender()return self end -function aa:getSelectedIndex()local ba=self.getResolved("items") -for ca,da in ipairs(ba)do if -type(da)=="table"and da.selected then return ca end end;return nil end -function aa:selectNext()local ba=self.getResolved("items") -local ca=self:getSelectedIndex() -if not ca then if#ba>0 then self:selectItem(1)end elseif ca<#ba then -if not -self.getResolved("multiSelection")then self:clearItemSelection()end;self:selectItem(ca+1)end;self:updateRender()return self end -function aa:selectPrevious()local ba=self.getResolved("items") -local ca=self:getSelectedIndex() -if not ca then if#ba>0 then self:selectItem(#ba)end elseif ca>1 then -if not -self.getResolved("multiSelection")then self:clearItemSelection()end;self:selectItem(ca-1)end;self:updateRender()return self end -function aa:onSelect(ba)self:registerCallback("select",ba)return self end;return aa end -project["elements/Accordion.lua"] = function(...) local aa=require("elementManager") -local ba=require("elements/VisualElement")local ca=aa.getElement("Container") -local da=require("libraries/colorHex")local _b=setmetatable({},ca)_b.__index=_b -_b.defineProperty(_b,"panels",{default={},type="table"}) -_b.defineProperty(_b,"panelHeaderHeight",{default=1,type="number",canTriggerRender=true}) -_b.defineProperty(_b,"allowMultiple",{default=false,type="boolean"}) -_b.defineProperty(_b,"headerBackground",{default=colors.gray,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"headerTextColor",{default=colors.white,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"expandedHeaderBackground",{default=colors.lightGray,type="color",canTriggerRender=true}) -_b.defineProperty(_b,"expandedHeaderTextColor",{default=colors.black,type="color",canTriggerRender=true})_b.defineEvent(_b,"mouse_click") -_b.defineEvent(_b,"mouse_up")function _b.new()local ab=setmetatable({},_b):__init() -ab.class=_b;ab.set("width",20)ab.set("height",10)ab.set("z",10)return -ab end -function _b:init(ab,bb) -ca.init(self,ab,bb)self.set("type","Accordion")end -function _b:newPanel(ab,bb)local cb=self.getResolved("panels")or{} -local db=#cb+1;local _c=self:addContainer()_c.set("x",1)_c.set("y",1) -_c.set("width",self.getResolved("width")) -_c.set("height",self.getResolved("height"))_c.set("visible",bb or false) -_c.set("ignoreOffset",true) -table.insert(cb,{id=db,title=tostring(ab or("Panel "..db)),expanded=bb or false,container=_c})self.set("panels",cb)self:updatePanelLayout()return _c end;_b.addPanel=_b.newPanel -function _b:updatePanelLayout() -local ab=self.getResolved("panels")or{} -local bb=self.getResolved("panelHeaderHeight")or 1;local cb=1;local db=self.getResolved("width") -local _c=self.getResolved("height") -for dc,_d in ipairs(ab)do local ad=cb+bb;_d.container.set("x",1) -_d.container.set("y",ad)_d.container.set("width",db) -_d.container.set("visible",_d.expanded)_d.container.set("ignoreOffset",false)cb=cb+bb -if -_d.expanded then local bd=0 -for dd,__a in -ipairs(_d.container._values.children or{})do -if not __a._destroyed then local a_a=__a.get("y")local b_a=__a.get("height")local c_a= -a_a+b_a-1;if c_a>bd then bd=c_a end end end;local cd=math.max(1,bd)_d.container.set("height",cd)cb= -cb+cd end end;local ac=cb-1;local bc=math.max(0,ac-_c) -local cc=self.getResolved("offsetY")if cc>bc then self.set("offsetY",bc)end -self:updateRender()end -function _b:togglePanel(ab)local bb=self.getResolved("panels")or{} -local cb=self.getResolved("allowMultiple") -for db,_c in ipairs(bb)do -if _c.id==ab then _c.expanded=not _c.expanded -if not cb and _c.expanded then for ac,bc in -ipairs(bb)do if ac~=db then bc.expanded=false end end end;self:updatePanelLayout() -self:dispatchEvent("panelToggled",ab,_c.expanded)break end end;return self end -function _b:expandPanel(ab)local bb=self.getResolved("panels")or{} -local cb=self.getResolved("allowMultiple") -for db,_c in ipairs(bb)do -if _c.id==ab then -if not _c.expanded then _c.expanded=true -if not cb then for ac,bc in ipairs(bb)do if ac~=db then -bc.expanded=false end end end;self:updatePanelLayout() -self:dispatchEvent("panelToggled",ab,true)end;break end end;return self end -function _b:collapsePanel(ab)local bb=self.getResolved("panels")or{} -for cb,db in -ipairs(bb)do -if db.id==ab then if db.expanded then db.expanded=false;self:updatePanelLayout() -self:dispatchEvent("panelToggled",ab,false)end;break end end;return self end -function _b:getPanel(ab)local bb=self.getResolved("panels")or{} -for cb,db in -ipairs(bb)do if db.id==ab then return db.container end end;return nil end -function _b:_getPanelMetrics()local ab=self.getResolved("panels")or{}local bb= -self.getResolved("panelHeaderHeight")or 1;local cb={}local db=1 -for _c,ac in ipairs(ab)do -table.insert(cb,{id=ac.id,title=ac.title,expanded=ac.expanded,headerY=db,headerHeight=bb})db=db+bb;if ac.expanded then -db=db+ac.container.get("height")end end;return{positions=cb,totalHeight=db-1}end -function _b:mouse_click(ab,bb,cb) -if not ba.mouse_click(self,ab,bb,cb)then return false end;local db,_c=ba.getRelativePosition(self,bb,cb) -local ac=self.getResolved("offsetY")local bc=_c+ac;local cc=self:_getPanelMetrics() -for dc,_d in ipairs(cc.positions)do local ad= -_d.headerY+_d.headerHeight-1;if -bc>=_d.headerY and bc<=ad then self:togglePanel(_d.id) -self.set("focusedChild",nil)return true end end;return ca.mouse_click(self,ab,bb,cb)end -function _b:mouse_scroll(ab,bb,cb) -if ba.mouse_scroll(self,ab,bb,cb)then -local db=self:_getPanelMetrics()local _c=self.getResolved("height")local ac=db.totalHeight -local bc=math.max(0,ac-_c) -if bc>0 then local cc=self.getResolved("offsetY")local dc=cc+ab -dc=math.max(0,math.min(bc,dc))self.set("offsetY",dc)return true end;return ca.mouse_scroll(self,ab,bb,cb)end;return false end -function _b:render()ba.render(self)local ab=self.getResolved("width") -local bb=self.getResolved("offsetY")local cb=self:_getPanelMetrics() -for db,_c in ipairs(cb.positions)do -local ac=_c.expanded and -self.getResolved("expandedHeaderBackground")or self.getResolved("headerBackground") -local bc= -_c.expanded and self.getResolved("expandedHeaderTextColor")or self.getResolved("headerTextColor")local cc=_c.headerY-bb -if -cc>=1 and cc<=self.getResolved("height")then -ba.multiBlit(self,1,cc,ab,_c.headerHeight," ",da[bc],da[ac])local dc=_c.expanded and"v"or">" -local _d=dc.." ".._c.title;ba.textFg(self,1,cc,_d,bc)end end;if not self.getResolved("childrenSorted")then -self:sortChildren()end -if -not self.getResolved("childrenEventsSorted")then for db in pairs(self._values.childrenEvents or{})do -self:sortChildrenEvents(db)end end -for db,_c in -ipairs(self.getResolved("visibleChildren")or{})do -if _c==self then error("CIRCULAR REFERENCE DETECTED!")return end;_c:render()_c:postRender()end end;return _b end -project["plugins/store.lua"] = function(...) local _a=require("propertySystem") -local aa=require("errorManager")local ba={}function ba.setup(da) -da.defineProperty(da,"stores",{default={},type="table"}) -da.defineProperty(da,"storeObserver",{default={},type="table"})end -function ba:initializeStore(da,_b,ab,bb) -local cb=self.get("stores")if cb[da]then -aa.error("Store '"..da.."' already exists")return self end;local db=bb or"stores/".. -self.get("name")..".store"local _c={} -if ab and -fs.exists(db)then local ac=fs.open(db,"r")_c= -textutils.unserialize(ac.readAll())or{}ac.close()end;cb[da]={value=ab and _c[da]or _b,persist=ab} -return self end;local ca={} -function ca:setStore(da,_b)local ab=self:getBaseFrame() -local bb=ab.get("stores")local cb=ab.get("storeObserver") -if not bb[da]then aa.error("Store '".. -da.."' not initialized")end -if bb[da].persist then -local db="stores/"..ab.get("name")..".store"local _c={} -if fs.exists(db)then local cc=fs.open(db,"r")_c= -textutils.unserialize(cc.readAll())or{}cc.close()end;_c[da]=_b;local ac=fs.getDir(db)if not fs.exists(ac)then -fs.makeDir(ac)end;local bc=fs.open(db,"w") -bc.write(textutils.serialize(_c))bc.close()end;bb[da].value=_b -if cb[da]then for db,_c in ipairs(cb[da])do _c(da,_b)end end;for db,_c in pairs(bb)do -if _c.computed then _c.value=_c.computeFn(self)if cb[db]then for ac,bc in ipairs(cb[db])do -bc(db,_c.value)end end end end -return self end -function ca:getStore(da)local _b=self:getBaseFrame()local ab=_b.get("stores")if -not ab[da]then -aa.error("Store '"..da.."' not initialized")end;if ab[da].computed then -return ab[da].computeFn(self)end;return ab[da].value end -function ca:onStoreChange(da,_b)local ab=self:getBaseFrame() -local bb=ab.get("stores")[da]if not bb then -aa.error("Cannot observe store '"..da.."': Store not initialized")return self end -local cb=ab.get("storeObserver")if not cb[da]then cb[da]={}end;table.insert(cb[da],_b) -return self end -function ca:removeStoreChange(da,_b)local ab=self:getBaseFrame() -local bb=ab.get("storeObserver") -if bb[da]then for cb,db in ipairs(bb[da])do -if db==_b then table.remove(bb[da],cb)break end end end;return self end -function ca:computed(da,_b)local ab=self:getBaseFrame()local bb=ab.get("stores")if bb[da]then -aa.error( -"Computed store '"..da.."' already exists")return self end -bb[da]={computeFn=_b,value=_b(self),computed=true}return self end -function ca:bind(da,_b)_b=_b or da;local ab=self:getBaseFrame()local bb=false -if -self.get(da)~=nil then self.set(da,ab:getStore(_b))end -self:onChange(da,function(cb,db)if bb then return end;bb=true;cb:setStore(_b,db)bb=false end) -self:onStoreChange(_b,function(cb,db)if bb then return end;bb=true;if self.get(da)~=nil then -self.set(da,db)end;bb=false end)return self end;return{BaseElement=ca,BaseFrame=ba} end -project["plugins/canvas.lua"] = function(...) local ba=require("libraries/colorHex") -local ca=require("errorManager")local da={}da.__index=da;local _b,ab=string.sub,string.rep -function da.new(cb) -local db=setmetatable({},da)db.commands={pre={},post={}}db.type="pre"db.element=cb;return db end -function da:clear()self.commands={pre={},post={}}return self end;function da:getValue(cb) -if type(cb)=="function"then return cb(self.element)end;return cb end -function da:setType(cb)if -cb=="pre"or cb=="post"then self.type=cb else -ca.error("Invalid type. Use 'pre' or 'post'.")end;return self end -function da:addCommand(cb) -local db=#self.commands[self.type]+1;self.commands[self.type][db]=cb;return db end -function da:setCommand(cb,db)self.commands[cb]=db;return self end;function da:removeCommand(cb) -table.remove(self.commands[self.type],cb)return self end -function da:text(cb,db,_c,ac,bc) -return -self:addCommand(function(cc) -local dc,_d=self:getValue(cb),self:getValue(db)local ad=self:getValue(_c)local bd=self:getValue(ac) -local cd=self:getValue(bc) -local dd=type(bd)=="number"and ba[bd]:rep(#_c)or bd -local __a=type(cd)=="number"and ba[cd]:rep(#_c)or cd;cc:drawText(dc,_d,ad) -if dd then cc:drawFg(dc,_d,dd)end;if __a then cc:drawBg(dc,_d,__a)end end)end;function da:bg(cb,db,_c)return -self:addCommand(function(ac)ac:drawBg(cb,db,_c)end)end -function da:fg(cb,db,_c)return self:addCommand(function(ac) -ac:drawFg(cb,db,_c)end)end -function da:rect(cb,db,_c,ac,bc,cc,dc) -return -self:addCommand(function(_d)local ad,bd=self:getValue(cb),self:getValue(db) -local cd,dd=self:getValue(_c),self:getValue(ac)local __a=self:getValue(bc)local a_a=self:getValue(cc) -local b_a=self:getValue(dc)if(type(a_a)=="number")then a_a=ba[a_a]end;if -(type(b_a)=="number")then b_a=ba[b_a]end -local c_a=b_a and _b(b_a:rep(cd),1,cd)local d_a=a_a and _b(a_a:rep(cd),1,cd)local _aa=__a and -_b(__a:rep(cd),1,cd) -for i=0,dd-1 do -if b_a then _d:drawBg(ad,bd+i,c_a)end;if a_a then _d:drawFg(ad,bd+i,d_a)end;if __a then -_d:drawText(ad,bd+i,_aa)end end end)end -function da:line(cb,db,_c,ac,bc,cc,dc) -local function _d(cd,dd,__a,a_a)local b_a={}local c_a=0;local d_a=math.abs(__a-cd) -local _aa=math.abs(a_a-dd)local aaa=(cd<__a)and 1 or-1 -local baa=(dd-_aa then caa=caa-_aa;cd=cd+aaa end;if daa0 do -aaa=aaa-1;daa=daa-2 *c_a;if baa>0 then baa=baa+c_a-daa else _aa=_aa+1 -caa=caa+2 *d_a;baa=baa+c_a-daa+caa end -_ba(_aa,aaa)end -return a_a end;local ad=_d(cb,db,_c,ac) -return -self:addCommand(function(bd)local cd=self:getValue(bc) -local dd=self:getValue(cc)local __a=self:getValue(dc) -local a_a=type(dd)=="number"and ba[dd]or dd -local b_a=type(__a)=="number"and ba[__a]or __a -for c_a,d_a in pairs(ad)do local _aa=math.floor(d_a.x)local aaa=math.floor(d_a.y)if cd then -bd:drawText(_aa,aaa,cd)end;if a_a then bd:drawFg(_aa,aaa,a_a)end;if b_a then -bd:drawBg(_aa,aaa,b_a)end end end)end;local bb={hooks={}} -function bb.setup(cb) -cb.defineProperty(cb,"canvas",{default=nil,type="table",getter=function(db)if not db._values.canvas then -db._values.canvas=da.new(db)end;return db._values.canvas end})end;function bb.hooks.render(cb)local db=cb.get("canvas") -if -db and#db.commands.pre>0 then for _c,ac in pairs(db.commands.pre)do ac(cb)end end end -function bb.hooks.postRender(cb) -local db=cb.get("canvas")if db and#db.commands.post>0 then for _c,ac in pairs(db.commands.post)do -ac(cb)end end end;return{VisualElement=bb,API=da} end -project["plugins/theme.lua"] = function(...) local _b=require("errorManager") -local ab={default={background=colors.cyan,foreground=colors.black},BaseFrame={background=colors.white,foreground=colors.black,Container={default={background=colors.cyan,foreground=colors.black},background=colors.black,Button={background=colors.cyan,foreground=colors.black,states={clicked={background=colors.white,foreground=colors.black}}},Input={background=colors.cyan,foreground=colors.black},Label={foreground=colors.white}},Button={background=colors.cyan,foreground=colors.black,states={clicked={background=colors.black,foreground=colors.cyan}}},Label={foreground=colors.black},names={basaltDebugLog={background=colors.red,foreground=colors.white}}}}local bb={default=ab}local cb="default" -local db={hooks={postInit={pre=function(dc)if dc._postInitialized then return dc end -dc:applyTheme()end}}} -function db.____getElementPath(dc,_d)if _d then table.insert(_d,1,dc._values.type)else -_d={dc._values.type}end;local ad=dc.parent;if ad then return -ad.____getElementPath(ad,_d)else return _d end end -local function _c(dc,_d)local ad=dc -for i=1,#_d do local bd=false;local cd=_d[i]for dd,__a in ipairs(cd)do -if ad[__a]then ad=ad[__a]bd=true;break end end;if not bd then return nil end end;return ad end -local function ac(dc,_d,ad,bd,cd) -if -_d.default and _d.default.names and _d.default.names[bd]then for dd,__a in pairs(_d.default.names[bd])do -if type(__a)~="table"then dc[dd]=__a end end end -if - -_d.default and _d.default[ad]and _d.default[ad].names and _d.default[ad].names[bd]then -for dd,__a in pairs(_d.default[ad].names[bd])do if -type(__a)~="table"then dc[dd]=__a end end end;if cd and cd.names and cd.names[bd]then -for dd,__a in pairs(cd.names[bd])do if type(__a)~= -"table"then dc[dd]=__a end end end end -local function bc(dc,_d,ad,bd)local cd={} -if dc.default then for a_a,b_a in pairs(dc.default)do -if type(b_a)~="table"then cd[a_a]=b_a end end end;local dd=dc -for i=1,#_d do local a_a=_d[i]local b_a=false -for c_a,d_a in ipairs(a_a)do -if dd[d_a]then dd=dd[d_a]b_a=true;if dd.default then -for _aa,aaa in -pairs(dd.default)do if type(aaa)~="table"then cd[_aa]=aaa end end end;break end end;if not b_a then dd=nil;break end end;local __a=_c(dc,_d)if __a then -for a_a,b_a in pairs(__a)do if -type(b_a)~="table"or a_a=="states"then cd[a_a]=b_a end end end -ac(cd,dc,ad,bd,__a)return cd end -function db:applyTheme(dc)local _d={}if self._modifiedProperties then -for bd,cd in pairs(self._modifiedProperties)do _d[bd]=true end end -local ad=self:getTheme() -if(ad~=nil)then -for bd,cd in pairs(ad)do -if bd~="states"and not _d[bd]then -local dd=self._properties[bd] -if(dd)then -if( (dd.type)=="color")then if(type(cd)=="string")then -if(colors[cd])then cd=colors[cd]end end end;self.set(bd,cd)end end end -if ad.states then -for bd,cd in pairs(ad.states)do -for dd,__a in pairs(cd)do -if dd~="priority"then -local a_a=self._properties[dd]local b_a=dd:sub(1,1):upper()..dd:sub(2) -if(a_a)then -if( -(a_a.type)=="color")then if(type(__a)=="string")then -if(colors[__a])then __a=colors[__a]end end end -self["set"..b_a.."State"](self,bd,__a)end end end end end end;self._modifiedProperties=_d -if(dc~=false)then if(self:isType("Container"))then -local bd=self.get("children") -for cd,dd in ipairs(bd)do if(dd and dd.applyTheme)then dd:applyTheme()end end end end;return self end -function db:getTheme()local dc=self:____getElementPath() -local _d=self.get("type")local ad=self.get("name")return bc(bb[cb],dc,_d,ad)end;local cc={}function cc.setTheme(dc)bb.default=dc end -function cc.getTheme()return bb.default end -function cc.loadTheme(dc)local _d=fs.open(dc,"r") -if _d then local ad=_d.readAll()_d.close() -bb.default=textutils.unserializeJSON(ad)if not bb.default then -_b.error("Failed to load theme from "..dc)end else -_b.error("Could not open theme file: "..dc)end end;return{BaseElement=db,API=cc} end -project["plugins/reactive.lua"] = function(...) local cb=require("errorManager") -local db=require("propertySystem")local _c={colors=true,math=true,clamp=true,round=true} -local ac={clamp=function(__a,a_a,b_a)return -math.min(math.max(__a,a_a),b_a)end,round=function(__a) -return math.floor(__a+0.5)end,floor=math.floor,ceil=math.ceil,abs=math.abs} -local function bc(__a)return -{parent=__a:find("parent%."),self=__a:find("self%."),other=__a:find("[^(parent)][^(self)]%.")}end -local function cc(__a,a_a,b_a)local c_a=bc(__a) -if c_a.parent and not a_a.parent then -cb.header="Reactive evaluation error" -cb.error("Expression uses parent but no parent available")return function()return nil end end;__a=__a:gsub("^{(.+)}$","%1") -__a=__a:gsub("([%w_]+)%$([%w_]+)",function(baa,caa) -if baa=="self"then return -string.format('__getState("%s")',caa)elseif baa=="parent"then return -string.format('__getParentState("%s")',caa)else return -string.format('__getElementState("%s", "%s")',baa,caa)end end) -__a=__a:gsub("([%w_]+)%.([%w_]+)",function(baa,caa)if _c[baa]then return baa.."."..caa end;if -tonumber(baa)then return baa.."."..caa end;return -string.format('__getProperty("%s", "%s")',baa,caa)end) -local d_a=setmetatable({colors=colors,math=math,tostring=tostring,tonumber=tonumber,__getState=function(baa)return a_a:getState(baa)end,__getParentState=function(baa)return -a_a.parent:getState(baa)end,__getElementState=function(baa,caa)if tonumber(baa)then return nil end -local daa=a_a:getBaseFrame():getChild(baa)if not daa then cb.header="Reactive evaluation error" -cb.error("Could not find element: "..baa)return nil end;return -daa:getState(caa).value end,__getProperty=function(baa,caa)if -tonumber(baa)then return nil end -if baa=="self"then if a_a._properties[caa]then -return a_a.getResolved(caa)end;if -a_a._registeredStates and a_a._registeredStates[caa]then return a_a:hasState(caa)end -local daa=a_a.get("states")if daa and daa[caa]~=nil then return true end -cb.header="Reactive evaluation error" -cb.error("Property or state '"..caa.. -"' not found in element '"..a_a:getType().."'")return nil elseif baa=="parent"then if a_a.parent._properties[caa]then return -a_a.parent.getResolved(caa)end;if a_a.parent._registeredStates and -a_a.parent._registeredStates[caa]then -return a_a.parent:hasState(caa)end -local daa=a_a.parent.get("states")if daa and daa[caa]~=nil then return true end -cb.header="Reactive evaluation error" -cb.error("Property or state '"..caa.."' not found in parent element")return nil else local daa=a_a.parent:getChild(baa)if not daa then -cb.header="Reactive evaluation error" -cb.error("Could not find element: "..baa)return nil end;if -daa._properties[caa]then return daa.getResolved(caa)end -if daa._registeredStates and -daa._registeredStates[caa]then return daa:hasState(caa)end;local _ba=daa.get("states") -if _ba and _ba[caa]~=nil then return true end;cb.header="Reactive evaluation error" -cb.error("Property or state '"..caa.. -"' not found in element '"..baa.."'")return nil end end},{__index=ac})if(a_a._properties[b_a].type=="string")then -__a="tostring("..__a..")"elseif(a_a._properties[b_a].type=="number")then -__a="tonumber("..__a..")"end;local _aa,aaa=load( -"return "..__a,"reactive","t",d_a) -if not _aa then -cb.header="Reactive evaluation error"cb.error("Invalid expression: "..aaa)return -function()return nil end end;return _aa end -local function dc(__a,a_a) -for b_a in __a:gmatch("([%w_]+)%.")do -if not _c[b_a]then -if b_a=="self"then elseif b_a=="parent"then -if not a_a.parent then -cb.header="Reactive evaluation error"cb.error("No parent element available")return false end else -if(tonumber(b_a)==nil)then local c_a=a_a.parent:getChild(b_a)if not c_a then -cb.header="Reactive evaluation error" -cb.error("Referenced element not found: "..b_a)return false end end end end end;return true end;local _d=setmetatable({},{__mode="k"}) -local ad=setmetatable({},{__mode="k",__index=function(__a,a_a)__a[a_a]={}return -__a[a_a]end}) -local bd=setmetatable({},{__mode="k",__index=function(__a,a_a)__a[a_a]={}return __a[a_a]end}) -local function cd(__a,a_a,b_a)local c_a=bc(a_a) -if ad[__a][b_a]then for _aa,aaa in ipairs(ad[__a][b_a])do -aaa.target:removeObserver(aaa.property,aaa.callback)end end;local d_a={} -for _aa,aaa in a_a:gmatch("([%w_]+)%.([%w_]+)")do -if not _c[_aa]then local baa -if -_aa=="self"and c_a.self then baa=__a elseif _aa=="parent"and c_a.parent then baa=__a.parent elseif c_a.other then -baa=__a:getBaseFrame():getChild(_aa)end -if baa then local caa=false -if baa._properties[aaa]then caa=false elseif baa._registeredStates and -baa._registeredStates[aaa]then caa=true else local _ba=baa.get("states")if _ba and -_ba[aaa]~=nil then caa=true end end -local daa={target=baa,property=caa and"states"or aaa,callback=function()local _ba=bd[__a][b_a]local aba=__a.get(b_a) -if -_ba~=aba then bd[__a][b_a]=aba -if -__a._observers and __a._observers[b_a]then for bba,cba in ipairs(__a._observers[b_a])do cba()end end;__a:updateRender()end end}baa:observe(daa.property,daa.callback) -table.insert(d_a,daa)end end end;ad[__a][b_a]=d_a end -db.addSetterHook(function(__a,a_a,b_a,c_a) -if type(b_a)=="string"and b_a:match("^{.+}$")then -local d_a=b_a:gsub("^{(.+)}$","%1")local _aa=bc(d_a) -if _aa.parent and not __a.parent then return c_a.default end;if not dc(d_a,__a)then return c_a.default end -cd(__a,d_a,a_a)if not _d[__a]then _d[__a]={}end;if not _d[__a][b_a]then -local aaa=cc(b_a,__a,a_a)_d[__a][b_a]=aaa end -return -function(aaa) -if __a._destroyed or(_aa.parent and not -__a.parent)then return c_a.default end;local baa,caa=pcall(_d[__a][b_a]) -if not baa then -if caa and -caa:match("attempt to index.-nil value")then return c_a.default end;cb.header="Reactive evaluation error"if type(caa)=="string"then cb.error( -"Error evaluating expression: "..caa)else -cb.error("Error evaluating expression")end -return c_a.default end;bd[__a][a_a]=caa;return caa end end end)local dd={} -dd.hooks={destroy=function(__a) -if ad[__a]then -for a_a,b_a in pairs(ad[__a])do for c_a,d_a in ipairs(b_a)do -d_a.target:removeObserver(d_a.property,d_a.callback)end end;ad[__a]=nil;bd[__a]=nil;_d[__a]=nil end end}return{BaseElement=dd} end -project["plugins/xml.lua"] = function(...) local bb=require("errorManager")local cb=require("log") -local db={new=function(cd) -return -{tag=cd,value=nil,attributes={},children={},addChild=function(dd,__a) -table.insert(dd.children,__a)end,addAttribute=function(dd,__a,a_a)dd.attributes[__a]=a_a end}end} -local _c=function(cd,dd) -local __a,a_a=string.gsub(dd,"([%w:]+)=([\"'])(.-)%2",function(d_a,_aa,aaa) -cd:addAttribute(d_a,"\""..aaa.."\"")end) -local b_a,c_a=string.gsub(dd,"([%w:]+)={(.-)}",function(d_a,_aa)cd:addAttribute(d_a,_aa)end)end;local ac={} -ac={_customTagHandlers={},registerTagHandler=function(cd,dd)ac._customTagHandlers[cd]=dd -cb.info( -"XMLParser: Registered custom tag handler for '"..cd.."'")end,unregisterTagHandler=function(cd)ac._customTagHandlers[cd]= -nil -cb.info("XMLParser: Unregistered custom tag handler for '"..cd.."'")end,getTagHandler=function(cd)return -ac._customTagHandlers[cd]end,parseText=function(cd)local dd={}local __a=db.new() -table.insert(dd,__a)local a_a,b_a,c_a,d_a,_aa;local aaa,baa=1,1 -while true do -a_a,baa,b_a,c_a,d_a,_aa=string.find(cd,"<(%/?)([%w_:]+)(.-)(%/?)>",aaa)if not a_a then break end;local caa=string.sub(cd,aaa,a_a-1)if not -string.find(caa,"^%s*$")then local daa=(__a.value or"")..caa -dd[#dd].value=daa end -if _aa=="/"then local daa=db.new(c_a) -_c(daa,d_a)__a:addChild(daa)elseif b_a==""then local daa=db.new(c_a)_c(daa,d_a) -table.insert(dd,daa)__a=daa else local daa=table.remove(dd)__a=dd[#dd] -if#dd<1 then bb.error( -"XMLParser: nothing to close with "..c_a)end;if daa.tag~=c_a then -bb.error("XMLParser: trying to close "..daa.tag.." with "..c_a)end;__a:addChild(daa)end;aaa=baa+1 end;if#dd>1 then -error("XMLParser: unclosed "..dd[#dd].tag)end;return __a.children end} -local function bc(cd)local dd={}local __a=1 -while true do local a_a,b_a,c_a=cd:find("%${([^}]+)}",__a) -if not a_a then break end -table.insert(dd,{start=a_a,ending=b_a,expression=c_a,raw=cd:sub(a_a,b_a)})__a=b_a+1 end;return dd end -local function cc(cd,dd)if not cd then return cd end;if -cd:sub(1,1)=="\""and cd:sub(-1)=="\""then cd=cd:sub(2,-2)end;local __a=bc(cd) -for a_a,b_a in ipairs(__a)do -local c_a=b_a.expression;local d_a=b_a.start-1;local _aa=b_a.ending+1;if dd[c_a]then cd=cd:sub(1,d_a).. -tostring(dd[c_a])..cd:sub(_aa)else -bb.error( -"XMLParser: variable '"..c_a.."' not found in scope")end end -if cd:match("^%s*%s*$")then -local a_a=cd:match("")local b_a=_ENV;for baa,caa in pairs(dd)do b_a[baa]=caa end -local c_a,d_a=load("return "..a_a,nil,"bt",b_a)if not c_a then -bb.error("XMLParser: CDATA syntax error: "..tostring(d_a))end;local _aa,aaa=pcall(c_a)if not _aa then -bb.error( -"XMLParser: CDATA execution error: "..tostring(aaa))end;return aaa end -if cd=="true"then return true elseif cd=="false"then return false elseif colors[cd]then return colors[cd]elseif tonumber(cd)then return -tonumber(cd)else return cd end end -local function dc(cd,dd)local __a={} -for a_a,b_a in pairs(cd.children)do -if b_a.tag=="item"or b_a.tag=="entry"then -local c_a={} -for d_a,_aa in pairs(b_a.attributes)do c_a[d_a]=cc(_aa,dd)end;for d_a,_aa in pairs(b_a.children)do -if _aa.value then c_a[_aa.tag]=cc(_aa.value,dd)elseif# -_aa.children>0 then c_a[_aa.tag]=dc(_aa)end end -table.insert(__a,c_a)else if b_a.value then __a[b_a.tag]=cc(b_a.value,dd)elseif#b_a.children>0 then -__a[b_a.tag]=dc(b_a)end end end;return __a end -local function _d(cd,dd,__a,a_a)local b_a,c_a=dd:match("^(.+)State:(.+)$") -if b_a and c_a then -c_a=c_a:gsub("^\"",""):gsub("\"$","") -local d_a=b_a:sub(1,1):upper()..b_a:sub(2)local _aa="set"..d_a.."State" -if cd[_aa]then -cd[_aa](cd,c_a,cc(__a,a_a))return true else -cb.warn("XMLParser: State method '".._aa.. -"' not found for element '"..cd:getType().."'")return true end end;return false end;local ad={}function ad.setup(cd) -cd.defineProperty(cd,"customXML",{default={attributes={},children={}},type="table"})end -function ad:fromXML(cd,dd) -if(cd.attributes)then -for __a,a_a in -pairs(cd.attributes)do -if not _d(self,__a,a_a,dd)then -if(self._properties[__a])then -self.set(__a,cc(a_a,dd))elseif self[__a]then -if(__a:sub(1,2)=="on")then local b_a=a_a:gsub("\"","") -if(dd[b_a])then if( -type(dd[b_a])~="function")then -bb.error("XMLParser: variable '".. -b_a.."' is not a function for element '"..self:getType().. -"' "..__a)end -self[__a](self,dd[b_a])else -bb.error("XMLParser: variable '"..b_a.."' not found in scope")end else -bb.error("XMLParser: property '"..__a.. -"' not found in element '"..self:getType().."'")end else local b_a=self.get("customXML") -b_a.attributes[__a]=cc(a_a,dd)end end end end -if(cd.children)then -for __a,a_a in pairs(cd.children)do -if a_a.tag=="state"then -local b_a=a_a.attributes and a_a.attributes.name;if not b_a then -bb.error("XMLParser: tag requires 'name' attribute")end -b_a=b_a:gsub("^\"",""):gsub("\"$","") -if a_a.children then -for c_a,d_a in ipairs(a_a.children)do local _aa=d_a.tag;local aaa -if -d_a.attributes and d_a.attributes.value then aaa=cc(d_a.attributes.value,dd)elseif d_a.value then -aaa=cc(d_a.value,dd)else -cb.warn("XMLParser: State property '".._aa.."' has no value")aaa=nil end -if aaa~=nil then -local baa=_aa:sub(1,1):upper().._aa:sub(2)local caa="set"..baa.."State"if self[caa]then -self[caa](self,b_a,aaa)else -cb.warn("XMLParser: State method '"..caa.. -"' not found for element '"..self:getType().."'")end end end end elseif(self._properties[a_a.tag])then if -(self._properties[a_a.tag].type=="table")then self.set(a_a.tag,dc(a_a,dd))else -self.set(a_a.tag,cc(a_a.value,dd))end else local b_a={} -if(a_a.children)then -for c_a,d_a in -pairs(a_a.children)do -if(d_a.tag=="param")then -table.insert(b_a,cc(d_a.value,dd))elseif(d_a.tag=="table")then table.insert(b_a,dc(d_a,dd))end end end -if(self[a_a.tag])then if(#b_a>0)then -self[a_a.tag](self,table.unpack(b_a))elseif(a_a.value)then self[a_a.tag](self,cc(a_a.value,dd))else -self[a_a.tag](self)end else -local c_a=self.get("customXML")a_a.value=cc(a_a.value,dd)c_a.children[a_a.tag]=a_a end end end end;return self end;local bd={} -function bd:loadXML(cd,dd)dd=dd or{}local __a=ac.parseText(cd) -self:fromXML(__a,dd) -if(__a)then -for a_a,b_a in ipairs(__a)do -local c_a=b_a.tag:sub(1,1):upper()..b_a.tag:sub(2)if self["add"..c_a]then local d_a=self["add"..c_a](self) -d_a:fromXML(b_a,dd)end end end;return self end -function bd:fromXML(cd,dd)ad.fromXML(self,cd,dd) -if(cd.children)then -for __a,a_a in ipairs(cd.children)do local b_a= -a_a.tag:sub(1,1):upper()..a_a.tag:sub(2) -local c_a=ac.getTagHandler(a_a.tag) -if c_a then local d_a=c_a(a_a,self,dd)elseif self["add"..b_a]then -local d_a=self["add"..b_a](self)d_a:fromXML(a_a,dd)else -cb.warn("XMLParser: Unknown tag '".. -a_a.tag.."' - no handler or element found")end end end;return self end;return{API=ac,Container=bd,BaseElement=ad} end -project["plugins/debug.lua"] = function(...) local _b=require("log")local ab=require("libraries/colorHex") -local bb=10;local cb=false;local db={ERROR=1,WARN=2,INFO=3,DEBUG=4} -local function _c(dc) -local _d={renderCount=0,eventCount={},lastRender=os.epoch("utc"),properties={},children={}} -return -{trackProperty=function(ad,bd)_d.properties[ad]=bd end,trackRender=function() -_d.renderCount=_d.renderCount+1;_d.lastRender=os.epoch("utc")end,trackEvent=function(ad)_d.eventCount[ad]=( -_d.eventCount[ad]or 0)+1 end,dump=function()return -{type=dc.get("type"),id=dc.get("id"),stats=_d}end}end;local ac={} -function ac.debug(dc,_d)dc._debugger=_c(dc)dc._debugLevel=_d or db.INFO;return dc end;function ac.dumpDebug(dc)if not dc._debugger then return end -return dc._debugger.dump()end;local bc={} -function bc.openConsole(dc) -if -not dc._debugFrame then local _d=dc.get("width")local ad=dc.get("height") -dc._debugFrame=dc:addFrame("basaltDebugLog"):setWidth(_d):setHeight(ad):listenEvent("mouse_scroll",true) -dc._debugFrame:addButton("basaltDebugLogClose"):setWidth(9):setHeight(1):setX( -_d-8):setY(ad):setText("Close"):onClick(function() -dc:closeConsole()end)dc._debugFrame._scrollOffset=0 -dc._debugFrame._processedLogs={} -local function bd(b_a,c_a)local d_a={}while#b_a>0 do local _aa=b_a:sub(1,c_a)table.insert(d_a,_aa)b_a=b_a:sub( -c_a+1)end;return d_a end -local function cd()local b_a={}local c_a=dc._debugFrame.get("width") -for d_a,_aa in -ipairs(_b._logs)do local aaa=bd(_aa.message,c_a)for baa,caa in ipairs(aaa)do -table.insert(b_a,{text=caa,level=_aa.level})end end;return b_a end;local dd=#cd()-dc.get("height")dc._scrollOffset=dd -local __a=dc._debugFrame.render -dc._debugFrame.render=function(b_a)__a(b_a)b_a._processedLogs=cd() -local c_a=b_a.get("height")-2;local d_a=#b_a._processedLogs;local _aa=math.max(0,d_a-c_a) -b_a._scrollOffset=math.min(b_a._scrollOffset,_aa) -for i=1,c_a-2 do local aaa=i+b_a._scrollOffset -local baa=b_a._processedLogs[aaa] -if baa then -local caa= - -baa.level==_b.LEVEL.ERROR and colors.red or baa.level== -_b.LEVEL.WARN and colors.yellow or baa.level==_b.LEVEL.DEBUG and colors.lightGray or colors.white;b_a:textFg(2,i,baa.text,caa)end end end;local a_a=dc._debugFrame.dispatchEvent -dc._debugFrame.dispatchEvent=function(b_a,c_a,d_a,...) -if -(c_a=="mouse_scroll")then -b_a._scrollOffset=math.max(0,b_a._scrollOffset+d_a)b_a:updateRender()return true else return a_a(b_a,c_a,d_a,...)end end end -dc._debugFrame.set("width",dc.get("width")) -dc._debugFrame.set("height",dc.get("height"))dc._debugFrame.set("visible",true)return dc end -function bc.closeConsole(dc)if dc._debugFrame then -dc._debugFrame.set("visible",false)end;return dc end;function bc.toggleConsole(dc)if dc._debugFrame and dc._debugFrame:getVisible()then -dc:closeConsole()else dc:openConsole()end -return dc end -local cc={} -function cc.debugChildren(dc,_d)dc:debug(_d)for ad,bd in pairs(dc.get("children"))do if bd.debug then -bd:debug(_d)end end;return dc end;return{BaseElement=ac,Container=cc,BaseFrame=bc} end -project["plugins/animation.lua"] = function(...) local aa={}local ba={} -ba={linear=function(ab)return ab end,easeInQuad=function(ab)return ab*ab end,easeOutQuad=function(ab)return -1 - (1 -ab)* (1 -ab)end,easeInOutQuad=function(ab)if ab<0.5 then return 2 *ab*ab end;return 1 - ( --2 *ab+2)^2 /2 end,easeInCubic=function(ab)return -ab*ab*ab end,easeOutCubic=function(ab)return 1 - (1 -ab)^3 end,easeInOutCubic=function(ab)if -ab<0.5 then return 4 *ab*ab*ab end;return -1 - (-2 *ab+2)^3 /2 end,easeInQuart=function(ab) -return ab*ab*ab*ab end,easeOutQuart=function(ab)return 1 - (1 -ab)^4 end,easeInOutQuart=function(ab)if ab<0.5 then return -8 *ab*ab*ab*ab end;return -1 - (-2 *ab+2)^4 /2 end,easeInQuint=function(ab)return -ab*ab*ab*ab*ab end,easeOutQuint=function(ab)return 1 - (1 -ab)^5 end,easeInOutQuint=function(ab)if ab< -0.5 then return 16 *ab*ab*ab*ab*ab end;return 1 - (- -2 *ab+2)^5 /2 end,easeInSine=function(ab)return -1 -math.cos(ab*math.pi/2)end,easeOutSine=function(ab)return math.sin( -ab*math.pi/2)end,easeInOutSine=function(ab) -return- (math.cos( -math.pi*ab)-1)/2 end,easeInExpo=function(ab)if ab==0 then return 0 end -return 2 ^ (10 *ab-10)end,easeOutExpo=function(ab)if ab==1 then return 1 end;return -1 -2 ^ (-10 *ab)end,easeInOutExpo=function(ab)if ab==0 then return 0 end -if ab==1 then return 1 end;if ab<0.5 then return 2 ^ (20 *ab-10)/2 end;return(2 -2 ^ ( --20 *ab+10))/2 end,easeInCirc=function(ab)return -1 -math.sqrt(1 -ab*ab)end,easeOutCirc=function(ab)return math.sqrt( -1 - (ab-1)* (ab-1))end,easeInOutCirc=function(ab)if -ab<0.5 then -return(1 -math.sqrt(1 - (2 *ab)^2))/2 end;return -(math.sqrt(1 - (-2 *ab+2)^2)+1)/2 end,easeInBack=function(ab) -local bb=1.70158;local cb=bb+1;return cb*ab*ab*ab-bb*ab*ab end,easeOutBack=function(ab) -local bb=1.70158;local cb=bb+1 -return 1 +cb* (ab-1)^3 +bb* (ab-1)^2 end,easeInOutBack=function(ab)local bb=1.70158;local cb=bb*1.525;if ab<0.5 then -return( ( -2 *ab)^2 * ( (cb+1)*2 *ab-cb))/2 end -return( (2 *ab-2)^2 * -( (cb+1)* (ab*2 -2)+cb)+2)/2 end,easeInElastic=function(ab)local bb=( -2 *math.pi)/3;if ab==0 then return 0 end;if ab==1 then return 1 end;return -- ( -2 ^ (10 *ab-10))*math.sin((ab*10 -10.75)*bb)end,easeOutElastic=function(ab)local bb=( -2 *math.pi)/3;if ab==0 then return 0 end;if ab==1 then return 1 end;return - -2 ^ (-10 *ab)*math.sin((ab*10 -0.75)*bb)+1 end,easeInOutElastic=function(ab)local bb=( -2 *math.pi)/4.5;if ab==0 then return 0 end -if ab==1 then return 1 end;if ab<0.5 then -return- -(2 ^ (20 *ab-10)*math.sin((20 *ab-11.125)*bb))/2 end;return -(2 ^ (-20 *ab+10)*math.sin( -(20 *ab-11.125)*bb))/2 +1 end,easeInBounce=function(ab)return -1 -ba.easeOutBounce(1 -ab)end,easeOutBounce=function(ab) -local bb=7.5625;local cb=2.75 -if ab<1 /cb then return bb*ab*ab elseif ab<2 /cb then ab=ab-1.5 /cb;return -bb*ab*ab+0.75 elseif ab<2.5 /cb then ab=ab-2.25 /cb;return bb*ab*ab+0.9375 else ab= -ab-2.625 /cb;return bb*ab*ab+0.984375 end end,easeInOutBounce=function(ab)if -ab<0.5 then -return(1 -ba.easeOutBounce(1 -2 *ab))/2 end;return -(1 +ba.easeOutBounce(2 *ab-1))/2 end}local ca={}ca.__index=ca -function ca.new(ab,bb,cb,db,_c)local ac=setmetatable({},ca)ac.element=ab -ac.type=bb;ac.args=cb;ac.duration=db or 1;ac.startTime=0;ac.isPaused=false -ac.handlers=aa[bb]ac.easing=_c;return ac end;function ca:start()self.startTime=os.epoch("local")/1000;if -self.handlers.start then self.handlers.start(self)end -return self end -function ca:update(ab)local bb=math.min(1, -ab/self.duration) -local cb=ba[self.easing](bb)return self.handlers.update(self,cb)end;function ca:complete()if self.handlers.complete then -self.handlers.complete(self)end end -local da={}da.__index=da -function da.registerAnimation(ab,bb)aa[ab]=bb -da[ab]=function(cb,...)local db={...}local _c="linear" -if( -type(db[#db])=="string")then _c=table.remove(db,#db)end;local ac=table.remove(db,#db) -return cb:addAnimation(ab,db,ac,_c)end end;function da.registerEasing(ab,bb)ba[ab]=bb end -function da.new(ab)local bb={}bb.element=ab -bb.sequences={{}}bb.sequenceCallbacks={}bb.currentSequence=1;bb.timer=nil -setmetatable(bb,da)return bb end -function da:sequence()table.insert(self.sequences,{})self.currentSequence=# -self.sequences;self.sequenceCallbacks[self.currentSequence]={start=nil,update=nil,complete= -nil}return self end -function da:onStart(ab) -if -not self.sequenceCallbacks[self.currentSequence]then self.sequenceCallbacks[self.currentSequence]={}end -self.sequenceCallbacks[self.currentSequence].start=ab;return self end -function da:onUpdate(ab) -if -not self.sequenceCallbacks[self.currentSequence]then self.sequenceCallbacks[self.currentSequence]={}end -self.sequenceCallbacks[self.currentSequence].update=ab;return self end -function da:onComplete(ab) -if -not self.sequenceCallbacks[self.currentSequence]then self.sequenceCallbacks[self.currentSequence]={}end -self.sequenceCallbacks[self.currentSequence].complete=ab;return self end -function da:addAnimation(ab,bb,cb,db)local _c=ca.new(self.element,ab,bb,cb,db) -table.insert(self.sequences[self.currentSequence],_c)return self end -function da:start()self.currentSequence=1;self.timer=nil -if -(self.sequenceCallbacks[self.currentSequence])then if(self.sequenceCallbacks[self.currentSequence].start)then -self.sequenceCallbacks[self.currentSequence].start(self.element)end end -if -#self.sequences[self.currentSequence]>0 then self.timer=os.startTimer(0.05)for ab,bb in -ipairs(self.sequences[self.currentSequence])do bb:start()end end;return self end -function da:event(ab,bb) -if ab=="timer"and bb==self.timer then -local cb=os.epoch("local")/1000;local db=true;local _c={} -local ac=self.sequenceCallbacks[self.currentSequence] -for bc,cc in ipairs(self.sequences[self.currentSequence])do -local dc=cb-cc.startTime;local _d=dc/cc.duration;local ad=cc:update(dc)if ac and ac.update then -ac.update(self.element,_d)end;if not ad then table.insert(_c,cc)db=false else -cc:complete()end end -if db then -if ac and ac.complete then ac.complete(self.element)end -if self.currentSequence<#self.sequences then -self.currentSequence=self.currentSequence+1;_c={} -local bc=self.sequenceCallbacks[self.currentSequence]if bc and bc.start then bc.start(self.element)end -for cc,dc in -ipairs(self.sequences[self.currentSequence])do dc:start()table.insert(_c,dc)end end end;if#_c>0 then self.timer=os.startTimer(0.05)end -return true end end -function da:stop()if self.timer then pcall(os.cancelTimer,self.timer) -self.timer=nil end -for ab,bb in ipairs(self.sequences)do for cb,db in ipairs(bb)do -pcall(function()if db and -db.complete then db:complete()end end)end end;if -self.element and type(self.element.set)=="function"then -pcall(function()self.element.set("animation",nil)end)end end -da.registerAnimation("move",{start=function(ab)ab.startX=ab.element.get("x") -ab.startY=ab.element.get("y")end,update=function(ab,bb)local cb=ab.startX+ -(ab.args[1]-ab.startX)*bb;local db=ab.startY+ -(ab.args[2]-ab.startY)*bb -ab.element.set("x",math.floor(cb))ab.element.set("y",math.floor(db))return bb>=1 end,complete=function(ab) -ab.element.set("x",ab.args[1])ab.element.set("y",ab.args[2])end}) -da.registerAnimation("resize",{start=function(ab)ab.startW=ab.element.get("width") -ab.startH=ab.element.get("height")end,update=function(ab,bb)local cb=ab.startW+ -(ab.args[1]-ab.startW)*bb;local db=ab.startH+ -(ab.args[2]-ab.startH)*bb -ab.element.set("width",math.floor(cb))ab.element.set("height",math.floor(db)) -return bb>=1 end,complete=function(ab) -ab.element.set("width",ab.args[1])ab.element.set("height",ab.args[2])end}) -da.registerAnimation("moveOffset",{start=function(ab)ab.startX=ab.element.get("offsetX") -ab.startY=ab.element.get("offsetY")end,update=function(ab,bb)local cb=ab.startX+ (ab.args[1]-ab.startX)* -bb;local db=ab.startY+ (ab.args[2]- -ab.startY)*bb -ab.element.set("offsetX",math.floor(cb))ab.element.set("offsetY",math.floor(db))return -bb>=1 end,complete=function(ab) -ab.element.set("offsetX",ab.args[1])ab.element.set("offsetY",ab.args[2])end}) -da.registerAnimation("number",{start=function(ab) -ab.startValue=ab.element.get(ab.args[1])ab.targetValue=ab.args[2]end,update=function(ab,bb) -local cb= -ab.startValue+ (ab.targetValue-ab.startValue)*bb -ab.element.set(ab.args[1],math.floor(cb))return bb>=1 end,complete=function(ab) -ab.element.set(ab.args[1],ab.targetValue)end}) -da.registerAnimation("entries",{start=function(ab) -ab.startColor=ab.element.get(ab.args[1])ab.colorList=ab.args[2]end,update=function(ab,bb) -local cb=ab.colorList;local db=math.floor(#cb*bb)+1;if db>#cb then db=#cb end -ab.element.set(ab.args[1],cb[db])end,complete=function(ab) -ab.element.set(ab.args[1],ab.colorList[ -#ab.colorList])end}) -da.registerAnimation("morphText",{start=function(ab)local bb=ab.element.get(ab.args[1]) -local cb=ab.args[2]local db=math.max(#bb,#cb) -local _c=string.rep(" ",math.floor(db-#bb)/2)ab.startText=_c..bb.._c -ab.targetText=cb..string.rep(" ",db-#cb)ab.length=db end,update=function(ab,bb) -local cb="" -for i=1,ab.length do local db=ab.startText:sub(i,i) -local _c=ab.targetText:sub(i,i) -if bb<0.5 then -cb=cb.. (math.random()>bb*2 and db or" ")else cb=cb.. -(math.random()> (bb-0.5)*2 and" "or _c)end end;ab.element.set(ab.args[1],cb)return bb>=1 end,complete=function(ab) -ab.element.set(ab.args[1],ab.targetText:gsub("%s+$",""))end}) -da.registerAnimation("typewrite",{start=function(ab)ab.targetText=ab.args[2] -ab.element.set(ab.args[1],"")end,update=function(ab,bb) -local cb=math.floor(#ab.targetText*bb) -ab.element.set(ab.args[1],ab.targetText:sub(1,cb))return bb>=1 end}) -da.registerAnimation("fadeText",{start=function(ab)ab.chars={}for i=1,#ab.args[2]do -ab.chars[i]={char=ab.args[2]:sub(i,i),visible=false}end end,update=function(ab,bb) -local cb=""for db,_c in ipairs(ab.chars)do -if math.random()=1 end}) -da.registerAnimation("scrollText",{start=function(ab)ab.width=ab.element.get("width")ab.startText= -ab.element.get(ab.args[1])or"" -ab.targetText=ab.args[2]or""ab.startText=tostring(ab.startText) -ab.targetText=tostring(ab.targetText)end,update=function(ab,bb) -local cb=ab.width -if bb<0.5 then local db=bb/0.5;local _c=math.floor(cb*db) -local ac=( -ab.startText:sub(_c+1)..string.rep(" ",cb)):sub(1,cb)ab.element.set(ab.args[1],ac)else -local db=(bb-0.5)/0.5;local _c=math.floor(cb* (1 -db)) -local ac=string.rep(" ",_c)..ab.targetText;local bc=ac:sub(1,cb)ab.element.set(ab.args[1],bc)end;return bb>=1 end,complete=function(ab)local bb=( -ab.targetText..string.rep(" ",ab.width)) -ab.element.set(ab.args[1],bb)end}) -da.registerAnimation("marquee",{start=function(ab)ab.width=ab.element.get("width")ab.text=tostring( -ab.args[2]or"") -ab.speed=tonumber(ab.args[3])or 0.15;ab.offset=0;ab.lastShift=-1 -ab.padded=ab.text..string.rep(" ",ab.width)end,update=function(ab,bb)local cb= -os.epoch("local")/1000 -ab.startTime -local db=math.max(0.01,ab.speed)local _c=math.floor(cb/db) -if _c~=ab.lastShift then ab.lastShift=_c -local ac=#ab.padded;local bc=(_c%ac)+1;local cc=ab.padded..ab.padded -local dc=cc:sub(bc,bc+ab.width-1)ab.element.set(ab.args[1],dc)end;return false end,complete=function(ab) -end}) -da.registerAnimation("custom",{start=function(ab)ab.callback=ab.args[1]if -type(ab.callback)~="function"then -error("custom animation requires a function as first argument")end end,update=function(ab,bb)local cb= -os.epoch("local")/1000 -ab.startTime -ab.callback(ab.element,bb,cb)return bb>=1 end,complete=function(ab)if ab.callback then -ab.callback(ab.element,1,ab.duration)end end})local _b={hooks={}} -function _b.hooks.handleEvent(ab,bb,...)if bb=="timer"then local cb=ab.get("animation")if cb then -cb:event(bb,...)end end end -function _b.setup(ab) -ab.defineProperty(ab,"animation",{default=nil,type="table"})ab.defineEvent(ab,"timer")end -function _b.stopAnimation(ab)local bb=ab.get("animation") -if -bb and type(bb.stop)=="function"then bb:stop()else ab.set("animation",nil)end;return ab end -function _b:animate()local ab=da.new(self)self.set("animation",ab)return ab end;return{VisualElement=_b} end -project["plugins/benchmark.lua"] = function(...) local ca=require("log")local da=setmetatable({},{__mode="k"})local function _b()return -{methods={}}end -local function ab(_c,ac)local bc=_c[ac] -if not da[_c]then da[_c]=_b()end -if not da[_c].methods[ac]then -da[_c].methods[ac]={calls=0,totalTime=0,minTime=math.huge,maxTime=0,lastTime=0,startTime=0,path={},methodName=ac,originalMethod=bc}end -_c[ac]=function(cc,...)cc:startProfile(ac)local dc=bc(cc,...) -cc:endProfile(ac)return dc end end;local bb={} -function bb:startProfile(_c)local ac=da[self]if not ac then ac=_b()da[self]=ac end;if not -ac.methods[_c]then -ac.methods[_c]={calls=0,totalTime=0,minTime=math.huge,maxTime=0,lastTime=0,startTime=0,path={},methodName=_c}end -local bc=ac.methods[_c]bc.startTime=os.clock()*1000;bc.path={}local cc=self;while cc do -table.insert(bc.path,1, -cc.get("name")or cc.get("id"))cc=cc.parent end;return self end -function bb:endProfile(_c)local ac=da[self] -if not ac or not ac.methods[_c]then return self end;local bc=ac.methods[_c]local cc=os.clock()*1000 -local dc=cc-bc.startTime;bc.calls=bc.calls+1;bc.totalTime=bc.totalTime+dc -bc.minTime=math.min(bc.minTime,dc)bc.maxTime=math.max(bc.maxTime,dc)bc.lastTime=dc;return self end -function bb:benchmark(_c)if not self[_c]then -ca.error("Method ".._c.." does not exist")return self end;da[self]=_b() -da[self].methodName=_c;da[self].isRunning=true;ab(self,_c)return self end -function bb:logBenchmark(_c)local ac=da[self] -if not ac or not ac.methods[_c]then return self end;local bc=ac.methods[_c] -if bc then local cc= -bc.calls>0 and(bc.totalTime/bc.calls)or 0 -ca.info(string.format( -"Benchmark results for %s.%s: ".. -"Path: %s ".."Calls: %d ".. -"Average time: %.2fms ".."Min time: %.2fms ".."Max time: %.2fms ".. -"Last time: %.2fms ".."Total time: %.2fms",table.concat(bc.path,"."),bc.methodName,table.concat(bc.path,"/"),bc.calls,cc, -bc.minTime~=math.huge and bc.minTime or 0,bc.maxTime,bc.lastTime,bc.totalTime))end;return self end -function bb:stopBenchmark(_c)local ac=da[self] -if not ac or not ac.methods[_c]then return self end;local bc=ac.methods[_c]if bc and bc.originalMethod then -self[_c]=bc.originalMethod end;ac.methods[_c]=nil;if -not next(ac.methods)then da[self]=nil end;return self end -function bb:getBenchmarkStats(_c)local ac=da[self] -if not ac or not ac.methods[_c]then return nil end;local bc=ac.methods[_c]return -{averageTime=bc.totalTime/bc.calls,totalTime=bc.totalTime,calls=bc.calls,minTime=bc.minTime,maxTime=bc.maxTime,lastTime=bc.lastTime}end;local cb={} -function cb:benchmarkContainer(_c)self:benchmark(_c) -for ac,bc in -pairs(self.get("children"))do bc:benchmark(_c)if bc:isType("Container")then -bc:benchmarkContainer(_c)end end;return self end -function cb:logContainerBenchmarks(_c,ac)ac=ac or 0;local bc=string.rep(" ",ac)local cc=0;local dc={} -for ad,bd in -pairs(self.get("children"))do local cd=da[bd] -if cd and cd.methods[_c]then local dd=cd.methods[_c] -cc=cc+dd.totalTime -table.insert(dc,{element=bd,type=bd.get("type"),calls=dd.calls,totalTime=dd.totalTime,avgTime=dd.totalTime/dd.calls})end end;local _d=da[self] -if _d and _d.methods[_c]then local ad=_d.methods[_c] -local bd=ad.totalTime-cc;local cd=bd/ad.calls -ca.info(string.format("%sBenchmark %s (%s): ".."%.2fms/call (Self: %.2fms/call) ".. -"[Total: %dms, Calls: %d]",bc,self.get("type"),_c, -ad.totalTime/ad.calls,cd,ad.totalTime,ad.calls)) -if#dc>0 then -for dd,__a in ipairs(dc)do -if __a.element:isType("Container")then __a.element:logContainerBenchmarks(_c, -ac+1)else -ca.info(string.format("%s> %s: %.2fms/call [Total: %dms, Calls: %d]", -bc.." ",__a.type,__a.avgTime,__a.totalTime,__a.calls))end end end end;return self end -function cb:stopContainerBenchmark(_c) -for ac,bc in pairs(self.get("children"))do if bc:isType("Container")then -bc:stopContainerBenchmark(_c)else bc:stopBenchmark(_c)end end;self:stopBenchmark(_c)return self end;local db={} -function db.start(_c,ac)ac=ac or{}local bc=_b()bc.name=_c -bc.startTime=os.clock()*1000;bc.custom=true;bc.calls=0;bc.totalTime=0;bc.minTime=math.huge;bc.maxTime=0 -bc.lastTime=0;da[_c]=bc end -function db.stop(_c)local ac=da[_c]if not ac or not ac.custom then return end;local bc= -os.clock()*1000;local cc=bc-ac.startTime;ac.calls=ac.calls+1;ac.totalTime= -ac.totalTime+cc;ac.minTime=math.min(ac.minTime,cc) -ac.maxTime=math.max(ac.maxTime,cc)ac.lastTime=cc -ca.info(string.format("Custom Benchmark '%s': ".. -"Calls: %d ".."Average time: %.2fms ".. -"Min time: %.2fms ".. -"Max time: %.2fms ".."Last time: %.2fms ".."Total time: %.2fms",_c,ac.calls, -ac.totalTime/ac.calls,ac.minTime,ac.maxTime,ac.lastTime,ac.totalTime))end -function db.getStats(_c)local ac=da[_c]if not ac then return nil end;return -{averageTime=ac.totalTime/ac.calls,totalTime=ac.totalTime,calls=ac.calls,minTime=ac.minTime,maxTime=ac.maxTime,lastTime=ac.lastTime}end;function db.clear(_c)da[_c]=nil end;function db.clearAll()for _c,ac in pairs(da)do -if ac.custom then da[_c]=nil end end end;return -{BaseElement=bb,Container=cb,API=db} end -project["plugins/responsive.lua"] = function(...) local c=require("errorManager")local d={} -function d:responsive() -local _a={_element=self,_rules={},_currentStateName=nil,_currentCondition=nil,_stateCounter=0} -function _a:when(aa) -if self._currentCondition then c.header="Responsive Builder Error" -c.error("Previous when() must be followed by apply() before starting a new when()")end;self._stateCounter=self._stateCounter+1;self._currentStateName="__responsive_".. -self._stateCounter;self._currentCondition=aa;return -self end -function _a:apply(aa)if not self._currentCondition then c.header="Responsive Builder Error" -c.error("apply() must follow a when() call")end;if -type(aa)~="table"then c.header="Responsive Builder Error" -c.error("apply() requires a table of properties")end -self._element:registerResponsiveState(self._currentStateName,self._currentCondition,100) -for ba,ca in pairs(aa)do -local da=ba:sub(1,1):upper()..ba:sub(2)local _b="set"..da.."State" -if self._element[_b]then -self._element[_b](self._element,self._currentStateName,ca)else c.header="Responsive Builder Error" -c.error("Unknown property: "..ba)end end -table.insert(self._rules,{stateName=self._currentStateName,condition=self._currentCondition,properties=aa})self._currentCondition=nil;self._currentStateName=nil;return self end -function _a:otherwise(aa)if self._currentCondition then c.header="Responsive Builder Error" -c.error("otherwise() cannot be used after when() without apply()")end;if type(aa)~= -"table"then c.header="Responsive Builder Error" -c.error("otherwise() requires a table of properties")end;self._stateCounter= -self._stateCounter+1 -local ba="__responsive_otherwise_"..self._stateCounter;local ca={} -for _b,ab in ipairs(self._rules)do table.insert(ca,ab.condition)end;local da -if type(ca[1])=="string"then local _b={}for ab,bb in ipairs(ca)do -table.insert(_b,"not ("..bb..")")end;da=table.concat(_b," and ")else -da=function(_b)for ab,bb in -ipairs(ca)do if bb(_b)then return false end end;return true end end -self._element:registerResponsiveState(ba,da,50) -for _b,ab in pairs(aa)do -local bb=_b:sub(1,1):upper().._b:sub(2)local cb="set"..bb.."State"if self._element[cb]then -self._element[cb](self._element,ba,ab)else c.header="Responsive Builder Error" -c.error("Unknown property: ".._b)end end;return self end -function _a:done()if self._currentCondition then c.header="Responsive Builder Error" -c.error("Unfinished when() without apply()")end -return self._element end;return _a end;return{BaseElement=d} end -project["elementManager.lua"] = function(...) local _c=table.pack(...) -local ac=fs.getDir(_c[2]or"basalt")local bc=_c[1]if(ac==nil)then -error("Unable to find directory ".. -_c[2].." please report this bug to our discord.")end -local cc=require("log")local dc=package.path;local _d="path;/path/?.lua;/path/?/init.lua;" -local ad=_d:gsub("path",ac)local bd={}bd._elements={}bd._plugins={}bd._APIs={} -bd._config={autoLoadMissing=false,allowRemoteLoading=false,allowDiskLoading=true,remoteSources={},diskMounts={},useGlobalCache=false,globalCacheName="_BASALT_ELEMENT_CACHE"}local cd=fs.combine(ac,"elements") -local dd=fs.combine(ac,"plugins")cc.info("Loading elements from "..cd) -if -fs.exists(cd)then -for d_a,_aa in ipairs(fs.list(cd))do local aaa=_aa:match("(.+).lua")if aaa then cc.debug( -"Found element: "..aaa) -bd._elements[aaa]={class=nil,plugins={},loaded=false,source="local",path=nil}end end end;cc.info("Loading plugins from "..dd) -if -fs.exists(dd)then -for d_a,_aa in ipairs(fs.list(dd))do local aaa=_aa:match("(.+).lua") -if aaa then cc.debug("Found plugin: ".. -aaa) -local baa=require(fs.combine("plugins",aaa)) -if type(baa)=="table"then -for caa,daa in pairs(baa)do -if(caa~="API")then if(bd._plugins[caa]==nil)then -bd._plugins[caa]={}end -table.insert(bd._plugins[caa],daa)else bd._APIs[aaa]=daa end end end end end end -if(minified)then if(minified_elementDirectory==nil)then -error("Unable to find minified_elementDirectory please report this bug to our discord.")end;for d_a,_aa in -pairs(minified_elementDirectory)do -bd._elements[d_a:gsub(".lua","")]={class=nil,plugins={},loaded=false,source="local",path=nil}end;if( -minified_pluginDirectory==nil)then -error("Unable to find minified_pluginDirectory please report this bug to our discord.")end -for d_a,_aa in -pairs(minified_pluginDirectory)do local aaa=d_a:gsub(".lua","") -local baa=require(fs.combine("plugins",aaa)) -if type(baa)=="table"then -for caa,daa in pairs(baa)do -if(caa~="API")then if(bd._plugins[caa]==nil)then -bd._plugins[caa]={}end -table.insert(bd._plugins[caa],daa)else bd._APIs[aaa]=daa end end end end end -local function __a(d_a,_aa)if not bd._config.useGlobalCache then return end -if not -_G[bd._config.globalCacheName]then _G[bd._config.globalCacheName]={}end;_G[bd._config.globalCacheName][d_a]=_aa;cc.debug( -"Cached element in _G: "..d_a)end -local function a_a(d_a)if not bd._config.useGlobalCache then return nil end -if - -_G[bd._config.globalCacheName]and _G[bd._config.globalCacheName][d_a]then -cc.debug("Loaded element from _G cache: "..d_a)return _G[bd._config.globalCacheName][d_a]end;return nil end -function bd.configure(d_a)for _aa,aaa in pairs(d_a)do -if bd._config[_aa]~=nil then bd._config[_aa]=aaa end end end -function bd.registerDiskMount(d_a)if not fs.exists(d_a)then -error("Disk mount path does not exist: "..d_a)end -table.insert(bd._config.diskMounts,d_a)cc.info("Registered disk mount: "..d_a) -local _aa=fs.combine(d_a,"elements") -if fs.exists(_aa)then -for aaa,baa in ipairs(fs.list(_aa))do -local caa=baa:match("(.+).lua") -if caa then -if not bd._elements[caa]then -cc.debug("Found element on disk: "..caa) -bd._elements[caa]={class=nil,plugins={},loaded=false,source="disk",path=fs.combine(_aa,baa)}end end end end end -function bd.registerRemoteSource(d_a,_aa)if not bd._config.allowRemoteLoading then -error("Remote loading is disabled. Enable with ElementManager.configure({allowRemoteLoading = true})")end -bd._config.remoteSources[d_a]=_aa -if not bd._elements[d_a]then -bd._elements[d_a]={class=nil,plugins={},loaded=false,source="remote",path=_aa}else bd._elements[d_a].source="remote" -bd._elements[d_a].path=_aa end -cc.info("Registered remote source for "..d_a..": ".._aa)end -local function b_a(d_a)if not http then -error("HTTP API is not available. Enable it in your CC:Tweaked config.")end -cc.info("Loading element from remote: "..d_a)local _aa=http.get(d_a)if not _aa then -error("Failed to download from: "..d_a)end;local aaa=_aa.readAll()_aa.close()if -not aaa or aaa==""then -error("Empty response from: "..d_a)end;local baa,caa=load(aaa,d_a,"t",_ENV)if not baa then -error( -"Failed to load element from "..d_a..": "..tostring(caa))end;local daa=baa()return daa end -local function c_a(d_a)if not fs.exists(d_a)then -error("Element file does not exist: "..d_a)end -cc.info("Loading element from disk: "..d_a)local _aa,aaa=loadfile(d_a)if not _aa then -error("Failed to load element from ".. -d_a..": "..tostring(aaa))end;local baa=_aa()return baa end -function bd.tryAutoLoad(d_a) -if bd._config.allowDiskLoading then -for _aa,aaa in ipairs(bd._config.diskMounts)do -local baa=fs.combine(aaa,"elements")local caa=fs.combine(baa,d_a..".lua") -if fs.exists(caa)then -bd._elements[d_a]={class= -nil,plugins={},loaded=false,source="disk",path=caa}bd.loadElement(d_a)return true end end end -if -bd._config.allowRemoteLoading and bd._config.remoteSources[d_a]then bd.loadElement(d_a)return true end;return false end -function bd.loadElement(d_a) -if not bd._elements[d_a]then -if bd._config.autoLoadMissing then -local _aa=bd.tryAutoLoad(d_a)if not _aa then -error("Element '"..d_a.."' not found and could not be auto-loaded")end else -error("Element '"..d_a.."' not found")end end -if not bd._elements[d_a].loaded then local _aa=bd._elements[d_a].source or -"local"local aaa;local baa=false;aaa=a_a(d_a) -if aaa then -baa=true -cc.info("Loaded element from _G cache: "..d_a)elseif _aa=="local"then package.path=ad.."rom/?" -aaa=require(fs.combine("elements",d_a))package.path=dc elseif _aa=="disk"then if not bd._config.allowDiskLoading then -error( -"Disk loading is disabled for element: "..d_a)end -aaa=c_a(bd._elements[d_a].path)__a(d_a,aaa)elseif _aa=="remote"then if not bd._config.allowRemoteLoading then -error( -"Remote loading is disabled for element: "..d_a)end -aaa=b_a(bd._elements[d_a].path)__a(d_a,aaa)else -error("Unknown source type: ".._aa)end -bd._elements[d_a]={class=aaa,plugins=aaa.plugins,loaded=true,source=baa and"cache"or _aa,path=bd._elements[d_a].path}if not baa then -cc.debug("Loaded element: "..d_a.." from ".._aa)end -if(bd._plugins[d_a]~=nil)then -for caa,daa in -pairs(bd._plugins[d_a])do if(daa.setup)then daa.setup(aaa)end -if(daa.hooks)then -for _ba,aba in pairs(daa.hooks)do -local bba=aaa[_ba]if(type(bba)~="function")then -error("Element ".. -d_a.." does not have a method ".._ba)end -if(type(aba)=="function")then -aaa[_ba]=function(cba,...) -local dba=bba(cba,...)local _ca=aba(cba,...)return _ca==nil and dba or _ca end elseif(type(aba)=="table")then -aaa[_ba]=function(cba,...)if aba.pre then aba.pre(cba,...)end -local dba=bba(cba,...)if aba.post then aba.post(cba,...)end;return dba end end end end;for _ba,aba in pairs(daa)do -if _ba~="setup"and _ba~="hooks"then aaa[_ba]=aba end end end end end end -function bd.getElement(d_a) -if not bd._elements[d_a]then -if bd._config.autoLoadMissing then -local _aa=bd.tryAutoLoad(d_a)if not _aa then -error("Element '"..d_a.."' not found")end else -error("Element '"..d_a.."' not found")end end -if not bd._elements[d_a].loaded then bd.loadElement(d_a)end;return bd._elements[d_a].class end;function bd.getElementList()return bd._elements end;function bd.getAPI(d_a) -return bd._APIs[d_a]end -function bd.hasElement(d_a)return bd._elements[d_a]~=nil end -function bd.isElementLoaded(d_a)return -bd._elements[d_a]and bd._elements[d_a].loaded or false end -function bd.clearGlobalCache()if _G[bd._config.globalCacheName]then _G[bd._config.globalCacheName]= -nil -cc.info("Cleared global element cache")end end -function bd.getCacheStats()if not _G[bd._config.globalCacheName]then -return{size=0,elements={}}end;local d_a={}for _aa,aaa in -pairs(_G[bd._config.globalCacheName])do table.insert(d_a,_aa)end;return -{size=#d_a,elements=d_a}end -function bd.preloadElements(d_a)for _aa,aaa in ipairs(d_a)do -if bd._elements[aaa]and -not bd._elements[aaa].loaded then bd.loadElement(aaa)end end end;return bd end -project["log.lua"] = function(...) local aa={}aa._logs={}aa._enabled=false;aa._logToFile=false -aa._logFile="basalt.log"fs.delete(aa._logFile) -aa.LEVEL={DEBUG=1,INFO=2,WARN=3,ERROR=4} -local ba={[aa.LEVEL.DEBUG]="Debug",[aa.LEVEL.INFO]="Info",[aa.LEVEL.WARN]="Warn",[aa.LEVEL.ERROR]="Error"} -local ca={[aa.LEVEL.DEBUG]=colors.lightGray,[aa.LEVEL.INFO]=colors.white,[aa.LEVEL.WARN]=colors.yellow,[aa.LEVEL.ERROR]=colors.red}function aa.setLogToFile(ab)aa._logToFile=ab end -function aa.setEnabled(ab)aa._enabled=ab end;local function da(ab) -if aa._logToFile then local bb=io.open(aa._logFile,"a")if bb then -bb:write(ab.."\n")bb:close()end end end -local function _b(ab,...)if -not aa._enabled then return end;local bb=os.date("%H:%M:%S") -local cb=debug.getinfo(3,"Sl")local db=cb.source:match("@?(.*)")local _c=cb.currentline -local ac=string.format("[%s:%d]",db:match("([^/\\]+)%.lua$"),_c)local bc="["..ba[ab].."]"local cc="" -for _d,ad in ipairs(table.pack(...))do if _d>1 then cc= -cc.." "end;cc=cc..tostring(ad)end;local dc=string.format("%s %s%s %s",bb,ac,bc,cc)da(dc) -table.insert(aa._logs,{time=bb,level=ab,message=cc})end;function aa.debug(...)_b(aa.LEVEL.DEBUG,...)end;function aa.info(...) -_b(aa.LEVEL.INFO,...)end -function aa.warn(...)_b(aa.LEVEL.WARN,...)end;function aa.error(...)_b(aa.LEVEL.ERROR,...)end;return aa end -project["propertySystem.lua"] = function(...) local ba=require("libraries/utils").deepCopy -local ca=require("libraries/expect")local da=require("errorManager")local _b={}_b.__index=_b -_b._properties={}local ab={}_b._setterHooks={}function _b.addSetterHook(cb) -table.insert(_b._setterHooks,cb)end -local function bb(cb,db,_c,ac)for bc,cc in ipairs(_b._setterHooks)do -local dc=cc(cb,db,_c,ac)if dc~=nil then _c=dc end end;return _c end -function _b.defineProperty(cb,db,_c) -if not rawget(cb,'_properties')then cb._properties={}end -cb._properties[db]={type=_c.type,default=_c.default,canTriggerRender=_c.canTriggerRender,getter=_c.getter,setter=_c.setter,allowNil=_c.allowNil}local ac=db:sub(1,1):upper()..db:sub(2) -cb[ -"get"..ac]=function(bc,...)ca(1,bc,"element")local cc=bc._values[db] -if type(cc)== -"function"and _c.type~="function"then cc=cc(bc)end -return _c.getter and _c.getter(bc,cc,...)or cc end -cb["set"..ac]=function(bc,cc,...)ca(1,bc,"element")cc=bb(bc,db,cc,_c)if -type(cc)~="function"then -if _c.type=="table"then if cc==nil then -if not _c.allowNil then ca(2,cc,_c.type)end end else ca(2,cc,_c.type)end end;if -_c.setter then cc=_c.setter(bc,cc,...)end -bc:_updateProperty(db,cc)return bc end -cb["get"..ac.."State"]=function(bc,cc,...)ca(1,bc,"element")return -bc.getPropertyState(db,cc,...)end -cb["set"..ac.."State"]=function(bc,cc,dc,...)ca(1,bc,"element") -bc.setPropertyState(db,cc,dc,...)return bc end end -function _b.combineProperties(cb,db,...)local _c={...}for bc,cc in pairs(_c)do -if not cb._properties[cc]then da.error("Property not found: ".. -cc)end end;local ac= -db:sub(1,1):upper()..db:sub(2) -cb["get"..ac]=function(bc) -ca(1,bc,"element")local cc={} -for dc,_d in pairs(_c)do table.insert(cc,bc.get(_d))end;return table.unpack(cc)end -cb["set"..ac]=function(bc,...)ca(1,bc,"element")local cc={...}for dc,_d in pairs(_c)do -bc.set(_d,cc[dc])end;return bc end end -function _b.blueprint(cb,db,_c,ac) -if not ab[cb]then -local cc={basalt=_c,__isBlueprint=true,_values=db or{},_events={},render=function()end,dispatchEvent=function()end,init=function()end} -cc.loaded=function(_d,ad)_d.loadedCallback=ad;return cc end -cc.create=function(_d)local ad=cb.new()ad:init({},_d.basalt)for bd,cd in pairs(_d._values)do -ad._values[bd]=cd end;for bd,cd in pairs(_d._events)do -for dd,__a in ipairs(cd)do ad[bd](ad,__a)end end -if(ac~=nil)then ac:addChild(ad)end;ad:updateRender()_d.loadedCallback(ad) -ad:postInit()return ad end;local dc=cb -while dc do -if rawget(dc,'_properties')then for _d,ad in pairs(dc._properties)do -if -type(ad.default)=="table"then cc._values[_d]=ba(ad.default)else cc._values[_d]=ad.default end end end -dc=getmetatable(dc)and rawget(getmetatable(dc),'__index')end;ab[cb]=cc end;local bc={_values={},_events={},loadedCallback=function()end} -bc.get=function(cc) -local dc=bc._values[cc]local _d=cb._properties[cc]if -type(dc)=="function"and _d.type~="function"then dc=dc(bc)end;return dc end -bc.set=function(cc,dc)bc._values[cc]=dc;return bc end -setmetatable(bc,{__index=function(cc,dc) -if dc:match("^on%u")then return -function(_d,ad) -cc._events[dc]=cc._events[dc]or{}table.insert(cc._events[dc],ad)return cc end end -if dc:match("^get%u")then -local _d=dc:sub(4,4):lower()..dc:sub(5)return function()return cc._values[_d]end end;if dc:match("^set%u")then -local _d=dc:sub(4,4):lower()..dc:sub(5) -return function(ad,bd)cc._values[_d]=bd;return cc end end -return ab[cb][dc]end})return bc end -function _b.createFromBlueprint(cb,db,_c)local ac=cb.new({},_c) -for bc,cc in pairs(db._values)do if type(cc)=="table"then -ac._values[bc]=ba(cc)else ac._values[bc]=cc end end;return ac end -function _b:__init()self._values={}self._observers={}self._states={} -self._modifiedProperties={} -self.set=function(bc,cc,...)local dc=self._values[bc]local _d=self._properties[bc] -if -(_d~=nil)then if(_d.setter)then cc=_d.setter(self,cc,...)end;if _d.canTriggerRender then -self:updateRender()end;self._values[bc]=bb(self,bc,cc,_d) -self._modifiedProperties[bc]=true -if dc~=cc and self._observers[bc]then for ad,bd in -ipairs(self._observers[bc])do bd(self,cc,dc)end end end end -self.get=function(bc,...)local cc=self._values[bc]local dc=self._properties[bc] -if -(dc==nil)then da.error("Property not found: "..bc)return end;if type(cc)=="function"and dc.type~="function"then -cc=cc(self)end;return -dc.getter and dc.getter(self,cc,...)or cc end -self.setPropertyState=function(bc,cc,dc,...)local _d=self._properties[bc] -if(_d~=nil)then if(_d.setter)then -dc=_d.setter(self,dc,...)end;dc=bb(self,bc,dc,_d)if -not self._states[cc]then self._states[cc]={}end -self._states[cc][bc]=dc;local ad=self._values.currentState -if ad==cc then if _d.canTriggerRender then -self:updateRender()end -if self._observers[bc]then for bd,cd in -ipairs(self._observers[bc])do cd(self,dc,nil)end end end end end -self.getPropertyState=function(bc,cc,...)local dc=self._states and self._states[cc]and -self._states[cc][bc]local _d= -dc~=nil and dc or self._values[bc] -local ad=self._properties[bc]if(ad==nil)then da.error("Property not found: "..bc) -return end;if -type(_d)=="function"and ad.type~="function"then _d=_d(self)end;return -ad.getter and ad.getter(self,_d,...)or _d end -self.getResolved=function(bc,...)local cc=self:getActiveStates()local dc=nil;for ad,bd in ipairs(cc)do -if -self._states and -self._states[bd.name]and self._states[bd.name][bc]~=nil then dc=self._states[bd.name][bc]break end end;if dc== -nil then dc=self._values[bc]end -local _d=self._properties[bc]if(_d==nil)then da.error("Property not found: "..bc) -return end;if -type(dc)=="function"and _d.type~="function"then dc=dc(self)end;return -_d.getter and _d.getter(self,dc,...)or dc end;local cb={}local db=getmetatable(self).__index -while db do if -rawget(db,'_properties')then -for bc,cc in pairs(db._properties)do if not cb[bc]then cb[bc]=cc end end end;db=getmetatable(db)and -rawget(getmetatable(db),'__index')end;self._properties=cb;local _c=getmetatable(self)local ac=_c.__index -setmetatable(self,{__index=function(bc,cc) -local dc=self._properties[cc] -if dc then local _d=self._values[cc]if -type(_d)=="function"and dc.type~="function"then _d=_d(self)end;return _d end -if type(ac)=="function"then return ac(bc,cc)else return ac[cc]end end,__newindex=function(bc,cc,dc) -local _d=self._properties[cc] -if _d then if _d.setter then dc=_d.setter(self,dc)end -dc=bb(self,cc,dc,_d)self:_updateProperty(cc,dc)else rawset(bc,cc,dc)end end,__tostring=function(bc)return -string.format("Object: %s (id: %s)",bc._values.type,bc.id)end}) -for bc,cc in pairs(cb)do if self._values[bc]==nil then -if type(cc.default)=="table"then -self._values[bc]=ba(cc.default)else self._values[bc]=cc.default end end end;return self end -function _b:_updateProperty(cb,db)local _c=self._values[cb] -if type(_c)=="function"then _c=_c(self)end;self._modifiedProperties[cb]=true;self._values[cb]=db -local ac= -type(db)=="function"and db(self)or db -if _c~=ac then -if self._properties[cb].canTriggerRender then self:updateRender()end -if self._observers[cb]then for bc,cc in ipairs(self._observers[cb])do -cc(self,ac,_c)end end end;return self end -function _b:observe(cb,db) -self._observers[cb]=self._observers[cb]or{}table.insert(self._observers[cb],db)return self end -function _b:removeObserver(cb,db) -if self._observers[cb]then -for _c,ac in ipairs(self._observers[cb])do if ac==db then -table.remove(self._observers[cb],_c) -if#self._observers[cb]==0 then self._observers[cb]=nil end;break end end end;return self end;function _b:removeAllObservers(cb) -if cb then self._observers[cb]=nil else self._observers={}end;return self end -function _b:instanceProperty(cb,db) -_b.defineProperty(self,cb,db)self._values[cb]=db.default;return self end -function _b:removeProperty(cb)self._values[cb]=nil;self._properties[cb]=nil;self._observers[cb]= -nil -local db=cb:sub(1,1):upper()..cb:sub(2)self["get"..db]=nil;self["set"..db]=nil;self["get"..db.."State"]= -nil;self["set"..db.."State"]=nil;return self end -function _b:getPropertyConfig(cb)return self._properties[cb]end;return _b end -project["main.lua"] = function(...) local ad=require("elementManager") -local bd=require("errorManager")local cd=require("propertySystem") -local dd=require("libraries/expect")local __a={}__a.traceback=true;__a._events={}__a._schedule={} -__a._eventQueue={}__a._plugins={}__a.isRunning=false;__a.LOGGER=require("log") -if(minified)then -__a.path=fs.getDir(shell.getRunningProgram())else __a.path=fs.getDir(select(2,...))end;local a_a=nil;local b_a=nil;local c_a={}local d_a=type;local _aa={}local aaa=10;local baa=0;local caa=false -local function daa() -if(caa)then return end;baa=os.startTimer(0.2)caa=true end -local function _ba(aca)for _=1,aca do local bca=_aa[1]if(bca)then bca:create()end -table.remove(_aa,1)end end;local function aba(aca,bca) -if(aca=="timer")then if(bca==baa)then _ba(aaa)caa=false;baa=0;if(#_aa>0)then daa()end -return true end end end -function __a.create(aca,bca,cca,dca)if( -d_a(bca)=="string")then bca={name=bca}end -if(bca==nil)then bca={name=aca}end;local _da=ad.getElement(aca) -if(cca)then -local ada=cd.blueprint(_da,bca,__a,dca)table.insert(_aa,ada)daa()return ada else local ada=_da.new() -ada:init(bca,__a)return ada end end -function __a.createFrame()local aca=__a.create("BaseFrame")aca:postInit()if -(a_a==nil)then a_a=tostring(term.current()) -__a.setActiveFrame(aca,true)end;return aca end;function __a.getElementManager()return ad end -function __a.getErrorManager()return bd end -function __a.getMainFrame()local aca=tostring(term.current())if(c_a[aca]==nil)then -a_a=aca;__a.createFrame()end;return c_a[aca]end -function __a.setActiveFrame(aca,bca)local cca=aca:getTerm()if(bca==nil)then bca=true end -if(cca~=nil)then c_a[tostring(cca)]= -bca and aca or nil;aca:updateRender()end end;function __a.getActiveFrame(aca)if(aca==nil)then aca=term.current()end;return -c_a[tostring(aca)]end -function __a.setFocus(aca)if -(b_a==aca)then return end -if(b_a~=nil)then b_a:dispatchEvent("blur")end;b_a=aca -if(b_a~=nil)then b_a:dispatchEvent("focus")end end;function __a.getFocus()return b_a end -function __a.schedule(aca)dd(1,aca,"function") -local bca=coroutine.create(aca)local cca,dca=coroutine.resume(bca) -if(cca)then -table.insert(__a._schedule,{coroutine=bca,filter=dca})else bd.header="Basalt Schedule Error"bd.error(dca)end;return bca end -function __a.removeSchedule(aca) -for bca,cca in ipairs(__a._schedule)do if(cca.coroutine==aca)then -table.remove(__a._schedule,bca)return true end end;return false end -local bba={mouse_click=true,mouse_up=true,mouse_scroll=true,mouse_drag=true}local cba={key=true,key_up=true,char=true} -local function dba(aca,...)if(aca=="terminate")then __a.stop() -return end;if aba(aca,...)then return end;local bca={...} -local function cca() -if(bba[aca])then if -c_a[a_a]then -c_a[a_a]:dispatchEvent(aca,table.unpack(bca))end elseif(cba[aca])then if(b_a~=nil)then -b_a:dispatchEvent(aca,table.unpack(bca))end else for bda,cda in pairs(c_a)do -cda:dispatchEvent(aca,table.unpack(bca))end end end -for bda,cda in pairs(__a._eventQueue)do -if -coroutine.status(cda.coroutine)=="suspended"then -if cda.filter==aca or cda.filter==nil then cda.filter=nil -local dda,__b=coroutine.resume(cda.coroutine,aca,...) -if not dda then bd.header="Basalt Event Error"bd.error(__b)end;cda.filter=__b end end;if coroutine.status(cda.coroutine)=="dead"then -table.remove(__a._eventQueue,bda)end end;local dca={coroutine=coroutine.create(cca),filter=aca} -local _da,ada=coroutine.resume(dca.coroutine,aca,...) -if(not _da)then bd.header="Basalt Event Error"bd.error(ada)end;if(ada~=nil)then dca.filter=ada end -table.insert(__a._eventQueue,dca) -for bda,cda in ipairs(__a._schedule)do -if -coroutine.status(cda.coroutine)=="suspended"then -if aca==cda.filter or cda.filter==nil then cda.filter=nil -local dda,__b=coroutine.resume(cda.coroutine,aca,...) -if(not dda)then bd.header="Basalt Schedule Error"bd.error(__b)end;cda.filter=__b end end;if(coroutine.status(cda.coroutine)=="dead")then -__a.removeSchedule(cda.coroutine)end end;if __a._events[aca]then -for bda,cda in ipairs(__a._events[aca])do cda(...)end end end;local function _ca() -for aca,bca in pairs(c_a)do bca:render()bca:postRender()end end -function __a.update(...)local aca=function(...)__a.isRunning=true -dba(...)_ca()end -local bca,cca=pcall(aca,...) -if not(bca)then bd.header="Basalt Runtime Error"bd.error(cca)end;__a.isRunning=false end;function __a.stop()__a.isRunning=false;term.clear() -term.setCursorPos(1,1)end -function __a.run(aca)if(__a.isRunning)then -bd.error("Basalt is already running")end;if(aca==nil)then __a.isRunning=true else -__a.isRunning=aca end -local function bca()_ca()while __a.isRunning do -dba(os.pullEventRaw())if(__a.isRunning)then _ca()end end end -while __a.isRunning do local cca,dca=pcall(bca)if not(cca)then bd.header="Basalt Runtime Error" -bd.error(dca)end end end;function __a.getElementClass(aca)return ad.getElement(aca)end;function __a.getAPI(aca)return -ad.getAPI(aca)end -function __a.onEvent(aca,bca)dd(1,aca,"string") -dd(2,bca,"function") -if not __a._events[aca]then __a._events[aca]={}end;table.insert(__a._events[aca],bca)end -function __a.removeEvent(aca,bca)dd(1,aca,"string")dd(2,bca,"function")if not -__a._events[aca]then return false end;for cca,dca in ipairs(__a._events[aca])do -if -dca==bca then table.remove(__a._events[aca],cca)return true end end;return false end -function __a.triggerEvent(aca,...)dd(1,aca,"string") -if __a._events[aca]then -for bca,cca in -ipairs(__a._events[aca])do local dca,_da=pcall(cca,...)if not dca then bd.header="Basalt Event Callback Error" -bd.error( -"Error in event callback for '"..aca.."': "..tostring(_da))end end end end -function __a.requireElements(aca,bca)if type(aca)=="string"then aca={aca}end -dd(1,aca,"table")if bca~=nil then dd(2,bca,"boolean")end;local cca={}local dca={}for _da,ada in -ipairs(aca)do -if not ad.hasElement(ada)then table.insert(cca,ada)elseif not -ad.isElementLoaded(ada)then table.insert(dca,ada)end end -if -#dca>0 then -for _da,ada in ipairs(dca)do local bda,cda=pcall(ad.loadElement,ada) -if not bda then -__a.LOGGER.warn( -"Failed to load element "..ada..": "..tostring(cda))table.insert(cca,ada)end end end -if#cca>0 then -if bca then local _da={}for ada,bda in ipairs(cca)do local cda=ad.tryAutoLoad(bda)if not cda then -table.insert(_da,bda)end end -if -#_da>0 then -local ada="Missing required elements: "..table.concat(_da,", ") -ada=ada.."\n\nThese elements could not be auto-loaded." -ada=ada.."\nPlease install them or register remote sources."bd.error(ada)end else -local _da="Missing required elements: "..table.concat(cca,", ")_da=_da.."\n\nSuggestions:" -_da=_da.."\n • Use basalt.requireElements({...}, true) to auto-load" -_da=_da.."\n • Register remote sources with elementManager.registerRemoteSource()" -_da=_da.."\n • Register disk mounts with elementManager.registerDiskMount()"bd.error(_da)end end -__a.LOGGER.info("All required elements are available: "..table.concat(aca,", "))return true end -function __a.loadManifest(aca)dd(1,aca,"string") -if not fs.exists(aca)then bd.error( -"Manifest file not found: "..aca)end;local bca;local cca,dca=pcall(dofile,aca)if not cca then -bd.error("Failed to load manifest: "..tostring(dca))end;bca=dca;if type(bca)~="table"then -bd.error("Manifest must return a table")end -if bca.config then -ad.configure(bca.config)__a.LOGGER.debug("Applied manifest config")end;if bca.diskMounts then -for _da,ada in ipairs(bca.diskMounts)do ad.registerDiskMount(ada)end end;if bca.remoteSources then -for _da,ada in -pairs(bca.remoteSources)do ad.registerRemoteSource(_da,ada)end end;if bca.requiredElements then local _da= -bca.autoLoadMissing~=false -__a.requireElements(bca.requiredElements,_da)end -if bca.optionalElements then for _da,ada in -ipairs(bca.optionalElements)do pcall(ad.loadElement,ada)end end -if bca.preloadElements then ad.preloadElements(bca.preloadElements)end -__a.LOGGER.info("Manifest loaded successfully: ".. (bca.name or aca))return bca end -function __a.install(aca,bca)dd(1,aca,"string") -if bca~=nil then dd(2,bca,"string")end -if ad.hasElement(aca)and ad.isElementLoaded(aca)then return true end -if bca then -if bca:match("^https?://")then ad.registerRemoteSource(aca,bca)else if not -fs.exists(bca)then -bd.error("Source file not found: "..bca)end end end;local cca=ad.tryAutoLoad(aca)if cca then return true else return false end end -function __a.configure(aca)dd(1,aca,"table")ad.configure(aca)end;return __a end -project["layoutManager.lua"] = function(...) local b={}b._cache={} -function b.load(c)if b._cache[c]then return b._cache[c]end -local d,_a=pcall(require,c)if not d then -error("Failed to load layout: "..c.."\n".._a)end;if type(_a)~="table"then -error("Layout must return a table: "..c)end;if type(_a.calculate)~="function"then -error( -"Layout must have a calculate() function: "..c)end;b._cache[c]=_a;return _a end -function b.apply(c,d)local _a=b.load(d)local aa={layout=_a,container=c,options={}} -_a.calculate(aa)b._applyPositions(aa)return aa end -function b._applyPositions(c)if not c._positions then return end -for d,_a in pairs(c._positions)do -if -not d._destroyed then d.set("x",_a.x)d.set("y",_a.y) -d.set("width",_a.width)d.set("height",_a.height) -d._layoutValues={x=_a.x,y=_a.y,width=_a.width,height=_a.height}end end end -function b._wasChangedByUser(c)if not c._layoutValues then return false end -local d=c.get("x")local _a=c.get("y")local aa=c.get("width")local ba=c.get("height") -return d~= -c._layoutValues.x or _a~=c._layoutValues.y or aa~= -c._layoutValues.width or -ba~=c._layoutValues.height end -function b.update(c) -if c and c.layout and c.layout.calculate then -if c._positions then for d,_a in pairs(c._positions)do -if not -d._destroyed then d._userModified=b._wasChangedByUser(d)end end end;c.layout.calculate(c)b._applyPositions(c)end end -function b.destroy(c)if c and c.layout and c.layout.destroy then -c.layout.destroy(c)end;if c then c._positions=nil end end;return b end -project["libraries/colorHex.lua"] = function(...) local b={}for i=0,15 do b[2 ^i]=("%x"):format(i) -b[("%x"):format(i)]=2 ^i end;return b end -project["libraries/utils.lua"] = function(...) local d,_a=math.floor,string.len;local aa={} -function aa.getCenteredPosition(ba,ca,da)local _b=_a(ba)local ab=d( -(ca-_b+1)/2 +0.5)local bb=d(da/2 +0.5)return ab,bb end -function aa.deepCopy(ba)if type(ba)~="table"then return ba end;local ca={}for da,_b in pairs(ba)do -ca[aa.deepCopy(da)]=aa.deepCopy(_b)end;return ca end -function aa.copy(ba)local ca={}for da,_b in pairs(ba)do ca[da]=_b end;return ca end;function aa.reverse(ba)local ca={}for i=#ba,1,-1 do table.insert(ca,ba[i])end -return ca end -function aa.uuid() -return -string.format('%04x%04x-%04x-%04x-%04x-%04x%04x%04x',math.random(0,0xffff),math.random(0,0xffff),math.random(0,0xffff), -math.random(0,0x0fff)+0x4000,math.random(0,0x3fff)+0x8000,math.random(0,0xffff),math.random(0,0xffff),math.random(0,0xffff))end -function aa.split(ba,ca)local da={}for _b in(ba..ca):gmatch("(.-)"..ca)do -table.insert(da,_b)end;return da end;function aa.removeTags(ba)return ba:gsub("{[^}]+}","")end -function aa.wrapText(ba,ca)if -ba==nil then return{}end;ba=aa.removeTags(ba)local da={} -local _b=aa.split(ba,"\n\n") -for ab,bb in ipairs(_b)do -if#bb==0 then table.insert(da,"")if ab<#_b then -table.insert(da,"")end else local cb=aa.split(bb,"\n") -for db,_c in ipairs(cb)do -local ac=aa.split(_c," ")local bc="" -for cc,dc in ipairs(ac)do if#bc==0 then bc=dc elseif#bc+#dc+1 <=ca then bc=bc.." "..dc else -table.insert(da,bc)bc=dc end end;if#bc>0 then table.insert(da,bc)end end;if ab<#_b then table.insert(da,"")end end end;return da end;return aa end -project["libraries/collectionentry.lua"] = function(...) local b={} -b.__index=function(c,d)local _a=rawget(b,d)if _a then return _a end;if c._data[d]~=nil then return -c._data[d]end;local aa=c._parent[d]if aa then return aa end -return nil end -function b.new(c,d)local _a={_parent=c,_data=d}return setmetatable(_a,b)end -function b:_findIndex()for c,d in ipairs(self._parent:getItems())do -if d==self then return c end end;return nil end;function b:setText(c)self._data.text=c -self._parent:updateRender()return self end;function b:getText() -return self._data.text end -function b:moveUp(c)local d=self._parent:getItems() -local _a=self:_findIndex()if not _a then return self end;c=c or 1;local aa=math.max(1,_a-c)if _a~=aa then -table.remove(d,_a)table.insert(d,aa,self) -self._parent:updateRender()end;return self end -function b:moveDown(c)local d=self._parent:getItems() -local _a=self:_findIndex()if not _a then return self end;c=c or 1;local aa=math.min(#d,_a+c)if _a~=aa then -table.remove(d,_a)table.insert(d,aa,self) -self._parent:updateRender()end;return self end -function b:moveToTop()local c=self._parent:getItems() -local d=self:_findIndex()if not d or d==1 then return self end;table.remove(c,d) -table.insert(c,1,self)self._parent:updateRender()return self end -function b:moveToBottom()local c=self._parent:getItems() -local d=self:_findIndex()if not d or d==#c then return self end;table.remove(c,d) -table.insert(c,self)self._parent:updateRender()return self end;function b:getIndex()return self:_findIndex()end -function b:swapWith(c) -local d=self._parent:getItems()local _a=self:getIndex()local aa=c:getIndex() -if _a and aa and _a~=aa then -d[_a],d[aa]=d[aa],d[_a]self._parent:updateRender()end;return self end -function b:remove()if self._parent and self._parent.removeItem then -self._parent:removeItem(self)return true end;return false end -function b:select()if self._parent and self._parent.selectItem then -self._parent:selectItem(self)end;return self end -function b:unselect()if self._parent and self._parent.unselectItem then -self._parent:unselectItem(self)end end -function b:isSelected() -if self._parent and self._parent.getSelectedItem then return -self._parent:getSelectedItem()==self end;return false end;return b end -project["libraries/expect.lua"] = function(...) local c=require("errorManager") -local function d(_a,aa,ba)local ca=type(aa) -if ba=="element"then if ca=="table"and -aa.get("type")~=nil then return true end end -if ba=="color"then if ca=="number"then return true end;if -ca=="string"and colors[aa]then return true end end;if ca~=ba then c.header="Basalt Type Error" -c.error(string.format("Bad argument #%d: expected %s, got %s",_a,ba,ca))end;return true end;return d end -return project["main.lua"]() \ No newline at end of file diff --git a/global-libraries/pixelui_compressed.lua b/global-libraries/pixelui_compressed.lua deleted file mode 100644 index 08686d0..0000000 --- a/global-libraries/pixelui_compressed.lua +++ /dev/null @@ -1,14335 +0,0 @@ -local G=assert(rawget(_G,"term"),"term API unavailable") -local e=assert(rawget(_G,"colors"),"colors API unavailable") -local I=assert(rawget(_G,"os"),"os API unavailable") -local ie=assert(I.pullEvent,"os.pullEvent unavailable") -local ne=assert(rawget(_G,"window"),"window API unavailable") -local a=assert(rawget(_G,"keys"),"keys API unavailable") -local Q=table.pack or function(...) -return{n=select("#",...),...} -end -local K=assert(table.unpack,"table.unpack unavailable") -local t=require("cc.expect").expect -local J=require("global.shrekbox") -local y={ -version="0.1.0" -} -local H={ -linear=function(e) -return e -end, -easeInQuad=function(e) -return e*e -end, -easeOutQuad=function(e) -local e=1-e -return 1-e*e -end, -easeInOutQuad=function(e) -if e<.5 then -return 2*e*e -end -local e=-2*e+2 -return 1-(e*e)/2 -end, -easeOutCubic=function(e) -local e=1-e -return 1-e*e*e -end -} -local s={} -s.__index=s -local b={} -b.__index=b -setmetatable(b,{__index=s}) -local i={} -i.__index=i -setmetatable(i,{__index=b}) -local S={} -S.__index=S -setmetatable(S,{__index=s}) -local E={} -E.__index=E -setmetatable(E,{__index=s}) -local _={} -_.__index=_ -setmetatable(_,{__index=s}) -local g={} -g.__index=g -setmetatable(g,{__index=s}) -local k={} -k.__index=k -setmetatable(k,{__index=s}) -local u={} -u.__index=u -setmetatable(u,{__index=s}) -local j={} -j.__index=j -local N={} -N.__index=N -local f={} -f.__index=f -setmetatable(f,{__index=s}) -local l={} -l.__index=l -setmetatable(l,{__index=s}) -local c={} -c.__index=c -setmetatable(c,{__index=s}) -local w={} -w.__index=w -setmetatable(w,{__index=s}) -local q={} -q.__index=q -setmetatable(q,{__index=s}) -local x={} -x.__index=x -setmetatable(x,{__index=s}) -local d={} -d.__index=d -setmetatable(d,{__index=s}) -local m={} -m.__index=m -setmetatable(m,{__index=s}) -local r={} -r.__index=r -setmetatable(r,{__index=s}) -local v={} -v.__index=v -setmetatable(v,{__index=s}) -local D={} -D.__index=D -setmetatable(D,{__index=s}) -local h={} -h.__index=h -local p={"top","right","bottom","left"} -local se=string.char(7) -local O={ -info={bg=e.blue,fg=e.white,accent=e.lightBlue,icon="i"}, -success={bg=e.green,fg=e.black,accent=e.lime,icon="+"}, -warning={bg=e.orange,fg=e.black,accent=e.yellow,icon="!"}, -error={bg=e.red,fg=e.white,accent=e.white,icon="x"} -} -local function ee(e) -if e==nil then -return"info" -end -local e=tostring(e):lower() -if O[e]then -return e -end -return"info" -end -local function Z(e) -if e==nil then -return 1,1,1,1 -end -if type(e)=="number"then -local e=math.max(0,math.floor(e)) -return e,e,e,e -end -local n,i,s,o=1,1,1,1 -if type(e)=="table"then -local t=e.horizontal or e.x -local a=e.vertical or e.y -if t~=nil then -t=math.max(0,math.floor(t)) -n=t -i=t -end -if a~=nil then -a=math.max(0,math.floor(a)) -s=a -o=a -end -if e.left~=nil then -n=math.max(0,math.floor(e.left)) -end -if e.right~=nil then -i=math.max(0,math.floor(e.right)) -end -if e.top~=nil then -s=math.max(0,math.floor(e.top)) -end -if e.bottom~=nil then -o=math.max(0,math.floor(e.bottom)) -end -end -return n,i,s,o -end -local function z(e,a,t) -if a<=0 then -t[#t+1]="" -return -end -e=(e or""):gsub("\r","") -if e==""then -t[#t+1]="" -return -end -local e=e -while#e>a do -local n=e:sub(1,a) -local o -for e=a,1,-1 do -local t=n:sub(e,e) -if t:match("%s")then -o=e-1 -break -end -end -if o and o>=1 then -local i=e:sub(1,o) -i=i:gsub("%s+$","") -if i==""then -i=e:sub(1,a) -o=a -end -t[#t+1]=i -e=e:sub(o+1) -else -t[#t+1]=n -e=e:sub(a+1) -end -e=e:gsub("^%s+","") -if e==""then -break -end -end -if e~=""then -t[#t+1]=e -elseif#t==0 then -t[#t+1]="" -end -end -local function X(e) -if e==nil then -return nil -end -if e==false then -return nil -end -if type(e)~="string"then -return nil -end -local e=e:lower():gsub("%s+","_"):gsub("-","_") -if e=="manual"or e=="none"then -return nil -end -if e=="topright"then -e="top_right" -elseif e=="topleft"then -e="top_left" -elseif e=="bottomright"then -e="bottom_right" -elseif e=="bottomleft"then -e="bottom_left" -end -if e=="top_right"or e=="top_left"or e=="bottom_right"or e=="bottom_left"then -return e -end -return nil -end -local function Y(e) -local i,o,a,t=1,1,1,1 -if e==nil then -return{top=i,right=o,bottom=a,left=t} -end -if type(e)=="number"then -local e=math.max(0,math.floor(e)) -i,o,a,t=e,e,e,e -elseif type(e)=="table"then -if e.all~=nil then -local e=math.max(0,math.floor(e.all)) -i,o,a,t=e,e,e,e -end -if e.vertical~=nil then -local e=math.max(0,math.floor(e.vertical)) -i,a=e,e -end -if e.horizontal~=nil then -local e=math.max(0,math.floor(e.horizontal)) -o,t=e,e -end -if e.top~=nil then -i=math.max(0,math.floor(e.top)) -end -if e.right~=nil then -o=math.max(0,math.floor(e.right)) -end -if e.bottom~=nil then -a=math.max(0,math.floor(e.bottom)) -end -if e.left~=nil then -t=math.max(0,math.floor(e.left)) -end -end -return{top=i,right=o,bottom=a,left=t} -end -local function P(t,i) -local e={} -if i<=0 then -e[1]="" -return e -end -t=tostring(t or"") -if t==""then -e[1]="" -return e -end -local a=1 -while true do -local o=t:find("\n",a,true) -if not o then -z(t:sub(a),i,e) -break -end -z(t:sub(a,o-1),i,e) -a=o+1 -end -if#e==0 then -e[1]="" -end -return e -end -local function o(e) -if not e then -return nil -end -local t={} -for e,a in pairs(e)do -t[e]=a -end -return t -end -local z={ -close={ -label="X", -fg=e.white, -bg=e.red, -hoverFg=e.white, -hoverBg=e.red, -pressFg=e.lightGray, -pressBg=e.red -}, -maximize={ -label="[]", -maximizeLabel="[]", -restoreLabel="][", -fg=e.white, -bg=e.gray, -hoverFg=e.white, -hoverBg=e.lightGray, -pressFg=e.black, -pressBg=e.lightGray -}, -minimize={ -label="_", -fg=e.white, -bg=e.gray, -hoverFg=e.white, -hoverBg=e.lightGray, -pressFg=e.black, -pressBg=e.lightGray -} -} -local function n(a,e) -local a=z[a] -local a=o(a)or{} -if e==nil or e==false or e==true then -return a -end -t(1,e,"table") -if e.label~=nil then -a.label=tostring(e.label) -end -if e.maximizeLabel~=nil then -a.maximizeLabel=tostring(e.maximizeLabel) -end -if e.restoreLabel~=nil then -a.restoreLabel=tostring(e.restoreLabel) -end -if e.fg~=nil then -a.fg=e.fg -end -if e.bg~=nil then -a.bg=e.bg -end -if e.restoreFg~=nil then -a.restoreFg=e.restoreFg -end -if e.restoreBg~=nil then -a.restoreBg=e.restoreBg -end -if e.width~=nil then -local e=math.max(1,math.floor(e.width)) -a.width=e -end -if e.padding~=nil then -a.padding=math.max(0,math.floor(e.padding)) -end -return a -end -local function A(e) -local t=(type(e)=="table"and e.buttons)or nil -local a -local o -local i -if type(e)=="table"then -a=e.closeButton or(type(t)=="table"and t.close)or nil -o=e.maximizeButton or(type(t)=="table"and t.maximize)or nil -i=e.minimizeButton or(type(t)=="table"and t.minimize)or nil -end -return{ -close=n("close",a), -maximize=n("maximize",o), -minimize=n("minimize",i) -} -end -local function n(e,t) -if e==nil then -return t -end -if type(e)=="string"then -return H[e]or t -elseif type(e)=="function"then -return e -end -error("Invalid easing value",3) -end -local function z(e,a) -if e==nil then -return nil -end -if e==false then -return{enabled=false} -end -t(1,e,"table") -local t={ -enabled=e.enabled~=false -} -if e.duration~=nil then -if type(e.duration)~="number"then -error("animation duration must be numeric",3) -end -t.duration=math.max(0,e.duration) -end -if e.easing~=nil then -t.easing=n(e.easing,a) -end -return t -end -local function B(e) -local a=.2 -local o=H.easeOutQuad -if e==nil then -return{ -enabled=true, -duration=a, -easing=o -} -end -t(1,e,"table") -if e.duration~=nil and type(e.duration)~="number"then -error("animation duration must be numeric",3) -end -local t={ -enabled=e.enabled~=false, -duration=math.max(0,e.duration or a), -easing=n(e.easing,o) -} -local a={"maximize","minimize","restore"} -for o=1,#a do -local a=a[o] -local e=z(e[a],t.easing) -if e then -t[a]=e -end -end -return t -end -local function W(e) -if e>=0 then -return math.floor(e+.5) -end -return math.ceil(e-.5) -end -local function M(a,e) -t(nil,a,"string") -t(nil,e,"number") -if e<1 or e~=math.floor(e)then -error(('%s must be a positive integer, got "%s"'):format(a,tostring(e)),3) -end -end -local function R(a) -if not a or a==false then -return nil -end -if a==true then -return{ -color=e.lightGray, -top=true, -right=true, -bottom=true, -left=true, -thickness=1 -} -end -t(1,a,"table") -local e={ -color=a.color or e.lightGray, -top=true, -right=true, -bottom=true, -left=true, -thickness=math.max(1,math.floor(a.thickness or 1)) -} -local function o(a,t) -if t~=nil then -e[a]=not not t -end -end -if a.sides then -e.top=false -e.right=false -e.bottom=false -e.left=false -if#a.sides>0 then -for t=1,#a.sides do -local t=a.sides[t] -if e[t]~=nil then -e[t]=true -end -end -else -for t,a in pairs(a.sides)do -if e[t]~=nil then -e[t]=not not a -end -end -end -else -for e=1,#p do -o(p[e],a[p[e]]) -end -end -if e.thickness<1 then -e.thickness=1 -end -return e -end -local function V(a) -local e=a.border -local t=e and math.max(1,math.floor(e.thickness or 1))or 0 -local n=(e and e.left)and t or 0 -local o=(e and e.right)and t or 0 -local i=(e and e.top)and t or 0 -local e=(e and e.bottom)and t or 0 -local t=math.max(0,a.width-n-o) -local a=math.max(0,a.height-i-e) -return n,o,i,e,t,a -end -local function ae(e,a) -if e==nil then -return{ -enabled=a~=false, -height=1, -bg=nil, -fg=nil, -align="left", -buttons=A(nil), -buttonSpacing=1 -} -end -if e==false then -return{enabled=false,height=0,bg=nil,fg=nil,align="left",buttons=A(nil),buttonSpacing=1} -end -if e==true then -return{enabled=true,height=1,bg=nil,fg=nil,align="left",buttons=A(nil),buttonSpacing=1} -end -t(1,e,"table") -local t=e.enabled -if t==nil then -t=true -end -if not t then -return{enabled=false,height=0,bg=nil,fg=nil,align="left"} -end -local t=e.height -if type(t)~="number"or t<1 then -t=1 -else -t=math.floor(t) -end -local a=e.align and tostring(e.align):lower()or"left" -if a~="left"and a~="center"and a~="right"then -a="left" -end -local o=e.buttonSpacing~=nil and math.max(0,math.floor(e.buttonSpacing))or 1 -return{ -enabled=true, -height=t, -bg=e.bg, -fg=e.fg, -align=a, -buttons=A(e), -buttonSpacing=o -} -end -local function te(a,e,t) -if t~=nil and e~=nil and tt then -return t -end -return a -end -local p="^(%a[%w_]*)%.([%a_][%w_]*)$" -local function n(e) -if type(e)~="string"then -return nil,nil -end -local t,e=e:match(p) -return t,e -end -local function z(e,t) -local e=tonumber(e) -if not e then -error("constraints."..t.." must be numeric",3) -end -if e>1 then -e=e/100 -end -if e<0 then -e=0 -elseif e>1 then -e=1 -end -return e -end -local function p(e,t) -if e==nil then -return nil -end -local a=type(e) -if a=="number"then -return{kind="absolute",value=math.max(1,math.floor(e))} -end -if a=="boolean"then -if e then -return{kind="relative",target="parent",property=t} -end -return nil -end -if a=="string"then -local e,a=n(e) -if not e then -error("constraints."..t.." string references must look like 'parent.'",3) -end -if e~="parent"then -error("constraints."..t.." currently only supports references to the parent",3) -end -return{kind="relative",target=e,property=a,offset=0} -end -if a=="table"then -if e.reference or e.of then -local a=e.reference or e.of -local a,o=n(a) -if not a then -error("constraints."..t.." reference tables must include 'reference' or 'of' matching 'parent.'",3) -end -if a~="parent"then -error("constraints."..t.." references currently only support the parent",3) -end -local e=e.offset and math.floor(e.offset)or 0 -return{kind="relative",target=a,property=o,offset=e} -end -if e.percent~=nil then -local o=z(e.percent,t..".percent") -local a=e.of or("parent."..t) -local a,i=n(a) -if not a then -error("constraints."..t..".percent requires an 'of' reference such as 'parent.width'",3) -end -if a~="parent"then -error("constraints."..t..".percent currently only supports the parent",3) -end -local e=e.offset and math.floor(e.offset)or 0 -return{ -kind="percent", -percent=o, -target=a, -property=i, -offset=e -} -end -if e.match~=nil then -return p(e.match,t) -end -if e.value~=nil then -return p(e.value,t) -end -error("constraints."..t.." table must include percent, reference/of, match, or value fields",3) -end -return nil -end -local function T(e,t) -if e==nil then -return nil -end -local a=type(e) -if a=="boolean"then -if not e then -return nil -end -return{ -kind="center", -target="parent", -property=t=="x"and"centerX"or"centerY", -offset=0 -} -end -if a=="string"then -local e,a=n(e) -if not e then -error("constraints.center"..(t=="x"and"X"or"Y").." string references must look like 'parent.'",3) -end -if e~="parent"then -error("constraints.center"..(t=="x"and"X"or"Y").." currently only supports the parent",3) -end -return{kind="center",target=e,property=a,offset=0} -end -if a=="table"then -local o=e.reference or e.of or e.target or e.align -local a=e.offset and math.floor(e.offset)or 0 -if o then -local e,o=n(o) -if not e then -error("constraints.center"..(t=="x"and"X"or"Y").." reference tables must use 'parent.'",3) -end -if e~="parent"then -error("constraints.center"..(t=="x"and"X"or"Y").." currently only supports the parent",3) -end -return{kind="center",target=e,property=o,offset=a} -end -return{ -kind="center", -target="parent", -property=t=="x"and"centerX"or"centerY", -offset=a -} -end -return nil -end -local function oe(e) -if e==nil then -return nil -end -if type(e)~="table"then -error("constraints must be a table if provided",3) -end -local t={} -if e.minWidth~=nil then -if type(e.minWidth)~="number"then -error("constraints.minWidth must be a number",3) -end -t.minWidth=math.max(1,math.floor(e.minWidth)) -end -if e.maxWidth~=nil then -if type(e.maxWidth)~="number"then -error("constraints.maxWidth must be a number",3) -end -t.maxWidth=math.max(1,math.floor(e.maxWidth)) -end -if e.minHeight~=nil then -if type(e.minHeight)~="number"then -error("constraints.minHeight must be a number",3) -end -t.minHeight=math.max(1,math.floor(e.minHeight)) -end -if e.maxHeight~=nil then -if type(e.maxHeight)~="number"then -error("constraints.maxHeight must be a number",3) -end -t.maxHeight=math.max(1,math.floor(e.maxHeight)) -end -if t.minWidth and t.maxWidth and t.maxWidth1 then -return 1 -end -return e -end -local function F(e,o,t,a) -if not e or e.enabled==false then -return 0,nil -end -o=math.max(0,o or 0) -t=math.max(0,t or 0) -a=math.max(0,a or 0) -if a<=1 or t<=0 then -return 0,nil -end -if t<=2 then -return 0,nil -end -if not e.alwaysVisible and o<=t then -return 0,nil -end -local a=math.max(1,a-1) -local t=math.max(1,math.floor(e.width or 1)) -if t>a then -t=a -end -if t<=0 then -return 0,nil -end -return t,e -end -local function C(n,i,a,t,r,d,u,e) -if not e or t<=0 then -return -end -local h=math.max(1,math.floor(e.width or 1)) -local o=e.trackColor -local s=e.arrowColor -local l=e.thumbColor -local c=math.max(1,math.floor(e.minThumbSize or 1)) -local e=math.max(0,h-1) -local m=T..string.rep(" ",e) -n.text(i,a,m,s,o) -if t>=2 then -local e=p..string.rep(" ",e) -n.text(i,a+t-1,e,s,o) -end -local a=a+1 -local e=math.max(0,t-2) -local t=string.rep(" ",h) -for e=0,e-1 do -n.text(i,a+e,t,o,o) -end -local s=math.max(0,(r or 0)-(d or 0)) -if s<=0 or e<=0 then -return -end -local u=math.max(0,math.min(s,math.floor((u or 0)+.5))) -local t=d/r -local t=math.max(c,math.floor(e*t+.5)) -if t>e then -t=e -end -if t<1 then -t=1 -end -local o=e-t -local e=a -if o>0 then -local t=z(s==0 and 0 or(u/s)) -e=a+math.floor(t*o+.5) -if e>a+o then -e=a+o -end -end -local a=string.rep(" ",h) -for t=0,t-1 do -n.text(i,e+t,a,l,l) -end -end -local function U(t,a,e,i,o) -if a<=0 then -return o or 0 -end -local e=math.max(0,(e or 0)-(i or 0)) -if e<=0 then -return 0 -end -local o=math.max(0,math.min(e,math.floor((o or 0)+.5))) -if t<=0 then -return math.max(0,o-1) -elseif t>=a-1 then -return math.min(e,o+1) -end -local a=a-2 -if a<=0 then -return o -end -local t=t-1 -if t<0 then -t=0 -elseif t>a then -t=a -end -local t=math.floor((t/a)*e+.5) -if t<0 then -t=0 -elseif t>e then -t=e -end -return t -end -local function n(h,s,n,t,e,i,o,a) -if t<=0 or e<=0 then -return -end -local a=a or" " -local t=a:rep(t) -for e=0,e-1 do -h.text(s,n+e,t,i,o) -end -end -local function T(t,i,n,a,o,s) -if a<=0 or o<=0 or not t then -return -end -local s=s or e.black -local e=(i-1)*2+1 -local i=(n-1)*3+1 -local a=a*2 -local o=o*3 -for o=0,o-1 do -local o=i+o -for a=0,a-1 do -t.pixel(e+a,o,s) -end -end -end -local function z(e,t,a,n,o) -if n<=0 or o<=0 then -return -end -local i=J.transparent -for n=0,n-1 do -e.pixel(t+n,a,i) -if o>1 then -e.pixel(t+n,a+o-1,i) -end -end -for o=1,math.max(0,o-2)do -e.pixel(t,a+o,i) -if n>1 then -e.pixel(t+n-1,a+o,i) -end -end -end -local function p(s,t,h,i,o,e,a) -if i<=0 or o<=0 then -return -end -local n=e.color -local r=a or n -local t=(t-1)*2+1 -local a=(h-1)*3+1 -local i=i*2 -local o=o*3 -local l=3 -local u=2 -local h=math.min(e.thickness,o) -local d=math.min(e.thickness,i) -local l=math.min(o,math.max(h,l)) -local u=math.min(i,math.max(d,u)) -local function m(h,e,n) -for e=0,e-1 do -local e=h+e -if e=a+o then break end -for a=0,i-1 do -s.pixel(t+a,e,n) -end -end -end -local function c(h,e,n) -for e=0,e-1 do -local e=h+e -if e=a+o then break end -for a=0,i-1 do -s.pixel(t+a,e,n) -end -end -end -local function f(e,h,n) -for h=0,h-1 do -local e=e+h -if e=t+i then break end -for t=0,o-1 do -s.pixel(e,a+t,n) -end -end -end -local function w(h,e,n) -for e=0,e-1 do -local e=h+e -if e=t+i then break end -for t=0,o-1 do -s.pixel(e,a+t,n) -end -end -end -if e.left then -f(t,u,r) -end -if e.right then -f(t+i-u,u,r) -end -if e.top then -m(a,l,r) -end -if e.bottom then -m(a+o-l,l,r) -end -if e.top then -c(a,h,n) -end -if e.bottom then -c(a+o-h,h,n) -end -if e.left then -w(t,d,n) -end -if e.right then -w(t+i-d,d,n) -end -end -function r:new(i,a) -a=a or{} -t(1,i,"table") -if a~=nil then -t(2,a,"table") -end -local t=o(a)or{} -t.focusable=false -t.width=math.max(12,math.floor(t.width or 24)) -t.height=math.max(3,math.floor(t.height or 5)) -if t.visible==nil then -t.visible=false -end -local e=setmetatable({},r) -e:_init_base(i,t) -e.focusable=false -local i=a.anchor~=nil -local t=X(a.anchor) -if not t and not i then -if a.x~=nil or a.y~=nil then -t=nil -else -t="top_right" -end -end -e.anchor=t -e.anchorMargins=Y(a.anchorMargin) -e.anchorAnimationDuration=math.max(.05,tonumber(a.anchorAnimationDuration)or .2) -e.anchorEasing=a.anchorEasing or"easeOutCubic" -e._anchorDirty=true -e._anchorAnimationHandle=nil -e.title=a.title~=nil and tostring(a.title)or nil -e.message=a.message~=nil and tostring(a.message)or"" -e.icon=a.icon~=nil and tostring(a.icon)or nil -e.severity=ee(a.severity) -local t=a.duration -if t~=nil then -t=tonumber(t)or 0 -else -t=3 -end -if t<0 then -t=0 -end -e.duration=t -e.autoHide=a.autoHide~=false -e.dismissOnClick=a.dismissOnClick~=false -e.onDismiss=a.onDismiss -if e.onDismiss~=nil and type(e.onDismiss)~="function"then -error("config.onDismiss must be a function",2) -end -e.variantOverrides=a.variants and o(a.variants)or nil -e.styleOverride=a.style and o(a.style)or nil -e.paddingLeft,e.paddingRight,e.paddingTop,e.paddingBottom=Z(a.padding) -e._hideTimer=nil -e._wrappedLines={""} -e._lastWrapWidth=nil -e._lastMessage=nil -e:_refreshWrap(true) -return e -end -function r:_applyPadding(e,i) -local e,a,t,o=Z(e) -if i or e~=self.paddingLeft or a~=self.paddingRight or t~=self.paddingTop or o~=self.paddingBottom then -self.paddingLeft=e -self.paddingRight=a -self.paddingTop=t -self.paddingBottom=o -self:_refreshWrap(true) -self._anchorDirty=true -end -end -function r:setPadding(e) -self:_applyPadding(e,false) -end -function r:getAnchor() -return self.anchor -end -function r:getAnchorMargins() -return o(self.anchorMargins) -end -function r:refreshAnchor(e) -if not self.anchor then -self._anchorDirty=false -return -end -self._anchorDirty=true -if e and self.visible then -self:_applyAnchorPosition(true) -else -self:_applyAnchorPosition(false) -end -end -function r:setAnchor(t) -local e=X(t) -if e==nil and t~=nil then -self.anchor=nil -else -self.anchor=e -end -self:refreshAnchor(false) -end -function r:setAnchorMargin(e) -self.anchorMargins=Y(e) -self:refreshAnchor(false) -end -function r:_computeAnchorPosition() -local i=self.anchor -if not i then -return nil,nil -end -local e=self.parent -if not e then -return nil,nil -end -local n=e.width -local o=e.height -if type(n)~="number"or type(o)~="number"then -return nil,nil -end -local h=self.width -local s=self.height -local a=self.anchorMargins or Y(nil) -local t -local e -if i=="top_right"then -t=n-h-(a.right or 0)+1 -e=(a.top or 0)+1 -elseif i=="top_left"then -t=(a.left or 0)+1 -e=(a.top or 0)+1 -elseif i=="bottom_right"then -t=n-h-(a.right or 0)+1 -e=o-s-(a.bottom or 0)+1 -elseif i=="bottom_left"then -t=(a.left or 0)+1 -e=o-s-(a.bottom or 0)+1 -else -return nil,nil -end -if t<1 then -t=1 -end -if e<1 then -e=1 -end -if t+h-1>n then -t=math.max(1,n-h+1) -end -if e+s-1>o then -e=math.max(1,o-s+1) -end -return t,e -end -function r:getAnchorTargetPosition() -return self:_computeAnchorPosition() -end -function r:_applyAnchorPosition(a) -if not self.anchor then -self._anchorDirty=false -return -end -local e,t=self:_computeAnchorPosition() -if not e or not t then -return -end -if self._anchorAnimationHandle then -self._anchorAnimationHandle:cancel() -self._anchorAnimationHandle=nil -end -if a and self.app and self.app.animate then -local i=math.max(2,math.floor(self.width/6)) -local n=math.max(1,math.floor(self.height/3)) -local o=e -local a=t -if self.anchor=="top_right"then -o=e+i -a=math.max(1,t-n) -elseif self.anchor=="top_left"then -o=e-i -a=math.max(1,t-n) -elseif self.anchor=="bottom_right"then -o=e+i -a=t+n -elseif self.anchor=="bottom_left"then -o=e-i -a=t+n -end -s.setPosition(self,o,a) -local r=self.anchorAnimationDuration or .2 -local h=self.anchorEasing or"easeOutCubic" -local o=o -local a=a -local i=e-o -local n=t-a -self._anchorAnimationHandle=self.app:animate({ -duration=r, -easing=h, -update=function(e) -local t=math.floor(o+i*e+.5) -local e=math.floor(a+n*e+.5) -s.setPosition(self,t,e) -end, -onComplete=function() -s.setPosition(self,e,t) -self._anchorAnimationHandle=nil -end, -onCancel=function() -s.setPosition(self,e,t) -self._anchorAnimationHandle=nil -end -}) -self._anchorDirty=false -return -end -if self.x~=e or self.y~=t then -s.setPosition(self,e,t) -end -self._anchorDirty=false -end -function r:_getActiveBorder() -if self.border then -return self.border -end -return nil -end -function r:_refreshWrap(o,t) -local e -if t~=nil then -e=math.max(0,math.floor(t)) -else -local t=self:_getActiveBorder() -local a=(t and t.left)and t.thickness or 0 -local t=(t and t.right)and t.thickness or 0 -e=math.max(0,self.width-a-t-(self.paddingLeft or 0)-(self.paddingRight or 0)) -end -if e<0 then -e=0 -end -if not o and self._lastWrapWidth==e and self._lastMessage==self.message then -return -end -self._wrappedLines=P(self.message,e) -self._lastWrapWidth=e -self._lastMessage=self.message -end -function r:_getStyle() -local a=self.severity -local e=O.info -if a~=nil then -local t=O[a] -if t then -e=t -end -else -a="info" -end -local t=e -if self.variantOverrides then -local a=self.variantOverrides[a] -if a then -t=o(e)or e -for e,a in pairs(a)do -t[e]=a -end -end -end -if self.styleOverride then -if t==e then -t=o(e)or e -end -for a,e in pairs(self.styleOverride)do -t[a]=e -end -end -return t or e -end -function r:_cancelTimer() -if self._hideTimer then -if I.cancelTimer then -pcall(I.cancelTimer,self._hideTimer) -end -self._hideTimer=nil -end -end -function r:_scheduleHide(e) -if not self.autoHide then -return -end -local e=e -if e==nil then -e=self.duration -end -if not e or e<=0 then -return -end -self._hideTimer=I.startTimer(e) -end -function r:setTitle(e) -if e==nil then -self.title=nil -else -self.title=tostring(e) -end -end -function r:getTitle() -return self.title -end -function r:setMessage(e) -if e==nil then -e="" -end -local e=tostring(e) -if self.message~=e then -self.message=e -self:_refreshWrap(true) -end -end -function r:getMessage() -return self.message -end -function r:setSeverity(e) -local e=ee(e) -if self.severity~=e then -self.severity=e -end -end -function r:getSeverity() -return self.severity -end -function r:setIcon(e) -if e==nil or e==""then -self.icon=nil -return -end -self.icon=tostring(e) -end -function r:getIcon() -return self.icon -end -function r:setAutoHide(e) -e=not not e -if self.autoHide~=e then -self.autoHide=e -if not e then -self:_cancelTimer() -end -end -end -function r:isAutoHide() -return self.autoHide -end -function r:setDuration(e) -if e==nil then -return -end -local e=tonumber(e)or 0 -if e<0 then -e=0 -end -self.duration=e -if self.visible and self.autoHide then -self:_cancelTimer() -self:_scheduleHide(e) -end -end -function r:getDuration() -return self.duration -end -function r:setDismissOnClick(e) -self.dismissOnClick=not not e -end -function r:isDismissOnClick() -return self.dismissOnClick -end -function r:setOnDismiss(e) -if e~=nil and type(e)~="function"then -error("onDismiss handler must be a function",2) -end -self.onDismiss=e -end -function r:setVariants(e) -if e~=nil and type(e)~="table"then -error("variants must be a table",2) -end -self.variantOverrides=e and o(e)or nil -end -function r:setStyle(e) -if e~=nil and type(e)~="table"then -error("style must be a table",2) -end -self.styleOverride=e and o(e)or nil -end -function r:present(e) -t(1,e,"table") -if e.title~=nil then -self:setTitle(e.title) -end -if e.message~=nil then -self:setMessage(e.message) -end -if e.icon~=nil then -self:setIcon(e.icon) -end -if e.severity~=nil then -self:setSeverity(e.severity) -end -if e.duration~=nil then -self:setDuration(e.duration) -end -if e.autoHide~=nil then -self:setAutoHide(e.autoHide) -end -if e.style~=nil then -self:setStyle(e.style) -end -if e.variants~=nil then -self:setVariants(e.variants) -end -self:show(e.duration) -end -function r:show(t) -local e=self.visible -self.visible=true -self:_refreshWrap(true) -self:_cancelTimer() -if self.anchor then -if not e then -self:_applyAnchorPosition(true) -elseif self._anchorDirty then -self:_applyAnchorPosition(false) -end -end -local e=nil -if t~=nil then -e=tonumber(t)or 0 -if e<0 then -e=0 -end -end -self:_scheduleHide(e) -end -function r:hide(t) -local e=self.visible -self.visible=false -self:_cancelTimer() -if self._anchorAnimationHandle then -self._anchorAnimationHandle:cancel() -self._anchorAnimationHandle=nil -end -if t~=false and e and self.onDismiss then -self.onDismiss(self) -end -end -function r:setSize(e,t) -s.setSize(self,e,t) -self:_refreshWrap(true) -self._anchorDirty=true -if self.anchor then -self:_applyAnchorPosition(false) -end -end -function r:setBorder(e) -s.setBorder(self,e) -self:_refreshWrap(true) -self._anchorDirty=true -end -function r:_renderLine(i,a,s,t,e,n,o) -if t<=0 then -return -end -local e=e or"" -if#e>t then -e=e:sub(1,t) -end -if#e0 then -local a=a:sub(1,1) -o.text(h,e,a,c,t) -if n>=3 then -o.text(h+1,e," ",c,t) -s=2 -else -s=1 -end -r=h+s -end -local a=math.max(0,n-s) -self:_refreshWrap(false,a) -if self.title and self.title~=""and i>0 and a>0 then -self:_renderLine(o,r,e,a,self.title,f,t) -e=e+1 -i=i-1 -end -if i>0 and a>0 then -local n=self._wrappedLines or{""} -local i=math.min(i,#n) -for i=1,i do -self:_renderLine(o,r,e,a,n[i],d,t) -e=e+1 -end -end -end -function r:handleEvent(e,...) -if not self.visible then -return false -end -if e=="timer"then -local e=... -if self._hideTimer and e==self._hideTimer then -self._hideTimer=nil -self:hide(true) -return true -end -elseif e=="mouse_click"then -local a,e,t=... -if self.dismissOnClick and self:containsPoint(e,t)then -self:hide(true) -return true -end -elseif e=="monitor_touch"then -local a,e,t=... -if self.dismissOnClick and self:containsPoint(e,t)then -self:hide(true) -return true -end -end -return false -end -function r:onFocusChanged() -end -function v:new(n,a) -a=a or{} -t(1,n,"table") -if a~=nil then -t(2,a,"table") -end -local i=o(a)or{} -i.focusable=false -i.width=math.max(3,math.floor(i.width or 8)) -i.height=math.max(3,math.floor(i.height or 5)) -local t=setmetatable({},v) -t:_init_base(n,i) -t.focusable=false -t.color=a.color or e.cyan -t.secondaryColor=a.secondaryColor or e.lightBlue -t.tertiaryColor=a.tertiaryColor or e.blue -t.trailColor=a.trailColor or e.gray -t.trailPalette=a.trailPalette and o(a.trailPalette)or nil -t.segmentCount=math.max(6,math.floor(a.segments or a.segmentCount or 12)) -t.thickness=math.max(1,math.floor(a.thickness or 2)) -t.radiusPixels=a.radius and math.max(2,math.floor(a.radius))or nil -local e=tonumber(a.speed) -if not e or e<=0 then -e=.08 -end -t.speed=math.max(.01,e) -t.fadeSteps=math.max(0,math.floor(a.fadeSteps or 2)) -local e=a.direction -if type(e)=="string"then -local t=e:lower() -if t=="counterclockwise"or t=="anticlockwise"or t=="ccw"then -e=-1 -else -e=1 -end -elseif type(e)=="number"then -e=e>=0 and 1 or-1 -else -e=1 -end -t.direction=e -t._phase=0 -t._tickTimer=nil -t._paused=a.autoStart==false -if not t._paused then -t:_scheduleTick() -end -return t -end -function v:_cancelTick() -if self._tickTimer then -if I.cancelTimer then -pcall(I.cancelTimer,self._tickTimer) -end -self._tickTimer=nil -end -end -function v:_scheduleTick() -self:_cancelTick() -if self._paused then -return -end -if not self.speed or self.speed<=0 then -return -end -self._tickTimer=I.startTimer(self.speed) -end -function v:start() -if not self._paused then -return -end -self._paused=false -self:_scheduleTick() -end -function v:stop() -if self._paused then -return -end -self._paused=true -self:_cancelTick() -end -function v:setSpeed(e) -if e==nil then -return -end -local e=tonumber(e) -if not e then -return -end -if e<=0 then -self.speed=0 -self:_cancelTick() -return -end -e=math.max(.01,e) -if e~=self.speed then -self.speed=e -if not self._paused then -self:_scheduleTick() -end -end -end -function v:setDirection(e) -if e==nil then -return -end -local e=e -if type(e)=="string"then -local t=e:lower() -if t=="counterclockwise"or t=="anticlockwise"or t=="ccw"then -e=-1 -else -e=1 -end -elseif type(e)=="number"then -e=e>=0 and 1 or-1 -else -e=1 -end -if e~=self.direction then -self.direction=e -end -end -function v:setSegments(e) -if e==nil then -return -end -local e=math.max(3,math.floor(e)) -if e~=self.segmentCount then -self.segmentCount=e -self._phase=self._phase%e -end -end -function v:setThickness(e) -if e==nil then -return -end -local e=math.max(1,math.floor(e)) -self.thickness=e -end -function v:setRadius(e) -if e==nil then -self.radiusPixels=nil -return -end -local e=math.max(2,math.floor(e)) -self.radiusPixels=e -end -function v:setColor(e) -if e==nil then -return -end -t(1,e,"number") -self.color=e -end -function v:setSecondaryColor(e) -if e==nil then -self.secondaryColor=nil -return -end -t(1,e,"number") -self.secondaryColor=e -end -function v:setTertiaryColor(e) -if e==nil then -self.tertiaryColor=nil -return -end -t(1,e,"number") -self.tertiaryColor=e -end -function v:setTrailColor(e) -if e==nil then -self.trailColor=nil -return -end -t(1,e,"number") -self.trailColor=e -end -function v:setTrailPalette(e) -if e~=nil then -t(1,e,"table") -end -self.trailPalette=e and o(e)or nil -end -function v:setFadeSteps(e) -if e==nil then -return -end -local e=math.max(0,math.floor(e)) -self.fadeSteps=e -end -function v:_computeTrailColors() -local e={} -local t=self.trailPalette -if type(t)=="table"then -for a=1,#t do -local t=t[a] -if t then -e[#e+1]=t -end -end -end -if#e==0 then -if self.secondaryColor then -e[#e+1]=self.secondaryColor -end -if self.tertiaryColor then -e[#e+1]=self.tertiaryColor -end -end -local t=math.max(0,math.floor(self.fadeSteps or 0)) -if t>0 then -local a=self.trailColor or e[#e]or self.color -for t=1,t do -e[#e+1]=a -end -elseif#e==0 and self.trailColor then -e[1]=self.trailColor -end -if#e==0 then -e[1]=self.color -end -return e -end -function v:draw(r,d) -if not self.visible then -return -end -local o,i,t,a=self:getAbsoluteRect() -if t<=0 or a<=0 then -return -end -local e=self.bg or self.app.background -n(r,o,i,t,a,e,e) -z(r,o,i,t,a) -if self.border then -p(d,o,i,t,a,self.border,e) -end -local l=(self.border and self.border.left)and 1 or 0 -local c=(self.border and self.border.right)and 1 or 0 -local u=(self.border and self.border.top)and 1 or 0 -local m=(self.border and self.border.bottom)and 1 or 0 -local s=o+l -local h=i+u -local o=math.max(0,t-l-c) -local a=math.max(0,a-u-m) -if o<=0 or a<=0 then -return -end -n(r,s,h,o,a,e,e) -local w=s+(o-1)/2 -local l=h+(a-1)/2 -local i=math.floor(math.min(o,a)/2) -local t=self.radiusPixels and math.floor(self.radiusPixels)or i -if t>i then -t=i -end -if t<1 then -t=1 -end -local n=math.max(1,math.min(math.floor(self.thickness or 1),t)) -local i=t+.35 -local t=math.max(0,t-n+.35) -local m=i*i -local f=t*t -local t=math.max(3,math.floor(self.segmentCount or 12)) -local n=self._phase%t -local c=self.direction>=0 and 1 or-1 -local r=math.pi*2 -local u=self:_computeTrailColors() -for a=0,a-1 do -local h=h+a -local i=h-l -for a=0,o-1 do -local s=s+a -local a=s-w -local l=a*a+i*i -local o=e -if l<=m and l>=f then -local a=math.atan(i,a) -if a<0 then -a=a+r -end -local i=math.floor(a/r*t)%t -local a -if c>=0 then -a=(n-i)%t -else -a=(i-n)%t -end -if a==0 then -o=self.color or e -else -local t=math.floor(a+1e-4) -if t<1 then -t=1 -end -o=u[t]or e -end -end -d.pixel(s,h,o) -end -end -end -function v:handleEvent(a,...) -if a=="timer"then -local e=... -if self._tickTimer and e==self._tickTimer then -self._tickTimer=nil -local t=math.max(3,math.floor(self.segmentCount or 12)) -local e=self.direction>=0 and 1 or-1 -local e=(self._phase+e)%t -if e<0 then -e=e+t -end -self._phase=e -if not self._paused then -self:_scheduleTick() -end -return true -end -end -return s.handleEvent(self,a,...) -end -local function O(e) -local t,a=e.x,e.y -local e=e.parent -while e do -t=t+e.x-1 -a=a+e.y-1 -e=e.parent -end -return t,a -end -function s:_init_base(o,a) -t(1,o,"table") -a=a or{} -t(2,a,"table","nil") -self.app=o -self.parent=nil -self.x=math.floor(a.x or 1) -self.y=math.floor(a.y or 1) -self.width=math.floor(a.width or 1) -self.height=math.floor(a.height or 1) -self.bg=a.bg or e.black -self.fg=a.fg or e.white -self.visible=a.visible~=false -self.z=a.z or 0 -self.id=a.id -self.border=R(a.border) -self.focusable=a.focusable==true -self._focused=false -self.constraints=nil -M("width",self.width) -M("height",self.height) -if a.constraints~=nil then -self.constraints=oe(a.constraints) -local e,t=self:_applySizeConstraints(self.width,self.height) -self.width=e -self.height=t -end -end -function s:setSize(e,t) -M("width",e) -M("height",t) -local t,e=self:_applySizeConstraints(e,t) -self.width=t -self.height=e -end -function s:_applyConstraintLayout() -local t=self.constraints -if not t then -return -end -local e=self.parent -local function s(t) -if not e then -return nil -end -if t=="width"then -return e.width -elseif t=="height"then -return e.height -elseif t=="centerX"then -if e.width then -return(e.width-1)/2+1 -end -elseif t=="centerY"then -if e.height then -return(e.height-1)/2+1 -end -elseif t=="right"then -return e.width -elseif t=="bottom"then -return e.height -elseif t=="left"or t=="x"then -return 1 -elseif t=="top"or t=="y"then -return 1 -end -return nil -end -local function a(e,t) -if not e then -return nil -end -if e.kind=="absolute"then -return e.value -end -if e.kind=="relative"then -local t=s(e.property) -if t==nil then -return nil -end -local e=e.offset or 0 -local e=math.floor(t+e) -return math.max(1,e) -end -if e.kind=="percent"then -local t=s(e.property) -if t==nil then -return nil -end -local a=e.offset or 0 -local e=math.floor(t*e.percent+.5)+a -return math.max(1,e) -end -return nil -end -local o=a(t.width,"width") -local a=a(t.height,"height") -local n=e and e.width or nil -local i=e and e.height or nil -if not o and t.widthPercent and n then -o=math.max(1,math.floor(n*t.widthPercent+.5)) -end -if not a and t.heightPercent and i then -a=math.max(1,math.floor(i*t.heightPercent+.5)) -end -local o=o or self.width -local a=a or self.height -local o,a=self:_applySizeConstraints(o,a) -if o~=self.width or a~=self.height then -self:setSize(o,a) -end -n=e and e.width or nil -i=e and e.height or nil -local function h(i,t,a,o,n) -if not i then -return nil -end -if not e or not a or a<=0 then -return nil -end -local t=i.property or(t=="x"and"centerX"or"centerY") -local e -if t=="centerX"or t=="centerY"then -e=math.floor((a-o)/2)+1 -elseif t=="right"or t=="bottom"or t=="width"or t=="height"then -e=a-o+1 -elseif t=="left"or t=="top"or t=="x"or t=="y"then -e=1 -else -local t=s(t) -if t then -e=math.floor(t-math.floor(o/2)) -else -e=math.floor((a-o)/2)+1 -end -end -local t=(i.offset or 0)+n -e=math.floor(e+t) -if e<1 then -e=1 -end -local t=math.max(1,a-o+1) -if e>t then -e=t -end -return e -end -local o=math.floor(t.offsetX or 0) -local s=math.floor(t.offsetY or 0) -local a=self.x -local e=self.y -local o=h(t.centerX,"x",n,self.width,o) -if o then -a=o -end -local t=h(t.centerY,"y",i,self.height,s) -if t then -e=t -end -if a~=self.x or e~=self.y then -self:setPosition(a,e) -end -end -function s:_applySizeConstraints(e,t) -local a=math.floor(e) -local t=math.floor(t) -if a<1 then -a=1 -end -if t<1 then -t=1 -end -local e=self.constraints -if e then -if e.minWidth and ae.maxWidth then -a=e.maxWidth -end -if e.minHeight and te.maxHeight then -t=e.maxHeight -end -end -return a,t -end -function s:setConstraints(e) -if e==nil or e==false then -self.constraints=nil -else -self.constraints=oe(e) -end -local t,e=self:_applySizeConstraints(self.width,self.height) -if t~=self.width or e~=self.height then -self:setSize(t,e) -end -self:_applyConstraintLayout() -end -local function Y(e) -if not e then -return nil -end -if e.kind=="absolute"then -return e.value -elseif e.kind=="relative"then -local t=string.format("%s.%s",e.target or"parent",e.property or"width") -if e.offset and e.offset~=0 then -return{reference=t,offset=e.offset} -end -return t -elseif e.kind=="percent"then -local t=string.format("%s.%s",e.target or"parent",e.property or"width") -local t={percent=e.percent,of=t} -if e.offset and e.offset~=0 then -t.offset=e.offset -end -return t -end -return nil -end -local function M(e) -if not e then -return nil -end -local t=string.format("%s.%s",e.target or"parent",e.property or"center") -if e.offset and e.offset~=0 then -return{reference=t,offset=e.offset} -end -return t -end -function s:getConstraints() -if not self.constraints then -return nil -end -local e=self.constraints -local t={} -if e.minWidth then -t.minWidth=e.minWidth -end -if e.maxWidth then -t.maxWidth=e.maxWidth -end -if e.minHeight then -t.minHeight=e.minHeight -end -if e.maxHeight then -t.maxHeight=e.maxHeight -end -local a=Y(e.width) -if a~=nil then -t.width=a -end -local a=Y(e.height) -if a~=nil then -t.height=a -end -if e.widthPercent then -t.widthPercent=e.widthPercent -end -if e.heightPercent then -t.heightPercent=e.heightPercent -end -local a=M(e.centerX) -if a~=nil then -t.centerX=a -end -local a=M(e.centerY) -if a~=nil then -t.centerY=a -end -if e.offsetX and e.offsetX~=0 then -t.offsetX=e.offsetX -end -if e.offsetY and e.offsetY~=0 then -t.offsetY=e.offsetY -end -if next(t)then -return t -end -return nil -end -function s:setPosition(a,e) -t(1,a,"number") -t(2,e,"number") -self.x=math.floor(a) -self.y=math.floor(e) -end -function s:setZ(e) -t(1,e,"number") -self.z=e -end -function s:setBorder(e) -if e==nil then -self.border=nil -return -end -if e==false then -self.border=nil -return -end -if e==true then -self.border=R(true) -return -end -t(1,e,"table","boolean") -self.border=R(e) -end -function s:isFocused() -return self._focused -end -function s:setFocused(e) -e=not not e -if self._focused==e then -return -end -self._focused=e -self:onFocusChanged(e) -end -function s:onFocusChanged(e) -end -function s:getAbsoluteRect() -local e,t=O(self) -return e,t,self.width,self.height -end -function s:getSize() -return self.width,self.height -end -function s:containsPoint(e,a) -local t,o,i,n=self:getAbsoluteRect() -return e>=t and e=o and a0 and l>0 then -n(h,d,u,t,l,e,e) -T(r,d,u,t,l,e) -elseif o>0 and a>0 then -n(h,s,i,o,a,e,e) -T(r,s,i,o,a,e) -end -z(h,s,i,o,a) -local n=self.title -if type(n)=="string"and#n>0 then -local o=t>0 and t or o -local s=t>0 and d or s -local a=(a>2)and(i+1)or i -if o>0 then -local t=n -if#t>o then -t=t:sub(1,o) -end -if#t=e.x1 and a<=e.x2 and t>=e.y1 and t<=e.y2 then -return o -end -end -return nil -end -function i:_drawTitleButton(d,m,a,s,l,u) -local e=a.buttonRects and a.buttonRects[s] -if not e then -return -end -local t=a.buttonMetrics and a.buttonMetrics[s] -if not t then -return -end -local o=t.style or{} -local c=math.max(0,t.padding or 0) -local i=e.width-c*2 -if i<=0 then -return -end -local r=o.fg or l -local h=o.bg or u -local t=tostring(o.label or"") -if s=="maximize"then -local e=tostring(o.maximizeLabel or t) -local i=tostring(o.restoreLabel or e) -if a.maximizeState=="restore"then -t=i -r=o.restoreFg or r -h=o.restoreBg or h -else -t=e -end -end -if#t>i then -t=t:sub(1,i) -end -local o=h or u or self.bg or self.app.background -n(d,e.x1,e.y1,e.width,a.barHeight,o,o) -T(m,e.x1,e.y1,e.width,a.barHeight,o) -if#t>0 then -local a=e.x1+c -local i=math.floor((i-#t)/2) -if i>0 then -a=a+i -end -d.text(a,e.y1,t,r or l,o) -end -end -function i:_fillTitleBarPixels(t,e,a) -if not t or not e then -return -end -local n=(e.barX-1)*2+1 -local o=(e.barY-1)*3+1 -local s=e.barWidth*2 -local e=math.min(e.barHeight*3,self.height*3) -for i=0,e-1 do -for e=0,s-1 do -t.pixel(n+e,o+i,a) -end -end -end -function i:_hitTestResize(t,s) -if not self.resizable then -return nil -end -local o,e=O(self) -local n=o+math.max(0,self.width-1) -local i=e+math.max(0,self.height-1) -local a=1 -if self.border and self.border.thickness then -a=math.max(1,math.floor(self.border.thickness)) -end -local e={} -local n=t>=n-a+1 and t<=n -local t=t>=o and t<=o+a-1 -if n then -e.right=true -elseif t then -e.left=true -end -if s>=i-a+1 and s<=i then -e.bottom=true -end -if not e.right and not e.left and not e.bottom then -return nil -end -return e -end -function i:_beginResize(t,a,o,i,e) -if not e then -return -end -self:_restoreFromMaximize() -self._resizing=true -self._resizeSource=t -self._resizeIdentifier=a -self._resizeEdges=e -local e=self.constraints or{} -self._resizeStart={ -pointerX=o, -pointerY=i, -width=self.width, -height=self.height, -x=self.x, -y=self.y, -minWidth=e.minWidth or 1, -minHeight=e.minHeight or 1 -} -self:bringToFront() -if self.app then -self.app:setFocus(nil) -end -end -function i:_updateResize(t,a) -if not self._resizing or not self._resizeStart then -return -end -local e=self._resizeStart -local o=t-e.pointerX -local i=a-e.pointerY -local t=e.width -local a=e.height -if self._resizeEdges.right then -t=e.width+o -elseif self._resizeEdges.left then -t=e.width-o -end -if self._resizeEdges.bottom then -a=e.height+i -end -if tt then -e=t -end -else -if e<1 then -e=1 -end -end -if e~=self.x then -self:setPosition(e,self.y) -end -end -end -function i:_endResize() -self._resizing=false -self._resizeSource=nil -self._resizeIdentifier=nil -self._resizeEdges=nil -self._resizeStart=nil -end -function i:_restoreFromMaximize() -if not self._isMaximized and not self._isMinimized then -return -end -self:restore(true) -end -function i:_computeMaximizedGeometry() -local e=self.parent -if e then -local o,i,n,i,t,a=V(e) -local t=math.max(1,t) -local a=math.max(1,a) -local i=o+1 -local o=n+1 -if self.app and e==self.app.root then -i=1 -o=1 -t=e.width -a=e.height -end -return{x=i,y=o,width=t,height=a} -end -local e=self.app and self.app.root or nil -if e then -return{x=1,y=1,width=e.width,height=e.height} -end -return{x=self.x,y=self.y,width=self.width,height=self.height} -end -function i:_computeMinimizedGeometry() -local t=self._restoreRect or{x=self.x,y=self.y,width=self.width,height=self.height} -local e -if self.minimizedHeight then -e=self.minimizedHeight -else -local a,a,o,t=self:_computeInnerOffsets() -local a=self:_getVisibleTitleBarHeight() -e=o+t+math.max(1,a) -if e<1 then -e=1 -end -end -return{ -x=t.x, -y=t.y, -width=t.width, -height=e -} -end -function i:_captureRestoreRect() -local e={ -x=self.x, -y=self.y, -width=self.width, -height=self.height -} -self._restoreRect=e -self._normalRect=o(e) -end -function i:maximize() -if not self.maximizable or self._isMaximized then -return -end -if self._isMinimized then -self:restore(true) -end -self:_captureRestoreRect() -local e=self:_computeMaximizedGeometry() -self._isMaximized=true -self._isMinimized=false -self:bringToFront() -self:_invalidateTitleLayout() -self:_transitionGeometry("maximize",e,function() -self._restoreRect=self._restoreRect or o(e) -if self.onMaximize then -self:onMaximize() -end -end) -end -function i:restore(a) -if not self._isMaximized and not self._isMinimized then -return -end -local t=self._normalRect or self._restoreRect or{ -x=self.x, -y=self.y, -width=self.width, -height=self.height -} -self._isMaximized=false -self._isMinimized=false -self:_invalidateTitleLayout() -local function e() -self._restoreRect=nil -self._normalRect=nil -if self.onRestore then -self:onRestore() -end -end -if a then -self:_stopGeometryAnimation() -self:_applyGeometry(t) -e() -return -end -self:_transitionGeometry("restore",t,e) -end -function i:toggleMaximize() -if self._isMaximized then -self:restore() -else -if self._isMinimized then -self:restore(true) -end -self:maximize() -end -end -function i:minimize() -if not self.minimizable or self._isMinimized then -return -end -if self._isMaximized then -self:restore(true) -end -self:_captureRestoreRect() -local e=self:_computeMinimizedGeometry() -self._isMinimized=true -self._isMaximized=false -self:bringToFront() -self:_invalidateTitleLayout() -self:_transitionGeometry("minimize",e,function() -if self.onMinimize then -self:onMinimize() -end -end) -end -function i:toggleMinimize() -if self._isMinimized then -self:restore() -else -if self._isMaximized then -self:restore(true) -end -self:minimize() -end -end -function i:isMinimized() -return not not self._isMinimized -end -function i:close() -if self.onClose then -local e=self:onClose() -if e==false then -return -end -end -self:_stopGeometryAnimation() -self.visible=false -self:_endDrag() -self:_endResize() -self._isMaximized=false -self._isMinimized=false -self._restoreRect=nil -self._normalRect=nil -end -function i:_getVisibleTitleBarHeight() -local e=self._titleBar -if not e or not e.enabled then -return 0 -end -local a,a,a,a,a,t=self:_computeInnerOffsets() -if t<=0 then -return 0 -end -local e=math.max(1,math.floor(e.height or 1)) -if e>t then -e=t -end -return e -end -function i:setTitleBar(e) -self._titleBar=ae(e,nil) -self:_refreshTitleBarState() -self:_invalidateTitleLayout() -end -function i:getTitleBar() -local e=self._titleBar -if not e then -return nil -end -local e=o(e) -if e and e.buttons then -local t={} -if e.buttons.close then -t.close=o(e.buttons.close) -end -if e.buttons.maximize then -t.maximize=o(e.buttons.maximize) -end -e.buttons=t -end -return e -end -function i:setDraggable(e) -self.draggable=not not e -end -function i:isDraggable() -return not not self.draggable -end -function i:setResizable(e) -self.resizable=not not e -self:_invalidateTitleLayout() -end -function i:isResizable() -return not not self.resizable -end -function i:setClosable(e) -self.closable=not not e -self:_invalidateTitleLayout() -end -function i:isClosable() -return not not self.closable -end -function i:setMaximizable(e) -self.maximizable=not not e -self:_invalidateTitleLayout() -end -function i:isMaximizable() -return not not self.maximizable -end -function i:setMinimizable(e) -self.minimizable=not not e -self:_invalidateTitleLayout() -end -function i:isMinimizable() -return not not self.minimizable -end -function i:setHideBorderWhenMaximized(e) -local e=not not e -if self.hideBorderWhenMaximized==e then -return -end -self.hideBorderWhenMaximized=e -self:_invalidateTitleLayout() -end -function i:hidesBorderWhenMaximized() -return not not self.hideBorderWhenMaximized -end -function i:setMinimizedHeight(e) -if e==nil then -self.minimizedHeight=nil -if self._isMinimized then -self:_applyGeometry(self:_computeMinimizedGeometry()) -end -return -end -t(1,e,"number") -self.minimizedHeight=math.max(1,math.floor(e)) -if self._isMinimized then -self:_applyGeometry(self:_computeMinimizedGeometry()) -end -end -function i:getMinimizedHeight() -return self.minimizedHeight -end -function i:setGeometryAnimation(e) -if e==nil then -self._geometryAnimation=B(nil) -return -end -t(1,e,"table") -self._geometryAnimation=B(e) -end -function i:setOnMinimize(e) -if e~=nil then -t(1,e,"function") -end -self.onMinimize=e -end -function i:setTitle(e) -b.setTitle(self,e) -self:_invalidateTitleLayout() -end -function i:getContentOffset() -local a,e,t=self:_computeInnerOffsets() -local e=self:_getVisibleTitleBarHeight() -return a,t+e -end -function i:setSize(e,t) -b.setSize(self,e,t) -self:_refreshTitleBarState() -self:_invalidateTitleLayout() -end -function i:setBorder(e) -s.setBorder(self,e) -self:_refreshTitleBarState() -self:_invalidateTitleLayout() -end -function i:bringToFront() -local e=self.parent -if not e then -return -end -e._orderCounter=(e._orderCounter or 0)+1 -self._orderIndex=e._orderCounter -end -function i:_pointInTitleBar(t,a) -local e=self:_computeTitleLayout() -if not e then -return false -end -if self._titleButtonRects then -for o,e in pairs(self._titleButtonRects)do -if t>=e.x1 and t<=e.x2 and a>=e.y1 and a<=e.y2 then -return false -end -end -end -local i=e.barX -local o=e.barY -local n=i+math.max(0,e.barWidth-1) -local e=o+math.max(0,e.barHeight-1) -return t>=i and t<=n and a>=o and a<=e -end -function i:_beginDrag(o,e,t,a) -self:_restoreFromMaximize() -local i,n=O(self) -self._dragging=true -self._dragSource=o -self._dragIdentifier=e -self._dragOffsetX=t-i -self._dragOffsetY=a-n -self:bringToFront() -if self.app then -self.app:setFocus(nil) -end -end -function i:_updateDragPosition(a,t) -if not self._dragging then -return -end -local e=self.parent -local i=self._dragOffsetX or 0 -local o=self._dragOffsetY or 0 -local a=a-i -local t=t-o -if e then -local o,i=O(e) -local h=o -local s=i -local n=o+math.max(0,e.width-self.width) -local e=i+math.max(0,e.height-self.height) -a=te(a,h,n) -t=te(t,s,e) -local e=a-o+1 -local t=t-i+1 -self:setPosition(e,t) -else -self:setPosition(a,t) -end -end -function i:_endDrag() -self._dragging=false -self._dragSource=nil -self._dragIdentifier=nil -self._dragOffsetX=0 -self._dragOffsetY=0 -end -function i:draw(a,o) -if not self.visible then -return -end -local r,h,d,l=self:getAbsoluteRect() -local i,c,u,c,s,t=self:_computeInnerOffsets() -local c=r+i -local u=h+u -local i=self.bg or self.app.background -if s>0 and t>0 then -n(a,c,u,s,t,i,i) -T(o,c,u,s,t,i) -else -n(a,r,h,d,l,i,i) -T(o,r,h,d,l,i) -end -z(a,r,h,d,l) -local u=self._titleBar -local t=nil -local s=nil -if u then -t=self:_computeTitleLayout() -if t then -s=u.bg or i -local s=s or i -local h=u.fg or self.fg or e.white -n(a,t.barX,t.textBaseline,t.barWidth,t.barHeight,s,s) -T(o,t.barX,t.textBaseline,t.barWidth,t.barHeight,s) -local n=t.titleWidth or 0 -local i=self.title or"" -if n>0 and i~=""then -if#i>n then -i=i:sub(1,n) -end -local e=n-#i -local n=u.align or"left" -local o=i -if e>0 then -if n=="center"then -local t=math.floor(e/2) -local e=e-t -o=string.rep(" ",t)..i..string.rep(" ",e) -elseif n=="right"then -o=string.rep(" ",e)..i -else -o=i..string.rep(" ",e) -end -end -a.text(t.titleStart,t.textBaseline,o,h,s) -end -local i=u.fg or self.fg or e.white -local e=t.buttonOrder or{} -for n=1,#e do -local e=e[n] -if e=="maximize"and self.maximizable then -self:_drawTitleButton(a,o,t,e,i,s) -elseif e=="close"and self.closable then -self:_drawTitleButton(a,o,t,e,i,s) -elseif e=="minimize"and self.minimizable then -self:_drawTitleButton(a,o,t,e,i,s) -end -end -end -end -if self:_isBorderVisible()then -p(o,r,h,d,l,self.border,i) -end -local t=M(self._children) -Y(t) -if#t==0 then -return -end -if not(a and a.text and o and o.pixel)then -for e=1,#t do -t[e]:draw(a,o) -end -return -end -local i=r -local e=h -local w=r+d-1 -local f=h+l-1 -local u=(i-1)*2+1 -local r=(e-1)*3+1 -local m=u+d*2-1 -local c=r+l*3-1 -local h=a.text -local s=o.pixel -local function l() -a.text=h -o.pixel=s -end -a.text=function(t,n,a,d,r) -if not a or a==""then -return -end -if nf then -return -end -local t=t -local e=1 -local s=#a -if ts then -return -end -local i=w-t+1 -if i<=0 then -return -end -local i=math.min(s,e+i-1) -if im or tc then -return -end -s(e,t,a) -end -local e,t=pcall(function() -for e=1,#t do -t[e]:draw(a,o) -end -end) -l() -if not e then -error(t,0) -end -end -function i:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"then -local o,e,t=... -local a=self:_hitTestTitleButton(e,t) -if a=="close"and self.closable then -self:close() -return true -elseif a=="maximize"and self.maximizable then -self:toggleMaximize() -return true -elseif a=="minimize"and self.minimizable then -self:toggleMinimize() -return true -end -local a=self:_hitTestResize(e,t) -if a then -self:_beginResize("mouse",o,e,t,a) -return true -end -if self.draggable and self:_pointInTitleBar(e,t)then -self:_beginDrag("mouse",o,e,t) -return true -end -elseif e=="mouse_drag"then -local t,e,a=... -if self._resizing and self._resizeSource=="mouse"and t==self._resizeIdentifier then -self:_updateResize(e,a) -return true -end -if self._dragging and self._dragSource=="mouse"and t==self._dragIdentifier then -self:_updateDragPosition(e,a) -return true -end -elseif e=="mouse_up"then -local e=... -if self._resizing and self._resizeSource=="mouse"and e==self._resizeIdentifier then -self:_endResize() -return true -end -if self._dragging and self._dragSource=="mouse"and e==self._dragIdentifier then -self:_endDrag() -return true -end -elseif e=="monitor_touch"then -local o,t,e=... -local a=self:_hitTestTitleButton(t,e) -if a=="close"and self.closable then -self:close() -return true -elseif a=="maximize"and self.maximizable then -self:toggleMaximize() -return true -elseif a=="minimize"and self.minimizable then -self:toggleMinimize() -return true -end -local a=self:_hitTestResize(t,e) -if a then -self:_beginResize("monitor",o,t,e,a) -return true -end -if self.draggable and self:_pointInTitleBar(t,e)then -self:_beginDrag("monitor",o,t,e) -return true -end -elseif e=="monitor_drag"then -local t,e,a=... -if self._resizing and self._resizeSource=="monitor"and t==self._resizeIdentifier then -self:_updateResize(e,a) -return true -end -if self._dragging and self._dragSource=="monitor"and t==self._dragIdentifier then -self:_updateDragPosition(e,a) -return true -end -elseif e=="monitor_up"then -local e=... -if self._resizing and self._resizeSource=="monitor"and e==self._resizeIdentifier then -self:_endResize() -return true -end -if self._dragging and self._dragSource=="monitor"and e==self._dragIdentifier then -self:_endDrag() -return true -end -end -return b.handleEvent(self,e,...) -end -local function A(e) -return e=="mouse_click"or e=="mouse_up"or e=="mouse_drag"or e=="mouse_scroll"or e=="monitor_touch"or e=="monitor_up"or e=="monitor_drag"or e=="monitor_scroll" -end -local function O(e,...) -if e=="mouse_click"or e=="mouse_up"or e=="mouse_drag"then -local a,t,e=... -return t,e -elseif e=="mouse_scroll"then -local a,e,t=... -return e,t -elseif e=="monitor_touch"or e=="monitor_up"or e=="monitor_drag"or e=="monitor_scroll"then -local a,t,e=... -return t,e -end -return nil,nil -end -local T=setmetatable({},{__index=i}) -T.__index=T -function T:new(t,a) -a=a or{} -local t=i.new(i,t,a) -setmetatable(t,T) -local i=a.modal~=false -t.modal=i -local o=a.backdropColor -if o==false then -o=nil -end -if i and o==nil then -o=e.gray -end -t.backdropColor=o -if a.backdropPixelColor~=nil then -if a.backdropPixelColor==false then -t.backdropPixelColor=nil -else -t.backdropPixelColor=a.backdropPixelColor -end -else -t.backdropPixelColor=o -end -t.closeOnBackdrop=a.closeOnBackdrop~=false -t.closeOnEscape=a.closeOnEscape~=false -t._modalRaised=false -if a.resizable==nil then -t:setResizable(false) -end -if a.maximizable==nil then -t:setMaximizable(false) -end -if a.minimizable==nil then -t:setMinimizable(false) -end -return t -end -function T:setModal(t) -t=not not t -if self.modal==t then -return -end -self.modal=t -if t and self.backdropColor==nil then -self.backdropColor=e.black -if self.backdropPixelColor==nil then -self.backdropPixelColor=self.backdropColor -end -end -self._modalRaised=false -end -function T:isModal() -return not not self.modal -end -function T:setBackdropColor(t,e) -if t==false then -self.backdropColor=nil -else -self.backdropColor=t -end -if e==false then -self.backdropPixelColor=nil -elseif e~=nil then -self.backdropPixelColor=e -else -self.backdropPixelColor=self.backdropColor -end -end -function T:getBackdropColor() -return self.backdropColor -end -function T:setCloseOnBackdrop(e) -self.closeOnBackdrop=not not e -end -function T:setCloseOnEscape(e) -self.closeOnEscape=not not e -end -function T:draw(e,t) -if not self.visible then -return -end -if self.modal then -if not self._modalRaised then -self:bringToFront() -self._modalRaised=true -end -else -self._modalRaised=false -end -i.draw(self,e,t) -end -function T:_consumeModalEvent(e,...) -if not self.modal then -return false -end -if e=="key"then -local e=... -if self.closeOnEscape and e==a.escape then -self:close() -return true -end -return true -end -if e=="char"or e=="paste"or e=="key_up"then -return true -end -if A(e)then -local o,a=O(e,...) -local t=false -if o and a then -t=self:containsPoint(o,a) -end -if not t and(e=="mouse_click"or e=="monitor_touch")then -if self.closeOnBackdrop then -self:close() -end -end -return true -end -return false -end -function T:handleEvent(e,...) -if not self.visible then -return false -end -local t=i.handleEvent(self,e,...) -if t then -return true -end -if self.modal then -if self:_consumeModalEvent(e,...)then -return true -end -end -return false -end -function T:close() -local e=self.visible -i.close(self) -if e and not self.visible then -self._modalRaised=false -end -end -local function M(e,t,o) -local a=t -local t=o -if type(e)=="number"then -local e=math.max(0,math.floor(e)) -a,t=e,e -elseif type(e)=="table"then -if e.horizontal~=nil then -a=math.max(0,math.floor(e.horizontal)) -elseif e.x~=nil then -a=math.max(0,math.floor(e.x)) -end -if e.vertical~=nil then -t=math.max(0,math.floor(e.vertical)) -elseif e.y~=nil then -t=math.max(0,math.floor(e.y)) -end -end -return a,t -end -local function O(e,t) -if type(e)~="string"then -return t -end -local e=e:lower() -if e~="left"and e~="center"and e~="right"then -return t -end -return e -end -local A=setmetatable({},{__index=T}) -A.__index=A -function A:new(i,a) -a=a or{} -if a.modal==nil then -a.modal=true -end -if a.resizable==nil then -a.resizable=false -end -local t=T.new(T,i,a) -setmetatable(t,A) -t.autoClose=a.autoClose~=false -t.buttonAlign=O(a.buttonAlign,"center") -t.buttonGap=math.max(0,math.floor(a.buttonGap or 2)) -t.buttonHeight=math.max(1,math.floor(a.buttonHeight or 3)) -t.minButtonWidth=math.max(1,math.floor(a.minButtonWidth or 6)) -t.buttonLabelPadding=math.max(0,math.floor(a.buttonLabelPadding or 2)) -t.buttonAreaSpacing=math.max(0,math.floor(a.buttonAreaSpacing or 1)) -t.contentPaddingX,t.contentPaddingY=M(a.contentPadding,2,1) -t.messagePaddingX,t.messagePaddingY=M(a.messagePadding,1,1) -t.messageFg=a.messageFg or e.lightBlue -t.messageBg=a.messageBg or e.white -t.wrapMessage=a.wrap~=false -t._buttons={} -local o -if a.contentBorder==false then -o=nil -else -o={color=a.contentBorderColor or e.lightGray} -end -t._contentFrame=b:new(i,{ -width=1, -height=1, -bg=t.messageBg, -fg=t.messageFg, -border=o -}) -t._contentFrame.focusable=false -t:addChild(t._contentFrame) -t._messageLabel=E:new(i,{ -text=a.message or"", -wrap=t.wrapMessage, -bg=t.messageBg, -fg=t.messageFg, -width=1, -height=1, -align=a.messageAlign or"left", -verticalAlign=a.messageVerticalAlign or"top" -}) -t._messageLabel.focusable=false -t._contentFrame:addChild(t._messageLabel) -t.onResult=a.onResult -t:setMessage(a.message or"") -t:setButtons(a.buttons) -if a.bg==nil then -t.bg=e.gray -end -t:_updateLayout() -return t -end -function A:setMessage(e) -if e==nil then -e="" -end -e=tostring(e) -self.message=e -if self._messageLabel then -self._messageLabel:setText(e) -end -self:_updateLayout() -end -function A:getMessage() -return self.message or"" -end -function A:setOnResult(e) -if e~=nil then -t(1,e,"function") -end -self.onResult=e -end -function A:_createButtonEntry(t,i) -local a={} -local o -local s=self.autoClose -local h -local d -local l -local r -local n -if type(t)=="string"then -o=t -a.id=t -elseif type(t)=="table"then -o=t.label or t.id or("Button "..tostring(i)) -a.id=t.id or t.value or o -if t.autoClose~=nil then -s=not not t.autoClose -end -if t.width~=nil then -h=math.max(1,math.floor(t.width)) -end -if t.height~=nil then -d=math.max(1,math.floor(t.height)) -end -l=t.bg -r=t.fg -n=t.onSelect -else -error("MsgBox button config at index "..tostring(i).." must be a string or table",3) -end -o=tostring(o) -if not a.id or a.id==""then -a.id=tostring(i) -end -local h=h or math.max(self.minButtonWidth,#o+self.buttonLabelPadding*2) -local i=d or self.buttonHeight -local e=S:new(self.app,{ -label=o, -width=h, -height=i, -bg=l or e.white, -fg=r or e.black -}) -e.focusable=false -a.button=e -a.autoClose=s -a.config=t -a.onSelect=n -e.onClick=function() -self:_handleButtonSelection(a) -end -return a -end -function A:setButtons(e) -if self._buttons then -for e=1,#self._buttons do -local e=self._buttons[e] -if e and e.button and e.button.parent then -e.button.parent:removeChild(e.button) -end -end -end -self._buttons={} -if e==nil then -e={{id="ok",label="OK",autoClose=true}} -end -if type(e)~="table"then -error("MsgBox:setButtons expects a table or nil",2) -end -for t=1,#e do -local e=self:_createButtonEntry(e[t],t) -self._buttons[#self._buttons+1]=e -self:addChild(e.button) -end -self:_updateLayout() -end -function A:_handleButtonSelection(e) -if not e then -return -end -if e.onSelect then -e.onSelect(self,e.id,e.button) -end -local t -if self.onResult then -t=self.onResult(self,e.id,e.button) -end -if e.autoClose and t~=false then -self:close() -end -end -function A:setButtonAlign(e) -self.buttonAlign=O(e,self.buttonAlign) -self:_updateLayout() -end -function A:setAutoClose(e) -self.autoClose=not not e -end -function A:setButtonGap(e) -self.buttonGap=math.max(0,math.floor(e or self.buttonGap)) -self:_updateLayout() -end -function A:_updateLayout() -if not self._contentFrame then -return -end -local o,t,a,t,e,i=self:_computeInnerOffsets() -local t=self:_getVisibleTitleBarHeight() -local n=math.max(1,e) -local e=math.max(1,i-t) -local o=o+1 -local s=a+t+1 -local a=#self._buttons -local i=a>0 and self.buttonHeight or 0 -local t=e -local t=t -if a>0 then -local a=self.buttonAreaSpacing -if e<=i then -a=0 -t=math.max(1,e-i) -else -local o=math.max(0,e-i-1) -if a>o then -a=o -end -t=math.max(1,e-i-a) -end -self._buttonRowY=s+t+a -else -self._buttonRowY=nil -end -self._contentFrame:setPosition(o,s) -self._contentFrame:setSize(n,math.max(1,t)) -local e=math.max(1,n-self.messagePaddingX*2) -local i=math.max(1,t-self.messagePaddingY*2) -self._messageLabel:setSize(e,i) -self._messageLabel:setPosition(self.messagePaddingX+1,self.messagePaddingY+1) -if a>0 then -local e=0 -for t=1,a do -local a=self._buttons[t].button -e=e+a.width -if t>1 then -e=e+self.buttonGap -end -end -local i -if self.buttonAlign=="left"then -i=o -elseif self.buttonAlign=="right"then -i=o+math.max(0,n-e) -else -i=o+math.max(0,math.floor((n-e)/2)) -end -local o=i -local t=self._buttonRowY or(s+t) -for e=1,a do -local e=self._buttons[e] -local e=e.button -e:setSize(e.width,self.buttonHeight) -e:setPosition(o,t) -o=o+e.width+self.buttonGap -end -end -end -function A:setSize(t,e) -i.setSize(self,t,e) -self:_updateLayout() -end -function A:setBorder(e) -i.setBorder(self,e) -self:_updateLayout() -end -function A:setTitleBar(e) -i.setTitleBar(self,e) -self:_updateLayout() -end -function S:new(a,e) -local t=setmetatable({},S) -t:_init_base(a,e) -t.label=(e and e.label)or"Button" -t.onPress=e and e.onPress or nil -t.onRelease=e and e.onRelease or nil -t.onClick=e and e.onClick or nil -if e and e.clickEffect~=nil then -t.clickEffect=not not e.clickEffect -else -t.clickEffect=true -end -t._pressed=false -t.focusable=false -return t -end -function S:setLabel(e) -t(1,e,"string") -self.label=e -end -function S:setOnClick(e) -if e~=nil then -t(1,e,"function") -end -self.onClick=e -end -function S:draw(h,c) -if not self.visible then -return -end -local i,o,a,t=self:getAbsoluteRect() -local s=self.bg or e.gray -local r=self.fg or e.white -local e=s -local d=r -if self.clickEffect and self._pressed then -e,d=d,e -end -local l,u=i+1,o+1 -local s=math.max(0,a-2) -local r=math.max(0,t-2) -if s>0 and r>0 then -n(h,l,u,s,r,e,e) -else -n(h,i,o,a,t,e,e) -end -z(h,i,o,a,t) -if self.border then -p(c,i,o,a,t,self.border,e) -end -local t=self.label or"" -local a=s>0 and s or a -if#t>a then -t=t:sub(1,a) -end -local n=0 -if a>#t then -n=math.floor((a-#t)/2) -end -local t=string.rep(" ",n)..t -if#t0 and l or i -local a -if r>0 then -a=u+math.floor((r-1)/2) -else -a=o -end -h.text(i,a,t,d,e) -end -function S:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"then -local a,t,e=... -if self:containsPoint(t,e)then -self.app:setFocus(nil) -self._pressed=true -if self.onPress then -self.onPress(self,a,t,e) -end -return true -end -elseif e=="mouse_drag"then -local a,e,t=... -if self._pressed then -if not self:containsPoint(e,t)then -self._pressed=false -if self.onRelease then -self.onRelease(self,a,e,t) -end -return false -end -return true -end -elseif e=="mouse_up"then -local a,e,t=... -if self._pressed then -self._pressed=false -if self:containsPoint(e,t)then -self.app:setFocus(nil) -if self.onRelease then -self.onRelease(self,a,e,t) -end -if self.onClick then -self.onClick(self,a,e,t) -end -return true -end -end -elseif e=="monitor_touch"then -local a,t,e=... -if self:containsPoint(t,e)then -self.app:setFocus(nil) -if self.onPress then -self.onPress(self,1,t,e) -end -if self.onRelease then -self.onRelease(self,1,t,e) -end -if self.onClick then -self.onClick(self,1,t,e) -end -return true -end -end -return false -end -function E:new(i,e) -e=e or{} -local a=o(e)or{} -a.focusable=false -a.height=math.max(1,math.floor(a.height or 1)) -a.width=math.max(1,math.floor(a.width or 1)) -local t=setmetatable({},E) -t:_init_base(i,a) -t.focusable=false -local a=e and e.text -if a==nil then -a="" -end -t.text=tostring(a) -t.wrap=not not(e and e.wrap) -local a=(e and e.align)and tostring(e.align):lower()or"left" -if a~="left"and a~="center"and a~="right"then -a="left" -end -t.align=a -local e=(e and e.verticalAlign)and tostring(e.verticalAlign):lower()or"top" -if e=="center"then -e="middle" -end -if e~="top"and e~="middle"and e~="bottom"then -e="top" -end -t.verticalAlign=e -t._lines={""} -t._lastInnerWidth=nil -t._lastText=nil -t._lastWrap=nil -t:_updateLines(true) -return t -end -function E:_getInnerMetrics() -local e=self.border -local t=(e and e.left)and 1 or 0 -local a=(e and e.right)and 1 or 0 -local o=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local i=math.max(0,self.width-t-a) -local n=math.max(0,self.height-o-e) -return t,a,o,e,i,n -end -function E:_wrapLine(t,a,e) -if a<=0 then -e[#e+1]="" -return -end -t=t:gsub("\r","") -if t==""then -e[#e+1]="" -return -end -local t=t -while#t>a do -local n=t:sub(1,a) -local o -for e=a,1,-1 do -local t=n:sub(e,e) -if t:match("%s")then -o=e-1 -break -end -end -if o and o>=1 then -local i=t:sub(1,o) -i=i:gsub("%s+$","") -if i==""then -i=t:sub(1,a) -o=a -end -e[#e+1]=i -t=t:sub(o+1) -else -e[#e+1]=n -t=t:sub(a+1) -end -t=t:gsub("^%s+","") -if t==""then -break -end -end -if t~=""then -e[#e+1]=t -elseif#e==0 then -e[#e+1]="" -end -end -function E:_updateLines(e) -local t=tostring(self.text or"") -local i=not not self.wrap -local a,a,a,a,o=self:_getInnerMetrics() -if not e and self._lastText==t and self._lastWrap==i and self._lastInnerWidth==o then -return -end -local e={} -if t==""then -e[1]="" -else -local a=1 -while true do -local n=t:find("\n",a,true) -if not n then -local t=t:sub(a) -t=t:gsub("\r","") -if i then -self:_wrapLine(t,o,e) -else -e[#e+1]=t -end -break -end -local t=t:sub(a,n-1) -t=t:gsub("\r","") -if i then -self:_wrapLine(t,o,e) -else -e[#e+1]=t -end -a=n+1 -end -end -if#e==0 then -e[1]="" -end -self._lines=e -self._lastText=t -self._lastWrap=i -self._lastInnerWidth=o -end -function E:setText(e) -if e==nil then -e="" -end -e=tostring(e) -if self.text~=e then -self.text=e -self:_updateLines(true) -end -end -function E:getText() -return self.text -end -function E:setWrap(e) -e=not not e -if self.wrap~=e then -self.wrap=e -self:_updateLines(true) -end -end -function E:isWrapping() -return self.wrap -end -function E:setHorizontalAlign(e) -if e==nil then -e="left" -else -t(1,e,"string") -end -local t=e:lower() -if t~="left"and t~="center"and t~="right"then -error("Invalid horizontal alignment '"..e.."'",2) -end -if self.align~=t then -self.align=t -end -end -function E:setVerticalAlign(a) -if a==nil then -a="top" -else -t(1,a,"string") -end -local e=a:lower() -if e=="center"then -e="middle" -end -if e~="top"and e~="middle"and e~="bottom"then -error("Invalid vertical alignment '"..a.."'",2) -end -if self.verticalAlign~=e then -self.verticalAlign=e -end -end -function E:setSize(t,e) -s.setSize(self,t,e) -self:_updateLines(true) -end -function E:setBorder(e) -s.setBorder(self,e) -self:_updateLines(true) -end -function E:draw(u,w) -if not self.visible then -return -end -local s,h,d,l=self:getAbsoluteRect() -local r=self.bg or self.app.background or e.black -local f=self.fg or e.white -n(u,s,h,d,l,r,r) -z(u,s,h,d,l) -local e,t,o,t,a,i=self:_getInnerMetrics() -local t=s+e -local m=h+o -self:_updateLines(false) -local c=self._lines or{""} -local o=#c -if o==0 then -c={""} -o=1 -end -if a>0 and i>0 then -local e=math.min(o,i) -local n=1 -if o>e then -if self.verticalAlign=="bottom"then -n=o-e+1 -elseif self.verticalAlign=="middle"then -n=math.floor((o-e)/2)+1 -end -end -local o=0 -if i>e then -if self.verticalAlign=="bottom"then -o=i-e -elseif self.verticalAlign=="middle"then -o=math.floor((i-e)/2) -end -end -local i=m+o -for e=0,e-1 do -local e=c[n+e]or"" -if#e>a then -e=e:sub(1,a) -end -local o=t -if self.align=="center"then -o=t+math.floor((a-#e)/2) -elseif self.align=="right"then -o=t+a-#e -end -if ot+a then -o=t+a-#e -end -if#e>0 then -u.text(o,i,e,f,r) -end -i=i+1 -end -end -if self.border then -p(w,s,h,d,l,self.border,r) -end -end -function _:new(n,t) -t=t or{} -local o=o(t)or{} -local i="Option" -if t and t.label~=nil then -i=tostring(t.label) -end -o.focusable=true -o.height=o.height or 1 -o.width=o.width or math.max(4,#i+4) -local a=setmetatable({},_) -a:_init_base(n,o) -a.focusable=true -a.label=i -a.allowIndeterminate=not not(t and t.allowIndeterminate) -a.indeterminate=not not(t and t.indeterminate) -if not a.allowIndeterminate then -a.indeterminate=false -end -a.checked=not a.indeterminate and not not(t and t.checked) -a.onChange=t and t.onChange or nil -a.focusBg=t and t.focusBg or e.lightGray -a.focusFg=t and t.focusFg or e.black -return a -end -function _:_notifyChange() -if self.onChange then -self.onChange(self,self.checked,self.indeterminate) -end -end -function _:_setState(t,e,o) -t=not not t -e=not not e -if e then -t=false -end -if not self.allowIndeterminate then -e=false -end -local a=(self.checked~=t)or(self.indeterminate~=e) -if not a then -return false -end -self.checked=t -self.indeterminate=e -if not o then -self:_notifyChange() -end -return true -end -function _:setLabel(e) -t(1,e,"string") -self.label=e -end -function _:setOnChange(e) -if e~=nil then -t(1,e,"function") -end -self.onChange=e -end -function _:setAllowIndeterminate(e) -e=not not e -if self.allowIndeterminate==e then -return -end -self.allowIndeterminate=e -if not e and self.indeterminate then -self:_setState(self.checked,false,true) -self:_notifyChange() -end -end -function _:setChecked(e) -t(1,e,"boolean") -self:_setState(e,false,false) -end -function _:isChecked() -return self.checked -end -function _:setIndeterminate(e) -if not self.allowIndeterminate then -if e then -error("Indeterminate state is disabled for this CheckBox",2) -end -return -end -t(1,e,"boolean") -self:_setState(self.checked,e,false) -end -function _:isIndeterminate() -return self.indeterminate -end -function _:toggle() -self:_activate() -end -function _:_activate() -if self.allowIndeterminate then -if self.indeterminate then -self:_setState(false,false,false) -elseif self.checked then -self:_setState(false,true,false) -else -self:_setState(true,false,false) -end -else -if self.indeterminate then -self:_setState(true,false,false) -else -self:_setState(not self.checked,false,false) -end -end -end -function _:draw(d,a) -if not self.visible then -return -end -local h,s,t,i=self:getAbsoluteRect() -local o=self.bg or e.black -local e=self.fg or e.white -local o=o -local r=e -if self:isFocused()then -o=self.focusBg or o -r=self.focusFg or r -end -n(d,h,s,t,i,o,o) -z(d,h,s,t,i) -if self.border then -p(a,h,s,t,i,self.border,o) -end -if t<=0 or i<=0 then -return -end -local e=" " -if self.indeterminate then -e="-" -elseif self.checked then -e="x" -end -local a="["..e.."]" -local e={} -e[#e+1]=a -local a=#a -if t>a then -e[#e+1]=" " -a=a+1 -end -if t>a then -local o=self.label or"" -local t=t-a -if#o>t then -o=o:sub(1,t) -end -e[#e+1]=o -a=a+#o -end -local e=table.concat(e) -if#et then -e=e:sub(1,t) -end -local t=s+math.floor((i-1)/2) -d.text(h,t,e,r,o) -end -function _:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"then -local a,t,e=... -if self:containsPoint(t,e)then -self.app:setFocus(self) -self:_activate() -return true -end -elseif e=="monitor_touch"then -local a,e,t=... -if self:containsPoint(e,t)then -self.app:setFocus(self) -self:_activate() -return true -end -elseif e=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.space or e==a.enter then -self:_activate() -return true -end -end -return false -end -function g:new(i,t) -t=t or{} -local o=o(t)or{} -o.focusable=true -o.height=math.max(1,math.floor(o.height or 3)) -o.width=math.max(4,math.floor(o.width or 10)) -local a=setmetatable({},g) -a:_init_base(i,o) -a.focusable=true -local o=t.value -if o==nil then -o=t.on -end -a.value=not not o -a.labelOn=(t and t.labelOn)or"On" -a.labelOff=(t and t.labelOff)or"Off" -a.trackColorOn=(t and t.trackColorOn)or(t and t.onColor)or e.green -a.trackColorOff=(t and t.trackColorOff)or(t and t.offColor)or e.red -a.trackColorDisabled=(t and t.trackColorDisabled)or e.lightGray -a.thumbColor=(t and t.thumbColor)or e.white -a.knobColorDisabled=(t and t.knobColorDisabled)or e.lightGray -a.onLabelColor=t and t.onLabelColor or nil -a.offLabelColor=t and t.offLabelColor or nil -a.focusBg=t and t.focusBg or e.lightGray -a.focusFg=t and t.focusFg or e.black -a.focusOutline=t and t.focusOutline or a.focusFg or e.white -a.showLabel=not(t and t.showLabel==false) -a.disabled=not not(t and t.disabled) -a.onChange=t and t.onChange or nil -a.knobMargin=math.max(0,math.floor(t.knobMargin or 0)) -if t.knobWidth~=nil then -if type(t.knobWidth)~="number"then -error("Toggle knobWidth must be a number",3) -end -a.knobWidth=math.max(1,math.floor(t.knobWidth)) -else -a.knobWidth=nil -end -if t.transitionDuration~=nil then -if type(t.transitionDuration)~="number"then -error("Toggle transitionDuration must be a number",3) -end -a.transitionDuration=math.max(0,t.transitionDuration) -else -a.transitionDuration=.2 -end -local e=t.transitionEasing -if type(e)=="string"then -e=H[e]or H.easeInOutQuad -elseif type(e)~="function"then -e=H.easeInOutQuad -end -a.transitionEasing=e -a._thumbProgress=a.value and 1 or 0 -a._animationHandle=nil -return a -end -function g:_cancelAnimation() -if self._animationHandle then -self._animationHandle:cancel() -self._animationHandle=nil -end -end -function g:_setThumbProgress(e) -if e==nil then -e=self.value and 1 or 0 -end -if e<0 then -e=0 -elseif e>1 then -e=1 -end -self._thumbProgress=e -end -function g:_animateThumb(e) -e=math.max(0,math.min(1,e or(self.value and 1 or 0))) -if self.disabled then -self:_cancelAnimation() -self:_setThumbProgress(e) -return -end -if not self.app or self.transitionDuration<=0 then -self:_cancelAnimation() -self:_setThumbProgress(e) -return -end -local t=self._thumbProgress -if t==nil then -t=self.value and 1 or 0 -end -if math.abs(t-e)<1e-4 then -self:_cancelAnimation() -self:_setThumbProgress(e) -return -end -self:_cancelAnimation() -local a=e-t -local o=self.transitionEasing or H.easeInOutQuad -self._animationHandle=self.app:animate({ -duration=self.transitionDuration, -easing=o, -update=function(e) -local e=t+a*e -if e<0 then -e=0 -elseif e>1 then -e=1 -end -self._thumbProgress=e -end, -onComplete=function() -self._thumbProgress=e -self._animationHandle=nil -end, -onCancel=function() -self._animationHandle=nil -end -}) -end -function g:_emitChange() -if self.onChange then -self.onChange(self,self.value) -end -end -function g:setOnChange(e) -if e~=nil then -t(1,e,"function") -end -self.onChange=e -end -function g:setValue(e,t) -e=not not e -if self.value==e then -self:_animateThumb(e and 1 or 0) -return -end -self.value=e -self:_animateThumb(e and 1 or 0) -if not t then -self:_emitChange() -end -end -function g:isOn() -return self.value -end -function g:toggle() -if self.disabled then -return -end -self:setValue(not self.value) -end -function g:setLabels(e,a) -if e~=nil then -t(1,e,"string") -self.labelOn=e -end -if a~=nil then -t(2,a,"string") -self.labelOff=a -end -end -function g:setShowLabel(e) -self.showLabel=not not e -end -function g:setDisabled(e) -e=not not e -if self.disabled==e then -return -end -self.disabled=e -if e then -self:_cancelAnimation() -self:_setThumbProgress(self.value and 1 or 0) -else -self:_animateThumb(self.value and 1 or 0) -end -end -function g:isDisabled() -return self.disabled -end -function g:setColors(e,o,a,s,n,i,h) -if e~=nil then -t(1,e,"number") -self.trackColorOn=e -end -if o~=nil then -t(2,o,"number") -self.trackColorOff=o -end -if a~=nil then -t(3,a,"number") -self.thumbColor=a -end -if s~=nil then -t(4,s,"number") -self.onLabelColor=s -end -if n~=nil then -t(5,n,"number") -self.offLabelColor=n -end -if i~=nil then -t(6,i,"number") -self.trackColorDisabled=i -end -if h~=nil then -t(7,h,"number") -self.knobColorDisabled=h -end -end -function g:setTransition(a,e) -if a~=nil then -t(1,a,"number") -self.transitionDuration=math.max(0,a) -end -if e~=nil then -if type(e)=="string"then -local t=H[e] -if not t then -error("Unknown easing '"..e.."'",2) -end -self.transitionEasing=t -elseif type(e)=="function"then -self.transitionEasing=e -else -error("Toggle transition easing must be a function or easing name",2) -end -end -end -function g:setKnobStyle(a,e) -if a~=nil then -t(1,a,"number") -self.knobWidth=math.max(1,math.floor(a)) -end -if e~=nil then -t(2,e,"number") -self.knobMargin=math.max(0,math.floor(e)) -end -end -function g:draw(l,d) -if not self.visible then -return -end -local a,t,s,o=self:getAbsoluteRect() -local i=self.bg or e.black -local w=self.fg or e.white -n(l,a,t,s,o,i,i) -z(l,a,t,s,o) -if self.border then -p(d,a,t,s,o,self.border,i) -end -local h,r,s,r,o,i=V(self) -if o<=0 or i<=0 then -return -end -local a=a+h -local s=t+s -local t=o -local o=i -local h=self._thumbProgress -if h==nil then -h=self.value and 1 or 0 -end -if h<0 then -h=0 -elseif h>1 then -h=1 -end -local y=self.trackColorOn or e.green -local m=self.trackColorOff or e.red -local c=self.trackColorDisabled or m -local i=self.disabled and c or m -n(l,a,s,t,o,i,i) -local i=math.floor(t*h+.5) -if i>0 then -if i>t then -i=t -end -local e=self.disabled and c or y -n(l,a,s,i,o,e,e) -end -local r=self.knobMargin or 0 -if r<0 then -r=0 -end -if r*2>=t then -r=math.max(0,math.floor((t-1)/2)) -end -local u=math.max(1,t-r*2) -local i=self.knobWidth and math.max(1,math.min(math.floor(self.knobWidth),u)) -if not i then -i=math.max(1,math.floor(u/2)) -if u>=4 then -i=math.max(2,i) -end -end -local u=math.max(0,u-i) -local f=math.floor(u*h+.5) -if f>u then -f=u -end -local u=a+r+f -if u+i-1>a+t-1 then -u=a+t-i -elseif u0 then -local e=math.max(0,t-2) -if e>0 and#i>e then -i=i:sub(1,e) -end -local r=self.value and(self.onLabelColor or w)or(self.offLabelColor or w) -local n -if h>=.5 then -n=self.disabled and c or y -else -n=self.disabled and c or m -end -local o=s+math.floor((o-1)/2) -local e=a+math.floor((t-#i)/2) -if ea+t-1 then -e=a+t-#i -end -if#i>0 then -l.text(e,o,i,r,n) -end -end -if self:isFocused()then -local e=self.focusOutline or self.focusFg or e.white -if t>0 then -for t=0,t-1 do -d.pixel(a+t,s,e) -if o>1 then -d.pixel(a+t,s+o-1,e) -end -end -end -if o>0 then -for o=0,o-1 do -d.pixel(a,s+o,e) -if t>1 then -d.pixel(a+t-1,s+o,e) -end -end -end -end -if self.disabled then -local i=self.knobColorDisabled or e.lightGray -for e=0,t-1,2 do -local e=a+e -d.pixel(e,s,i) -if o>1 then -d.pixel(e,s+o-1,i) -end -end -end -end -function g:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"or e=="monitor_touch"then -local a,t,e=... -if self:containsPoint(t,e)then -if self.disabled then -return true -end -self.app:setFocus(self) -self:toggle() -return true -end -elseif e=="key"then -if not self:isFocused()or self.disabled then -return false -end -local e=... -if e==a.space or e==a.enter then -self:toggle() -return true -end -elseif e=="char"then -if not self:isFocused()or self.disabled then -return false -end -local e=... -if e==" "then -self:toggle() -return true -end -end -return false -end -function q:new(n,a) -a=a or{} -local o=o(a)or{} -local i="Option" -if a and a.label~=nil then -i=tostring(a.label) -end -o.focusable=true -o.height=o.height or 1 -o.width=o.width or math.max(4,#i+4) -local t=setmetatable({},q) -t:_init_base(n,o) -t.focusable=true -t.label=i -if a and a.value~=nil then -t.value=a.value -else -t.value=i -end -if a and a.group~=nil then -if type(a.group)~="string"then -error("RadioButton group must be a string",2) -end -t.group=a.group -else -t.group=nil -end -t.selected=not not(a and a.selected) -t.onChange=a and a.onChange or nil -t.focusBg=a and a.focusBg or e.lightGray -t.focusFg=a and a.focusFg or e.black -t._registeredGroup=nil -t._dotChar=se -if t.group and t.app then -t:_registerWithGroup() -if t.selected then -t.app:_selectRadioInGroup(t.group,t,true) -else -local e=t.app._radioGroups -if e then -local e=e[t.group] -if e and e.selected and e.selected~=t then -t.selected=false -end -end -end -end -t:_applySelection(t.selected,true) -return t -end -function q:_registerWithGroup() -if self.app and self.group then -self.app:_registerRadioButton(self) -end -end -function q:_unregisterFromGroup() -if self.app and self._registeredGroup then -self.app:_unregisterRadioButton(self) -end -end -function q:_notifyChange() -if self.onChange then -self.onChange(self,self.selected,self.value) -end -end -function q:_applySelection(e,t) -e=not not e -if self.selected==e then -return -end -self.selected=e -if not t then -self:_notifyChange() -end -end -function q:setLabel(e) -t(1,e,"string") -self.label=e -end -function q:setValue(e) -self.value=e -end -function q:getValue() -return self.value -end -function q:setGroup(e) -t(1,e,"string","nil") -if self.group==e then -return -end -self:_unregisterFromGroup() -self.group=e -if self.group then -self:_registerWithGroup() -end -end -function q:getGroup() -return self.group -end -function q:setOnChange(e) -if e~=nil then -t(1,e,"function") -end -self.onChange=e -end -function q:setSelected(e) -e=not not e -if self.group and self.app then -if e then -self.app:_selectRadioInGroup(self.group,self,false) -else -local e=self.app._radioGroups -local e=e and e[self.group] -if e and e.selected==self then -self.app:_selectRadioInGroup(self.group,nil,false) -else -self:_applySelection(false,false) -end -end -return -end -if self.selected==e then -return -end -self:_applySelection(e,false) -end -function q:isSelected() -return self.selected -end -function q:_activate() -if self.group then -if not self.selected then -self:setSelected(true) -end -else -self:setSelected(not self.selected) -end -end -function q:draw(h,d) -if not self.visible then -return -end -local o,i,t,s=self:getAbsoluteRect() -local a=self.bg or e.black -local e=self.fg or e.white -local a=a -local r=e -if self:isFocused()then -a=self.focusBg or a -r=self.focusFg or r -end -n(h,o,i,t,s,a,a) -z(h,o,i,t,s) -if self.border then -p(d,o,i,t,s,self.border,a) -end -local n=i+math.floor((s-1)/2) -local e=self.selected and(self._dotChar or"*")or" " -local e="("..e..")" -local i=self.label or"" -local e=e -if#i>0 then -e=e.." "..i -end -if#e>t then -e=e:sub(1,t) -elseif#e0 then -h.text(o,n,e,r,a) -end -end -function q:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"then -local a,e,t=... -if self:containsPoint(e,t)then -self.app:setFocus(self) -self:_activate() -return true -end -elseif e=="monitor_touch"then -local a,e,t=... -if self:containsPoint(e,t)then -self.app:setFocus(self) -self:_activate() -return true -end -elseif e=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.space or e==a.enter then -self:_activate() -return true -end -end -return false -end -function k:new(i,a) -a=a or{} -local o=o(a)or{} -o.focusable=false -o.height=o.height or 1 -o.width=o.width or 12 -local t=setmetatable({},k) -t:_init_base(i,o) -t.focusable=false -t.min=type(a.min)=="number"and a.min or 0 -t.max=type(a.max)=="number"and a.max or 1 -if t.max<=t.min then -t.max=t.min+1 -end -local o=a.value -if type(o)~="number"then -o=t.min -end -t.value=t:_clampValue(o) -t.trackColor=(a.trackColor)or e.gray -t.fillColor=(a.fillColor)or e.lightBlue -t.textColor=(a.textColor)or t.fg or e.white -t.label=a.label or nil -t.showPercent=not not a.showPercent -t.indeterminate=not not a.indeterminate -t.indeterminateSpeed=math.max(.1,a.indeterminateSpeed or 1.2) -t._indeterminateProgress=0 -t._animationHandle=nil -if not t.border then -t.border=R(true) -end -if t.indeterminate then -t:_startIndeterminateAnimation() -end -return t -end -function k:_clampValue(e) -if type(e)~="number"then -e=self.min -end -if eself.max then -return self.max -end -return e -end -function k:_stopIndeterminateAnimation() -if self._animationHandle then -self._animationHandle:cancel() -self._animationHandle=nil -end -self._indeterminateProgress=0 -end -function k:_startIndeterminateAnimation() -if not self.app or self._animationHandle then -return -end -local e=self.indeterminateSpeed or 1.2 -self._animationHandle=self.app:animate({ -duration=e, -easing=H.linear, -update=function(t,e) -self._indeterminateProgress=e or 0 -end, -onComplete=function() -self._animationHandle=nil -if self.indeterminate then -self:_startIndeterminateAnimation() -else -self._indeterminateProgress=0 -end -end, -onCancel=function() -self._animationHandle=nil -end -}) -end -function k:setRange(a,e) -t(1,a,"number") -t(2,e,"number") -if e<=a then -error("ProgressBar max must be greater than min",2) -end -self.min=a -self.max=e -self.value=self:_clampValue(self.value) -end -function k:getRange() -return self.min,self.max -end -function k:setValue(e) -if self.indeterminate then -return -end -t(1,e,"number") -e=self:_clampValue(e) -if e~=self.value then -self.value=e -end -end -function k:getValue() -return self.value -end -function k:getPercent() -local e=self.max-self.min -if e<=0 then -return 0 -end -return(self.value-self.min)/e -end -function k:setIndeterminate(e) -e=not not e -if self.indeterminate==e then -return -end -self.indeterminate=e -if e then -self:_startIndeterminateAnimation() -else -self:_stopIndeterminateAnimation() -end -end -function k:isIndeterminate() -return self.indeterminate -end -function k:setLabel(e) -if e~=nil then -t(1,e,"string") -end -self.label=e -end -function k:setShowPercent(e) -self.showPercent=not not e -end -function k:setColors(e,a,o) -if e~=nil then -t(1,e,"number") -self.trackColor=e -end -if a~=nil then -t(2,a,"number") -self.fillColor=a -end -if o~=nil then -t(3,o,"number") -self.textColor=o -end -end -function k:draw(o,a) -if not self.visible then -return -end -local t,s,l,r=self:getAbsoluteRect() -local i=self.trackColor or(self.bg or e.gray) -local h=self.fillColor or e.lightBlue -local f=self.textColor or(self.fg or e.white) -n(o,t,s,l,r,i,i) -z(o,t,s,l,r) -if self.border then -p(a,t,s,l,r,self.border,i) -end -local e=self.border -local c=(e and e.left)and 1 or 0 -local m=(e and e.right)and 1 or 0 -local u=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local a=t+c -local d=s+u -local t=math.max(0,l-c-m) -local s=math.max(0,r-u-e) -if t<=0 or s<=0 then -return -end -n(o,a,d,t,s,i,i) -local l=0 -local u=0 -local r=0 -if self.indeterminate then -r=math.max(1,math.floor(t/3)) -if r>t then -r=t -end -local t=t-r -local e=self._indeterminateProgress or 0 -if e<0 then e=0 end -if e>1 then e=1 end -u=math.floor(t*e+.5) -n(o,a+u,d,r,s,h,h) -else -local e=self:getPercent() -if e<0 then e=0 end -if e>1 then e=1 end -l=math.floor(t*e+.5) -if l>0 then -n(o,a,d,l,s,h,h) -end -end -local e=self.label or"" -if self.showPercent and not self.indeterminate then -local t=math.floor(self:getPercent()*100+.5) -local t=tostring(t).."%" -if e~=""then -e=e.." "..t -else -e=t -end -end -if e~=""and s>0 then -if#e>t then -e=e:sub(1,t) -end -local s=d+math.floor((s-1)/2) -local n=a+math.floor((t-#e)/2) -if n=u and eo then -return -end -if i>a then -return -end -local e=e -local o=0 -if i<1 then -o=1-i -if o>=#e then -return -end -e=e:sub(o+1) -i=1 -end -local a=a-i+1 -if a<=0 then -return -end -if#e>a then -e=e:sub(1,a) -end -r.text(s+i-1,h+n-1,e,c or u,d or l) -end -t.pixel=function(e,n,i) -local t=math.floor(e or 1) -local e=math.floor(n or 1) -if t<1 or t>a or e<1 or e>o then -return -end -d.pixel(s+t-1,h+e-1,i or u) -end -self._ctx=t -self.onDraw(self,t) -end -if self.border then -p(d,s,h,a,o,self.border,self.bg or self.app.background or e.black) -end -end -function u:new(i,a) -a=a or{} -local o=o(a)or{} -o.focusable=true -o.width=o.width or 12 -if o.height==nil then -o.height=a.showValue and 2 or 1 -end -local t=setmetatable({},u) -t:_init_base(i,o) -t.focusable=true -t.min=type(a.min)=="number"and a.min or 0 -t.max=type(a.max)=="number"and a.max or 1 -if t.max<=t.min then -t.max=t.min+1 -end -if a.step~=nil then -if type(a.step)~="number"then -error("Slider step must be a number",2) -end -t.step=a.step>0 and a.step or 0 -else -t.step=0 -end -t.range=not not a.range -t.showValue=not not a.showValue -t.trackColor=a.trackColor or e.gray -t.fillColor=a.fillColor or e.lightBlue -t.handleColor=a.handleColor or e.white -if a.formatValue~=nil then -if type(a.formatValue)~="function"then -error("Slider formatValue must be a function",2) -end -t.formatValue=a.formatValue -else -t.formatValue=nil -end -t.onChange=a.onChange -t._activeHandle=nil -t._focusedHandle=t.range and"lower"or"single" -t._dragging=false -if t.range then -local o -local e -if type(a.value)=="table"then -o=a.value[1] -e=a.value[2] -end -if type(a.startValue)=="number"then -o=a.startValue -end -if type(a.endValue)=="number"then -e=a.endValue -end -if type(o)~="number"then -o=t.min -end -if type(e)~="number"then -e=t.max -end -if o>e then -o,e=e,o -end -t.lowerValue=t:_applyStep(o) -t.upperValue=t:_applyStep(e) -if t.lowerValue>t.upperValue then -t.lowerValue,t.upperValue=t.upperValue,t.lowerValue -end -else -local e=a.value -if type(e)~="number"then -e=t.min -end -t.value=t:_applyStep(e) -end -if not t.border then -t.border=R(true) -end -return t -end -function u:_clampValue(e) -if type(e)~="number"then -e=self.min -end -if eself.max then -return self.max -end -return e -end -function u:_applyStep(e) -e=self:_clampValue(e) -local t=self.step or 0 -if t>0 then -local a=(e-self.min)/t -e=self.min+math.floor(a+.5)*t -e=self:_clampValue(e) -end -return e -end -function u:_getInnerMetrics() -local e=self.border -local a=(e and e.left)and 1 or 0 -local o=(e and e.right)and 1 or 0 -local t=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local i,n=self:getAbsoluteRect() -local s=math.max(0,self.width-a-o) -local o=math.max(0,self.height-t-e) -local h=i+a -local i=n+t -return h,i,s,o,a,t,e -end -function u:_valueToPosition(o,a) -if a<=1 then -return 0 -end -local t=self.max-self.min -local e=0 -if t>0 then -e=(o-self.min)/t -end -if e<0 then -e=0 -elseif e>1 then -e=1 -end -return math.floor(e*(a-1)+.5) -end -function u:_positionToValue(e,t) -if t<=1 then -return self.min -end -if e<0 then -e=0 -elseif e>t-1 then -e=t-1 -end -local e=e/(t-1) -local e=self.min+(self.max-self.min)*e -return self:_applyStep(e) -end -function u:_notifyChange() -if not self.onChange then -return -end -if self.range then -self.onChange(self,self.lowerValue,self.upperValue) -else -self.onChange(self,self.value) -end -end -function u:setOnChange(e) -if e~=nil then -t(1,e,"function") -end -self.onChange=e -end -function u:_setSingleValue(e,t) -e=self:_applyStep(e) -if self.value~=e then -self.value=e -if not t then -self:_notifyChange() -end -return true -end -return false -end -function u:setValue(e) -if self.range then -return -end -t(1,e,"number") -self:_setSingleValue(e,false) -end -function u:getValue() -return self.value -end -function u:_setLowerValue(e,t) -e=self:_applyStep(e) -if eself.upperValue then -e=self.upperValue -end -if self.lowerValue~=e then -self.lowerValue=e -if not t then -self:_notifyChange() -end -return true -end -return false -end -function u:_setUpperValue(e,t) -e=self:_applyStep(e) -if e>self.max then -e=self.max -end -if ea then -e,a=a,e -end -local t=false -t=self:_setLowerValue(e,true)or t -t=self:_setUpperValue(a,true)or t -if t and not o then -self:_notifyChange() -end -end -function u:getRangeValues() -return self.lowerValue,self.upperValue -end -function u:setRangeLimits(e,a) -t(1,e,"number") -t(2,a,"number") -if a<=e then -error("Slider max must be greater than min",2) -end -self.min=e -self.max=a -if self.range then -local e=false -e=self:_setLowerValue(self.lowerValue,true)or e -e=self:_setUpperValue(self.upperValue,true)or e -if e then -self:_notifyChange() -end -else -if self:_setSingleValue(self.value,true)then -self:_notifyChange() -end -end -end -function u:setStep(e) -if e==nil then -e=0 -else -t(1,e,"number") -end -if e<=0 then -self.step=0 -else -self.step=e -end -if self.range then -local e=false -e=self:_setLowerValue(self.lowerValue,true)or e -e=self:_setUpperValue(self.upperValue,true)or e -if e then -self:_notifyChange() -end -else -if self:_setSingleValue(self.value,true)then -self:_notifyChange() -end -end -end -function u:setShowValue(e) -self.showValue=not not e -end -function u:setColors(a,e,o) -if a~=nil then -t(1,a,"number") -self.trackColor=a -end -if e~=nil then -t(2,e,"number") -self.fillColor=e -end -if o~=nil then -t(3,o,"number") -self.handleColor=o -end -end -function u:_formatNumber(o) -local a=self.step or 0 -local e -if a>0 then -local t=0 -local a=a -while a<1 and t<4 do -a=a*10 -t=t+1 -end -local t="%0."..tostring(t).."f" -e=t:format(o) -else -e=string.format("%0.2f",o) -end -if e:find(".",1,true)then -e=e:gsub("0+$","") -e=e:gsub("%.$","") -end -return e -end -function u:_formatDisplayValue() -if self.formatValue then -local t,e -if self.range then -t,e=pcall(self.formatValue,self,self.lowerValue,self.upperValue) -else -t,e=pcall(self.formatValue,self,self.value) -end -if t and type(e)=="string"then -return e -end -end -if self.range then -return self:_formatNumber(self.lowerValue).." - "..self:_formatNumber(self.upperValue) -end -return self:_formatNumber(self.value) -end -function u:_getStepForNudge(t) -local e=self.step or 0 -if e<=0 then -e=(self.max-self.min)/math.max(1,(self.range and 20 or 40)) -end -if e<=0 then -e=1 -end -if t and t>1 then -e=e*t -end -return e -end -function u:_positionFromPoint(e) -local a,o,t=self:_getInnerMetrics() -if t<=0 then -return nil,t -end -local e=math.floor(e-a) -if e<0 then -e=0 -elseif e>t-1 then -e=t-1 -end -return e,t -end -function u:_beginInteraction(e) -local t,a=self:_positionFromPoint(e) -if not t then -return false -end -if self.range then -local n=self:_valueToPosition(self.lowerValue,a) -local s=self:_valueToPosition(self.upperValue,a) -local e=self._focusedHandle or"lower" -local i=math.abs(t-n) -local o=math.abs(t-s) -if i==o then -if t>s then -e="upper" -elseif t=0 and 1 or-1 -local e=math.abs(e) -local e=self:_getStepForNudge(e) -e=e*t -if self.range then -local t=self._focusedHandle or"lower" -if t=="upper"then -self:_setUpperValue(self.upperValue+e) -else -self:_setLowerValue(self.lowerValue+e) -end -else -self:_setSingleValue(self.value+e) -end -end -function u:onFocusChanged(e) -if e then -if self.range then -if self._focusedHandle~="lower"and self._focusedHandle~="upper"then -self._focusedHandle="lower" -end -else -self._focusedHandle="single" -end -end -end -function u:draw(a,f) -if not self.visible then -return -end -local l,d,h,r=self:getAbsoluteRect() -local s=self.bg or self.app.background or e.black -n(a,l,d,h,r,s,s) -z(a,l,d,h,r) -local i,c,t,u=self:_getInnerMetrics() -if t<=0 or u<=0 then -if self.border then -p(f,l,d,h,r,self.border,s) -end -return -end -local o -local m=nil -if self.showValue and u>=2 then -m=c -o=c+u-1 -else -o=c+math.floor((u-1)/2) -end -n(a,i,o,t,1,self.trackColor,self.trackColor) -local u -if self:isFocused()then -u=self._activeHandle or self._focusedHandle -end -local function c(n,s) -if n<0 or n>=t then -return -end -local t=self.handleColor or e.white -if u and s==u then -t=self.fg or e.white -end -a.text(i+n,o," ",t,t) -end -if self.range then -local e=self:_valueToPosition(self.lowerValue,t) -local t=self:_valueToPosition(self.upperValue,t) -if t0 then -n(a,i+e,o,s,1,self.fillColor,self.fillColor) -end -c(e,"lower") -c(t,"upper") -else -local t=self:_valueToPosition(self.value,t) -local e=t+1 -if e>0 then -n(a,i,o,e,1,self.fillColor,self.fillColor) -end -c(t,"single") -end -if self.showValue and m then -local o=self:_formatDisplayValue() -if o and o~=""then -if#o>t then -o=o:sub(1,t) -end -local t=i+math.floor((t-#o)/2) -if t0 then -self:_nudgeValue(1) -elseif e<0 then -self:_nudgeValue(-1) -end -return true -end -elseif t=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.left or e==a.down then -self:_nudgeValue(-1) -return true -elseif e==a.right or e==a.up then -self:_nudgeValue(1) -return true -elseif e==a.home then -if self.range then -self:setRangeValues(self.min,self.upperValue) -self._focusedHandle="lower" -else -self:setValue(self.min) -end -return true -elseif e==a["end"]then -if self.range then -self:setRangeValues(self.lowerValue,self.max) -self._focusedHandle="upper" -else -self:setValue(self.max) -end -return true -elseif e==a.tab then -if self.range then -self:_switchFocusedHandle() -return true -end -elseif e==a.pageUp then -self:_nudgeValue(-5) -return true -elseif e==a.pageDown then -self:_nudgeValue(5) -return true -end -elseif t=="key_up"then -if self._activeHandle then -self:_endInteraction() -end -end -return false -end -function l:new(i,a) -a=a or{} -local o=o(a)or{} -o.focusable=true -o.width=math.max(8,math.floor(o.width or 24)) -o.height=math.max(3,math.floor(o.height or 7)) -local t=setmetatable({},l) -t:_init_base(i,o) -t.focusable=true -t.headerBg=a.headerBg or t.bg or e.gray -t.headerFg=a.headerFg or t.fg or e.white -t.rowBg=a.rowBg or t.bg or e.black -t.rowFg=a.rowFg or t.fg or e.white -t.highlightBg=a.highlightBg or e.lightBlue -t.highlightFg=a.highlightFg or e.black -t.zebra=not not a.zebra -t.zebraBg=a.zebraBg or e.gray -t.placeholder=a.placeholder or"No rows" -t.allowRowSelection=a.selectable~=false -t.sortColumn=a.sortColumn -t.sortDirection=(a.sortDirection=="desc")and"desc"or"asc" -t.onSelect=a.onSelect or nil -t.onSort=a.onSort or nil -t.columns={} -t.data={} -t._rows={} -t._columnMetrics={} -t._totalColumnWidth=0 -t.scrollOffset=1 -t.selectedIndex=0 -t.typeSearchTimeout=a.typeSearchTimeout or .75 -t._typeSearch={buffer="",lastTime=0} -t.columns=t:_normalizeColumns(a.columns or{}) -t:_recomputeColumnMetrics() -t:setData(a.data or{}) -if a.selectedIndex then -t:setSelectedIndex(a.selectedIndex,true) -end -if t.sortColumn then -t:setSort(t.sortColumn,t.sortDirection,true) -end -if not t.border then -t.border=R(true) -end -t.scrollbar=L(a.scrollbar,t.bg or e.black,t.fg or e.white) -t:_ensureSelectionVisible() -return t -end -function l:_normalizeColumns(e) -local a={} -if type(e)=="table"then -for t=1,#e do -local e=e[t] -if type(e)~="table"then -error("Table column configuration must be a table",3) -end -local t=e.id or e.key or e.title -if type(t)~="string"or t==""then -error("Table column is missing an id",3) -end -local e={ -id=t, -title=e.title or t, -key=e.key or t, -accessor=e.accessor, -format=e.format, -comparator=e.comparator, -color=e.color, -align=e.align or"left", -sortable=e.sortable~=false, -width=math.max(3,math.floor(e.width or 10)) -} -a[#a+1]=e -end -end -return a -end -function l:_recomputeColumnMetrics() -self._columnMetrics={} -local t=0 -for a=1,#self.columns do -local e=self.columns[a] -e.width=math.max(3,math.floor(e.width or 10)) -self._columnMetrics[a]={ -offset=t, -width=e.width -} -t=t+e.width -end -self._totalColumnWidth=t -end -function l:_ensureColumnsForData() -if#self.columns>0 then -return -end -local e=self.data[1] -if type(e)=="table"then -local t={} -for e,a in pairs(e)do -if type(e)=="string"then -t[#t+1]={ -id=e, -title=e, -key=e, -align="left", -sortable=true, -width=math.max(3,math.min(20,tostring(a or""):len()+2)) -} -end -end -table.sort(t,function(e,t) -return e.id4 then -e[#e+1]="..." -break -end -e[#e+1]=tostring(t).."="..tostring(o) -end -table.sort(e,function(e,t) -return e#self._rows then -self.selectedIndex=1 -end -else -self.selectedIndex=0 -end -self:_clampScroll() -self:_ensureSelectionVisible() -end -function l:_getColumnById(e) -if not e then -return nil -end -for t=1,#self.columns do -if self.columns[t].id==e then -return self.columns[t] -end -end -return nil -end -function l:_applySort(e,a,u) -local t=self:_getColumnById(e) -if not t or t.sortable==false then -return -end -self.sortColumn=t.id -self.sortDirection=a=="desc"and"desc"or"asc" -local d=self.sortDirection=="desc" -local r=t.comparator -table.sort(self._rows,function(h,s) -local n=self.data[h] -local i=self.data[s] -local o=l._resolveColumnValue(t,n) -local a=l._resolveColumnValue(t,i) -local e=0 -if r then -local a,t=pcall(r,o,a,n,i,t) -if a and type(t)=="number"then -e=t -end -end -if e==0 then -if type(o)=="number"and type(a)=="number"then -if oa then -e=1 -else -e=0 -end -else -local t=tostring(o or""):lower() -local a=tostring(a or""):lower() -if ta then -e=1 -else -e=0 -end -end -end -if e==0 then -return h0 -end -return e<0 -end) -if not u and self.onSort then -self.onSort(self,self.sortColumn,self.sortDirection) -end -self:_ensureSelectionVisible() -end -function l:setSort(e,a,t) -if e==nil then -self.sortColumn=nil -self.sortDirection="asc" -self:_refreshRows() -return -end -self:_applySort(e,a or self.sortDirection,t) -end -function l:getSort() -return self.sortColumn,self.sortDirection -end -function l:setOnSort(e) -if e~=nil then -t(1,e,"function") -end -self.onSort=e -end -function l:setScrollbar(t) -self.scrollbar=L(t,self.bg or e.black,self.fg or e.white) -self:_clampScroll() -end -function l:setOnSelect(e) -if e~=nil then -t(1,e,"function") -end -self.onSelect=e -end -function l:getSelectedIndex() -return self.selectedIndex -end -function l:getSelectedRow() -if self.selectedIndex>=1 and self.selectedIndex<=#self._rows then -return self.data[self._rows[self.selectedIndex]] -end -return nil -end -function l:setSelectedIndex(e,a) -if not self.allowRowSelection then -self.selectedIndex=0 -return -end -if#self._rows==0 then -self.selectedIndex=0 -self.scrollOffset=1 -return -end -t(1,e,"number") -e=math.floor(e) -if e<1 then -e=1 -elseif e>#self._rows then -e=#self._rows -end -local t=e~=self.selectedIndex -self.selectedIndex=e -self:_ensureSelectionVisible() -if t and not a then -self:_notifySelect() -end -end -function l:_notifySelect() -if self.onSelect then -self.onSelect(self,self:getSelectedRow(),self.selectedIndex) -end -end -function l:_getInnerMetrics() -local e=self.border -local a=(e and e.left)and 1 or 0 -local i=(e and e.right)and 1 or 0 -local t=(e and e.top)and 1 or 0 -local n=(e and e.bottom)and 1 or 0 -local e,o=self:getAbsoluteRect() -local i=math.max(0,self.width-a-i) -local n=math.max(0,self.height-t-n) -local a=e+a -local e=o+t -return a,e,i,n -end -function l:_computeLayoutMetrics() -local i,s,t,a=self:_getInnerMetrics() -if t<=0 or a<=0 then -return{ -innerX=i, -innerY=s, -innerWidth=t, -innerHeight=a, -headerHeight=0, -rowsHeight=0, -contentWidth=0, -scrollbarWidth=0, -scrollbarStyle=nil, -scrollbarX=i -} -end -local h=a>=1 and 1 or 0 -local r=math.max(0,a-h) -local e,n=F(self.scrollbar,#self._rows,r,t) -if e>0 and t-e<1 then -e=math.max(0,t-1) -if e<=0 then -e=0 -n=nil -end -end -local o=t-e -if o<1 then -o=t -e=0 -n=nil -end -return{ -innerX=i, -innerY=s, -innerWidth=t, -innerHeight=a, -headerHeight=h, -rowsHeight=r, -contentWidth=o, -scrollbarWidth=e, -scrollbarStyle=n, -scrollbarX=i+o -} -end -function l:_getRowsVisible() -local e=self:_computeLayoutMetrics() -if e.innerWidth<=0 or e.innerHeight<=0 or e.contentWidth<=0 then -return 0 -end -local e=e.rowsHeight -if e<0 then -e=0 -end -return e -end -function l:_clampScroll() -local e=self:_getRowsVisible() -if#self._rows==0 or e<=0 then -self.scrollOffset=1 -return -end -local e=math.max(1,#self._rows-e+1) -if self.scrollOffset<1 then -self.scrollOffset=1 -elseif self.scrollOffset>e then -self.scrollOffset=e -end -end -function l:_ensureSelectionVisible() -self:_clampScroll() -if not self.allowRowSelection or self.selectedIndex<1 or self.selectedIndex>#self._rows then -return -end -local e=self:_getRowsVisible() -if e<=0 then -return -end -if self.selectedIndexself.scrollOffset+e-1 then -self.scrollOffset=self.selectedIndex-e+1 -end -self:_clampScroll() -end -function l:_rowFromPoint(a,t) -if not self:containsPoint(a,t)then -return nil -end -local e=self:_computeLayoutMetrics() -if e.innerWidth<=0 or e.innerHeight<=0 or e.contentWidth<=0 then -return nil -end -local o=e.innerY+e.headerHeight -if t=o+e.rowsHeight then -return nil -end -if a=e.innerX+e.contentWidth then -return nil -end -local t=t-o -if t<0 or t>=e.rowsHeight then -return nil -end -local e=self.scrollOffset+t -if e<1 or e>#self._rows then -return nil -end -return e -end -function l:_columnFromPoint(a,t) -if not self:containsPoint(a,t)then -return nil -end -local e=self:_computeLayoutMetrics() -if e.innerWidth<=0 or e.innerHeight<=0 or e.contentWidth<=0 then -return nil -end -if e.headerHeight<=0 or t~=e.innerY then -return nil -end -if a=e.innerX+e.contentWidth then -return nil -end -local n=e.contentWidth -local t=e.innerX -for o=1,#self.columns do -local i=math.max(1,math.min(self.columns[o].width,n)) -if o==#self.columns then -i=e.innerX+e.contentWidth-t -end -if i<=0 then -break -end -if a>=t and a0 then -a.text(i,d,string.rep(" ",o),w,w) -local n=i -local h=o -for e=1,#self.columns do -local s=self.columns[e] -local t=math.max(1,math.min(s.width,h)) -if e==#self.columns then -t=math.max(1,h) -end -if t<=0 then -break -end -local e=s.title or s.id -local r="" -if self.sortColumn==s.id then -r=self.sortDirection=="desc"and"v"or"^" -end -if r~=""and t>=2 then -if#e>=t then -e=e:sub(1,t-1) -end -e=e..r -elseif t>#e then -e=e..string.rep(" ",t-#e) -else -e=e:sub(1,t) -end -a.text(n,d,e,q,w) -n=n+t -h=o-(n-i) -if h<=0 then -break -end -end -end -local v=d+v -local h=j -local w=self.rowBg or s -local b=self.rowFg or b -if h<=0 then -if y>0 then -local e=(r and r.background)or s -n(a,t.scrollbarX,d,y,g,e,e) -end -if self.border then -p(k,f,m,u,c,self.border,s) -end -return -end -if#self._rows==0 then -for e=0,h-1 do -local e=v+e -a.text(i,e,string.rep(" ",o),b,w) -end -if self.placeholder and self.placeholder~=""then -local t=self.placeholder -if#t>o then -t=t:sub(1,o) -end -local n=h>0 and math.min(h-1,math.floor(h/2))or 0 -local n=v+n -local o=i+math.floor((o-#t)/2) -if o#self._rows then -a.text(i,c,string.rep(" ",o),b,w) -else -local n=self._rows[t] -local u=self.data[n] -local m=self.allowRowSelection and t==self.selectedIndex -local h=w -local d=b -if m then -h=self.highlightBg or e.lightGray -d=self.highlightFg or e.black -elseif self.zebra and(t%2==0)then -h=self.zebraBg or h -end -local s=i -local r=o -for e=1,#self.columns do -local t=self.columns[e] -local n=math.max(1,math.min(t.width,r)) -if e==#self.columns then -n=math.max(1,r) -end -if n<=0 then -break -end -local e=l._resolveColumnValue(t,u) -e=self:_formatCell(t,u,e) -if#e>n then -e=e:sub(1,n) -end -if t.align=="right"then -if#e0 then -local e=(r and r.background)or s -n(a,t.scrollbarX,d,y,g,e,e) -if r and h>0 then -local e=math.max(0,self.scrollOffset-1) -C(a,t.scrollbarX,v,h,#self._rows,h,e,r) -end -end -if self.border then -p(k,f,m,u,c,self.border,s) -end -end -function l:_handleTypeSearch(t) -if not t or t==""then -return -end -local e=self._typeSearch -if not e then -e={buffer="",lastTime=0} -self._typeSearch=e -end -local a=I.clock() -local o=self.typeSearchTimeout or .75 -if a-(e.lastTime or 0)>o then -e.buffer="" -end -e.buffer=(e.buffer or"")..t:lower() -e.lastTime=a -self:_searchForPrefix(e.buffer) -end -function l:_searchForPrefix(e) -if not e or e==""then -return -end -if#self._rows==0 then -return -end -local t=self.selectedIndex>=1 and self.selectedIndex or 0 -for a=1,#self._rows do -local t=((t+a-1)%#self._rows)+1 -local o=self.data[self._rows[t]] -local a=self.columns[1] -local a=l._resolveColumnValue(a,o) -local a=tostring(a or""):lower() -if a:sub(1,#e)==e then -self:setSelectedIndex(t) -return -end -end -end -function l:onFocusChanged(e) -if not e and self._typeSearch then -self._typeSearch.buffer="" -self._typeSearch.lastTime=0 -end -end -function l:handleEvent(o,...) -if not self.visible then -return false -end -local function i(a,t) -if not self:containsPoint(a,t)then -return false -end -self.app:setFocus(self) -local e=self:_computeLayoutMetrics() -if e.scrollbarStyle and e.scrollbarWidth>0 and e.rowsHeight>0 then -local i=e.scrollbarX -local o=e.innerY+e.headerHeight -if a>=i and a=o and t0 then -self:setSelectedIndex(math.max(1,(self.selectedIndex>0)and(self.selectedIndex-1)or 1)) -end -return true -elseif e==a.down then -if self.allowRowSelection and#self._rows>0 then -self:setSelectedIndex(math.min(#self._rows,(self.selectedIndex>0 and self.selectedIndex or 0)+1)) -end -return true -elseif e==a.home then -if self.allowRowSelection and#self._rows>0 then -self:setSelectedIndex(1) -else -self.scrollOffset=1 -end -return true -elseif e==a["end"]then -if self.allowRowSelection and#self._rows>0 then -self:setSelectedIndex(#self._rows) -else -self.scrollOffset=math.max(1,#self._rows-self:_getRowsVisible()+1) -self:_clampScroll() -end -return true -elseif e==a.pageUp then -local e=math.max(1,self:_getRowsVisible()-1) -self.scrollOffset=self.scrollOffset-e -self:_clampScroll() -if self.allowRowSelection and self.selectedIndex>0 then -self:setSelectedIndex(math.max(1,self.selectedIndex-e),true) -self:_notifySelect() -end -return true -elseif e==a.pageDown then -local e=math.max(1,self:_getRowsVisible()-1) -self.scrollOffset=self.scrollOffset+e -self:_clampScroll() -if self.allowRowSelection and self.selectedIndex>0 then -self:setSelectedIndex(math.min(#self._rows,self.selectedIndex+e),true) -self:_notifySelect() -end -return true -elseif e==a.enter then -if self.allowRowSelection then -self:_notifySelect() -end -return true -elseif e==a.space then -if self.allowRowSelection then -self:_notifySelect() -end -return true -end -end -return false -end -function c:new(i,a) -a=a or{} -local o=o(a)or{} -o.focusable=true -o.height=math.max(3,math.floor(o.height or 7)) -o.width=math.max(6,math.floor(o.width or 20)) -local t=setmetatable({},c) -t:_init_base(i,o) -t.focusable=true -t.highlightBg=(a and a.highlightBg)or e.lightGray -t.highlightFg=(a and a.highlightFg)or e.black -t.placeholder=(a and a.placeholder)or nil -t.indentWidth=math.max(1,math.floor((a and a.indentWidth)or 2)) -local o=(a and a.toggleSymbols)or{} -t.toggleSymbols={ -expanded=tostring(o.expanded or"-"), -collapsed=tostring(o.collapsed or"+"), -leaf=tostring(o.leaf or" ") -} -t.onSelect=a and a.onSelect or nil -t.onToggle=a and a.onToggle or nil -t.nodes={} -t._flatNodes={} -t.scrollOffset=1 -t.selectedNode=nil -t._selectedIndex=0 -t.typeSearchTimeout=(a and a.typeSearchTimeout)or .75 -t._typeSearch={buffer="",lastTime=0} -if not t.border then -t.border=R(true) -end -t.scrollbar=L(a and a.scrollbar,t.bg or e.black,t.fg or e.white) -t:setNodes((a and a.nodes)or{}) -return t -end -function c:setOnSelect(e) -if e~=nil then -t(1,e,"function") -end -self.onSelect=e -end -function c:setOnToggle(e) -if e~=nil then -t(1,e,"function") -end -self.onToggle=e -end -function c:setScrollbar(t) -self.scrollbar=L(t,self.bg or e.black,self.fg or e.white) -self:_ensureSelectionVisible() -end -function c:_copyNodes(e,i) -local a={} -if type(e)~="table"then -return a -end -for o=1,#e do -local e=e[o] -if e~=nil then -local t -if type(e)=="string"then -t={ -label=e, -data=nil, -expanded=false -} -elseif type(e)=="table"then -t={ -label=e.label and tostring(e.label)or string.format("Node %d",o), -data=e.data, -expanded=not not e.expanded -} -else -t={ -label=tostring(e), -data=nil, -expanded=false -} -end -t.parent=i -if e and type(e.children)=="table"and#e.children>0 then -t.children=self:_copyNodes(e.children,t) -if t.expanded==nil then -t.expanded=false -end -else -t.children={} -t.expanded=false -end -a[#a+1]=t -end -end -return a -end -function c:setNodes(e) -e=e or{} -t(1,e,"table") -local a=self.selectedNode -local t=self._selectedIndex -self.nodes=self:_copyNodes(e,nil) -self.scrollOffset=1 -self.selectedNode=nil -self._selectedIndex=0 -self:_rebuildFlatNodes() -local e=self.selectedNode -if a~=e or self._selectedIndex~=t then -self:_notifySelect() -end -end -function c:getSelectedNode() -return self.selectedNode -end -function c:setSelectedNode(e) -if e==nil then -if self.selectedNode~=nil then -self.selectedNode=nil -self._selectedIndex=0 -self:_notifySelect() -end -return -end -self:_selectNode(e,false) -end -function c:expandNode(e) -self:_toggleNode(e,true) -end -function c:collapseNode(e) -self:_toggleNode(e,false) -end -function c:toggleNode(e) -self:_toggleNode(e,nil) -end -function c:_rebuildFlatNodes() -local t={} -local function a(e,o) -for i=1,#e do -local e=e[i] -t[#t+1]={node=e,depth=o} -if e.expanded and e.children and#e.children>0 then -a(e.children,o+1) -end -end -end -a(self.nodes,0) -self._flatNodes=t -local e=self:_findVisibleIndex(self.selectedNode) -if e then -self._selectedIndex=e -elseif#t>0 then -self._selectedIndex=1 -self.selectedNode=t[1].node -else -self._selectedIndex=0 -self.selectedNode=nil -end -self:_ensureSelectionVisible() -end -function c:_findVisibleIndex(e) -if e==nil then -return nil -end -local t=self._flatNodes -for a=1,#t do -if t[a].node==e then -return a -end -end -return nil -end -function c:_getInnerMetrics() -local e=self.border -local t=(e and e.left)and 1 or 0 -local o=(e and e.right)and 1 or 0 -local a=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local n=math.max(0,self.width-t-o) -local i=math.max(0,self.height-a-e) -return t,o,a,e,n,i -end -function c:_getInnerHeight() -local t,t,t,t,t,e=self:_getInnerMetrics() -if e<1 then -e=1 -end -return e -end -function c:_computeLayoutMetrics() -local o,i=self:getAbsoluteRect() -local e,s,n,s,t,a=self:_getInnerMetrics() -local o=o+e -local s=i+n -if t<=0 or a<=0 then -return{ -innerX=o, -innerY=s, -innerWidth=t, -innerHeight=a, -contentWidth=0, -scrollbarWidth=0, -scrollbarStyle=nil, -scrollbarX=o -} -end -local e,n=F(self.scrollbar,#self._flatNodes,a,t) -if e>0 and t-e<1 then -e=math.max(0,t-1) -if e<=0 then -e=0 -n=nil -end -end -local i=t-e -if i<1 then -i=t -e=0 -n=nil -end -return{ -innerX=o, -innerY=s, -innerWidth=t, -innerHeight=a, -contentWidth=i, -scrollbarWidth=e, -scrollbarStyle=n, -scrollbarX=o+i -} -end -function c:_ensureSelectionVisible() -local e=#self._flatNodes -local t=self:_getInnerHeight() -if e==0 then -self.scrollOffset=1 -return -end -if self._selectedIndex<1 then -self._selectedIndex=1 -elseif self._selectedIndex>e then -self._selectedIndex=e -end -if self.scrollOffset<1 then -self.scrollOffset=1 -end -local e=math.max(1,e-t+1) -if self.scrollOffset>e then -self.scrollOffset=e -end -if self._selectedIndexself.scrollOffset+t-1 then -self.scrollOffset=self._selectedIndex-t+1 -if self.scrollOffset>e then -self.scrollOffset=e -end -end -end -function c:_setSelectedIndex(e,a) -local t=#self._flatNodes -if t==0 then -self.selectedNode=nil -self._selectedIndex=0 -self.scrollOffset=1 -if not a then -self:_notifySelect() -end -return -end -if e<1 then -e=1 -elseif e>t then -e=t -end -self._selectedIndex=e -self.selectedNode=self._flatNodes[e].node -self:_ensureSelectionVisible() -if not a then -self:_notifySelect() -end -end -function c:_selectNode(t,a) -if not t then -return -end -local e=t.parent -while e do -if not e.expanded then -e.expanded=true -end -e=e.parent -end -self:_rebuildFlatNodes() -local e=self:_findVisibleIndex(t) -if e then -self:_setSelectedIndex(e,a) -end -end -function c:_moveSelection(a) -if a==0 then -return -end -local t=#self._flatNodes -if t==0 then -return -end -local e=self._selectedIndex -if e<1 then -e=1 -end -e=e+a -if e<1 then -e=1 -elseif e>t then -e=t -end -self:_setSelectedIndex(e,false) -end -function c:_scrollBy(t) -if t==0 then -return -end -local e=#self._flatNodes -if e==0 then -self.scrollOffset=1 -return -end -local a=self:_getInnerHeight() -local e=math.max(1,e-a+1) -self.scrollOffset=math.min(e,math.max(1,self.scrollOffset+t)) -end -function c:_rowFromPoint(i,t) -if not self:containsPoint(i,t)then -return nil -end -local e=self:_computeLayoutMetrics() -if e.innerWidth<=0 or e.innerHeight<=0 or e.contentWidth<=0 then -return nil -end -local o=e.innerX -local a=e.innerY -if i=o+e.contentWidth then -return nil -end -if t=a+e.innerHeight then -return nil -end -local t=t-a -local t=self.scrollOffset+t -if t<1 or t>#self._flatNodes then -return nil -end -return t,o,e.contentWidth -end -function c:_toggleNode(e,a) -if not e or not e.children or#e.children==0 then -return false -end -local t -if a==nil then -t=not e.expanded -else -t=not not a -end -if e.expanded==t then -return false -end -e.expanded=t -self:_rebuildFlatNodes() -if self.onToggle then -self.onToggle(self,e,t) -end -return true -end -function c:_notifySelect() -if self.onSelect then -self.onSelect(self,self.selectedNode,self._selectedIndex) -end -end -function c:onFocusChanged(e) -if not e and self._typeSearch then -self._typeSearch.buffer="" -self._typeSearch.lastTime=0 -end -end -function c:_searchForPrefix(e) -if not e or e==""then -return -end -local a=self._flatNodes -local t=#a -if t==0 then -return -end -local o=self._selectedIndex>=1 and self._selectedIndex or 0 -for i=1,t do -local t=((o+i-1)%t)+1 -local a=a[t].node -local a=a and a.label or"" -if a:lower():sub(1,#e)==e then -self:_setSelectedIndex(t,false) -return -end -end -end -function c:_handleTypeSearch(t) -if not t or t==""then -return -end -local e=self._typeSearch -if not e then -e={buffer="",lastTime=0} -self._typeSearch=e -end -local a=I.clock() -local o=self.typeSearchTimeout or .75 -if a-(e.lastTime or 0)>o then -e.buffer="" -end -e.buffer=e.buffer..t:lower() -e.lastTime=a -self:_searchForPrefix(e.buffer) -end -function c:draw(s,h) -if not self.visible then -return -end -local a,i,o,t=self:getAbsoluteRect() -local r=self.bg or e.black -local m=self.fg or e.white -n(s,a,i,o,t,r,r) -z(s,a,i,o,t) -if self.border then -p(h,a,i,o,t,self.border,r) -end -local o=self:_computeLayoutMetrics() -local t=o.innerWidth -local h=o.innerHeight -local i=o.contentWidth -local c=o.scrollbarWidth -local d=o.scrollbarStyle -if t<=0 or h<=0 or i<=0 then -return -end -local u=o.innerX -local l=o.innerY -local f=self._flatNodes -local w=#f -if w==0 then -for e=0,h-1 do -s.text(u,l+e,string.rep(" ",i),m,r) -end -local t=self.placeholder -if type(t)=="string"and#t>0 then -local t=t -if#t>i then -t=t:sub(1,i) -end -local a=u+math.floor((i-#t)/2) -if a0 then -local e=(d and d.background)or r -n(s,o.scrollbarX,l,c,h,e,e) -if d then -C(s,o.scrollbarX,l,h,0,h,0,d) -end -end -return -end -for t=0,h-1 do -local d=l+t -local h=self.scrollOffset+t -if h>w then -s.text(u,d,string.rep(" ",i),m,r) -else -local t=f[h] -local o=t.node -local t=t.depth or 0 -local t=t*self.indentWidth -if t>i-1 then -t=i-1 -end -if t<0 then -t=0 -end -local a=t>0 and string.rep(" ",t)or"" -local n -if o and o.children and#o.children>0 then -n=o.expanded and self.toggleSymbols.expanded or self.toggleSymbols.collapsed -else -n=self.toggleSymbols.leaf -end -n=tostring(n or" ") -local t=i-t -local a=a -if t>0 then -local e=n:sub(1,1) -a=a..e -t=t-1 -end -if t>0 then -a=a.." " -t=t-1 -end -if t>0 then -local e=(o and o.label)or"" -if#e>t then -e=e:sub(1,t) -end -a=a..e -t=t-#e -end -if t>0 then -a=a..string.rep(" ",t) -elseif#a>i then -a=a:sub(1,i) -end -local o=r -local t=m -if h==self._selectedIndex then -o=self.highlightBg or e.lightGray -t=self.highlightFg or e.black -end -s.text(u,d,a,t,o) -end -end -if c>0 then -local e=(d and d.background)or r -n(s,o.scrollbarX,l,c,h,e,e) -if d then -C(s,o.scrollbarX,l,h,#self._flatNodes,h,math.max(0,self.scrollOffset-1),d) -end -end -end -function c:handleEvent(t,...) -if not self.visible then -return false -end -if t=="mouse_click"then -local e,a,t=... -local o,n,i=self:_rowFromPoint(a,t) -if o then -self.app:setFocus(self) -local e=self:_computeLayoutMetrics() -if e.scrollbarStyle and e.scrollbarWidth>0 then -local o=e.scrollbarX -if a>=o and a=e.innerY and ti-1 then -t=i-1 -end -local o=n+t -if e.node and e.node.children and#e.node.children>0 and t=o and a0 then -local o=e.scrollbarX -if a>=o and a=e.innerY and ti-1 then -e=i-1 -end -local o=n+e -if t.node and t.node.children and#t.node.children>0 and e=o and a0 then -self:_scrollBy(1) -elseif e<0 then -self:_scrollBy(-1) -end -return true -end -elseif t=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.up then -self:_moveSelection(-1) -return true -elseif e==a.down then -self:_moveSelection(1) -return true -elseif e==a.pageUp then -self:_moveSelection(-self:_getInnerHeight()) -return true -elseif e==a.pageDown then -self:_moveSelection(self:_getInnerHeight()) -return true -elseif e==a.home then -self:_setSelectedIndex(1,false) -return true -elseif e==a["end"]then -self:_setSelectedIndex(#self._flatNodes,false) -return true -elseif e==a.left then -local e=self.selectedNode -if e then -if e.children and#e.children>0 and e.expanded then -self:_toggleNode(e,false) -return true -elseif e.parent then -self:_selectNode(e.parent,false) -return true -end -end -elseif e==a.right then -local e=self.selectedNode -if e and e.children and#e.children>0 then -if not e.expanded then -self:_toggleNode(e,true) -else -local e=e.children[1] -if e then -self:_selectNode(e,false) -end -end -return true -end -elseif e==a.enter or e==a.space then -local e=self.selectedNode -if e and e.children and#e.children>0 then -self:_toggleNode(e,nil) -else -self:_notifySelect() -end -return true -end -elseif t=="char"then -local e=... -if self:isFocused()and e and#e>0 then -self:_handleTypeSearch(e:sub(1,1)) -return true -end -elseif t=="paste"then -local e=... -if self:isFocused()and e and#e>0 then -self:_handleTypeSearch(e:sub(1,1)) -return true -end -end -return false -end -local function O(e,a,t) -if et then -return t -end -return e -end -local function M(r,a,t,h,s,n) -if not r then -return -end -n=n or e.white -local i=math.abs(h-a) -local l=a=o then -e=e+o -a=a+l -end -if n<=i then -e=e+i -t=t+d -end -end -end -function w:new(i,t) -t=t or{} -local o=o(t)or{} -o.focusable=true -o.height=math.max(3,math.floor(o.height or 8)) -o.width=math.max(6,math.floor(o.width or 18)) -local a=setmetatable({},w) -a:_init_base(i,o) -a.focusable=true -a.data={} -a.labels={} -a.chartType="bar" -a.showAxis=not(t and t.showAxis==false) -a.showLabels=not(t and t.showLabels==false) -a.placeholder=(t and t.placeholder)or"No data" -a.barColor=(t and t.barColor)or e.lightBlue -a.highlightColor=(t and t.highlightColor)or e.orange -a.axisColor=(t and t.axisColor)or(a.fg or e.white) -a.lineColor=(t and t.lineColor)or(a.fg or e.white) -a.selectable=not(t and t.selectable==false) -if t and type(t.rangePadding)=="number"then -a.rangePadding=math.max(0,t.rangePadding) -else -a.rangePadding=.05 -end -if t and type(t.minValue)=="number"then -a.minValue=t.minValue -else -a.minValue=nil -end -if t and type(t.maxValue)=="number"then -a.maxValue=t.maxValue -else -a.maxValue=nil -end -a.onSelect=t and t.onSelect or nil -a.selectedIndex=nil -a._lastLayout=nil -if t and t.chartType then -a:setChartType(t.chartType) -end -if t and t.labels then -a:setLabels(t.labels) -end -if t and t.data then -a:setData(t.data) -end -if a.selectable then -if t and t.selectedIndex then -a:setSelectedIndex(t.selectedIndex,true) -else -a:_clampSelection(true) -end -else -a.selectedIndex=nil -end -return a -end -function w:_emitSelect() -if self.onSelect then -local e=self.selectedIndex -local t=e and self.data[e]or nil -self.onSelect(self,e,t) -end -end -function w:_clampSelection(a) -if not self.selectable then -if self.selectedIndex~=nil then -self.selectedIndex=nil -if not a then -self:_emitSelect() -end -end -return -end -local t=#self.data -if t==0 then -if self.selectedIndex~=nil then -self.selectedIndex=nil -if not a then -self:_emitSelect() -end -end -return -end -local e=self.selectedIndex -if type(e)~="number"then -e=1 -else -e=math.floor(e) -if e<1 then -e=1 -elseif e>t then -e=t -end -end -if self.selectedIndex~=e then -self.selectedIndex=e -if not a then -self:_emitSelect() -end -end -end -function w:setData(e) -t(1,e,"table") -local a={} -for t=1,#e do -local e=e[t] -if type(e)~="number"then -e=tonumber(e)or 0 -end -a[t]=e -end -self.data=a -if self.selectable then -self:_clampSelection(false) -elseif self.selectedIndex~=nil then -self.selectedIndex=nil -self:_emitSelect() -end -end -function w:getData() -return self.data -end -function w:setLabels(e) -if e==nil then -self.labels={} -return -end -t(1,e,"table") -local t={} -for a=1,#e do -local e=e[a] -if e~=nil then -t[a]=tostring(e) -end -end -self.labels=t -end -function w:getLabels() -return self.labels -end -function w:getLabel(e) -if type(e)~="number"then -return nil -end -if not self.labels then -return nil -end -return self.labels[math.floor(e)] -end -function w:setChartType(e) -if e==nil then -return -end -t(1,e,"string") -local e=e:lower() -if e~="bar"and e~="line"then -error("Chart type must be 'bar' or 'line'",2) -end -self.chartType=e -end -function w:setShowAxis(e) -self.showAxis=not not e -end -function w:setShowLabels(e) -self.showLabels=not not e -end -function w:setPlaceholder(e) -if e~=nil then -t(1,e,"string") -end -self.placeholder=e or"" -end -function w:setSelectable(e,t) -if e==nil then -e=true -else -e=not not e -end -if self.selectable==e then -return -end -self.selectable=e -if not e then -if self.selectedIndex~=nil then -self.selectedIndex=nil -if not t then -self:_emitSelect() -end -end -else -self:_clampSelection(t) -end -end -function w:setRange(e,a) -if e~=nil then -t(1,e,"number") -end -if a~=nil then -t(2,a,"number") -end -self.minValue=e -self.maxValue=a -end -function w:setRangePadding(e) -t(1,e,"number") -if e<0 then -e=0 -end -self.rangePadding=e -end -function w:setOnSelect(e) -if e~=nil then -t(1,e,"function") -end -self.onSelect=e -end -function w:setSelectedIndex(e,a) -if e==nil then -if self.selectedIndex~=nil then -self.selectedIndex=nil -if not a then -self:_emitSelect() -end -end -return false -end -if not self.selectable then -return false -end -t(1,e,"number") -local t=#self.data -if t==0 then -if self.selectedIndex~=nil then -self.selectedIndex=nil -if not a then -self:_emitSelect() -end -end -return false -end -local e=math.floor(e) -if e<1 then -e=1 -elseif e>t then -e=t -end -if self.selectedIndex==e then -return false -end -self.selectedIndex=e -if not a then -self:_emitSelect() -end -return true -end -function w:getSelectedIndex() -return self.selectedIndex -end -function w:getSelectedValue() -local e=self.selectedIndex -if not e then -return nil -end -return self.data[e] -end -function w:onFocusChanged(e) -if e and self.selectable then -self:_clampSelection(true) -end -end -function w:_indexFromPoint(t) -local e=self._lastLayout -if not e or not e.bars then -return nil -end -local a=e.bars -for o=1,#a do -local e=a[o] -if t>=e.left and t<=e.right then -return o -end -end -if t=e.innerX+e.innerWidth then -return nil -end -if e.innerWidth<=0 then -return nil -end -local t=t-e.innerX -local t=math.floor(t*e.dataCount/e.innerWidth)+1 -if t<1 or t>e.dataCount then -return nil -end -return t -end -function w:_moveSelection(a) -if a==0 then -return false -end -if not self.selectable then -return false -end -local t=#self.data -if t==0 then -return false -end -local e=self.selectedIndex or(a>0 and 0 or t+1) -e=e+a -if e<1 then -e=1 -elseif e>t then -e=t -end -return self:setSelectedIndex(e,false) -end -function w:draw(r,v) -if not self.visible then -return -end -local o,h,d,i=self:getAbsoluteRect() -local s=self.bg or e.black -local w=self.fg or e.white -n(r,o,h,d,i,s,s) -z(r,o,h,d,i) -if self.border then -p(v,o,h,d,i,self.border,s) -end -local t=self.border -local a=(t and t.thickness)or 0 -local l=(t and t.left)and a or 0 -local c=(t and t.right)and a or 0 -local u=(t and t.top)and a or 0 -local t=(t and t.bottom)and a or 0 -local o=o+l -local h=h+u -local a=math.max(0,d-l-c) -local t=math.max(0,i-u-t) -self._lastLayout=nil -if a<=0 or t<=0 then -return -end -local d=#self.data -if d==0 then -local i=self.placeholder or"" -if i~=""then -local i=i -if#i>a then -i=i:sub(1,a) -end -local a=o+math.floor((a-#i)/2) -if a=2)and 1 or 0 -local c=(self.showAxis and(t-i)>=2)and 1 or 0 -local l=t-c-i -if l<1 then -l=t -c=0 -i=0 -end -local m=h -local u=m+l-1 -local f=c>0 and(u+1)or nil -local c -if i>0 then -if f then -c=f+1 -else -c=u+1 -end -if c>h+t-1 then -c=h+t-1 -end -end -local t=math.huge -local i=-math.huge -for e=1,d do -local e=self.data[e]or 0 -if ei then -i=e -end -end -if t==math.huge then -t=0 -end -if i==-math.huge then -i=0 -end -local t=type(self.minValue)=="number"and self.minValue or t -local h=type(self.maxValue)=="number"and self.maxValue or i -if h==t then -h=h+1 -t=t-1 -end -local i=h-t -if i<=0 then -i=1 -h=t+i -end -local y=self.rangePadding or 0 -if y>0 then -local e=h-t -local e=e*y -if e==0 then -e=y -end -t=t-e -h=h+e -i=h-t -if i<=0 then -i=1 -h=t+i -end -end -local h={} -for i=1,d do -local t=o+math.floor((i-1)*a/d) -local e=o+math.floor(i*a/d)-1 -if eo+a-1 then -e=o+a-1 -end -local a=e-t+1 -if a<1 then -a=1 -end -h[i]={ -left=t, -right=e, -width=a, -center=t+math.floor((a-1)/2) -} -end -if self.chartType=="bar"then -for a=1,d do -local o=self.data[a]or 0 -local e=0 -if i>0 then -e=(o-t)/i -end -e=O(e,0,1) -local e=math.floor(e*l+.5) -if l>0 and e<=0 and o>t then -e=1 -end -if e>l then -e=l -end -if e<1 then -e=1 -end -local o=u-e+1 -if o0 then -e=(n-t)/i -end -e=O(e,0,1) -local t=math.max(l-1,0) -local e=u-math.floor(e*t+.5) -if eu then -e=u -end -a[o]={x=h[o].center,y=e} -end -for t=2,#a do -local e=a[t-1] -local t=a[t] -M(v,e.x,e.y,t.x,t.y,self.lineColor or w) -end -for t=1,#a do -local o=a[t] -local a=self.lineColor or w -local i="o" -if self.selectable and self.selectedIndex==t then -a=self.highlightColor or e.orange -i="O" -end -r.text(o.x,o.y,i,a,s) -end -end -if f then -n(r,o,f,a,1,s,s) -local e=string.rep("-",a) -r.text(o,f,e,self.axisColor or w,s) -end -if c then -n(r,o,c,a,1,s,s) -local t=self.labels or{} -for i=1,d do -local t=t[i] -if t and t~=""then -t=tostring(t) -local a=h[i] -local o=a.width -if o>0 and#t>o then -t=t:sub(1,o) -end -local o=a.left+math.floor((a.width-#t)/2) -if oa.right then -o=a.right-#t+1 -end -local e=(self.selectable and self.selectedIndex==i)and(self.highlightColor or e.orange)or(self.axisColor or w) -r.text(o,c,t,e,s) -end -end -end -self._lastLayout={ -innerX=o, -innerWidth=a, -dataCount=d, -bars=h -} -end -function w:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"or e=="monitor_touch"then -local a,e,t=... -if self:containsPoint(e,t)then -self.app:setFocus(self) -local e=self:_indexFromPoint(e) -if e and self.selectable then -self:setSelectedIndex(e,false) -end -return true -end -elseif e=="mouse_scroll"then -local e,a,t=... -if self:containsPoint(a,t)then -self.app:setFocus(self) -self:_computeTabLayout() -if self:_isPointInTabStrip(a,t)and self._scrollState and self._scrollState.scrollable then -local e=e>0 and 1 or-1 -if self:_adjustScroll(e)then -return true -end -end -if self.selectable then -if e>0 then -self:_moveSelection(1) -elseif e<0 then -self:_moveSelection(-1) -end -end -return true -end -elseif e=="key"then -if not self:isFocused()then -return false -end -if not self.selectable then -return false -end -local e=... -if e==a.left then -self:_moveSelection(-1) -return true -elseif e==a.right then -self:_moveSelection(1) -return true -elseif e==a.home then -self:setSelectedIndex(1,false) -return true -elseif e==a["end"]then -local e=#self.data -if e>0 then -self:setSelectedIndex(e,false) -end -return true -elseif e==a.enter or e==a.space then -self:_emitSelect() -return true -end -end -return false -end -function f:new(i,a) -a=a or{} -local o=o(a)or{} -o.focusable=true -o.height=o.height or 5 -o.width=o.width or 16 -local t=setmetatable({},f) -t:_init_base(i,o) -t.focusable=true -t.items={} -if a and type(a.items)=="table"then -for e=1,#a.items do -local e=a.items[e] -if e~=nil then -t.items[#t.items+1]=tostring(e) -end -end -end -if type(a.selectedIndex)=="number"then -t.selectedIndex=math.floor(a.selectedIndex) -elseif#t.items>0 then -t.selectedIndex=1 -else -t.selectedIndex=0 -end -t.highlightBg=(a and a.highlightBg)or e.lightGray -t.highlightFg=(a and a.highlightFg)or e.black -t.placeholder=(a and a.placeholder)or nil -t.onSelect=a and a.onSelect or nil -t.scrollOffset=1 -t.typeSearchTimeout=(a and a.typeSearchTimeout)or .75 -t._typeSearch={buffer="",lastTime=0} -if not t.border then -t.border=R(true) -end -t.scrollbar=L(a and a.scrollbar,t.bg or e.black,t.fg or e.white) -t:_normalizeSelection(true) -return t -end -function f:_getInnerMetrics() -local e=self.border -local t=(e and e.left)and 1 or 0 -local o=(e and e.right)and 1 or 0 -local a=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local i=math.max(0,self.width-t-o) -local n=math.max(0,self.height-a-e) -return t,o,a,e,i,n -end -function f:_getInnerHeight() -local t,t,t,t,t,e=self:_getInnerMetrics() -if e<1 then -e=1 -end -return e -end -function f:_computeLayoutMetrics() -local o,a=self:getAbsoluteRect() -local t,s,n,s,e,i=self:_getInnerMetrics() -local o=o+t -local s=a+n -if e<=0 or i<=0 then -return{ -innerX=o, -innerY=s, -innerWidth=e, -innerHeight=i, -contentWidth=0, -scrollbarWidth=0, -scrollbarStyle=nil, -scrollbarX=o -} -end -local t,n=F(self.scrollbar,#self.items,i,e) -if t>0 and e-t<1 then -t=math.max(0,e-1) -if t<=0 then -t=0 -n=nil -end -end -local a=e-t -if a<1 then -a=e -t=0 -n=nil -end -return{ -innerX=o, -innerY=s, -innerWidth=e, -innerHeight=i, -contentWidth=a, -scrollbarWidth=t, -scrollbarStyle=n, -scrollbarX=o+a -} -end -function f:_clampScroll() -local e=self:_getInnerHeight() -local e=math.max(1,#self.items-e+1) -if self.scrollOffset<1 then -self.scrollOffset=1 -elseif self.scrollOffset>e then -self.scrollOffset=e -end -end -function f:_ensureSelectionVisible() -if self.selectedIndex<1 or self.selectedIndex>#self.items then -self:_clampScroll() -return -end -local e=self:_getInnerHeight() -if self.selectedIndexself.scrollOffset+e-1 then -self.scrollOffset=self.selectedIndex-e+1 -end -self:_clampScroll() -end -function f:_normalizeSelection(t) -local e=#self.items -if e==0 then -self.selectedIndex=0 -self.scrollOffset=1 -return -end -if self.selectedIndex<1 then -self.selectedIndex=1 -elseif self.selectedIndex>e then -self.selectedIndex=e -end -self:_ensureSelectionVisible() -if not t then -self:_notifySelect() -end -end -function f:getItems() -local e={} -for t=1,#self.items do -e[t]=self.items[t] -end -return e -end -function f:setItems(a) -t(1,a,"table") -local e={} -for t=1,#a do -local t=a[t] -if t~=nil then -e[#e+1]=tostring(t) -end -end -local a=self:getSelectedItem() -local t=self.selectedIndex -self.items=e -if#e==0 then -self.selectedIndex=0 -self.scrollOffset=1 -if(t~=0 or a~=nil)and self.onSelect then -self.onSelect(self,nil,0) -end -return -end -self:_normalizeSelection(true) -local e=self:getSelectedItem() -if(t~=self.selectedIndex)or(a~=e)then -self:_notifySelect() -end -end -function f:getSelectedItem() -if self.selectedIndex>=1 and self.selectedIndex<=#self.items then -return self.items[self.selectedIndex] -end -return nil -end -function f:setSelectedIndex(e,a) -if#self.items==0 then -self.selectedIndex=0 -self.scrollOffset=1 -return -end -t(1,e,"number") -e=math.floor(e) -if e<1 then -e=1 -elseif e>#self.items then -e=#self.items -end -if self.selectedIndex~=e then -self.selectedIndex=e -self:_ensureSelectionVisible() -if not a then -self:_notifySelect() -end -else -self:_ensureSelectionVisible() -end -end -function f:getSelectedIndex() -return self.selectedIndex -end -function f:setOnSelect(e) -if e~=nil then -t(1,e,"function") -end -self.onSelect=e -end -function f:setPlaceholder(e) -if e~=nil then -t(1,e,"string") -end -self.placeholder=e -end -function f:setHighlightColors(a,e) -if a~=nil then -t(1,a,"number") -self.highlightBg=a -end -if e~=nil then -t(2,e,"number") -self.highlightFg=e -end -end -function f:setScrollbar(t) -self.scrollbar=L(t,self.bg or e.black,self.fg or e.white) -self:_clampScroll() -end -function f:_notifySelect() -if self.onSelect then -self.onSelect(self,self:getSelectedItem(),self.selectedIndex) -end -end -function f:onFocusChanged(e) -if not e and self._typeSearch then -self._typeSearch.buffer="" -self._typeSearch.lastTime=0 -end -end -function f:_itemIndexFromPoint(a,t) -if not self:containsPoint(a,t)then -return nil -end -local e=self:_computeLayoutMetrics() -if e.innerWidth<=0 or e.innerHeight<=0 or e.contentWidth<=0 then -return nil -end -local i=e.innerX -local o=e.innerY -if a=i+e.contentWidth then -return nil -end -if t=o+e.innerHeight then -return nil -end -local e=t-o -local e=self.scrollOffset+e -if e<1 or e>#self.items then -return nil -end -return e -end -function f:_moveSelection(t) -if#self.items==0 then -return -end -local e=self.selectedIndex -if e<1 then -e=1 -end -e=e+t -if e<1 then -e=1 -elseif e>#self.items then -e=#self.items -end -self:setSelectedIndex(e) -end -function f:_scrollBy(e) -if e==0 then -return -end -self.scrollOffset=self.scrollOffset+e -self:_clampScroll() -end -function f:_handleTypeSearch(t) -if not t or t==""then -return -end -local e=self._typeSearch -if not e then -e={buffer="",lastTime=0} -self._typeSearch=e -end -local a=I.clock() -local o=self.typeSearchTimeout or .75 -if a-(e.lastTime or 0)>o then -e.buffer="" -end -e.buffer=e.buffer..t:lower() -e.lastTime=a -self:_searchForPrefix(e.buffer) -end -function f:_searchForPrefix(e) -if not e or e==""then -return -end -local t=#self.items -if t==0 then -return -end -local o=self.selectedIndex>=1 and self.selectedIndex or 0 -for a=1,t do -local a=((o+a-1)%t)+1 -local t=self.items[a] -if t and t:lower():sub(1,#e)==e then -self:setSelectedIndex(a) -return -end -end -end -function f:draw(a,h) -if not self.visible then -return -end -local t,o,s,i=self:getAbsoluteRect() -local r=self.bg or e.black -local u=self.fg or e.white -n(a,t,o,s,i,r,r) -z(a,t,o,s,i) -if self.border then -p(h,t,o,s,i,self.border,r) -end -local t=self:_computeLayoutMetrics() -local s=t.innerWidth -local i=t.innerHeight -local o=t.contentWidth -if s<=0 or i<=0 or o<=0 then -return -end -local d=t.innerX -local h=t.innerY -local l=t.scrollbarWidth -local s=t.scrollbarStyle -local c=#self.items -local r=r -local f=self.highlightBg or e.lightGray -local m=self.highlightFg or e.black -if c==0 then -for e=0,i-1 do -a.text(d,h+e,string.rep(" ",o),u,r) -end -if l>0 then -local e=(s and s.background)or r -n(a,t.scrollbarX,h,l,i,e,e) -end -local n=self.placeholder -if type(n)=="string"and#n>0 then -local t=n -if#t>o then -t=t:sub(1,o) -end -local o=d+math.floor((o-#t)/2) -if oc then -a.text(d,i,string.rep(" ",o),u,r) -else -local e=self.items[t]or"" -if#e>o then -e=e:sub(1,o) -end -local e=e -if#e0 then -local e=(s and s.background)or r -n(a,t.scrollbarX,h,l,i,e,e) -if s then -C(a,t.scrollbarX,h,i,#self.items,i,math.max(0,self.scrollOffset-1),s) -end -end -end -function f:handleEvent(o,...) -if not self.visible then -return false -end -if o=="mouse_click"then -local e,a,t=... -if self:containsPoint(a,t)then -self.app:setFocus(self) -local e=self:_computeLayoutMetrics() -if e.scrollbarStyle and e.scrollbarWidth>0 then -local o=e.scrollbarX -if a>=o and a=e.innerY and t0 then -local o=e.scrollbarX -if a>=o and a=e.innerY and t0 then -self:_scrollBy(1) -elseif e<0 then -self:_scrollBy(-1) -end -return true -end -elseif o=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.up then -self:_moveSelection(-1) -return true -elseif e==a.down then -self:_moveSelection(1) -return true -elseif e==a.pageUp then -self:_moveSelection(-self:_getInnerHeight()) -return true -elseif e==a.pageDown then -self:_moveSelection(self:_getInnerHeight()) -return true -elseif e==a.home then -if#self.items>0 then -self:setSelectedIndex(1) -end -return true -elseif e==a["end"]then -if#self.items>0 then -self:setSelectedIndex(#self.items) -end -return true -elseif e==a.enter or e==a.space then -self:_notifySelect() -return true -end -elseif o=="char"then -local e=... -if self:isFocused()and e and#e>0 then -self:_handleTypeSearch(e:sub(1,1)) -return true -end -elseif o=="paste"then -local e=... -if self:isFocused()and e and#e>0 then -self:_handleTypeSearch(e:sub(1,1)) -return true -end -end -return false -end -function x:new(i,t) -t=t or{} -local o=o(t)or{} -o.focusable=true -o.height=o.height or 3 -o.width=o.width or 16 -local a=setmetatable({},x) -a:_init_base(i,o) -a.focusable=true -a.items={} -if t and type(t.items)=="table"then -for e=1,#t.items do -local e=t.items[e] -if e~=nil then -a.items[#a.items+1]=tostring(e) -end -end -end -a.dropdownBg=(t and t.dropdownBg)or e.black -a.dropdownFg=(t and t.dropdownFg)or e.white -a.highlightBg=(t and t.highlightBg)or e.lightBlue -a.highlightFg=(t and t.highlightFg)or e.black -a.placeholder=(t and t.placeholder)or"Select..." -a.onChange=t and t.onChange or nil -if t and type(t.selectedIndex)=="number"then -a.selectedIndex=math.floor(t.selectedIndex) -elseif#a.items>0 then -a.selectedIndex=1 -else -a.selectedIndex=0 -end -a:_normalizeSelection() -if not a.border then -a.border=R(true) -end -a._open=false -a._hoverIndex=nil -return a -end -function x:_normalizeSelection() -if#self.items==0 then -self.selectedIndex=0 -return -end -if self.selectedIndex<1 then -self.selectedIndex=1 -elseif self.selectedIndex>#self.items then -self.selectedIndex=#self.items -end -end -function x:setItems(a) -t(1,a,"table") -local e={} -for t=1,#a do -local t=a[t] -if t~=nil then -e[#e+1]=tostring(t) -end -end -local t=self:getSelectedItem() -local a=self.selectedIndex -self.items=e -if#e==0 then -self.selectedIndex=0 -if a~=0 or t~=nil then -self:_notifyChange() -end -self:_setOpen(false) -return -end -self:_normalizeSelection() -local e=self:getSelectedItem() -if a~=self.selectedIndex or t~=e then -self:_notifyChange() -end -if self._open then -self._hoverIndex=self.selectedIndex -end -end -function x:getSelectedItem() -if self.selectedIndex>=1 and self.selectedIndex<=#self.items then -return self.items[self.selectedIndex] -end -return nil -end -function x:setSelectedIndex(e,a) -if e==nil then -return -end -t(1,e,"number") -if#self.items==0 then -self.selectedIndex=0 -return -end -e=math.floor(e) -if e<1 then -e=1 -elseif e>#self.items then -e=#self.items -end -if self.selectedIndex~=e then -self.selectedIndex=e -if not a then -self:_notifyChange() -end -end -if self._open then -self._hoverIndex=self.selectedIndex -end -end -function x:setOnChange(e) -if e~=nil then -t(1,e,"function") -end -self.onChange=e -end -function x:_notifyChange() -if self.onChange then -self.onChange(self,self:getSelectedItem(),self.selectedIndex) -end -end -function x:_setOpen(e) -e=not not e -if e and#self.items==0 then -e=false -end -if self._open==e then -return -end -self._open=e -if e then -if self.app then -self.app:_registerPopup(self) -end -if self.selectedIndex>=1 and self.selectedIndex<=#self.items then -self._hoverIndex=self.selectedIndex -elseif#self.items>0 then -self._hoverIndex=1 -else -self._hoverIndex=nil -end -else -if self.app then -self.app:_unregisterPopup(self) -end -self._hoverIndex=nil -end -end -function x:onFocusChanged(e) -if not e then -self:_setOpen(false) -end -end -function x:_isPointInDropdown(o,a) -if not self._open or#self.items==0 then -return false -end -local t,e,i,n=self:getAbsoluteRect() -local e=e+n -return o>=t and o=e and a#self.items then -return nil -end -return e -end -function x:_handlePress(e,t) -local i,i,a,o=self:getAbsoluteRect() -if a<=0 or o<=0 then -return false -end -if self:containsPoint(e,t)then -self.app:setFocus(self) -if self._open then -self:_setOpen(false) -else -self:_setOpen(true) -end -return true -end -if self:_isPointInDropdown(e,t)then -local e=self:_indexFromPoint(e,t) -if e then -self:setSelectedIndex(e) -end -self.app:setFocus(self) -self:_setOpen(false) -return true -end -if self._open then -self:_setOpen(false) -end -return false -end -function x:draw(s,d) -if not self.visible then -return -end -local o,a,h,i=self:getAbsoluteRect() -local t=self.bg or e.black -local r=self.fg or e.white -n(s,o,a,h,i,t,t) -z(s,o,a,h,i) -if self.border then -p(d,o,a,h,i,self.border,t) -end -local e=self.border -local n=(e and e.left)and 1 or 0 -local u=(e and e.right)and 1 or 0 -local d=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local l=o+n -local n=math.max(0,h-n-u) -local u=a+d -local e=math.max(0,i-d-e) -local h=n>0 and 1 or 0 -local o=math.max(0,n-h) -local i -if e>0 then -i=u+math.floor((e-1)/2) -else -i=a -end -local e=self:getSelectedItem() -if not e or e==""then -e=self.placeholder or"" -end -if o>0 then -if#e>o then -e=e:sub(1,o) -end -local a=math.max(0,o-#e) -local e=e..string.rep(" ",a) -s.text(l,i,e,r,t) -end -if h>0 then -local e=self._open and string.char(30)or string.char(31) -local a=l+n-1 -s.text(a,i,e,r,t) -end -end -function x:_drawDropdown(r,c) -if not self._open or#self.items==0 or self.visible==false then -return -end -local t,i,a,e=self:getAbsoluteRect() -local i=i+e -local l=#self.items -local e=self.border -local s=(e and e.left)and 1 or 0 -local h=(e and e.right)and 1 or 0 -local u=t+s -local s=math.max(0,a-s-h) -local d=self._hoverIndex or(self.selectedIndex>0 and self.selectedIndex or nil) -local e=(e and e.bottom)and 1 or 0 -local h=l+e -n(r,t,i,a,h,self.dropdownBg,self.dropdownBg) -z(r,t,i,a,h) -for e=1,l do -local t=i+e-1 -local a=self.items[e]or"" -local e=d~=nil and d==e -local o=e and(self.highlightBg or self.dropdownBg)or self.dropdownBg -local i=e and(self.highlightFg or self.dropdownFg)or self.dropdownFg -if s>0 then -local e=a -if#e>s then -e=e:sub(1,s) -end -local a=math.max(0,s-#e) -local e=e..string.rep(" ",a) -r.text(u,t,e,i,o) -end -end -if self.border then -local e=o(self.border) -if e then -e.top=false -p(c,t,i,a,h,e,self.dropdownBg) -end -end -end -function x:handleEvent(e,...) -if not self.visible then -return false -end -if e=="mouse_click"then -local a,e,t=... -return self:_handlePress(e,t) -elseif e=="monitor_touch"then -local a,e,t=... -return self:_handlePress(e,t) -elseif e=="mouse_scroll"then -local e,t,a=... -if self:containsPoint(t,a)or self:_isPointInDropdown(t,a)then -self.app:setFocus(self) -if e>0 then -self:setSelectedIndex(self.selectedIndex+1) -elseif e<0 then -self:setSelectedIndex(self.selectedIndex-1) -end -return true -end -elseif e=="mouse_move"then -local t,e=... -if self._open then -self._hoverIndex=self:_indexFromPoint(t,e) -end -elseif e=="mouse_drag"then -local a,e,t=... -if self._open then -self._hoverIndex=self:_indexFromPoint(e,t) -end -elseif e=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.down then -self:setSelectedIndex(self.selectedIndex+1) -return true -elseif e==a.up then -self:setSelectedIndex(self.selectedIndex-1) -return true -elseif e==a.home then -self:setSelectedIndex(1) -return true -elseif e==a["end"]then -self:setSelectedIndex(#self.items) -return true -elseif e==a.enter or e==a.space then -if self._open then -self:_setOpen(false) -else -self:_setOpen(true) -end -return true -elseif e==a.escape then -if self._open then -self:_setOpen(false) -return true -end -end -elseif e=="char"then -if not self:isFocused()or#self.items==0 then -return false -end -local e=... -if e and#e>0 then -local a=e:sub(1,1):lower() -local e=self.selectedIndex>=1 and self.selectedIndex or 0 -for t=1,#self.items do -local e=((e+t-1)%#self.items)+1 -local t=self.items[e] -if t and t:sub(1,1):lower()==a then -self:setSelectedIndex(e) -return true -end -end -end -end -return false -end -local O="x" -local W=string.char(7) -local function M(t) -if t==nil then -return nil -end -local e=type(t) -if e=="number"then -local e=math.floor(t) -if e<0 then -e=0 -elseif e>255 then -e=255 -end -return string.char(e) -end -local e=tostring(t) -if e==""then -return nil -end -return e -end -local function Y(a) -local t={ -enabled=false, -char=O, -spacing=1, -fg=nil, -bg=nil -} -local function o(e) -if e==nil then -return -end -local a=type(e) -if a=="boolean"then -t.enabled=e -elseif a=="string"or a=="number"then -t.enabled=true -local e=M(e) -if e then -t.char=e -end -elseif a=="table"then -if e.enabled~=nil then -t.enabled=not not e.enabled -end -local a=e.char or e.label or e.symbol or e.glyph or e.text or e.value -if a==nil and e.code~=nil then -a=e.code -end -a=M(a) -if a then -t.char=a -end -local a=e.spacing or e.gap or e.padding -if a~=nil then -t.spacing=math.max(0,math.floor(a)) -end -if e.fg or e.color or e.foreground or e.textColor then -t.fg=e.fg or e.color or e.foreground or e.textColor -end -if e.bg or e.background or e.fill then -t.bg=e.bg or e.background or e.fill -end -else -t.enabled=not not e -end -end -if a then -if a.tabCloseButton~=nil then -o(a.tabCloseButton) -elseif a.closeButton~=nil then -o(a.closeButton) -end -if a.enableCloseButton~=nil then -t.enabled=not not a.enableCloseButton -end -local e=M(a.closeButtonChar) -if e then -t.char=e -end -if a.closeButtonSpacing~=nil then -t.spacing=math.max(0,math.floor(a.closeButtonSpacing)) -end -if a.closeButtonFg~=nil then -t.fg=a.closeButtonFg -end -if a.closeButtonBg~=nil then -t.bg=a.closeButtonBg -end -end -if not t.char or t.char==""then -t.char=O -end -t.spacing=math.max(0,math.floor(t.spacing or 0)) -return t -end -local function V(a) -local t=nil -local o=1 -local function i(e) -if e==nil then -return -end -local a=type(e) -if a=="boolean"then -if e then -t=W -else -t=nil -end -elseif a=="table"then -if e.enabled==false then -t=nil -else -local a=e.char or e.symbol or e.glyph or e.text or e.value -if a==nil and e.code~=nil then -a=e.code -end -a=M(a) -if a then -t=a -elseif e.enabled==true and not t then -t=W -end -local e=e.spacing or e.gap or e.padding -if e~=nil then -o=math.max(0,math.floor(e)) -end -end -else -local e=M(e) -if e then -t=e -end -end -end -if a then -if a.tabIndicator~=nil then -i(a.tabIndicator) -elseif a.currentTabIndicator~=nil then -i(a.currentTabIndicator) -elseif a.indicator~=nil then -i(a.indicator) -end -local e=M(a.indicatorChar) -if e then -t=e -end -if a.indicatorSpacing~=nil then -o=math.max(0,math.floor(a.indicatorSpacing)) -end -end -if t~=nil and t~=""then -return t,o -end -return nil,o -end -function d:new(i,t) -t=t or{} -local o=o(t)or{} -if t and t.focusable==false then -o.focusable=false -else -o.focusable=true -end -o.width=math.max(8,math.floor(o.width or 18)) -o.height=math.max(3,math.floor(o.height or 7)) -local a=setmetatable({},d) -a:_init_base(i,o) -a.focusable=o.focusable~=false -a.tabSpacing=math.max(0,math.floor((t and t.tabSpacing)or 1)) -a.tabPadding=math.max(0,math.floor((t and t.tabPadding)or 2)) -a.tabHeight=math.max(1,math.floor((t and t.tabHeight)or 3)) -a.tabBg=(t and t.tabBg)or a.bg or e.black -a.tabFg=(t and t.tabFg)or a.fg or e.white -a.activeTabBg=(t and t.activeTabBg)or e.white -a.activeTabFg=(t and t.activeTabFg)or e.black -a.hoverTabBg=(t and t.hoverTabBg)or e.lightGray -a.hoverTabFg=(t and t.hoverTabFg)or e.black -a.disabledTabFg=(t and t.disabledTabFg)or e.lightGray -a.bodyBg=(t and t.bodyBg)or a.bg or e.black -a.bodyFg=(t and t.bodyFg)or a.fg or e.white -a.separatorColor=(t and t.separatorColor)or e.gray -a.bodyRenderer=(t and t.bodyRenderer)or(t and t.renderBody)or nil -a.emptyText=t and t.emptyText or nil -a.onSelect=t and t.onSelect or nil -a.autoShrink=t and t.autoShrink==false and false or true -local e=nil -if t then -if type(t.onCloseTab)=="function"then -e=t.onCloseTab -elseif type(t.onTabClose)=="function"then -e=t.onTabClose -end -end -a.onCloseTab=e -a.tabCloseButton=Y(t) -local o,e=V(t) -a.tabIndicatorChar=o -a.tabIndicatorSpacing=math.max(0,math.floor((e or 0))) -a.tabs={} -if t and type(t.tabs)=="table"then -a.tabs=a:_normalizeTabs(t.tabs) -end -if t and type(t.selectedIndex)=="number"then -a.selectedIndex=math.floor(t.selectedIndex) -elseif#a.tabs>0 then -a.selectedIndex=1 -else -a.selectedIndex=0 -end -a._hoverIndex=nil -a._tabRects={} -a._layoutCache=nil -a._scrollIndex=1 -a._scrollState={scrollable=false,first=1,last=0,canScrollLeft=false,canScrollRight=false} -a._tabStripRect=nil -a:_normalizeSelection(true) -return a -end -function d:_normalizeTabEntry(e,a) -if e==nil then -return nil -end -local t=type(e) -if t=="string"then -return{ -id=a, -label=e, -value=e, -disabled=false, -closeable=true -} -elseif t=="table"then -local t=e.label or e.text or e.title -if t==nil then -if e.id~=nil then -t=tostring(e.id) -elseif e.value~=nil then -t=tostring(e.value) -else -t=string.format("Tab %d",a) -end -else -t=tostring(t) -end -local e={ -id=e.id~=nil and e.id or e.value or a, -label=t, -value=e.value~=nil and e.value or e.id or e, -disabled=not not e.disabled, -content=e.content, -tooltip=e.tooltip, -contentRenderer=e.contentRenderer or e.render, -closeable=e.closeable~=false -} -if e.contentRenderer~=nil and type(e.contentRenderer)~="function"then -e.contentRenderer=nil -end -return e -else -return{ -id=a, -label=tostring(e), -value=e, -disabled=false, -closeable=true -} -end -end -function d:_normalizeTabs(a) -local e={} -for t=1,#a do -local t=self:_normalizeTabEntry(a[t],t) -if t then -e[#e+1]=t -end -end -return e -end -function d:_findFirstEnabled() -for t=1,#self.tabs do -local e=self.tabs[t] -if e and not e.disabled then -return t -end -end -return 0 -end -function d:_resolveSelectableIndex(t) -local a=#self.tabs -if a==0 then -return 0 -end -t=math.max(1,math.min(a,math.floor(t))) -local e=self.tabs[t] -if e and not e.disabled then -return t -end -for t=t+1,a do -e=self.tabs[t] -if e and not e.disabled then -return t -end -end -for t=t-1,1,-1 do -e=self.tabs[t] -if e and not e.disabled then -return t -end -end -return 0 -end -function d:_normalizeSelection(o) -local a=self.selectedIndex or 0 -local t=#self.tabs -local e=a -if t==0 then -e=0 -else -e=math.floor(e) -if e<1 or e>t then -e=math.max(1,math.min(t,e)) -end -if t>0 then -local t=self.tabs[e] -if not t or t.disabled then -e=self:_resolveSelectableIndex(e) -end -if e==0 then -e=self:_findFirstEnabled() -end -end -end -if e<0 then -e=0 -end -local t=e~=a -self.selectedIndex=e -if not o then -if t then -self:_notifySelect() -elseif a~=0 and e==0 then -self:_notifySelect() -end -end -end -function d:setTabs(a) -t(1,a,"table") -local n=self.selectedIndex or 0 -local e=self:getSelectedTab() -local i=e and e.id -local o=e and e.label -self.tabs=self:_normalizeTabs(a) -if i~=nil then -for t=1,#self.tabs do -local e=self.tabs[t] -if e and e.id==i and not e.disabled then -self.selectedIndex=t -break -end -end -end -if(self.selectedIndex or 0)<1 or(self.selectedIndex or 0)>#self.tabs then -if o~=nil then -for t=1,#self.tabs do -local e=self.tabs[t] -if e and e.label==o and not e.disabled then -self.selectedIndex=t -break -end -end -end -end -if(self.selectedIndex or 0)<1 or(self.selectedIndex or 0)>#self.tabs then -self.selectedIndex=n -end -self:_normalizeSelection(false) -self._scrollIndex=1 -self:_invalidateLayout() -end -function d:getTabs() -local t={} -for e=1,#self.tabs do -t[e]=o(self.tabs[e]) -end -return t -end -function d:addTab(e) -local e=self:_normalizeTabEntry(e,#self.tabs+1) -if not e then -return -end -self.tabs[#self.tabs+1]=e -if self.selectedIndex==0 then -self.selectedIndex=#self.tabs -self:_normalizeSelection(false) -else -self:_normalizeSelection(true) -end -self:_invalidateLayout() -end -function d:removeTab(e) -t(1,e,"number") -e=math.floor(e) -if e<1 or e>#self.tabs then -return -end -table.remove(self.tabs,e) -if self.selectedIndex==e then -self.selectedIndex=e -self:_normalizeSelection(false) -elseif self.selectedIndex>e then -self.selectedIndex=self.selectedIndex-1 -self:_normalizeSelection(true) -else -self:_normalizeSelection(true) -end -self:_ensureScrollIndexValid() -self:_invalidateLayout() -end -function d:setTabEnabled(e,a) -t(1,e,"number") -t(2,a,"boolean") -e=math.floor(e) -if e<1 or e>#self.tabs then -return -end -local t=self.tabs[e] -if not t then -return -end -if a then -if t.disabled then -t.disabled=false -if self.selectedIndex==0 then -self.selectedIndex=e -self:_normalizeSelection(false) -else -self:_normalizeSelection(true) -end -end -else -if not t.disabled then -t.disabled=true -if self.selectedIndex==e then -self:_normalizeSelection(false) -else -self:_normalizeSelection(true) -end -end -end -end -function d:setTabLabel(e,a) -t(1,e,"number") -t(2,a,"string") -e=math.floor(e) -if e<1 or e>#self.tabs then -return -end -local e=self.tabs[e] -if not e then -return -end -if e.label~=a then -e.label=a -self:_invalidateLayout() -end -end -function d:selectTabById(o,a) -for e=1,#self.tabs do -local t=self.tabs[e] -if t and t.id==o then -self:setSelectedIndex(e,a) -return true -end -end -return false -end -function d:getSelectedIndex() -return self.selectedIndex or 0 -end -function d:getSelectedTab() -local e=self.selectedIndex or 0 -if e>=1 and e<=#self.tabs then -return self.tabs[e] -end -return nil -end -function d:setSelectedIndex(e,a) -if#self.tabs==0 then -if self.selectedIndex~=0 then -self.selectedIndex=0 -if not a then -self:_notifySelect() -end -end -return -end -t(1,e,"number") -e=math.floor(e) -if e<1 then -e=1 -elseif e>#self.tabs then -e=#self.tabs -end -if self.tabs[e]and self.tabs[e].disabled then -e=self:_resolveSelectableIndex(e) -end -if e==0 then -if self.selectedIndex~=0 then -self.selectedIndex=0 -if not a then -self:_notifySelect() -end -end -return -end -if self.selectedIndex~=e then -self.selectedIndex=e -if not a then -self:_notifySelect() -end -end -end -function d:setOnSelect(e) -if e~=nil then -t(1,e,"function") -end -self.onSelect=e -end -function d:setOnCloseTab(e) -if e~=nil then -t(1,e,"function") -end -self.onCloseTab=e -end -function d:setBodyRenderer(e) -if e~=nil then -t(1,e,"function") -end -self.bodyRenderer=e -end -function d:setEmptyText(e) -if e~=nil then -t(1,e,"string") -end -self.emptyText=e -end -function d:setTabCloseButton(t) -local e -if t==nil then -e={tabCloseButton=false} -else -e={tabCloseButton=t} -end -self.tabCloseButton=Y(e) -self:_invalidateLayout() -end -function d:setTabIndicator(a,e) -if e~=nil then -t(2,e,"number") -end -local t={} -if a==nil then -t.tabIndicator=false -else -t.tabIndicator=a -end -if e~=nil then -t.indicatorSpacing=e -end -local e,t=V(t) -self.tabIndicatorChar=e -self.tabIndicatorSpacing=math.max(0,math.floor((t or 0))) -self:_invalidateLayout() -end -function d:setTabClosable(e,a) -t(1,e,"number") -if a~=nil then -t(2,a,"boolean") -end -e=math.floor(e) -if e<1 or e>#self.tabs then -return -end -local e=self.tabs[e] -if not e then -return -end -local t=(a~=false) -if e.closeable~=t then -e.closeable=t -self:_invalidateLayout() -end -end -function d:setAutoShrink(e) -if e==nil then -e=true -else -t(1,e,"boolean") -end -local e=not not e -if self.autoShrink~=e then -self.autoShrink=e -self._scrollIndex=1 -self:_invalidateLayout() -end -end -function d:_invalidateLayout() -self._tabRects={} -self._layoutCache=nil -self._tabStripRect=nil -self._scrollState=self._scrollState or{scrollable=false,first=1,last=0,canScrollLeft=false,canScrollRight=false} -end -function d:_ensureScrollIndexValid() -local t=#self.tabs -if t<=0 then -self._scrollIndex=1 -return -end -local e=self._scrollIndex or 1 -if e<1 then -e=1 -elseif e>t then -e=t -end -self._scrollIndex=e -end -function d:_isPointInTabStrip(t,a) -local e=self._tabStripRect -if not e then -return false -end -return t>=e.x and t=e.y and a0 then -if not a.canScrollRight then -return false -end -e=math.min(o,e+t) -else -if not a.canScrollLeft then -return false -end -e=math.max(1,e+t) -end -if e~=self._scrollIndex then -self._scrollIndex=e -self._hoverIndex=nil -self:_invalidateLayout() -local t=self.selectedIndex or 0 -self:_computeTabLayout() -local e=self._scrollState -if e and e.scrollable then -if te.last and e.last>=1 then -self:setSelectedIndex(e.last,true) -end -end -return true -end -return false -end -function d:_notifySelect() -if self.onSelect then -self.onSelect(self,self:getSelectedTab(),self.selectedIndex or 0) -end -end -function d:_emitSelect() -if self.onSelect then -self.onSelect(self,self:getSelectedTab(),self.selectedIndex or 0) -end -end -function d:_computeTabLayout() -local n,i,o,s=self:getAbsoluteRect() -local e=self.border -local a=(e and e.left)and 1 or 0 -local r=(e and e.right)and 1 or 0 -local t=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local h=n+a -local d=i+t -local a=math.max(0,o-a-r) -local e=math.max(0,s-t-e) -local o=math.min(e,self.tabHeight or 3) -if o<0 then -o=0 -end -local t=math.max(0,e-o) -local r={ -innerX=h, -innerY=d, -innerWidth=a, -innerHeight=e, -tabHeight=o, -bodyX=h, -bodyY=d+o, -bodyWidth=a, -bodyHeight=t -} -self._layoutCache=r -if o>0 and a>0 then -self._tabStripRect={x=h,y=d,width=a,height=o} -else -self._tabStripRect=nil -end -local t=#self.tabs -if a<=0 or o<=0 or t==0 then -self._tabRects={} -self._scrollState={scrollable=false,first=1,last=0,canScrollLeft=false,canScrollRight=false} -return r,self._tabRects -end -local u=math.max(0,self.tabSpacing or 0) -local l=math.max(0,self.tabPadding or 0) -local e=self.tabIndicatorChar -local i=math.max(0,self.tabIndicatorSpacing or 0) -local n=0 -if e and e~=""then -n=#e+i -end -local i=self.tabCloseButton or{enabled=false,char=O,spacing=1} -local e=i.char or O -if not e or e==""then -e=O -end -local w=i.enabled and e~=nil and e~="" -local m=math.max(1,#e) -local f=math.max(0,i.spacing or 0) -local s={} -local i=0 -local c=0 -for e=1,t do -local t=self.tabs[e] -local a=t and t.label and tostring(t.label)or string.format("Tab %d",e) -if a==""then -a=string.format("Tab %d",e) -end -local h=math.max(1,#a) -local o=w and t and t.closeable~=false -local t=o and m or 0 -local m=o and f or 0 -local u=l -local l=l -local d=u+l+n+h+t+m -local r=n+h+t -s[e]={ -index=e, -label=a, -labelLength=h, -padLeft=u, -padRight=l, -minPadLeft=0, -minPadRight=0, -indicatorWidth=n, -closeable=o, -closeCharWidth=t, -closeSpacing=m, -minCloseSpacing=0, -width=d, -minWidth=r -} -i=i+d -c=c+r -end -local e=u*math.max(0,t-1) -local n=i+e -local l=math.max(0,n-a) -if l>0 and self.autoShrink then -local e=i-c -if e>0 then -local h=math.min(l,e) -local o=h -while o>0 do -local a=false -for e=1,t do -if o<=0 then -break -end -local e=s[e] -if e.padLeft>e.minPadLeft then -e.padLeft=e.padLeft-1 -e.width=e.width-1 -o=o-1 -a=true -elseif e.padRight>e.minPadRight then -e.padRight=e.padRight-1 -e.width=e.width-1 -o=o-1 -a=true -elseif e.closeSpacing>e.minCloseSpacing then -e.closeSpacing=e.closeSpacing-1 -e.width=e.width-1 -o=o-1 -a=true -end -end -if not a then -break -end -end -local e=h-o -if e>0 then -i=i-e -n=n-e -l=math.max(0,n-a) -end -end -end -local c=l>0 -if not c then -self._scrollIndex=1 -end -local function n(i) -local e=0 -local o=i-1 -for i=i,t do -local t=s[i] -local n=math.min(t.width,a) -local t=n -if e>0 then -t=t+u -end -if e+t>a then -if e==0 then -o=i -e=n -end -break -end -e=e+t -o=i -end -if o1 do -local o=math.min(s[e-1].width,a) -local o=t+u+o -if o>a then -break -end -e=e-1 -t=o -end -return e -end -local l={} -local e -local i -if c then -self:_ensureScrollIndexValid() -e=math.max(1,math.min(self._scrollIndex or 1,t)) -local a=self.selectedIndex or 0 -local o=n(e) -if a>=1 and a<=t then -if ao then -e=m(a) -o=n(e) -while a>o and e=t then -break -end -end -if a1, -canScrollRight=ih then -a=h-n+1 -if a<1 then -break -end -end -local t={ -x1=n, -y1=d, -x2=n+a-1, -y2=d+o-1, -width=a, -padLeft=e.padLeft, -padRight=e.padRight, -indicatorWidth=e.indicatorWidth, -closeable=e.closeable, -closeCharWidth=e.closeCharWidth, -closeSpacingWidth=e.closeSpacing, -closeWidth=e.closeCharWidth+e.closeSpacing, -labelLength=e.labelLength, -label=e.label -} -t.labelAvailable=a-e.padLeft-e.padRight-e.indicatorWidth-t.closeWidth -if t.labelAvailable<0 then -t.labelAvailable=0 -end -if e.closeCharWidth>0 then -local e=t.x2-e.closeCharWidth+1 -if eh then -break -end -end -self._tabRects=l -r.firstTabIndex=e -r.lastTabIndex=i -return r,l -end -function d:_tabIndexFromPoint(a,t) -self:_computeTabLayout() -for o,e in pairs(self._tabRects)do -if e and a>=e.x1 and a<=e.x2 and t>=e.y1 and t<=e.y2 then -return o -end -end -return nil -end -function d:_hitTestTabArea(a,t) -self:_computeTabLayout() -for o,e in pairs(self._tabRects)do -if e and a>=e.x1 and a<=e.x2 and t>=e.y1 and t<=e.y2 then -if e.closeRect and a>=e.closeRect.x1 and a<=e.closeRect.x2 and t>=e.closeRect.y1 and t<=e.closeRect.y2 then -return o,"close" -end -return o,"tab" -end -end -return nil,nil -end -function d:_canCloseTab(t) -if not t or t.disabled then -return false -end -local e=self.tabCloseButton -if not e or not e.enabled then -return false -end -if not e.char or e.char==""then -return false -end -if t.closeable==false then -return false -end -return true -end -function d:_tryCloseTab(e) -if type(e)~="number"then -return false -end -e=math.floor(e) -if e<1 or e>#self.tabs then -return false -end -local t=self.tabs[e] -if not self:_canCloseTab(t)then -return false -end -if self.onCloseTab then -local e=self.onCloseTab(self,t,e) -if e==false then -return false -end -end -self:removeTab(e) -self._hoverIndex=nil -return true -end -function d:_moveSelection(a) -if#self.tabs==0 or a==0 then -return -end -a=a>0 and 1 or-1 -local t=#self.tabs -local e=self.selectedIndex -if e<1 or e>t then -e=a>0 and 0 or t+1 -end -for o=1,t do -e=e+a -if e<1 then -e=t -elseif e>t then -e=1 -end -local t=self.tabs[e] -if t and not t.disabled then -self:setSelectedIndex(e) -return -end -end -end -function d:_renderBody(i,n,a) -local o=a.bodyWidth or 0 -local h=a.bodyHeight or 0 -if o<=0 or h<=0 then -return -end -local t=self:getSelectedTab() -if not t then -return -end -local s=t.contentRenderer -if s~=nil and type(s)=="function"then -s(self,t,i,n,a) -return -end -if type(t.content)=="function"then -t.content(self,t,i,n,a) -return -end -if self.bodyRenderer then -self.bodyRenderer(self,t,i,n,a) -return -end -if type(t.content)=="string"then -local n=P(t.content,o) -local t=math.min(h,#n) -local s=self.bodyFg or self.tabFg or e.white -local h=self.bodyBg or self.bg or e.black -for t=1,t do -local e=n[t] -if#e>o then -e=e:sub(1,o) -end -if#e0 and t.innerWidth>0 then -n(s,t.innerX,t.innerY,t.innerWidth,t.tabHeight,o,o) -end -if self._hoverIndex and not self._tabRects[self._hoverIndex]then -self._hoverIndex=nil -end -local e=self.tabCloseButton or{enabled=false,char=O,spacing=1} -local r=e.char or O -if not r or r==""then -r=O -end -local g=e.enabled and r~=nil and r~="" -local k=e.fg -local q=e.bg -local u=self.tabIndicatorChar -local f=math.max(0,self.tabIndicatorSpacing or 0) -local m="" -local c="" -if u and u~=""then -m=u -c=string.rep(" ",#u) -if f>0 then -local e=string.rep(" ",f) -m=m..e -c=c..e -end -end -for l,e in pairs(self._tabRects)do -local a=self.tabs[l] -if a and e then -local i=o -local o=d -if l==self.selectedIndex and self.selectedIndex>0 then -i=self.activeTabBg or i -o=self.activeTabFg or o -if self:isFocused()then -i=self.hoverTabBg or i -o=self.hoverTabFg or o -end -elseif self._hoverIndex and self._hoverIndex==l and not a.disabled then -i=self.hoverTabBg or i -o=self.hoverTabFg or o -end -if a.disabled then -o=self.disabledTabFg or o -end -n(s,e.x1,e.y1,e.width,t.tabHeight,i,i) -local n=e.padLeft -if n==nil then -n=math.max(0,self.tabPadding or 0) -end -local h=e.padRight -if h==nil then -h=n -end -local w=e.label or a.label or string.format("Tab %d",l) -w=tostring(w) -local d=e.indicatorWidth or 0 -local y=e.closeWidth or 0 -local a=e.labelAvailable -if a==nil then -a=e.width-n-h-d-y -end -a=math.max(0,a) -local y=e.x1+n -local p=e.y1+math.max(0,math.floor((t.tabHeight-1)/2)) -local h="" -if d>0 then -local e -if l==self.selectedIndex and self.selectedIndex>0 then -e=m -else -e=c -end -if e==""then -e=u or"" -if f>0 then -e=e..string.rep(" ",f) -end -end -if#e>d then -h=e:sub(1,d) -else -h=e..string.rep(" ",d-#e) -end -end -if#h>a then -h=h:sub(1,a) -end -local t=#h -local d=math.max(0,a-t) -local t=w -if d>0 then -if#t>d then -t=t:sub(1,d) -end -if#t0 and y<=e.x2 then -s.text(y,p,t,o,i) -end -if g and e.closeable and e.closeCharWidth and e.closeCharWidth>0 and e.closeRect then -local t=r -if#t>e.closeCharWidth then -t=t:sub(1,e.closeCharWidth) -elseif#t0 then -local h=y+math.max(0,a)-1 -local a=e.closeRect.x1-t -if a<=h then -local e=h-a+1 -t=t-e -a=a+e -end -if t>0 then -if a0 then -s.text(a,p,string.rep(" ",t),o,i) -end -end -end -end -end -end -if t.bodyHeight>0 and t.bodyWidth>0 then -n(s,t.bodyX,t.bodyY,t.bodyWidth,t.bodyHeight,h,h) -local e=self:getSelectedTab() -if e then -self:_renderBody(s,v,t) -elseif self.emptyText then -local e=P(self.emptyText,t.bodyWidth) -local a=math.min(t.bodyHeight,#e) -for a=1,a do -local e=e[a] -if#e>t.bodyWidth then -e=e:sub(1,t.bodyWidth) -end -if#e0 then -self:_moveSelection(1) -elseif e<0 then -self:_moveSelection(-1) -end -return true -end -elseif t=="mouse_move"then -local e,t=... -if self:containsPoint(e,t)then -self._hoverIndex=self:_tabIndexFromPoint(e,t) -elseif self._hoverIndex then -self._hoverIndex=nil -end -elseif t=="mouse_drag"then -local a,e,t=... -if self:containsPoint(e,t)then -self._hoverIndex=self:_tabIndexFromPoint(e,t) -elseif self._hoverIndex then -self._hoverIndex=nil -end -elseif t=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.left then -self:_moveSelection(-1) -return true -elseif e==a.right then -self:_moveSelection(1) -return true -elseif e==a.up then -self:_moveSelection(-1) -return true -elseif e==a.down then -self:_moveSelection(1) -return true -elseif e==a.home then -self:setSelectedIndex(1) -return true -elseif e==a["end"]then -self:setSelectedIndex(#self.tabs) -return true -elseif e==a.tab then -self:_moveSelection(1) -return true -elseif e==a.enter or e==a.space then -self:_emitSelect() -return true -end -end -return false -end -function m:new(i,t) -t=t or{} -local o=o(t)or{} -o.focusable=true -o.width=math.max(1,math.floor(o.width or 1)) -o.height=math.max(1,math.floor(o.height or 1)) -local a=setmetatable({},m) -a:_init_base(i,o) -a.focusable=true -a.menuBg=(t and t.menuBg)or e.black -a.menuFg=(t and t.menuFg)or e.white -a.highlightBg=(t and t.highlightBg)or e.lightGray -a.highlightFg=(t and t.highlightFg)or e.black -a.shortcutFg=(t and t.shortcutFg)or a.menuFg -a.disabledFg=(t and t.disabledFg)or e.lightGray -a.separatorColor=(t and t.separatorColor)or a.disabledFg -a.maxWidth=math.max(8,math.floor((t and t.maxWidth)or 32)) -if a.border==nil then -a.border=R(true) -end -a.onSelect=t and t.onSelect or nil -a.items=a:_normalizeItems(t and t.items or{}) -a._levels={} -a._open=false -a._previousFocus=nil -return a -end -function m:setItems(e) -self.items=self:_normalizeItems(e) -if self._open then -self:close() -end -end -function m:setOnSelect(e) -if e~=nil then -t(1,e,"function") -end -self.onSelect=e -end -function m:isOpen() -return self._open -end -function m:draw(e,e) -end -function m:_normalizeItem(e) -if e==nil then -return nil -end -if e=="-"then -return{type="separator"} -end -local t=type(e) -if t=="string"then -return{type="item",label=e,shortcut=nil,disabled=false} -end -if t~="table"then -return nil -end -if e.separator or e.type=="separator"then -return{type="separator"} -end -local t=e.label or e.text -if t==nil then -return nil -end -t=tostring(t) -local t={ -type="item", -label=t, -shortcut=e.shortcut and tostring(e.shortcut)or nil, -disabled=not not e.disabled, -action=e.onSelect or e.action or e.callback, -id=e.id, -value=e.value, -data=e.data -} -local e=e.submenu or e.items -if e then -local e=self:_normalizeItems(e) -if#e>0 then -t.submenu=e -end -end -return t -end -function m:_normalizeItems(t) -if type(t)~="table"then -return{} -end -local e={} -local a=true -for o=1,#t do -local t=self:_normalizeItem(t[o]) -if t then -if t.type=="separator"then -if not a and#e>0 then -e[#e+1]=t -a=true -end -else -e[#e+1]=t -a=false -end -end -end -while#e>0 and e[#e].type=="separator"do -e[#e]=nil -end -return e -end -function m:_firstEnabledIndex(e) -for t=1,#e do -local e=e[t] -if e and e.type=="item"and not e.disabled then -return t -end -end -return nil -end -function m:_maxWidthForLevel() -local e=self.maxWidth -local t=self.app and self.app.root -if t and t.width then -e=math.max(1,math.min(e,t.width)) -else -e=math.max(4,e) -end -return e -end -function m:_measureItems(i,s) -if not i or#i==0 then -return nil -end -local o=0 -local a=0 -for e=1,#i do -local e=i[e] -if e.type=="item"then -local t=#(e.label or"") -if t>o then -o=t -end -local e=e.shortcut -if e and e~=""then -local e=#e -if e>a then -a=e -end -end -end -end -local r=(self.border and self.border.left)and 1 or 0 -local h=(self.border and self.border.right)and 1 or 0 -local u=(self.border and self.border.top)and 1 or 0 -local l=(self.border and self.border.bottom)and 1 or 0 -local t=2 -local n=(a>0)and 2 or 0 -local e=o+t+n+a -if es then -e=s-r-h -if ei then -a=i -end -n=(a>0)and 2 or 0 -local i=e-t-n-a -if i<1 then -i=1 -end -if o>i then -o=i -end -e=o+t+n+a -d=e+r+h -end -end -if a==0 then -n=0 -end -return{ -itemWidth=e, -labelWidth=o, -shortcutWidth=a, -shortcutGap=n, -arrowWidth=t, -leftPad=r, -rightPad=h, -topPad=u, -bottomPad=l, -itemCount=#i, -totalWidth=d, -totalHeight=#i+u+l -} -end -function m:_buildLevel(r,a,n,u,l,e) -e=e or self:_measureItems(r,self:_maxWidthForLevel()) -if not e or e.itemCount==0 then -return nil -end -local t=self.app and self.app.root or nil -local i=t and t.width or nil -local t=t and t.height or nil -local h=e.totalWidth -local s=e.totalHeight -local o=math.floor(a) -local a=math.floor(n) -if i then -if o<1 then -o=1 -end -if o+h-1>i then -o=math.max(1,i-h+1) -end -end -if t then -if a<1 then -a=1 -end -if a+s-1>t then -a=math.max(1,t-s+1) -end -end -local t=o+e.leftPad -local d=a+e.topPad -local n=t+e.itemWidth-1 -if n0 then -i=n-e.shortcutWidth-1 -if is then -local s=((self.border and self.border.left)and 1 or 0) -local a=a.rect.x-o.rect.width+s -local e=self:_buildLevel(t.submenu,a,h,e,i,n) -if e then -o=e -end -end -self:_closeLevelsAfter(e) -self._levels[#self._levels+1]=o -end -function m:_findItemAtPoint(i,a) -local e=self._levels -if not e or#e==0 then -return nil -end -for o=#e,1,-1 do -local e=e[o] -local t=e.rect -if i>=t.x and i=t.y and a=e.contentY and a=1 and t<=#e.items then -return o,t -end -end -return o,nil -end -end -return nil -end -function m:_setHighlight(e,o,i) -local a=self._levels[e] -if not a then -return -end -if not o then -a.highlightIndex=nil -self:_closeLevelsAfter(e) -return -end -local t=a.items[o] -if not t or t.type~="item"or t.disabled then -a.highlightIndex=nil -self:_closeLevelsAfter(e) -return -end -a.highlightIndex=o -if t.submenu and#t.submenu>0 then -if i then -self:_openSubmenu(e,o) -end -else -self:_closeLevelsAfter(e) -end -end -function m:_handlePointerHover(e,t) -local e,t=self:_findItemAtPoint(e,t) -if not e then -return false -end -self:_setHighlight(e,t,true) -return true -end -function m:_handlePointerPress(a,e,t) -local e,a=self:_findItemAtPoint(e,t) -if not e then -self:close() -return false -end -if not a then -self:_closeLevelsAfter(e) -local e=self._levels[e] -if e then -e.highlightIndex=nil -end -return true -end -local o=self._levels[e] -local t=o and o.items[a] -if not t then -self:_closeLevelsAfter(e) -return true -end -if t.type=="separator"then -self:_closeLevelsAfter(e) -o.highlightIndex=nil -return true -end -if t.disabled then -self:_setHighlight(e,a,false) -return true -end -self:_setHighlight(e,a,false) -if t.submenu and#t.submenu>0 then -self:_openSubmenu(e,a) -return true -end -self:_activateItem(e,t) -return true -end -function m:_moveHighlight(i) -local e=self._levels -if not e or#e==0 then -return -end -local o=#e -local a=e[o] -local t=#a.items -if t==0 then -return -end -local e=a.highlightIndex or 0 -for n=1,t do -e=e+i -if e<1 then -e=t -elseif e>t then -e=1 -end -local t=a.items[e] -if t and t.type=="item"and not t.disabled then -self:_setHighlight(o,e,true) -return -end -end -end -function m:_activateHighlightedSubmenu() -local e=self._levels -if not e or#e==0 then -return -end -local a=#e -local t=e[a] -local e=t.highlightIndex -if not e then -return -end -local t=t.items[e] -if t and t.submenu and#t.submenu>0 then -self:_openSubmenu(a,e) -local e=self._levels[a+1] -if e and not e.highlightIndex then -e.highlightIndex=self:_firstEnabledIndex(e.items) -end -end -end -function m:_activateHighlightedItem() -local e=self._levels -if not e or#e==0 then -return -end -local t=#e -local e=e[t] -local a=e.highlightIndex -if not a then -return -end -local e=e.items[a] -if not e or e.type~="item"or e.disabled then -return -end -if e.submenu and#e.submenu>0 then -self:_openSubmenu(t,a) -local e=self._levels[t+1] -if e and not e.highlightIndex then -e.highlightIndex=self:_firstEnabledIndex(e.items) -end -return -end -self:_activateItem(t,e) -end -function m:_typeSearch(a) -if not a or a==""then -return -end -local e=self._levels -if not e or#e==0 then -return -end -local o=#e -local e=e[o] -local t=#e.items -if t==0 then -return -end -local i=e.highlightIndex or 0 -local n=a:lower() -for a=1,t do -local t=((i+a-1)%t)+1 -local e=e.items[t] -if e and e.type=="item"and not e.disabled then -local e=(e.label or""):lower() -if e:sub(1,1)==n then -self:_setHighlight(o,t,true) -return -end -end -end -end -function m:_activateItem(t,e) -if not e or e.type~="item"or e.disabled then -return -end -if e.action then -e.action(self,e) -end -if self.onSelect then -self.onSelect(self,e) -end -self:close() -end -function m:_setOpen(e) -e=not not e -if e then -if self._open then -return -end -self._open=true -if self.app then -self._previousFocus=self.app:getFocus() -self.app:_registerPopup(self) -self.app:setFocus(self) -end -else -if not self._open then -return -end -self._open=false -if self.app then -self.app:_unregisterPopup(self) -if self.app:getFocus()==self then -local e=self._previousFocus -if e and e.app==self.app and e.visible~=false then -self.app:setFocus(e) -else -self.app:setFocus(nil) -end -end -end -self._previousFocus=nil -self._levels={} -end -end -function m:open(o,i,a) -t(1,o,"number") -t(2,i,"number") -if a~=nil then -t(3,a,"table") -end -local e -if a and a.items then -e=self:_normalizeItems(a.items) -else -e=self.items -end -if not e or#e==0 then -self:close() -return false -end -local t=self:_measureItems(e,self:_maxWidthForLevel()) -if not t then -self:close() -return false -end -local a=math.floor(o) -local o=math.floor(i) -local a=a-t.leftPad -local o=o-t.topPad -local e=self:_buildLevel(e,a,o,nil,nil,t) -if not e then -self:close() -return false -end -self._levels={e} -self:_setOpen(true) -return true -end -function m:close() -self:_setOpen(false) -end -function m:_drawDropdown(o,r) -if not self._open or self.visible==false then -return -end -local t=self._levels -if not t or#t==0 then -return -end -for a=1,#t do -local t=t[a] -local a=t.rect -n(o,a.x,a.y,a.width,a.height,self.menuBg,self.menuBg) -z(o,a.x,a.y,a.width,a.height) -local i=t.items -for n=1,#i do -local a=i[n] -local i=t.contentY+n-1 -local h=t.highlightIndex==n and a.type=="item"and not a.disabled -local n=h and(self.highlightBg or self.menuBg)or self.menuBg -local s=self.menuFg or e.white -if a.type=="separator"then -local a=self.separatorColor or s -local e=string.rep("-",t.metrics.itemWidth) -o.text(t.contentX,i,e,a,n) -else -local s=a.disabled and(self.disabledFg or e.lightGray)or(h and(self.highlightFg or s)or s) -o.text(t.contentX,i,string.rep(" ",t.metrics.itemWidth),s,n) -local e=a.label or"" -if#e>t.metrics.labelWidth then -e=e:sub(1,t.metrics.labelWidth) -end -if#e>0 then -o.text(t.contentX,i,e,s,n) -end -if t.shortcutX then -local e=a.shortcut or"" -if#e>t.metrics.shortcutWidth then -e=e:sub(#e-t.metrics.shortcutWidth+1) -end -local a=math.max(0,t.metrics.shortcutWidth-#e) -if a>0 then -e=string.rep(" ",a)..e -end -local a=self.shortcutFg or s -o.text(t.shortcutX,i,e,a,n) -end -if a.submenu and a.submenu[1]~=nil then -o.text(t.arrowX,i,">",s,n) -end -end -end -if self.border then -p(r,a.x,a.y,a.width,a.height,self.border,self.menuBg) -end -end -end -function m:handleEvent(e,...) -if not self.visible or not self._open then -return false -end -if e=="mouse_click"then -local e,t,a=... -return self:_handlePointerPress(e,t,a) -elseif e=="monitor_touch"then -local a,e,t=... -return self:_handlePointerPress(1,e,t) -elseif e=="mouse_move"then -local e,t=... -return self:_handlePointerHover(e,t) -elseif e=="mouse_drag"then -local a,t,e=... -return self:_handlePointerHover(t,e) -elseif e=="mouse_scroll"then -self:close() -return false -elseif e=="key"then -if not self:isFocused()then -return false -end -local e=... -if e==a.down then -self:_moveHighlight(1) -return true -elseif e==a.up then -self:_moveHighlight(-1) -return true -elseif e==a.right then -self:_activateHighlightedSubmenu() -return true -elseif e==a.left then -if#self._levels>1 then -self:_closeLevelsAfter(#self._levels-1) -else -self:close() -end -return true -elseif e==a.enter or e==a.space then -self:_activateHighlightedItem() -return true -elseif e==a.escape then -self:close() -return true -end -elseif e=="char"then -if not self:isFocused()then -return false -end -local e=... -if e and#e>0 then -self:_typeSearch(e:sub(1,1)) -return true -end -elseif e=="paste"then -if not self:isFocused()then -return false -end -local e=... -if e and#e>0 then -self:_typeSearch(e:sub(1,1)) -return true -end -end -return false -end -local o={} -o.__index=o -setmetatable(o,{__index=s}) -local W={ -["and"]=true, -["break"]=true, -["do"]=true, -["else"]=true, -["elseif"]=true, -["end"]=true, -["false"]=true, -["for"]=true, -["function"]=true, -["goto"]=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 -} -local P={ -print=true, -ipairs=true, -pairs=true, -next=true, -math=true, -table=true, -string=true, -coroutine=true, -os=true, -tonumber=true, -tostring=true, -type=true, -pcall=true, -xpcall=true, -select=true -} -local function Y(t) -if t==nil or t==""then -return{""} -end -local e={} -local a=1 -local i=#t -while a<=i do -local o=t:find("\n",a,true) -if not o then -e[#e+1]=t:sub(a) -break -end -e[#e+1]=t:sub(a,o-1) -a=o+1 -if a>i then -e[#e+1]="" -break -end -end -if#e==0 then -e[1]="" -end -return e -end -local function B(e) -return table.concat(e,"\n") -end -local function O(e,t,a) -if ea then -return a -end -return e -end -local function M(e,t,o,a) -if eo then -return 1 -end -if ta then -return 1 -end -return 0 -end -local function V(t,e,o,a,i,n) -if M(t,e,o,a)<0 then -return false -end -if M(t,e,i,n)>=0 then -return false -end -return true -end -local function X(a) -if a==nil then -return nil -end -if a==true then -a="lua" -end -if type(a)=="string"then -if a=="lua"then -return{ -language="lua", -keywords=W, -builtins=P, -keywordColor=e.orange, -commentColor=e.lightGray, -stringColor=e.yellow, -numberColor=e.cyan, -builtinColor=e.lightBlue -} -end -return nil -end -if type(a)=="table"then -local t={} -for a,e in pairs(a)do -t[a]=e -end -if t.language=="lua"then -t.keywords=t.keywords or W -t.builtins=t.builtins or P -if t.keywordColor==nil then -t.keywordColor=e.orange -end -if t.commentColor==nil then -t.commentColor=e.lightGray -end -if t.stringColor==nil then -t.stringColor=e.yellow -end -if t.numberColor==nil then -t.numberColor=e.cyan -end -if t.builtinColor==nil then -t.builtinColor=e.lightBlue -end -end -return t -end -return nil -end -function o:new(n,a) -a=a or{} -local i={} -for e,t in pairs(a)do -i[e]=t -end -i.focusable=true -i.width=math.max(4,math.floor(i.width or 16)) -i.height=math.max(1,math.floor(i.height or(a.multiline~=false and 5 or 1))) -local t=setmetatable({},o) -t:_init_base(n,i) -t.focusable=true -t.placeholder=a.placeholder or"" -t.placeholderColor=a.placeholderColor or a.placeholderFg -t.onChange=a.onChange or nil -t.onCursorMove=a.onCursorMove or nil -t.maxLength=a.maxLength or nil -t.multiline=a.multiline~=false -t.numericOnly=not not a.numericOnly -if t.numericOnly then -t.multiline=false -end -t.tabWidth=math.max(1,math.floor(a.tabWidth or 4)) -t.selectionBg=a.selectionBg or e.lightGray -t.selectionFg=a.selectionFg or e.black -t.overlayBg=a.overlayBg or e.gray -t.overlayFg=a.overlayFg or e.white -t.overlayActiveBg=a.overlayActiveBg or e.orange -t.overlayActiveFg=a.overlayActiveFg or e.black -t.autocomplete=a.autocomplete -t.autocompleteAuto=not not a.autocompleteAuto -t.autocompleteMaxItems=math.max(1,math.floor(a.autocompleteMaxItems or 5)) -t.autocompleteBg=a.autocompleteBg or e.gray -t.autocompleteFg=a.autocompleteFg or e.white -t.autocompleteHighlightBg=a.autocompleteHighlightBg or e.lightBlue -t.autocompleteHighlightFg=a.autocompleteHighlightFg or e.black -t.autocompleteBorder=R(a.autocompleteBorder==false and false or a.autocompleteBorder or true) -t.autocompleteMaxWidth=math.max(4,math.floor(a.autocompleteMaxWidth or math.max(t.width or i.width or 16,16))) -t.autocompleteGhostColor=a.autocompleteGhostColor or e.lightGray -t.syntax=X(a.syntax) -t._lines={""} -t.text="" -t._cursorLine=1 -t._cursorCol=1 -t._preferredCol=1 -t._selectionAnchor=nil -t._scrollX=0 -t._scrollY=0 -t._shiftDown=false -t._ctrlDown=false -t._dragging=false -t._dragButton=nil -t._dragAnchor=nil -t._find={ -visible=false, -activeField="find", -findText="", -replaceText="", -matchCase=false, -matches={}, -index=0 -} -t._autocompleteState={ -visible=false, -items={}, -selectedIndex=1, -anchorLine=1, -anchorCol=1, -prefix="", -ghost="", -trigger="auto", -rect=nil -} -t._open=false -t.scrollbar=L(a.scrollbar,t.bg or e.black,t.fg or e.white) -t:_setTextInternal(a.text or"",true,true) -if a.cursorPos then -t:_moveCursorToIndex(a.cursorPos) -end -t:_ensureCursorVisible() -return t -end -function o:setOnCursorMove(e) -if e~=nil then -t(1,e,"function") -end -self.onCursorMove=e -end -function o:setScrollbar(t) -self.scrollbar=L(t,self.bg or e.black,self.fg or e.white) -end -function o:setPlaceholderColor(e) -if e~=nil then -t(1,e,"number") -end -self.placeholderColor=e -end -function o:setNumericOnly(e) -if e==nil then -e=true -else -t(1,e,"boolean") -end -self.numericOnly=not not e -if self.numericOnly then -self.multiline=false -end -local e=self.text -if self.numericOnly then -e=self:_sanitizeNumericInput(e) -if not self:_isNumericText(e)then -e="" -end -end -if e~=self.text then -self:_setTextInternal(e,true,false) -end -end -function o:onFocusChanged(e) -if not e then -self:_hideAutocomplete() -end -self:_ensureCursorVisible() -end -function o:_applyMaxLength(e) -if not self.maxLength then -return e -end -if#e<=self.maxLength then -return e -end -return e:sub(1,self.maxLength) -end -function o:_positionToIndex(t,e) -t=O(t or 1,1,#self._lines) -local e=(e or 1)-1 -if e<0 then -e=0 -end -for t=1,t-1 do -e=e+#self._lines[t]+1 -end -return e+1 -end -function o:_getSelectionIndices() -if self:_hasSelection()then -local o,t,a,e=self:_getSelectionRange() -local t=self:_positionToIndex(o,t) -local e=self:_positionToIndex(a,e) -return t,e -end -local e=self:_positionToIndex(self._cursorLine,self._cursorCol) -return e,e -end -function o:_simulateReplacementText(e) -local t,a=self:_getSelectionIndices() -local t=self.text:sub(1,t-1) -local a=self.text:sub(a) -return t..(e or"")..a -end -function o:_sanitizeNumericInput(e) -if not e or e==""then -return"" -end -local e=tostring(e):gsub("[^0-9%+%-%.]","") -return e -end -function o:_isNumericText(e) -if e==nil or e==""then -return true -end -if e=="+"or e=="-"then -return true -end -if e=="."or e=="+."or e=="-."then -return true -end -if e:match("^[+-]?%d+$")then -return true -end -if e:match("^[+-]?%d+%.%d*$")then -return true -end -if e:match("^[+-]?%d*%.%d+$")then -return true -end -return false -end -function o:_allowsNumericInsertion(e) -local e=self:_simulateReplacementText(e) -return self:_isNumericText(e) -end -function o:_syncTextFromLines() -self.text=B(self._lines) -end -function o:_setTextInternal(e,a,t) -e=tostring(e or"") -if self.numericOnly then -e=self:_sanitizeNumericInput(e) -if not self:_isNumericText(e)then -e="" -end -end -e=self:_applyMaxLength(e) -self._lines=Y(e) -self:_syncTextFromLines() -if a then -self._cursorLine=#self._lines -self._cursorCol=(#self._lines[#self._lines]or 0)+1 -else -self._cursorLine=O(self._cursorLine,1,#self._lines) -local e=self._lines[self._cursorLine]or"" -self._cursorCol=O(self._cursorCol,1,#e+1) -end -self._preferredCol=self._cursorCol -self._selectionAnchor=nil -self:_ensureCursorVisible() -if not t then -self:_notifyChange() -self:_notifyCursorChange() -end -end -function o:_indexToPosition(e) -e=O(e or 1,1,#self.text+1) -local e=e-1 -for t=1,#self._lines do -local a=self._lines[t] -local a=#a -if e<=a then -return t,e+1 -end -e=e-(a+1) -end -local e=#self._lines -local t=#self._lines[e] -return e,t+1 -end -function o:_moveCursorToIndex(e) -local e,t=self:_indexToPosition(e) -self:_setCursorPosition(e,t) -end -function o:getCursorPosition() -return self._cursorLine,self._cursorCol -end -function o:getLineCount() -return#self._lines -end -function o:_getInnerMetrics() -local e=self.border -local t=(e and e.left)and 1 or 0 -local n=(e and e.right)and 1 or 0 -local a=(e and e.top)and 1 or 0 -local e=(e and e.bottom)and 1 or 0 -local o,i=self:getAbsoluteRect() -local o=o+t -local i=i+a -local s=math.max(0,self.width-t-n) -local n=math.max(0,self.height-a-e) -return o,i,s,n,t,a,e -end -function o:_getOverlayHeight(e) -if not self._find.visible then -return 0 -end -return math.min(2,e) -end -function o:_computeLayoutMetrics() -local n,i,a,e=self:getAbsoluteRect() -local s,r,t,o=self:_getInnerMetrics() -if t<=0 or o<=0 then -s=n -r=i -t=math.max(1,a) -o=math.max(1,e) -end -local h=self:_getOverlayHeight(o) -local n=math.max(1,o-h) -local e,a=F(self.scrollbar,#self._lines,n,t) -if e>0 and t-e<1 then -if a and(a.alwaysVisible or#self._lines>n)then -e=math.max(0,t-1) -else -e=0 -a=nil -end -end -if e<=0 then -e=0 -a=nil -end -local i=t-e -if i<1 then -i=t -e=0 -a=nil -end -return{ -innerX=s, -innerY=r, -innerWidth=t, -innerHeight=o, -contentWidth=i, -contentHeight=n, -overlayHeight=h, -scrollbarWidth=e, -scrollbarStyle=a, -scrollbarX=s+i -} -end -function o:_getContentSize() -local e=self:_computeLayoutMetrics() -return math.max(1,e.contentWidth),math.max(1,e.contentHeight) -end -function o:_ensureCursorVisible() -local t,e=self:_getContentSize() -local o=self._scrollY+1 -local a=self._scrollY+e -if self._cursorLinea then -self._scrollY=self._cursorLine-e -end -if self._scrollY<0 then -self._scrollY=0 -end -local e=math.max(0,#self._lines-e) -if self._scrollY>e then -self._scrollY=e -end -local a=self._scrollX+1 -local e=self._scrollX+t -if self._cursorCole then -self._scrollX=self._cursorCol-t -end -if self._scrollX<0 then -self._scrollX=0 -end -local e=self._lines[self._cursorLine]or"" -local e=math.max(0,#e+1-t) -if self._scrollX>e then -self._scrollX=e -end -end -function o:_notifyChange() -if self.onChange then -self.onChange(self,self.text) -end -end -function o:_notifyCursorChange() -if self.onCursorMove then -self.onCursorMove(self,self._cursorLine,self._cursorCol,self:getSelectionLength()) -end -end -function o:_hasSelection() -if not self._selectionAnchor then -return false -end -if self._selectionAnchor.line~=self._cursorLine then -return true -end -return self._selectionAnchor.col~=self._cursorCol -end -function o:getSelectionLength() -if not self:_hasSelection()then -return 0 -end -local t,e,a,o=self:_getSelectionRange() -local e=self:_collectRange(t,e,a,o) -return#e -end -function o:getSelectionText() -if not self:_hasSelection()then -return"" -end -local e,a,t,o=self:_getSelectionRange() -return self:_collectRange(e,a,t,o) -end -function o:_getSelectionRange() -if not self:_hasSelection()then -return nil -end -local e=self._selectionAnchor -local o,a=e.line,e.col -local e,t=self._cursorLine,self._cursorCol -if M(o,a,e,t)<=0 then -return o,a,e,t -else -return e,t,o,a -end -end -function o:_collectRange(t,i,a,o) -if t==a then -return(self._lines[t]or""):sub(i,o-1) -end -local e={} -e[#e+1]=(self._lines[t]or""):sub(i) -for t=t+1,a-1 do -e[#e+1]=self._lines[t]or"" -end -e[#e+1]=(self._lines[a]or""):sub(1,o-1) -return table.concat(e,"\n") -end -function o:_clearSelection() -self._selectionAnchor=nil -end -function o:_removeRange(e,a,t,o) -if e==t then -local t=self._lines[e] -self._lines[e]=t:sub(1,a-1)..t:sub(o) -else -local i=self._lines[e]:sub(1,a-1) -local a=self._lines[t]:sub(o) -for e=t,e+1,-1 do -table.remove(self._lines,e) -end -self._lines[e]=i..a -end -if#self._lines==0 then -self._lines[1]="" -end -end -function o:_insertAt(e,a,t) -if t==nil or t==""then -return e,a -end -local t=Y(t) -local o=self._lines[e] -local i=o:sub(1,a-1) -local a=o:sub(a) -self._lines[e]=i..t[1] -local e=e -for a=2,#t do -e=e+1 -table.insert(self._lines,e,t[a]) -end -self._lines[e]=self._lines[e]..a -local t=(#self._lines[e]-#a)+1 -return e,t -end -function o:_deleteSelection(n) -local e,t,a,o=self:_getSelectionRange() -if not e then -return 0 -end -local i=self:_collectRange(e,t,a,o) -self:_removeRange(e,t,a,o) -self._cursorLine=e -self._cursorCol=t -self._preferredCol=self._cursorCol -self:_clearSelection() -self:_syncTextFromLines() -self:_ensureCursorVisible() -if not n then -self:_notifyChange() -end -self:_notifyCursorChange() -return#i -end -function o:_replaceSelection(e,a) -local t=0 -if self:_hasSelection()then -t=self:_deleteSelection(true) -end -local t=#self.text -if self.maxLength then -local t=self.maxLength-t -if#e>t then -e=e:sub(1,t) -end -end -local t,e=self:_insertAt(self._cursorLine,self._cursorCol,e) -self._cursorLine=t -self._cursorCol=e -self._preferredCol=self._cursorCol -self:_clearSelection() -self:_syncTextFromLines() -self:_ensureCursorVisible() -if not a then -self:_notifyChange() -end -self:_notifyCursorChange() -return true -end -function o:_insertTextAtCursor(e) -if not e or e==""then -return false -end -if self.numericOnly then -local t=self:_sanitizeNumericInput(e) -if t==""then -return false -end -if not self:_allowsNumericInsertion(t)then -return false -end -e=t -end -return self:_replaceSelection(e,false) -end -function o:_insertCharacter(e) -if not e or e==""then -return false -end -return self:_insertTextAtCursor(e) -end -function o:_insertNewline() -if self.numericOnly then -return false -end -if not self.multiline then -return false -end -return self:_insertTextAtCursor("\n") -end -function o:_insertTab() -if self.numericOnly then -return false -end -local e=string.rep(" ",self.tabWidth) -return self:_insertTextAtCursor(e) -end -function o:_deleteBackward() -if self:_hasSelection()then -return self:_deleteSelection(false)>0 -end -if self._cursorLine==1 and self._cursorCol==1 then -return false -end -if self._cursorCol>1 then -local e=self._lines[self._cursorLine] -self._lines[self._cursorLine]=e:sub(1,self._cursorCol-2)..e:sub(self._cursorCol) -self._cursorCol=self._cursorCol-1 -else -local e=self._lines[self._cursorLine-1] -local t=self._lines[self._cursorLine] -local a=#e -self._lines[self._cursorLine-1]=e..t -table.remove(self._lines,self._cursorLine) -self._cursorLine=self._cursorLine-1 -self._cursorCol=a+1 -end -self._preferredCol=self._cursorCol -self:_syncTextFromLines() -self:_ensureCursorVisible() -self:_notifyChange() -self:_notifyCursorChange() -return true -end -function o:_deleteForward() -if self:_hasSelection()then -return self:_deleteSelection(false)>0 -end -local e=self._lines[self._cursorLine] -if self._cursorCol<=#e then -self._lines[self._cursorLine]=e:sub(1,self._cursorCol-1)..e:sub(self._cursorCol+1) -else -if self._cursorLine>=#self._lines then -return false -end -local t=table.remove(self._lines,self._cursorLine+1) -self._lines[self._cursorLine]=e..t -end -self:_syncTextFromLines() -self:_ensureCursorVisible() -self:_notifyChange() -self:_notifyCursorChange() -return true -end -function o:_setCursorPosition(t,a,e) -e=e or{} -t=O(t,1,#self._lines) -local o=self._lines[t]or"" -a=O(a,1,#o+1) -if e.extendSelection then -if not self._selectionAnchor then -self._selectionAnchor={line=self._cursorLine,col=self._cursorCol} -end -else -self:_clearSelection() -end -self._cursorLine=t -self._cursorCol=a -if not e.preservePreferred then -self._preferredCol=a -end -if self._selectionAnchor and self._selectionAnchor.line==self._cursorLine and self._selectionAnchor.col==self._cursorCol then -self:_clearSelection() -end -self:_ensureCursorVisible() -self:_notifyCursorChange() -if not e.keepAutocomplete then -self:_hideAutocomplete() -end -end -function o:_moveCursorLeft(e) -if self:_hasSelection()and not e then -local t,e=self:_getSelectionRange() -self:_setCursorPosition(t,e) -return -end -if self._cursorCol>1 then -self:_setCursorPosition(self._cursorLine,self._cursorCol-1,{extendSelection=e}) -elseif self._cursorLine>1 then -local t=self._cursorLine-1 -local a=(#self._lines[t]or 0)+1 -self:_setCursorPosition(t,a,{extendSelection=e}) -end -end -function o:_moveCursorRight(e) -if self:_hasSelection()and not e then -local a,a,e,t=self:_getSelectionRange() -self:_setCursorPosition(e,t) -return -end -local t=self._lines[self._cursorLine] -if self._cursorCol<=#t then -self:_setCursorPosition(self._cursorLine,self._cursorCol+1,{extendSelection=e}) -elseif self._cursorLine<#self._lines then -self:_setCursorPosition(self._cursorLine+1,1,{extendSelection=e}) -end -end -function o:_moveCursorVertical(e,t) -local e=O(self._cursorLine+e,1,#self._lines) -local a=self._lines[e]or"" -local a=O(self._preferredCol,1,#a+1) -self:_setCursorPosition(e,a,{extendSelection=t,preservePreferred=true}) -end -function o:_moveCursorUp(e) -self:_moveCursorVertical(-1,e) -end -function o:_moveCursorDown(e) -self:_moveCursorVertical(1,e) -end -function o:_moveCursorLineStart(e) -self:_setCursorPosition(self._cursorLine,1,{extendSelection=e}) -end -function o:_moveCursorLineEnd(t) -local e=self._lines[self._cursorLine] -self:_setCursorPosition(self._cursorLine,#e+1,{extendSelection=t}) -end -function o:_moveCursorDocumentStart(e) -self:_setCursorPosition(1,1,{extendSelection=e}) -end -function o:_moveCursorDocumentEnd(t) -local e=#self._lines -local a=#self._lines[e] -self:_setCursorPosition(e,a+1,{extendSelection=t}) -end -function o:_selectAll() -self._selectionAnchor={line=1,col=1} -self:_setCursorPosition(#self._lines,(#self._lines[#self._lines]or 0)+1,{extendSelection=true,keepAutocomplete=true}) -end -function o:_scrollLines(e) -if e==0 then -return -end -local a,t=self:_getContentSize() -local t=math.max(0,#self._lines-t) -self._scrollY=O(self._scrollY+e,0,t) -end -function o:_scrollColumns(e) -if e==0 then -return -end -local t=select(1,self:_getContentSize()) -local a=self._lines[self._cursorLine]or"" -local t=math.max(0,#a-t) -self._scrollX=O(self._scrollX+e,0,t) -end -function o:_cursorFromPoint(a,t) -local e=self:_computeLayoutMetrics() -local o=e.innerX -local i=e.innerY -local n=math.max(1,e.contentWidth) -local e=math.max(1,e.contentHeight) -local a=O(a-o,0,n-1) -local e=O(t-i,0,e-1) -local e=O(self._scrollY+e+1,1,#self._lines) -local t=self._lines[e]or"" -local t=O(self._scrollX+a+1,1,#t+1) -return e,t -end -function o:_computeSyntaxColors(o) -local t=self.syntax -if not t then -return nil -end -local a={} -local i=t.defaultColor or self.fg or e.white -for e=1,#o do -a[e]=i -end -local e=1 -while e<=#o do -local i=o:sub(e,e) -if i=='"'or i=="'"then -local i=i -a[e]=t.stringColor or a[e] -e=e+1 -while e<=#o do -a[e]=t.stringColor or a[e] -local t=o:sub(e,e) -if t==i and o:sub(e-1,e-1)~="\\"then -e=e+1 -break -end -e=e+1 -end -else -e=e+1 -end -end -for e,n,o in o:gmatch("()(%d+%.?%d*)()")do -if t.numberColor then -for e=e,o-1 do -if a[e]==i then -a[e]=t.numberColor -end -end -end -end -for s,n,o in o:gmatch("()([%a_][%w_]*)()")do -local e=n:lower() -if t.keywords and t.keywords[e]then -if t.keywordColor then -for e=s,o-1 do -if a[e]==i then -a[e]=t.keywordColor -end -end -end -elseif t.builtins and t.builtins[n]then -if t.builtinColor then -for e=s,o-1 do -if a[e]==i then -a[e]=t.builtinColor -end -end -end -end -end -local e=o:find("--",1,true) -if e then -local t=t.commentColor or i -for e=e,#o do -a[e]=t -end -end -return a -end -local function R(t,a,o,i) -if a==""then -return -end -local e=t[#t] -if e and e.fg==o and e.bg==i then -e.text=e.text..a -else -t[#t+1]={text=a,fg=o,bg=i} -end -end -function o:_buildLineSegments(n,t,r,h,e) -local a=self._lines[n]or"" -local o=self:_computeSyntaxColors(a) -local i=self._scrollX+1 -local s={} -for t=0,t-1 do -local t=i+t -local i -if t<=#a then -i=a:sub(t,t) -else -i=" " -end -local o=o and o[t]or r -local a=h -if e and V(n,t,e.startLine,e.startCol,e.endLine,e.endCol)then -a=self.selectionBg -o=self.selectionFg -end -R(s,i,o,a) -end -return s,a,o -end -function o:_drawSegments(a,t,o,e) -local t=t -for i=1,#e do -local e=e[i] -if e.text~=""then -a.text(t,o,e.text,e.fg,e.bg) -t=t+#e.text -end -end -end -function o:_drawFindOverlay(h,a,n,t,i) -if not self._find.visible then -return -end -local o=self:_getOverlayHeight(i) -if o<=0 then -return -end -local r=self.overlayBg or self.bg or e.gray -local d=self.overlayFg or self.fg or e.white -local c=self.overlayActiveBg or e.orange -local u=self.overlayActiveFg or e.black -local n=n+i-o -for e=0,o-1 do -h.text(a,n+e,string.rep(" ",t),d,r) -end -local e=self._find -local i=#e.matches -local i=i>0 and string.format("%d/%d",math.max(1,e.index),i)or"0/0" -local s=e.matchCase and"CASE"or"case" -local i=string.format("Find: %s %s %s",e.findText,i,s) -local l="Replace: "..e.replaceText -local s=i -if#s>t then -s=s:sub(1,t) -end -local i=l -if#i>t then -i=i:sub(1,t) -end -h.text(a,n,s..string.rep(" ",math.max(0,t-#s)),d,r) -h.text(a,n+math.max(o-1,0),i..string.rep(" ",math.max(0,t-#i)),d,r) -local i,s,l -if e.activeField=="find"then -i=a+6 -s=n -l=e.findText -else -i=a+9 -s=n+math.max(o-1,0) -l=e.replaceText -end -local e=l -if#e>t-(i-a)then -e=e:sub(1,t-(i-a)) -end -h.text(i,s,e..string.rep(" ",math.max(0,t-(i-a)-#e)),u,c) -if o>=2 then -local e="Ctrl+G next | Ctrl+Shift+G prev | Tab switch | Enter apply | Esc close" -if#e>t then -e=e:sub(1,t) -end -h.text(a,n+o-1,e..string.rep(" ",math.max(0,t-#e)),d,r) -end -end -function o:_setAutocompleteVisible(e) -local t=self._autocompleteState -e=not not e -if t.visible==e then -if not e then -t.rect=nil -end -return -end -t.visible=e -if e then -self._open=true -if self.app then -self.app:_registerPopup(self) -end -else -self._open=false -t.rect=nil -if self.app then -self.app:_unregisterPopup(self) -end -end -end -function o:_refreshAutocompleteGhost() -local e=self._autocompleteState -e.ghost=self:_computeAutocompleteGhost(e.items[e.selectedIndex],e.prefix,e.trigger) -end -function o:_hideAutocomplete() -local e=self._autocompleteState -if e.visible then -self:_setAutocompleteVisible(false) -else -e.rect=nil -end -e.items={} -e.ghost="" -e.prefix="" -e.trigger="auto" -e.selectedIndex=1 -e.anchorLine=self._cursorLine -e.anchorCol=self._cursorCol -end -function o:_isPointInAutocomplete(t,a) -local e=self._autocompleteState and self._autocompleteState.rect -if not e then -return false -end -return t>=e.x and t=e.y and a=e.contentY+e.itemCount then -return nil -end -if a=e.contentX+e.itemWidth then -return nil -end -local t=t-e.contentY+1 -if t<1 or t>e.itemCount then -return nil -end -return t -end -function o:_drawDropdown(c,y) -local t=self._autocompleteState -if not self.visible or not self._open then -if t then -t.rect=nil -end -return -end -if not t or not t.visible or#t.items==0 then -if t then -t.rect=nil -end -return -end -local a=self:_computeLayoutMetrics() -local w=a.innerX -local o=a.innerY -local h=a.contentWidth -local a=a.contentHeight -if h<=0 or a<=0 then -t.rect=nil -return -end -local a=O(t.anchorLine-(self._scrollY+1),0,a-1) -local f=o+a -local a=self.autocompleteBorder -local m=(a and a.top)and 1 or 0 -local o=(a and a.bottom)and 1 or 0 -local u=(a and a.left)and 1 or 0 -local r=(a and a.right)and 1 or 0 -local d=#t.items -local i=d+m+o -if i<=0 then -t.rect=nil -return -end -local o=0 -for e=1,d do -local e=t.items[e] -local e=e and e.label or"" -if#e>o then -o=#e -end -end -local s=self.autocompleteMaxWidth or h -s=math.max(1,s) -local l=math.min(h,s) -local o=math.max(l,o) -if o>s then -o=s -end -local l=self.app and self.app.root and self.app.root.width or(w+h-1) -if o+u+r>l then -o=math.max(1,l-u-r) -end -local r=o+u+r -if r<=0 or o<=0 then -t.rect=nil -return -end -local s=O(t.anchorCol-self._scrollX-1,0,h-1) -local s=w+s -local s=s -if s+r-1>l then -s=math.max(1,l-r+1) -end -if s<1 then -s=1 -end -local h=f+1 -if m>0 then -h=h+1 -end -local w=self.app and self.app.root and self.app.root.height or(f+i) -local h=h -if h+i-1>w then -local e=f-i -if e>=1 then -h=e -else -h=math.max(1,w-i+1) -end -end -local f=s+u -local u=h+m -t.rect={ -x=s, -y=h, -width=r, -height=i, -contentX=f, -contentY=u, -itemWidth=o, -itemCount=d -} -local l=self.autocompleteBg or self.bg or e.gray -n(c,s,h,r,i,l,l) -z(c,s,h,r,i) -local n=self.autocompleteFg or self.fg or e.white -local m=self.autocompleteHighlightBg or e.lightBlue -local v=self.autocompleteHighlightFg or e.black -for a=1,d do -local i=u+a-1 -if i<1 or i>w then -break -end -local e=t.items[a] -local e=e and e.label or"" -if#e>o then -e=e:sub(1,o) -end -local o=o-#e -if o>0 then -e=e..string.rep(" ",o) -end -local o=(a==t.selectedIndex)and m or l -local t=(a==t.selectedIndex)and v or n -c.text(f,i,e,t,o) -end -if a then -p(y,s,h,r,i,a,l) -end -end -function o:_updateAutocomplete(n) -if not self.autocomplete then -self:_hideAutocomplete() -return -end -local e=self._lines[self._cursorLine]or"" -local a=self._cursorCol-1 -local t=a -while t>=1 do -local e=e:sub(t,t) -if not e:match("[%w_]")then -break -end -t=t-1 -end -t=t+1 -local o=e:sub(t,a) -if o==""and n~="manual"then -self:_hideAutocomplete() -return -end -local e={} -if type(self.autocomplete)=="function"then -local a,t=pcall(self.autocomplete,self,o) -if a and type(t)=="table"then -e=t -end -elseif type(self.autocomplete)=="table"then -e=self.autocomplete -end -local a={} -local i=o:lower() -for t=1,#e do -local e=e[t] -if type(e)=="string"then -local t=e:lower() -if o==""or t:sub(1,#i)==i then -a[#a+1]={label=e,insert=e} -end -elseif type(e)=="table"and e.label then -local t=e.label -local n=t:lower() -if o==""or n:sub(1,#i)==i then -a[#a+1]={label=t,insert=e.insert or t} -end -end -end -if#a==0 then -self:_hideAutocomplete() -return -end -local e=self._autocompleteState -local i -if e.visible and e.items and e.selectedIndex and e.items[e.selectedIndex]then -local e=e.items[e.selectedIndex] -i=e.insert or e.label -end -e.trigger=n or"auto" -self:_setAutocompleteVisible(true) -e.items={} -local s=math.min(self.autocompleteMaxItems,#a) -local n=1 -for t=1,s do -local a=a[t] -e.items[t]=a -if i then -local e=a.insert or a.label -if e==i then -n=t -end -end -end -e.selectedIndex=n -e.anchorLine=self._cursorLine -e.anchorCol=t -e.prefix=o -self:_refreshAutocompleteGhost() -e.rect=nil -end -function o:_computeAutocompleteGhost(e,t,a) -if not e then -return"" -end -local e=e.insert or e.label or"" -if e==""then -return"" -end -if t==""then -if a=="manual"then -return e -end -return"" -end -local o=e:lower() -local a=t:lower() -if o:sub(1,#t)~=a then -return"" -end -return e:sub(#t+1) -end -function o:_acceptAutocomplete() -local e=self._autocompleteState -if not e.visible or#e.items==0 then -return false -end -local t=e.items[e.selectedIndex] -if not t then -return false -end -local o,a=self._cursorLine,self._cursorCol -self._selectionAnchor={line=e.anchorLine,col=e.anchorCol} -self._cursorLine=o -self._cursorCol=a -self:_replaceSelection(t.insert or t.label or"",false) -self:_hideAutocomplete() -return true -end -function o:_moveAutocompleteSelection(a) -local e=self._autocompleteState -if not e.visible then -return -end -local t=#e.items -if t==0 then -return -end -e.selectedIndex=((e.selectedIndex-1+a)%t)+1 -self:_refreshAutocompleteGhost() -end -function o:_toggleFindOverlay(e) -local t=self._find -if t.visible and(not e or t.activeField==e)then -self:_closeFindOverlay() -return -end -t.visible=true -if e then -t.activeField=e -end -if self:_hasSelection()and e=="find"then -t.findText=self:getSelectionText() -end -self:_updateFindMatches(true) -end -function o:_closeFindOverlay() -local e=self._find -if e.visible then -e.visible=false -e.matches={} -e.index=0 -end -end -function o:_toggleFindField() -local e=self._find -if not e.visible then -return -end -if e.activeField=="find"then -e.activeField="replace" -else -e.activeField="find" -end -end -function o:_editFindFieldText(t) -local e=self._find -if not e.visible then -return -end -t=tostring(t or"") -t=t:gsub("[\r\n]"," ") -if e.activeField=="find"then -e.findText=e.findText..t -self:_updateFindMatches(true) -elseif e.activeField=="replace"then -e.replaceText=e.replaceText..t -end -end -function o:_handleOverlayBackspace() -local e=self._find -if not e.visible then -return false -end -if e.activeField=="find"then -if#e.findText==0 then -return false -end -e.findText=e.findText:sub(1,-2) -self:_updateFindMatches(true) -else -if#e.replaceText==0 then -return false -end -e.replaceText=e.replaceText:sub(1,-2) -end -return true -end -function o:_updateFindMatches(t) -local e=self._find -e.matches={} -e.index=t and 0 or e.index -if not e.visible or e.findText==""then -return -end -local t=e.findText -local o=e.matchCase -for i=1,#self._lines do -local a=self._lines[i] -local n=o and a or a:lower() -local t=o and t or t:lower() -local a=1 -while true do -local t,o=n:find(t,a,true) -if not t then -break -end -e.matches[#e.matches+1]={ -line=i, -col=t, -length=o-t+1 -} -a=t+1 -end -end -end -function o:_selectMatch(e) -if not e then -return -end -self._selectionAnchor={line=e.line,col=e.col} -self:_setCursorPosition(e.line,e.col+e.length,{extendSelection=true,keepAutocomplete=true}) -self:_ensureCursorVisible() -self:_notifyCursorChange() -end -function o:_gotoMatch(o) -local e=self._find -if not e.visible then -return false -end -self:_updateFindMatches(false) -if#e.matches==0 then -return false -end -if e.index<1 then -local t=1 -for a=1,#e.matches do -local e=e.matches[a] -if M(e.line,e.col,self._cursorLine,self._cursorCol)>=0 then -t=a -break -end -end -e.index=t -else -e.index=((e.index-1+o)%#e.matches)+1 -end -self:_selectMatch(e.matches[e.index]) -return true -end -function o:_gotoNextMatch() -return self:_gotoMatch(1) -end -function o:_gotoPreviousMatch() -return self:_gotoMatch(-1) -end -function o:_replaceCurrentMatch() -local e=self._find -if not e.visible or#e.matches==0 then -return false -end -if e.index<1 or e.index>#e.matches then -e.index=1 -end -local t=e.matches[e.index] -self._selectionAnchor={line=t.line,col=t.col} -self:_setCursorPosition(t.line,t.col+t.length,{extendSelection=true,keepAutocomplete=true}) -self:_replaceSelection(e.replaceText or"",false) -self:_updateFindMatches(true) -return true -end -function o:_replaceAll() -local e=self._find -if not e.visible or e.findText==""then -return false -end -self:_updateFindMatches(true) -if#e.matches==0 then -return false -end -for t=#e.matches,1,-1 do -local t=e.matches[t] -local a=t.line -local o=t.col -local i=self._lines[a] -self._lines[a]=i:sub(1,o-1)..(e.replaceText or"")..i:sub(o+t.length) -end -self:_syncTextFromLines() -self:_ensureCursorVisible() -self:_notifyChange() -self:_notifyCursorChange() -self:_updateFindMatches(true) -return true -end -function o:_handleEscape() -if self._find.visible then -self:_closeFindOverlay() -return true -end -if self:_hasSelection()then -self:_clearSelection() -self:_notifyCursorChange() -return true -end -if self._autocompleteState.visible then -self:_hideAutocomplete() -return true -end -return false -end -function o:_handleKey(e,t) -if self._find.visible then -if e==a.tab then -self:_toggleFindField() -return true -elseif e==a.backspace then -return self:_handleOverlayBackspace() -elseif e==a.enter then -if self._find.activeField=="find"then -self:_gotoNextMatch() -else -self:_replaceCurrentMatch() -end -return true -elseif e==a.delete then -local e=self._find -if e.activeField=="find"then -e.findText="" -self:_updateFindMatches(true) -else -e.replaceText="" -end -return true -end -end -if self._ctrlDown then -if e==a.a then -self:_selectAll() -return true -elseif e==a.f then -self:_toggleFindOverlay("find") -return true -elseif e==a.h then -self:_toggleFindOverlay("replace") -return true -elseif e==a.g then -if self._shiftDown then -self:_gotoPreviousMatch() -else -self:_gotoNextMatch() -end -return true -elseif e==a.space then -self:_updateAutocomplete("manual") -return true -elseif e==a.r and self._shiftDown then -self:_replaceAll() -return true -elseif e==a.f and self._shiftDown then -local e=self._find -e.matchCase=not e.matchCase -self:_updateFindMatches(true) -return true -end -end -if self._autocompleteState.visible then -if e==a.enter or e==a.tab then -return self:_acceptAutocomplete() -elseif e==a.up then -self:_moveAutocompleteSelection(-1) -return true -elseif e==a.down then -self:_moveAutocompleteSelection(1) -return true -elseif e==a.escape then -self:_hideAutocomplete() -return true -end -end -if e==a.left then -self:_moveCursorLeft(self._shiftDown) -return true -elseif e==a.right then -self:_moveCursorRight(self._shiftDown) -return true -elseif e==a.up then -self:_moveCursorUp(self._shiftDown) -return true -elseif e==a.down then -self:_moveCursorDown(self._shiftDown) -return true -elseif e==a.home then -if self._ctrlDown then -self:_moveCursorDocumentStart(self._shiftDown) -else -self:_moveCursorLineStart(self._shiftDown) -end -return true -elseif e==a["end"]then -if self._ctrlDown then -self:_moveCursorDocumentEnd(self._shiftDown) -else -self:_moveCursorLineEnd(self._shiftDown) -end -return true -elseif e==a.backspace then -return self:_deleteBackward() -elseif e==a.delete then -return self:_deleteForward() -elseif e==a.enter then -return self:_insertNewline() -elseif e==a.tab then -return self:_insertTab() -elseif e==a.pageUp then -self:_scrollLines(-math.max(1,select(2,self:_getContentSize())-1)) -return true -elseif e==a.pageDown then -self:_scrollLines(math.max(1,select(2,self:_getContentSize())-1)) -return true -elseif e==a.escape then -return self:_handleEscape() -end -return false -end -function o:draw(a,q) -if not self.visible then -return -end -local b,y,m,w=self:getAbsoluteRect() -local s=self.bg or e.black -local f=self.fg or e.white -n(a,b,y,m,w,s,s) -z(a,b,y,m,w) -local o=self:_computeLayoutMetrics() -local d=o.innerX -local h=o.innerY -local t=o.innerWidth -local j=o.innerHeight -local i=o.contentWidth -local r=o.contentHeight -local c=o.overlayHeight -local u=o.scrollbarWidth -local v=o.scrollbarStyle -local k -local g=false -if self:_hasSelection()then -local t,o,a,e=self:_getSelectionRange() -k={ -startLine=t, -startCol=o, -endLine=a, -endCol=e -} -g=true -end -local t=self._autocompleteState -local l=s -for n=0,r-1 do -local o=self._scrollY+n+1 -local n=h+n -if o>#self._lines then -a.text(d,n,string.rep(" ",i),f,l) -else -local h,s,r=self:_buildLineSegments(o,i,f,l,k) -self:_drawSegments(a,d,n,h) -if self:isFocused()and o==self._cursorLine then -local e=self._cursorCol-self._scrollX-1 -if e>=0 and e=#t then -t="" -else -t=t:sub(e+1) -o=o+e -end -end -if t~=""then -if o<0 then -local e=-o -if e>=#t then -t="" -else -t=t:sub(e+1) -o=0 -end -end -if t~=""and o0 then -if#t>i then -t=t:sub(1,i) -end -if t~=""then -a.text(d+o,n,t,self.autocompleteGhostColor or e.lightGray,l) -end -end -end -end -end -end -end -end -if self.text==""and not self:isFocused()and self.placeholder~=""then -local t=self.placeholder -if#t>i then -t=t:sub(1,i) -end -local e=self.placeholderColor or e.lightGray -a.text(d,h,t..string.rep(" ",math.max(0,i-#t)),e,l) -end -self:_drawFindOverlay(a,d,h,i,j) -if v then -local t=o.scrollbarX -local e=v.background or s -n(a,t,h,u,r,e,e) -C(a,t,h,r,#self._lines,r,self._scrollY,v) -if c>0 then -n(a,t,h+r,u,c,e,e) -end -elseif u>0 then -n(a,o.scrollbarX,h,u,r+c,s,s) -end -if self.border then -p(q,b,y,m,w,self.border,s) -end -end -function o:handleEvent(t,...) -if not self.visible then -return false -end -if t=="mouse_click"then -local o,a,t=... -local i=self._autocompleteState -if i and i.visible and self:_isPointInAutocomplete(a,t)then -self.app:setFocus(self) -local e=self:_autocompleteIndexFromPoint(a,t) -if e then -if i.selectedIndex~=e then -i.selectedIndex=e -self:_refreshAutocompleteGhost() -end -if o==1 then -return self:_acceptAutocomplete() -elseif o==2 then -self:_hideAutocomplete() -return true -end -elseif o==2 then -self:_hideAutocomplete() -return true -end -return true -end -if self:containsPoint(a,t)then -self.app:setFocus(self) -local e=self:_computeLayoutMetrics() -if e.scrollbarStyle and e.scrollbarWidth>0 then -local o=e.scrollbarX -if a>=o and a=e.innerY and t0 then -local o=e.scrollbarX -if a>=o and a=e.innerY and t0 then -self:_moveAutocompleteSelection(1) -elseif e<0 then -self:_moveAutocompleteSelection(-1) -end -return true -end -if self:containsPoint(t,o)then -self:_scrollLines(e) -return true -end -elseif t=="char"then -local e=... -if self:isFocused()then -if self._find.visible then -self:_editFindFieldText(e) -return true -end -local e=self:_insertCharacter(e) -if e and self.autocompleteAuto then -self:_updateAutocomplete("auto") -end -return e -end -elseif t=="paste"then -local e=... -if self:isFocused()then -if self._find.visible then -self:_editFindFieldText(e) -return true -end -local e=self:_insertTextAtCursor(e) -if e and self.autocompleteAuto then -self:_updateAutocomplete("auto") -end -return e -end -elseif t=="key"then -local e,t=... -if e==a.leftShift or e==a.rightShift then -self._shiftDown=true -return true -elseif e==a.leftCtrl or e==a.rightCtrl then -self._ctrlDown=true -return true -end -if self:isFocused()then -return self:_handleKey(e,t) -end -elseif t=="key_up"then -local e=... -if e==a.leftShift or e==a.rightShift then -self._shiftDown=false -if not self:_hasSelection()then -self:_clearSelection() -end -return true -elseif e==a.leftCtrl or e==a.rightCtrl then -self._ctrlDown=false -return true -elseif e==a.escape then -if self:_handleEscape()then -return true -end -end -end -return false -end -function o:setText(e,a) -t(1,e,"string") -self:_setTextInternal(e,true,a) -end -function o:getText() -return self.text -end -function o:setOnChange(e) -if e~=nil then -t(1,e,"function") -end -self.onChange=e -end -function y.create(a) -if a~=nil then -t(1,a,"table") -end -a=a or{} -local s=false -local i -local o=a.window -if o==nil then -i=G.current() -local t,e=i.getSize() -o=ne.create(i,1,1,t,e,true) -o.setVisible(true) -s=true -end -local t=J.new(o) -t.profiler.start_frame() -t.profiler.start_region("user") -local d=t.add_pixel_layer(5,"pixelui_pixels") -local r=t.add_text_layer(10,"pixelui_ui") -local l,u=o.getSize() -local n=a.background or e.black -t.fill(n) -local c=math.max(.01,a.animationInterval or .05) -local t=setmetatable({ -window=o, -box=t, -layer=r, -pixelLayer=d, -background=n, -running=false, -_autoWindow=s, -_parentTerminal=i, -_focusWidget=nil, -_popupWidgets={}, -_popupLookup={}, -_animations={}, -_animationTimer=nil, -_animationInterval=c, -_radioGroups={}, -_threads={}, -_threadTimers={}, -_threadTicker=nil, -_threadIdCounter=0 -},h) -t.root=b:new(t,{ -x=1, -y=1, -width=l, -height=u, -bg=n, -fg=e.white, -border=a.rootBorder, -z=-math.huge -}) -return t -end -function h:getRoot() -return self.root -end -function h:setBackground(e) -t(1,e,"number") -self.background=e -self.box.fill(e) -end -function h:getLayer() -return self.layer -end -function h:getPixelLayer() -return self.pixelLayer -end -function h:createFrame(e) -return b:new(self,e) -end -function h:createWindow(e) -return i:new(self,e) -end -function h:createDialog(e) -return T:new(self,e) -end -function h:createMsgBox(e) -return A:new(self,e) -end -function h:createButton(e) -return S:new(self,e) -end -function h:createLabel(e) -return E:new(self,e) -end -function h:createCheckBox(e) -return _:new(self,e) -end -function h:createToggle(e) -return g:new(self,e) -end -function h:createTextBox(e) -return o:new(self,e) -end -function h:createComboBox(e) -return x:new(self,e) -end -function h:createTabControl(e) -return d:new(self,e) -end -function h:createContextMenu(e) -return m:new(self,e) -end -function h:createList(e) -return f:new(self,e) -end -function h:createTable(e) -return l:new(self,e) -end -function h:createTreeView(e) -return c:new(self,e) -end -function h:createChart(e) -return w:new(self,e) -end -function h:createRadioButton(e) -return q:new(self,e) -end -function h:createProgressBar(e) -return k:new(self,e) -end -function h:createNotificationToast(e) -return r:new(self,e) -end -function h:createLoadingRing(e) -return v:new(self,e) -end -function h:createFreeDraw(e) -return D:new(self,e) -end -function h:createSlider(e) -return u:new(self,e) -end -function h:_ensureAnimationTimer() -if not self._animationTimer then -self._animationTimer=I.startTimer(self._animationInterval) -end -end -function h:_updateAnimations() -local t=self._animations -if not t or#t==0 then -return -end -local o=I.clock() -local a=1 -while a<=#t do -local e=t[a] -if e._cancelled then -if e.onCancel then -e.onCancel(e.handle) -end -e._finished=true -table.remove(t,a) -else -if not e.startTime then -e.startTime=o -end -local i=o-e.startTime -local o -if e.duration<=0 then -o=1 -else -o=math.min(1,i/e.duration) -end -local i=e.easing(o) -if e.update then -e.update(i,o,e.handle) -end -if o>=1 then -e._finished=true -if e.onComplete then -e.onComplete(e.handle) -end -table.remove(t,a) -else -a=a+1 -end -end -end -end -function h:_clearAnimations(t) -local e=self._animations -if not e or#e==0 then -self._animations={} -self._animationTimer=nil -return -end -if t then -for t=1,#e do -local e=e[t] -if e and not e._finished then -if e.onCancel then -e.onCancel(e.handle) -end -e._finished=true -end -end -end -self._animations={} -self._animationTimer=nil -end -function h:animate(e) -t(1,e,"table") -local a=e.update -if a~=nil and type(a)~="function"then -error("options.update must be a function",2) -end -local i=e.onComplete -if i~=nil and type(i)~="function"then -error("options.onComplete must be a function",2) -end -local o=e.onCancel -if o~=nil and type(o)~="function"then -error("options.onCancel must be a function",2) -end -local t=e.easing -if t==nil then -t=H.linear -elseif type(t)=="string"then -t=H[t] -if not t then -error("Unknown easing '"..e.easing.."'",2) -end -elseif type(t)~="function"then -error("options.easing must be a function or easing name",2) -end -if e.duration~=nil and type(e.duration)~="number"then -error("options.duration must be a number",2) -end -local e=math.max(.01,e.duration or .3) -local e={ -update=a, -onComplete=i, -onCancel=o, -easing=t, -duration=e, -startTime=I.clock() -} -local t={} -function t:cancel() -if e._finished or e._cancelled then -return -end -e._cancelled=true -end -e.handle=t -self._animations[#self._animations+1]=e -if a then -a(0,0,t) -end -self:_ensureAnimationTimer() -return t -end -local a="running" -local p="completed" -local z="error" -local n="cancelled" -local R={} -local function O(e,a,...) -if not e then -return -end -for t=1,#e do -local e=e[t] -local t,e=pcall(e,...) -if not t then -print(a..tostring(e)) -end -end -end -function j:getId() -return self.id -end -function j:getName() -return self.name -end -function j:setName(e) -t(1,e,"string") -self.name=e -end -function j:getStatus() -return self.status -end -function j:isRunning() -return self.status==a -end -function j:isFinished() -local e=self.status -return e==p or e==z or e==n -end -function j:isCancelled() -return self._cancelRequested or self.status==n -end -function j:cancel() -if self.status~=a then -return false -end -self._cancelRequested=true -if self.waiting=="timer"and self.timerId then -local e=self.app._threadTimers -if e then -e[self.timerId]=nil -end -self.timerId=nil -end -self.waiting=nil -self._ready=true -self.app:_ensureThreadPump() -return true -end -function j:getResult() -if not self.result then -return nil -end -return K(self.result,1,self.result.n or#self.result) -end -function j:getResults() -if not self.result then -return nil -end -local e={n=self.result.n} -local t=self.result.n or#self.result -for t=1,t do -e[t]=self.result[t] -end -return e -end -function j:getError() -return self.error -end -function j:setMetadata(e,a) -t(1,e,"string") -local t=self.metadata[e] -if t==a then -return -end -self.metadata[e]=a -self:_emitMetadata(e,a) -end -function j:getMetadata(e) -t(1,e,"string") -return self.metadata[e] -end -function j:getAllMetadata() -local e={} -for a,t in pairs(self.metadata)do -e[a]=t -end -return e -end -function j:onStatusChange(e) -if e==nil then -return -end -t(1,e,"function") -local t=self._statusListeners -t[#t+1]=e -local e,t=pcall(e,self,self.status) -if not e then -print("Thread status listener error: "..tostring(t)) -end -end -function j:onMetadataChange(e) -if e==nil then -return -end -t(1,e,"function") -local t=self._metadataListeners -t[#t+1]=e -for a,t in pairs(self.metadata)do -local e,t=pcall(e,self,a,t) -if not e then -print("Thread metadata listener error: "..tostring(t)) -end -end -end -function j:_emitMetadata(e,t) -O(self._metadataListeners,"Thread metadata listener error: ",self,e,t) -end -function j:_setStatus(e) -if self.status==e then -return -end -self.status=e -O(self._statusListeners,"Thread status listener error: ",self,e) -end -local function O(e) -return setmetatable({_handle=e},N) -end -function N:checkCancelled() -if self._handle._cancelRequested then -error(R,0) -end -end -function N:isCancelled() -return self._handle._cancelRequested==true -end -function N:sleep(a) -if a~=nil then -t(1,a,"number") -else -a=0 -end -if a<0 then -a=0 -end -self:checkCancelled() -local e=self._handle -if e.timerId then -local t=e.app._threadTimers -if t then -t[e.timerId]=nil -end -e.timerId=nil -end -e.waiting="timer" -local a=I.startTimer(a) -e.timerId=a -local t=e.app._threadTimers -if not t then -t={} -e.app._threadTimers=t -end -t[a]=e -e._ready=false -return coroutine.yield("sleep") -end -function N:yield() -self:checkCancelled() -self._handle.waiting="yield" -return coroutine.yield("yield") -end -function N:setMetadata(t,e) -self._handle:setMetadata(t,e) -end -function N:setStatus(e) -self._handle:setMetadata("status",e) -end -function N:setDetail(e) -self._handle:setMetadata("detail",e) -end -function N:setProgress(e) -if e~=nil then -t(1,e,"number") -end -self._handle:setMetadata("progress",e) -end -function N:getHandle() -return self._handle -end -function h:_ensureThreadPump() -if not self._threads or self._threadTicker then -return -end -for e=1,#self._threads do -local e=self._threads[e] -if e and e.status==a and e._ready then -self._threadTicker=I.startTimer(0) -return -end -end -end -function h:_cleanupThread(e) -if e.timerId and self._threadTimers then -self._threadTimers[e.timerId]=nil -e.timerId=nil -end -e.waiting=nil -e._ready=false -e._resumeValue=nil -end -function h:_resumeThread(e) -if e.status~=a then -return -end -if e._cancelRequested then -e:_setStatus(n) -self:_cleanupThread(e) -return -end -local t=e._resumeValue -e._resumeValue=nil -local t=Q(coroutine.resume(e.co,t)) -local a=t[1] -if not a then -local t=t[2] -if t==R then -e:_setStatus(n) -else -if type(t)=="string"and debug and debug.traceback then -t=debug.traceback(e.co,t) -end -e.error=t -print("PixelUI thread error: "..tostring(t)) -e:_setStatus(z) -end -self:_cleanupThread(e) -return -end -if coroutine.status(e.co)=="dead"then -local a={n=t.n-1} -for o=2,t.n do -a[o-1]=t[o] -end -e.result=a -e:_setStatus(p) -self:_cleanupThread(e) -return -end -local t=t[2] -e.waiting=nil -if t=="sleep"then -return -elseif t=="yield"then -e._ready=true -else -e._ready=true -end -self:_ensureThreadPump() -end -function h:_serviceThreads() -if not self._threads or#self._threads==0 then -return -end -local t={} -for e=1,#self._threads do -local e=self._threads[e] -if e and e.status==a and e._ready then -e._ready=false -t[#t+1]=e -end -end -for e=1,#t do -self:_resumeThread(t[e]) -end -self:_ensureThreadPump() -end -function h:_shutdownThreads() -if not self._threads then -return -end -for e=1,#self._threads do -local e=self._threads[e] -if e and e.status==a then -e._cancelRequested=true -e:_setStatus(n) -self:_cleanupThread(e) -end -end -self._threadTimers={} -self._threadTicker=nil -end -function h:spawnThread(o,e) -t(1,o,"function") -if e~=nil then -t(2,e,"table") -else -e={} -end -if not self._threads then -self._threads={} -end -if not self._threadTimers then -self._threadTimers={} -end -self._threadIdCounter=(self._threadIdCounter or 0)+1 -local t=self._threadIdCounter -local i=e.name or("Thread "..tostring(t)) -local t=setmetatable({ -app=self, -id=t, -name=i, -status=a, -co=nil, -waiting=nil, -timerId=nil, -_ready=true, -_cancelRequested=false, -_resumeValue=nil, -metadata={}, -result=nil, -error=nil, -_statusListeners={}, -_metadataListeners={} -},j) -local a=coroutine.create(function() -local e=O(t) -t._context=e -local e=Q(o(e,self)) -return K(e,1,e.n) -end) -t.co=a -self._threads[#self._threads+1]=t -if e.onStatus then -t:onStatusChange(e.onStatus) -end -if e.onMetadata then -t:onMetadataChange(e.onMetadata) -end -self:_ensureThreadPump() -return t -end -function h:getThreads() -local e={} -if not self._threads then -return e -end -for t=1,#self._threads do -e[t]=self._threads[t] -end -return e -end -function h:_registerPopup(e) -if not e then -return -end -local t=self._popupLookup -if not t[e]then -t[e]=true -table.insert(self._popupWidgets,e) -end -end -function h:_unregisterPopup(e) -if not e then -return -end -local t=self._popupLookup -if not t[e]then -return -end -t[e]=nil -local t=self._popupWidgets -for a=#t,1,-1 do -if t[a]==e then -table.remove(t,a) -break -end -end -end -function h:_drawPopups() -local a=self._popupWidgets -if not a or#a==0 then -return -end -local o=self.layer -local i=self.pixelLayer -local t=1 -while t<=#a do -local e=a[t] -if e and e._open and e.visible~=false then -e:_drawDropdown(o,i) -t=t+1 -else -if e then -self._popupLookup[e]=nil -end -table.remove(a,t) -end -end -end -function h:_dispatchPopupEvent(o,...) -local t=self._popupWidgets -if not t or#t==0 then -return false -end -for a=#t,1,-1 do -local e=t[a] -if e and e._open and e.visible~=false then -if e:handleEvent(o,...)then -return true -end -else -if e then -self._popupLookup[e]=nil -end -table.remove(t,a) -end -end -return false -end -function h:_registerRadioButton(e) -if not e or not e.group then -return -end -local a=e.group -local o=self._radioGroups -local t=o[a] -if not t then -t={buttons={},lookup={},selected=nil} -o[a]=t -end -if not t.lookup[e]then -t.lookup[e]=true -t.buttons[#t.buttons+1]=e -end -e._registeredGroup=a -if t.selected then -if t.selected==e then -e:_applySelection(true,true) -else -e:_applySelection(false,true) -end -elseif e.selected then -self:_selectRadioInGroup(a,e,true) -end -end -function h:_unregisterRadioButton(t) -if not t then -return -end -local a=t._registeredGroup -if not a then -return -end -local e=self._radioGroups[a] -if not e then -t._registeredGroup=nil -return -end -e.lookup[t]=nil -for a=#e.buttons,1,-1 do -if e.buttons[a]==t then -table.remove(e.buttons,a) -break -end -end -if e.selected==t then -e.selected=nil -for t=1,#e.buttons do -local e=e.buttons[t] -if e then -e:_applySelection(false,true) -end -end -end -t._registeredGroup=nil -if not next(e.lookup)then -self._radioGroups[a]=nil -end -end -function h:_selectRadioInGroup(o,t,a) -if not o then -return -end -a=not not a -local i=self._radioGroups -local e=i[o] -if not e then -e={buttons={},lookup={},selected=nil} -i[o]=e -end -if t then -if not e.lookup[t]then -e.lookup[t]=true -e.buttons[#e.buttons+1]=t -end -t._registeredGroup=o -end -e.selected=t -for o=1,#e.buttons do -local e=e.buttons[o] -if e then -if e==t then -e:_applySelection(true,a) -else -e:_applySelection(false,a) -end -end -end -end -function h:setFocus(e) -if e~=nil then -t(1,e,"table") -if e.app~=self then -error("Cannot focus widget from a different PixelUI app",2) -end -if not e.focusable then -e=nil -end -end -if self._focusWidget==e then -return -end -if self._focusWidget then -local e=self._focusWidget -e:setFocused(false) -end -self._focusWidget=e -if e then -e:setFocused(true) -end -end -function h:getFocus() -return self._focusWidget -end -function h:render() -self.box.fill(self.background) -self.pixelLayer.clear() -self.layer.clear() -self.root:draw(self.layer,self.pixelLayer) -self:_drawPopups() -self.box.render() -end -function h:step(e,...) -if not e then -return -end -local t=false -if e=="timer"then -local o=... -if self._threadTicker and o==self._threadTicker then -self._threadTicker=nil -self:_serviceThreads() -t=true -end -local i=self._threadTimers -if i then -local e=i[o] -if e then -i[o]=nil -if e.status==a and e.timerId==o then -e.timerId=nil -e.waiting=nil -e._ready=true -e._resumeValue=true -end -t=true -end -end -if self._animationTimer and o==self._animationTimer then -self:_updateAnimations() -if self._animations and#self._animations>0 then -self._animationTimer=I.startTimer(self._animationInterval) -else -self._animationTimer=nil -end -t=true -end -end -if not t and e=="term_resize"then -if self._autoWindow then -local e=self._parentTerminal or G.current() -local e,t=e.getSize() -if self.window.reposition then -self.window.reposition(1,1,e,t) -end -end -local t,e=self.window.getSize() -self.root:setSize(t,e) -end -if not t and(e=="char"or e=="paste"or e=="key"or e=="key_up")then -local a=self._focusWidget -if a and a.visible~=false then -t=a:handleEvent(e,...) -end -end -if not t and(e=="mouse_click"or e=="mouse_up"or e=="mouse_drag"or e=="mouse_move"or e=="mouse_scroll"or e=="monitor_touch")then -t=self:_dispatchPopupEvent(e,...) -end -if not t then -t=self.root:handleEvent(e,...) -end -if not t and(e=="mouse_click"or e=="monitor_touch")then -self:setFocus(nil) -end -self:_serviceThreads() -self:render() -end -function h:run() -self.running=true -self:render() -while self.running do -local e={ie()} -if e[1]=="terminate"then -self.running=false -else -self:step(table.unpack(e)) -end -end -self:_shutdownThreads() -end -function h:stop() -self.running=false -self:_clearAnimations(true) -self:_shutdownThreads() -end -y.widgets={ -Frame=function(e,t) -return b:new(e,t) -end, -Window=function(t,e) -return i:new(t,e) -end, -Dialog=function(t,e) -return T:new(t,e) -end, -MsgBox=function(e,t) -return A:new(e,t) -end, -Button=function(t,e) -return S:new(t,e) -end, -Label=function(t,e) -return E:new(t,e) -end, -CheckBox=function(e,t) -return _:new(e,t) -end, -Toggle=function(t,e) -return g:new(t,e) -end, -TextBox=function(t,e) -return o:new(t,e) -end, -ComboBox=function(e,t) -return x:new(e,t) -end, -TabControl=function(t,e) -return d:new(t,e) -end, -ContextMenu=function(t,e) -return m:new(t,e) -end, -List=function(e,t) -return f:new(e,t) -end, -Table=function(t,e) -return l:new(t,e) -end, -TreeView=function(t,e) -return c:new(t,e) -end, -Chart=function(e,t) -return w:new(e,t) -end, -RadioButton=function(t,e) -return q:new(t,e) -end, -ProgressBar=function(t,e) -return k:new(t,e) -end, -Slider=function(e,t) -return u:new(e,t) -end, -LoadingRing=function(t,e) -return v:new(t,e) -end, -FreeDraw=function(t,e) -return D:new(t,e) -end, -NotificationToast=function(e,t) -return r:new(e,t) -end -} -y.Widget=s -y.Frame=b -y.Window=i -y.Dialog=T -y.MsgBox=A -y.Button=S -y.Label=E -y.CheckBox=_ -y.Toggle=g -y.TextBox=o -y.ComboBox=x -y.TabControl=d -y.ContextMenu=m -y.List=f -y.Table=l -y.TreeView=c -y.Chart=w -y.RadioButton=q -y.ProgressBar=k -y.Slider=u -y.LoadingRing=v -y.FreeDraw=D -y.NotificationToast=r -y.easings=H -y.ThreadHandle=j -y.ThreadContext=N -y.threadStatus={ -running=a, -completed=p, -error=z, -cancelled=n -} -return y diff --git a/global-libraries/shrekbox.lua b/global-libraries/shrekbox.lua deleted file mode 100644 index 7d3931e..0000000 --- a/global-libraries/shrekbox.lua +++ /dev/null @@ -1,1053 +0,0 @@ -local shrekbox = {} - ----@class Layer ----@field _render fun(rblit:blitmap) ----@field pixel fun(lx:integer,ly:integer,color:ccTweaked.colors.color) ----@field text fun(lx:integer,ly:integer,text:string,fg:ccTweaked.colors.color?,bg:ccTweaked.colors.color?) ----@field set_pos fun(sx:integer,sy:integer) ----@field get_pos fun():integer,integer ----@field stl_coords fun(sx:number,sy:number):number,number ----@field lts_coords fun(sx:number,sy:number):number,number ----@field buffer ccTweaked.colors.color[][] ----@field size fun():number,number ----@field clear fun() ----@field scale_x number ----@field scale_y number ----@field z number ----@field label string? ----@field hidden boolean? - ----@alias blittable {[1]:string,[2]:string,[3]:string} ----@alias blitmap table> ----@alias blitbuffer {[1]:string[],[2]:blitchar[],[3]:blitchar[]}[] - ----@generic A ----@generic B ----@generic V ----@param f fun(a:A,b:B,v:V):boolean? -local function iter_2d(t, f) - for a, at in pairs(t) do - for b, v in pairs(at) do - if f(a, b, v) then return end - end - end -end - -local function assert_int(n) - if n ~= math.floor(n) then - error(("Number %f is not an integer!"):format(n), 2) - end -end - ----@generic A ----@generic B ----@generic V ----@param t table> ----@param a A ----@param b B ----@param v V -local function set_index_2d(t, a, b, v) - t[a] = t[a] or {} - t[a][b] = v -end ----@generic A ----@generic B ----@generic V ----@param t table> ----@param a A ----@param b B ----@return V? -local function get_index_2d(t, a, b) - if not t[a] then return end - return t[a][b] -end - ----@alias oblit_part {v:blittable,y:integer,x:integer} ----@alias oblit oblit_part[] ----@alias background_lookup ccTweaked.colors.color[][] - - ----@param win ccTweaked.Window ----@param oblit oblit ----@param bg blitchar -local function normalize_blit_strings(a, b, c, width, bg) - a = a or "" - b = b or "" - c = c or "" - local lenA = #a - local lenB = #b - local lenC = #c - local target = width - local function adjust(str, len, padChar) - if len > target then - return str:sub(1, target) - elseif len < target then - return str .. string.rep(padChar, target - len) - end - return str - end - -- Default to space when background char is unavailable. - local bgChar = bg or " " - a = adjust(a, lenA, " ") - b = adjust(b, lenB, bgChar) - c = adjust(c, lenC, bgChar) - return a, b, c -end - -local function render_blit(win, oblit, bg) - local w, h = win.getSize() - for i = 1, h do - local v = oblit[i] - win.setCursorPos(1, i) - if v then - local text = table.concat(v[1]) - local fg = table.concat(v[2]):gsub(shrekbox.transparent_char, bg) - local bgStr = table.concat(v[3]):gsub(shrekbox.transparent_char, bg) - text, fg, bgStr = normalize_blit_strings(text, fg, bgStr, w, bg) - win.blit(text, fg, bgStr) - v[1] = {} - else - local blank = string.rep(" ", w) - local bgRow = string.rep(bg, w) - win.blit(blank, bgRow, bgRow) - end - end -end - -local blit_lut = {} -for i = 0, 15 do - local n = 2 ^ i - local s = colors.toBlit(n) - blit_lut[n] = s - blit_lut[s] = n -end -shrekbox._blit_lut = blit_lut --- Transparency -shrekbox.transparent = 0 -shrekbox.transparent_char = " " -blit_lut[shrekbox.transparent] = shrekbox.transparent_char -blit_lut[shrekbox.transparent_char] = shrekbox.transparent - -shrekbox.contrast = -1 -shrekbox.contrast_char = "_" -blit_lut[shrekbox.contrast] = shrekbox.contrast_char -blit_lut[shrekbox.contrast_char] = shrekbox.contrast - - ----@alias blitchar "0"|"1"|"2"|"3"|"4"|"5"|"6"|"7"|"8"|"9"|"a"|"b"|"c"|"d"|"e"|"f"|" "|"_" - -local closest_color_to_lookup = {} ----@type table -local palette_colors = {} ----@type table -local contrast_lookup = {} ----@param a number[] ----@param b number[] ----@return number -local function color_dist(a, b) - return (a[1] - b[1]) ^ 2 + (a[2] - b[2]) ^ 2 + (a[3] - b[3]) ^ 2 -end - ---- Array of colors from darkest (1) to brightest (16) ----@type blitchar[] -local color_bright_order = {} ---- Lookup from blitcolor to brightness index into color_bright_order ----@type table -local bright_pos_lookup = {} - ---- Array of colors from desaturated (1) to saturated (16) ----@type blitchar[] -local color_sat_order = {} ---- Lookup from blitcolor to saturation index into color_sat_order ----@type table -local sat_pos_lookup = {} - ----Shift a given blitchar color brighter/darker by a number of levels ----@param ch blitchar ----@param dir integer -function shrekbox.shift_brightness(ch, dir) - local index = bright_pos_lookup[ch] - index = math.max(1, math.min(index + dir, 16)) - return color_bright_order[index] -end - ----Shift a given blitchar color saturated/desaturated by a number of levels ----@param ch blitchar ----@param dir integer -function shrekbox.shift_saturation(ch, dir) - local index = sat_pos_lookup[ch] - index = math.max(1, math.min(index + dir, 16)) - return color_sat_order[index] -end - ----@return integer ----@return ... -function shrekbox.round(n, ...) - ---@diagnostic disable-next-line: missing-return-value - if not n then return end - return math.floor(n + 0.5), shrekbox.round(...) -end - ----@param c number[] ----@return number -local function brightness(c) - return c[1] ^ 2 + c[2] ^ 2 + c[3] ^ 2 -end - -local function rgb_2_hsl(c) - local r, g, b = c[1], c[2], c[3] - local cmax = math.max(r, g, b) - local cmin = math.min(r, g, b) - local delta = cmax - cmin - local hue, sat = 0, 0 - local lum = (cmax + cmin) / 2 - sat = lum > 0.5 and (delta / (2 - cmax - cmin)) or delta / (cmax + cmin) - if delta == 0 then - hue = 0 - sat = 0 - elseif delta == r then - hue = (g - b) / delta + (g < b and 6 or 0) - elseif delta == g then - hue = (b - r) / delta + 2 - elseif delta == b then - hue = (r - g) / delta + 4 - end - hue = hue / 6 - return { hue, sat, lum } -end -local expect = require("cc.expect").expect ----@param color blitchar ----@param a blitchar ----@param b blitchar ----@return blitchar -local function closer_color(color, a, b) - expect(1, color, "string") - expect(2, a, "string") - expect(3, b, "string") - a, b = a < b and a or b, b >= a and b or a - if color == a then return a end - if color == b then return b end - if a == b then return a end - if color == shrekbox.transparent_char then - return color - elseif a == shrekbox.transparent_char then - return b - elseif b == shrekbox.transparent_char then - return a - end - local lookup_index = a .. b - local lookup = get_index_2d(closest_color_to_lookup, lookup_index, color) - if lookup then return lookup end - local rgb = palette_colors[color] - if not rgb then error(("Invalid color %s"):format(color), 2) end - if not palette_colors[a] then error(("Invalid color a %s"):format(a), 2) end - if not palette_colors[b] then error(("Invalid color b %s"):format(b), 2) end - local a_dist = color_dist(rgb, palette_colors[a]) - local b_dist = color_dist(rgb, palette_colors[b]) - local closest = a_dist < b_dist and a or b - set_index_2d(closest_color_to_lookup, lookup_index, color, closest) - return closest -end -local function get_palette_colors() - local brighness_calc = {} - local saturation_calc = {} - for i = 0, 15 do - local color = 2 ^ i - local ch = blit_lut[color] - local rgb = { term.getPaletteColor(color) } - palette_colors[ch] = rgb - local bright = brightness(rgb) - brighness_calc[#brighness_calc + 1] = { bright, ch } - local hsl = rgb_2_hsl(rgb) - saturation_calc[#saturation_calc + 1] = { hsl[2], ch } - end - table.sort(brighness_calc, function(a, b) - return a[1] > b[1] - end) - table.sort(saturation_calc, function(a, b) - return a[1] > b[1] - end) - for i = 1, 16 do - color_bright_order[i] = brighness_calc[i][2] - bright_pos_lookup[brighness_calc[i][2]] = i - color_sat_order[i] = saturation_calc[i][2] - sat_pos_lookup[saturation_calc[i][2]] = i - end - local darkest, brightest = color_bright_order[1], color_bright_order[16] - closest_color_to_lookup = {} - for i = 0, 15 do - local color = 2 ^ i - local ch = blit_lut[color] - contrast_lookup[ch] = closer_color(ch, darkest, brightest) == darkest and brightest or darkest - end -end -get_palette_colors() - ----@type table -local textel_blit_lut = {} -local textel_majority_color_lut = {} ----@param textel_layout string[] ----@return blitchar ----@return blitchar -local function get_majority_color(textel_layout) - local color_frequency_map = {} - local max_count = 0 - local max_color = "0" - local max_2_count = 0 - local max_2_color = "0" - for i = 1, 6 do - local ch = textel_layout[i] - color_frequency_map[ch] = (color_frequency_map[ch] or 0) + 1 - if color_frequency_map[ch] > max_count then - if max_color ~= ch then - max_2_count = max_count - max_2_color = max_color - end - max_color = ch - max_count = color_frequency_map[ch] - elseif color_frequency_map[ch] > max_2_count then - max_2_count = color_frequency_map[ch] - max_2_color = ch - end - end - return max_color, max_2_color -end -local function clone_blit(t) - return { table.unpack(t, 1, 3) } -end -textel_blit_lut[shrekbox.transparent_char:rep(6)] = {} ----@param textel_layout string[] ----@return string[] ----@return blitchar common -local function get_textel_blit(textel_layout, box) - local textel_layout_s = table.concat(textel_layout, "") - if textel_blit_lut[textel_layout_s] then - return clone_blit(textel_blit_lut[textel_layout_s]), textel_majority_color_lut[textel_layout_s] - end - box.profiler.start_region("textel_blit") - local ch = textel_layout[6] - local textel = 0 - local max_color, max_2_color = get_majority_color(textel_layout) - local majority_color = max_color - ch = closer_color(ch, max_color, max_2_color) - if ch == max_color then - -- swap colors - max_color, max_2_color = max_2_color, max_color - end - local interp_colors = ch - for i = 5, 1, -1 do - ch = textel_layout[i] - ch = closer_color(ch, max_color, max_2_color) - interp_colors = interp_colors .. ch - textel = bit32.lshift(textel, 1) + (ch == max_color and 1 or 0) - end - ch = string.char(textel + 128) - local blit = { - ch, - max_color, - max_2_color - } - textel_blit_lut[textel_layout_s] = clone_blit(blit) - textel_majority_color_lut[textel_layout_s] = majority_color - box.profiler.end_region("textel_blit") - return blit, majority_color -end - -local transparency_lookup = { - [shrekbox.transparent_char] = true, - [shrekbox.contrast_char] = true -} ----@param fg blitchar ----@param bg blitchar ----@return boolean -local function is_blit_transparent(fg, bg) - return transparency_lookup[fg] or transparency_lookup[bg] -end ----@param box ShrekBox ----@param rblit blitbuffer ----@param x integer ----@param y integer -local function apply_transparent(ch, fg, bg, color, box, rblit, x, y) - box.profiler.start_region("trans") - bg = bg == shrekbox.transparent_char and color or bg - fg = (fg == shrekbox.transparent_char and color) or - (fg == shrekbox.contrast_char and contrast_lookup[bg]) or fg - rblit[y][1][x] = ch - rblit[y][2][x] = fg - rblit[y][3][x] = bg - box.profiler.end_region("trans") -end - ----@param box ShrekBox ----@param layer Layer -local function insert_default_layer_funcs(box, layer) - --- Get the size of this layer (in layer units) - ---@return number - ---@return number - function layer.size() - local sw, sh = box._get_window().getSize() - return sw * layer.scale_x, sh * layer.scale_y - end - - --- Screen To Layer Coordinates - ---@param sx number - ---@param sy number - ---@return number - ---@return number - function layer.stl_coords(sx, sy) - local lpx, lpy = layer.get_pos() - return (sx - lpx + 1) * layer.scale_x, (sy - lpy + 1) * layer.scale_y - end - - --- Layer to screen coordinates - ---@param lx number - ---@param ly number - ---@return number - ---@return number - function layer.lts_coords(lx, ly) - local lpx, lpy = layer.get_pos() - return lx / layer.scale_x - lpx + 1, ly / layer.scale_y - lpy + 1 - end -end - -local buffer_meta = { - __index = function(t, k) - t[k] = {} - return t[k] - end -} ----@return table> -local function generate_buffer() - return setmetatable({}, buffer_meta) -end - -local rblit_buffer_meta = { - __index = function(t, k) - t[k] = generate_buffer() - return t[k] - end -} -local function generate_rblit_buffer() - return setmetatable({}, rblit_buffer_meta) -end - -local function emtpy_textel() - return { ' ', ' ', ' ', ' ', ' ', ' ' } -end - ----@param buffer table> -local function pixel_layer_render(buffer, textel_buffer) - for ly, line in pairs(buffer) do - for lx, color in pairs(line) do - local iy = math.ceil(ly / 3) - local dy = (ly - 1) % 3 - local ix = math.ceil(lx / 2) - local dx = (lx - 1) % 2 - local i = (dy * 2 + dx) + 1 - local t = textel_buffer[iy][ix] or emtpy_textel() - t[i] = blit_lut[color] - textel_buffer[iy][ix] = t - -- if t[1] == shrekbox.transparent_char and t[1] == t[2] and t[2] == t[3] and t[4] == t[5] and t[5] == t[6] then - -- textel_buffer[iy][ix] = nil - -- end - end - end -end - ----@param box ShrekBox ----@param label string? ----@return Layer -local function new_pixel_layer(box, label) - local px, py = 1, 1 - local layer - local textel_buffer = generate_buffer() - layer = { - label = label, - scale_x = 2, - scale_y = 3, - z = 0, - clear = function() - textel_buffer = generate_buffer() - end, - buffer = generate_buffer(), - pixel = function(lx, ly, color) - box.profiler.start_region("pixel") - assert_int(lx) - assert_int(ly) - layer.buffer[ly][lx] = color - box.profiler.end_region("pixel") - end, - _render = function(rblit) - local tid = layer.label or ("pr_" .. layer.z) - box.profiler.start_region(tid) - pixel_layer_render(layer.buffer, textel_buffer) - box.profiler.start_region("gen_blit") - iter_2d(textel_buffer, function(y, x, v) - x = x + px - 1 - y = y + py - 1 - if not box.pos_on_screen(x, y) then return end - local ch, fg, bg = rblit[y][1][x], rblit[y][2][x], rblit[y][3][x] - if ch and is_blit_transparent(fg, bg) then - local c = get_majority_color(v) - apply_transparent(ch, fg, bg, c, box, rblit, x, y) - return - elseif ch then - return - end - local textel_blit, majority = get_textel_blit(v, box) - rblit[y][1][x] = textel_blit[1] - rblit[y][2][x] = textel_blit[2] - rblit[y][3][x] = textel_blit[3] - end) - box.profiler.end_region("gen_blit") - layer.buffer = generate_buffer() - box.profiler.end_region(tid) - end, - text = function(lx, ly, text, fg, bg) - assert_int(lx) - assert_int(ly) - error("NYI", 2) - end, - set_pos = function(sx, sy) - assert_int(sx) - assert_int(sy) - px, py = sx, sy - end, - get_pos = function() - return px, py - end - } - insert_default_layer_funcs(box, layer) - return layer -end - -local function empty_bixtel() - return { ' ', ' ', ' ' } -end ----@param buffer table> -local function bixel_layer_render(buffer, bixtel_buffer) - for ly, line in pairs(buffer) do - for lx, color in pairs(line) do - local iy = math.ceil(ly / 3) - local dy = (ly - 1) % 3 - local i = dy + 1 - local t = bixtel_buffer[iy][lx] or empty_bixtel() - t[i] = blit_lut[color] - bixtel_buffer[iy][lx] = t - -- if t[1] == shrekbox.transparent_char and t[1] == t[2] and t[2] == t[3] then - -- set_index_2d(bixtel_buffer, iy, lx, nil) - -- end - end - end -end ----@param box ShrekBox ----@param label string? ----@return Layer -local function new_bixel_layer(box, label) - local px, py = 1, 1 - local layer - local bixtel_buffer = generate_buffer() - layer = { - label = label, - scale_x = 1, - scale_y = 1.5, - z = 0, - clear = function() - bixtel_buffer = generate_buffer() - end, - buffer = generate_buffer(), - pixel = function(lx, ly, color) - box.profiler.start_region("bixel") - assert_int(lx) - assert_int(ly) - layer.buffer[ly][lx] = color - box.profiler.end_region("bixel") - end, - _render = function(rblit) - local tid = layer.label or ("br_" .. layer.z) - box.profiler.start_region(tid) - bixel_layer_render(layer.buffer, bixtel_buffer) - iter_2d(bixtel_buffer, function(y, x, v) - x = x + px - 1 - y = y + py - 1 - local upper_y = (y * 2) - 1 - local lower_y = y * 2 - if not box.pos_on_screen(x, y) then return end - local uch, ufg, ubg = rblit[upper_y][1][x], rblit[upper_y][2][x], rblit[upper_y][3][x] - local lch, lfg, lbg = rblit[lower_y][1][x], rblit[lower_y][2][x], rblit[lower_y][3][x] - local col_1 = v[1] - local col_2 = v[2] - local col_3 = v[3] - if uch and is_blit_transparent(ufg, ubg) then - apply_transparent(uch, ufg, ubg, col_1, box, rblit, x, upper_y) - elseif not uch then - rblit[upper_y][1][x] = "\143" - rblit[upper_y][2][x] = col_1 - rblit[upper_y][3][x] = col_2 - end - if lch and is_blit_transparent(lfg, lbg) then - apply_transparent(lch, lfg, lbg, col_3, box, rblit, x, lower_y) - elseif not lch then - rblit[lower_y][1][x] = "\131" - rblit[lower_y][2][x] = col_2 - rblit[lower_y][3][x] = col_3 - end - end) - layer.buffer = generate_buffer() - box.profiler.end_region(tid) - end, - text = function(lx, ly, text, fg, bg) - assert_int(lx) - assert_int(ly) - error("NYI", 2) - end, - set_pos = function(sx, sy) - assert_int(sx) - assert_int(sy) - px, py = sx, sy - end, - get_pos = function() - return px, py - end - } - insert_default_layer_funcs(box, layer) - return layer -end - ----@param box ShrekBox ----@return Layer -local function new_text_layer(box, label) - local text_buffer = {} - local px, py = 1, 1 - local layer - layer = { - label = label, - scale_x = 1, - scale_y = 1, - z = 0, - clear = function() - text_buffer = {} - layer.buffer = generate_buffer() - end, - buffer = generate_buffer(), - text = function(lx, ly, text, fg, bg) - assert_int(lx) - assert_int(ly) - local fgc = fg and blit_lut[fg] or shrekbox.contrast_char - local bgc = bg and blit_lut[bg] or shrekbox.transparent_char - for i = 1, #text do - local ch = text:sub(i, i) - set_index_2d(text_buffer, ly, lx + i - 1, { - ch, - fgc, - bgc - }) - end - end, - pixel = function(lx, ly, color) - assert_int(lx) - assert_int(ly) - if color == shrekbox.transparent then - set_index_2d(text_buffer, ly, lx, nil) - return - end - set_index_2d(text_buffer, ly, lx, { - " ", - blit_lut[color], - blit_lut[color] - }) - end, - _render = function(rblit) - local tid = layer.label or ("tr_" .. layer.z) - box.profiler.start_region(tid) - iter_2d(text_buffer, function(y, x, v) - x = x + px - 1 - y = y + py - 1 - if not box.pos_on_screen(x, y) then return end - local ofg, obg = v[2], v[3] - local ch, efg, ebg = rblit[y][1][x], rblit[y][2][x], rblit[y][3][x] - if ch and is_blit_transparent(efg, ebg) then - apply_transparent(ch, efg, ebg, obg, box, rblit, x, y) - return - elseif ch then - return - end - rblit[y][1][x] = v[1] - rblit[y][2][x] = ofg - rblit[y][3][x] = obg - end) - box.profiler.end_region(tid) - end, - set_pos = function(sx, sy) - assert_int(sx) - assert_int(sy) - px, py = sx, sy - end, - get_pos = function() - return px, py - end - } - insert_default_layer_funcs(box, layer) - return layer -end - - ----@diagnostic disable-next-line: undefined-global -local is_craftos_pc = not not periphemu -local epoch_unit = is_craftos_pc and "nano" or "utc" -local time_ms_divider = is_craftos_pc and 1000000 or 1 -local time_divider = is_craftos_pc and 1000000000 or 1000 ----@param root_name string ----@return Profiler -local function new_profiler(root_name) - ---@class Profile - ---@field total number - ---@field frame number - ---@field name string - ---@field t0 number? - ---@field children Profile[] - ---@field child_map table - ---@field parent Profile? - ---@field depth integer - ---@field count number - ---@field total_count number - ---@field average_time_ms number - ---@type Profile - local active_profile = nil - ---@class Profiler - local profiler = {} - ---@type table - local hidden_regions = {} - ---@type table - local collapsed_regions = {} - ---@type table - local yield_regions = {} - local total_frames = 1 - local active = false - - ---@param name string - local function add_profile(name) - ---@type Profile - local region_timer = { - total = 0, - frame = 0, - name = name, - parent = active_profile, - children = {}, - child_map = {}, - depth = 0, - count = 0, - total_count = 0, - average_time_ms = 1 - } - if active_profile then - region_timer.depth = active_profile.depth + 1 - active_profile.children[#active_profile.children + 1] = region_timer - active_profile.child_map[name] = region_timer - end - return region_timer - end - local root_profile = add_profile(root_name) - - ---Start a new region, creating it if it hasn't been made before. - ---Any code within this region will be timed. - ---Multiple calls in the same frame (and parent region) are timed together. - ---@param name string - function profiler.start_region(name) - if not active then return end - local child_region - if not active_profile and name == root_profile.name then - child_region = root_profile - else - child_region = active_profile.child_map[name] - end - if not child_region then - child_region = add_profile(name) - end - active_profile = child_region - ---@diagnostic disable-next-line: param-type-mismatch - active_profile.t0 = os.epoch(epoch_unit) - end - - ---Mark the end of a started region, will error if there is some other active region - ---@param name string - function profiler.end_region(name, _allow_empty) - if not active then return end - if not active_profile and _allow_empty then - return - end - assert(active_profile.name == name, ("Attempt to end region not active! %s"):format(name)) - local delta = os.epoch(epoch_unit) - active_profile.t0 - active_profile.frame = active_profile.frame + delta - active_profile.count = active_profile.count + 1 - active_profile = active_profile.parent - end - - ---Start a new yield region, creating it if it hasn't been made before. - ---Any code within this region will be timed and subtracted from the frametime. - ---Multiple calls in the same frame (and parent region) are timed together. - ---@param name string - function profiler.start_yield(name) - yield_regions[name] = true - profiler.start_region(name) - end - - ---Mark the end of a started yield region, will error if there is some other active region - ---@param name string - function profiler.end_yield(name) - profiler.end_region(name) - end - - function profiler.start_frame() - active = true - profiler.start_region(root_profile.name) - end - - ---@param profile Profile - local function profiler_end_frame(profile) - profile.total = profile.total + profile.frame - profile.total_count = profile.total_count + profile.count - if yield_regions[profile.name] then - local parent = assert(profile.parent) - repeat - parent.total = parent.total - profile.frame - parent = parent.parent - until not parent - end - profile.count = 0 - profile.frame = 0 - for i, v in ipairs(profile.children) do - profiler_end_frame(v) - end - end - function profiler.end_frame() - total_frames = total_frames + 1 - profiler.end_region(root_profile.name, true) - profiler_end_frame(root_profile) - end - - ---@param layer Layer - ---@param profile Profile - local function render_profile(layer, profile, y) - if hidden_regions[profile.name] then - return y - end - local s = ("|"):rep(profile.depth) - if collapsed_regions[profile.name] then - s = s .. "+" - else - s = s .. "\\" - end - local average_time_ms = profile.total / total_frames / time_ms_divider - profile.average_time_ms = average_time_ms - local average_count = profile.total_count / total_frames - local parent_time_ms = profile.parent and profile.parent.average_time_ms or average_time_ms - local percent = average_time_ms / parent_time_ms * 100 - if percent ~= percent then percent = 100 end - percent = shrekbox.round(percent) - if yield_regions[profile.name] then - s = s .. "[YLD] " - percent = 0 - else - s = s .. ("[%3d] "):format(percent) - end - s = s .. ("%s) avg:%.2fms, avg#:%.1f, #:%d"):format(profile.name, - average_time_ms, average_count, profile.count) - layer.text(1, y, s, colors.white, colors.black) - y = y + 1 - if not collapsed_regions[profile.name] then - for i, v in ipairs(profile.children) do - y = render_profile(layer, v, y) - end - end - return y - end - - ---@param layer Layer - ---@param y integer - function profiler.render(layer, y) - render_profile(layer, root_profile, y) - end - - ---Hide a profile and it's children by name - ---@param name string - ---@param hide boolean? true default - function profiler.hide(name, hide) - if hide == nil then hide = true end - hidden_regions[name] = hide - end - - ---Collapse (hide) a profile's children by name - ---@param name string - ---@param collapse boolean? true default - function profiler.collapse(name, collapse) - if collapse == nil then collapse = true end - collapsed_regions[name] = collapse - end - - ---@return Profile - function profiler._get_root() - return root_profile - end - - return profiler -end - ----@param win ccTweaked.Window -function shrekbox.new(win) - ---@class ShrekBox - local box = { - overlay = false - } - - local overlay_layer = new_text_layer(box, "overlay") - overlay_layer.z = math.huge - local background_layer = new_text_layer(box, "bg_fill") - overlay_layer.z = -math.huge - local layers = {} - local profiler = new_profiler("total") - -- By default collapse the internal shrekbox timings - -- You can uncollapse this by doing box.profiler.collapse("shrekbox", false) - profiler.collapse("shrekbox") - box.profiler = profiler - - function box.sort_layers() - table.sort(layers, function(a, b) - return a.z > b.z -- sort this the opposite way, render front to back - end) - end - - function box._get_window() - return win - end - - ---@param z number - ---@param label string? - function box.add_pixel_layer(z, label) - local layer = new_pixel_layer(box, label) - layers[#layers + 1] = layer - layer.z = z - box.sort_layers() - return layer - end - - ---@param z number - ---@param label string? - function box.add_bixel_layer(z, label) - local layer = new_bixel_layer(box, label) - layers[#layers + 1] = layer - layer.z = z - box.sort_layers() - return layer - end - - ---@param z number - ---@param label string? - function box.add_text_layer(z, label) - local layer = new_text_layer(box, label) - layers[#layers + 1] = layer - layer.z = z - box.sort_layers() - return layer - end - - ---Fill the background layer with a color - ---@param color ccTweaked.colors.color - function box.fill(color) - local ww, wh = win.getSize() - local s = (" "):rep(ww) - for y = 1, wh do - background_layer.text(1, y, s, color, color) - end - end - - local total_time = 1 - local last_frametime = 0 - local total_frames = 0 - local function render_debug() - local real_fps = total_frames / (total_time / time_divider) - local root = profiler._get_root() - local theoretical_fps = root.total_count / (root.total / time_divider) - if theoretical_fps ~= theoretical_fps then theoretical_fps = 0 end - overlay_layer.text(1, 1, - ("%3dfps (t:%3dfps)"):format(real_fps, theoretical_fps), - colors.white, colors.black) - profiler.render(overlay_layer, 2) - end - - ---@type blitbuffer - local rblit = generate_rblit_buffer() - local t0 = os.epoch(epoch_unit) - local function _render() - profiler.end_region("user", true) - win.setVisible(false) - win.setCursorPos(1, 1) - ---@diagnostic disable-next-line: param-type-mismatch - local t1 = os.epoch(epoch_unit) - last_frametime = (t1 - t0) - total_time = total_time + last_frametime - total_frames = total_frames + 1 - ---@diagnostic disable-next-line: param-type-mismatch - t0 = os.epoch(epoch_unit) - overlay_layer.clear() - if box.overlay then - render_debug() - overlay_layer._render(rblit) - end - profiler.end_frame() - profiler.start_frame() - profiler.start_region("shrekbox") - for i, v in ipairs(layers) do -- rendering from front to back - if not v.hidden then - v._render(rblit) - end - end - background_layer._render(rblit) - profiler.start_region("blit!") - render_blit(win, rblit, "a") - profiler.end_region("blit!") - profiler.end_region("shrekbox") - win.setVisible(true) - profiler.start_region("user") - end - - function box.render() - local ok, err = pcall(_render) - if not ok then - term.clear() - term.setCursorPos(1, 1) - print("An error occured while rendering!") - error(err, 0) - end - end - - ---Check if a position is on the visible window - ---@param sx number - ---@param sy number - ---@return boolean - function box.pos_on_screen(sx, sy) - local sw, sh = win.getSize() - if sx < 1 or sy < 1 then return false end - if sx > sw or sy > sh then return false end - return true - end - - box.fill(colors.black) - - return box -end - -function shrekbox.load_file(fn) - local f = assert(fs.open(fn, "r")) - local s = f.readAll() --[[@as string]] - f.close() - return s -end - -function shrekbox.save_file(fn, s) - local f = assert(fs.open(fn, "w")) - f.write(s) - f.close() -end - -return shrekbox diff --git a/install.lua b/install.lua new file mode 100644 index 0000000..32c1750 --- /dev/null +++ b/install.lua @@ -0,0 +1,37 @@ +local expect = require "cc.expect".expect +local base_url = "https://git.cadencoaster.com/Rivulet/ccde/raw/branch/main/" +local function download_to(path) + expect(2, path, "string") + term.write(path) + if not http.checkURL(base_url..path) then error("URL is not valid" ,2) end + local data = http.get(base_url..path).readAll() + fs.makeDir(fs.getDir(path)) + local file = fs.open(path,"w") + if not file then error("Failed to open file", 2) end + file.write(data) + file.close() + print(" OK") +end + +local filesystem = { + files = { + "startup/00_entrypointclient.lua", + "startup/99_phoneOS.lua", + "startup/01_repo.lua", + "libs/containers.lua", + "libs/windows.lua", + "apps/com.ruffles.launcher", + }, + dirs = { + "global-libraries", + "apps-meta", + }, +} + +for _,i in ipairs(filesystem.dirs) do + print("making dir",i) + fs.makeDir(i) +end +for _,i in ipairs(filesystem.files) do + download_to(i) +end \ No newline at end of file diff --git a/scan.lua b/scan.lua new file mode 100644 index 0000000..322399f --- /dev/null +++ b/scan.lua @@ -0,0 +1,58 @@ +local exceptions = { + ["/scan.lua"] = true, + ["/install.lua"] = true, + ["scan.lua"] = true, + ["install.lua"] = true, + ["/.settings"] = true, + [".settings"] = true, + ["/.git"] = true, + [".git"] = true, + ["/rom"] = true, + ["rom"] = true, + ["/scan.dat"] = true, + ["scan.dat"] = true +} +local function scan(dir) + local out = {} + for _,i in ipairs(fs.list(dir)) do + local path = fs.combine(dir,i) + if exceptions[path] == nil then + if fs.isDir(path) then + out[i] = scan(path) + else + local file = fs.open(path,"r") + out[i] = file.readAll() + file.close() + end + end + end + return out +end +local function format(scandata) + local format_data = { + files = {}, + dirs = {} + } + local function scan_format(dir,data) + if type(data) == "table" then + local has_scanned = false + for k,v in pairs(data) do + has_scanned = true + scan_format(fs.combine(dir,k),v) + end + if not has_scanned then + format_data.dirs[#format_data.dirs+1] = dir + end + else + format_data.files[#format_data.files+1] = dir + end + end + scan_format("/",scandata) + return format_data +end +local textdata = textutils.serialise(format(scan("/"))) +local firstpart = "" +local secondpart = "" +local outfile = fs.open("install.lua","w") +outfile.write() +outfile.close() \ No newline at end of file diff --git a/system-apps/dookie-clicker/startup.lua b/system-apps/dookie-clicker/startup.lua deleted file mode 100644 index ca60ecc..0000000 --- a/system-apps/dookie-clicker/startup.lua +++ /dev/null @@ -1,29 +0,0 @@ -local basalt = require("globals.basalt") - --- Get the main frame (your window) -local count = 0 -local main = basalt.getMainFrame() -main:setBackground(colors.gray) -local w,h = term.getSize() -main:addLabel({ -x = w/2-(7/2)+1, -y = 2, -text = "Welcome!", -foreground = colors.yellow -}) -local countLabel = main:addLabel({ -x = w/2-(7/2)+1, -y = 5, -text = "0", -foreground = colors.white -}) - -local button = main:addButton({ -x = w/2-(7/2)+1, -y = 10, -text = "Click Me!", -foreground = colors.white -}):center():onClick(function () count = count + 1 countLabel:setText(tostring(count)) end) - - -basalt.run() \ No newline at end of file