From 445b37cf52046b58bce2de1bbb74792f517551f6 Mon Sep 17 00:00:00 2001 From: Robert Jelic <36573031+NoryiE@users.noreply.github.com> Date: Sun, 8 May 2022 20:19:33 +0200 Subject: [PATCH] created minified file --- basalt.lua | 3730 +-------------------------------------------- source/basalt.lua | 3727 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3728 insertions(+), 3729 deletions(-) create mode 100644 source/basalt.lua diff --git a/basalt.lua b/basalt.lua index edb01e0..fdd122b 100644 --- a/basalt.lua +++ b/basalt.lua @@ -1,3729 +1 @@ --- current version 1 - -local theme = { - basaltBG = colors.lightGray, - basaltFG = colors.black, - FrameBG = colors.gray, - FrameFG = colors.black, - ButtonBG = colors.gray, - ButtonFG = colors.black, - CheckboxBG = colors.gray, - CheckboxFG = colors.black, - InputBG = colors.gray, - InputFG = colors.black, - textfieldBG = colors.gray, - textfieldFG = colors.black, - listBG = colors.gray, - listFG = colors.black, - dropdownBG = colors.gray, - dropdownFG = colors.black, - radioBG = colors.gray, - radioFG = colors.black, - selectionBG = colors.black, - selectionFG = colors.lightGray, -} - -local basalt = {debugger=true, version=1} -local activeFrame -local frames = {} - -local keyModifier = {} -local parentTerminal = term.current() - -local sub = string.sub - -local function getTextHorizontalAlign(text, w, textAlign) - local text = string.sub(text, 1, w) - local n = w-string.len(text) - if(textAlign=="right")then - text = string.rep(" ", n)..text - elseif(textAlign=="center")then - text = string.rep(" ", math.floor(n/2))..text..string.rep(" ", math.floor(n/2)) - text = text..(string.len(text) < w and " " or "") - else - text = text..string.rep(" ", n) - end - return text -end - -local function getTextVerticalAlign(h,textAlign) -local offset = 0 - if(textAlign=="center")then - offset = math.ceil(h / 2) - if(offset<1)then offset = 1 end - end - if(textAlign=="bottom")then - offset = h - end - return offset -end - -local function rpairs(t) - return function(t, i) - i = i - 1 - if i ~= 0 then - return i, t[i] - end - end, t, #t + 1 -end - -local tHex = { -- copy paste is a very important feature - [ colors.white ] = "0", - [ colors.orange ] = "1", - [ colors.magenta ] = "2", - [ colors.lightBlue ] = "3", - [ colors.yellow ] = "4", - [ colors.lime ] = "5", - [ colors.pink ] = "6", - [ colors.gray ] = "7", - [ colors.lightGray ] = "8", - [ colors.cyan ] = "9", - [ colors.purple ] = "a", - [ colors.blue ] = "b", - [ colors.brown ] = "c", - [ colors.green ] = "d", - [ colors.red ] = "e", - [ colors.black ] = "f", -} - -local function basaltDrawHelper() - local terminal = parentTerminal - local w,h = terminal.getSize() - local cacheT = {} - local cacheBG = {} - local cacheFG = {} - - local _cacheT = {} - local _cacheBG = {} - local _cacheFG = {} - - local emptySpaceLine - local emptyColorLines = {} - - local function createEmptyLines() - emptySpaceLine = (" "):rep(w) - for n=0,15 do - local nColor = 2^n - local sHex = tHex[nColor] - emptyColorLines[nColor] = sHex:rep(w) - end - end - ---- - createEmptyLines() - - local function recreateWindowArray() - local emptyText = emptySpaceLine - local emptyFG = emptyColorLines[colors.white] - local emptyBG = emptyColorLines[colors.black] - for n=1,h do - cacheT[n] = sub(cacheT[n] == nil and emptyText or cacheT[n]..emptyText:sub(1,w-cacheT[n]:len()),1,w) - cacheFG[n] = sub(cacheFG[n] == nil and emptyFG or cacheFG[n]..emptyFG:sub(1,w-cacheFG[n]:len()),1,w) - cacheBG[n] = sub(cacheBG[n] == nil and emptyBG or cacheBG[n]..emptyBG:sub(1,w-cacheBG[n]:len()),1,w) - end - end - recreateWindowArray() - - local function setText(x,y,text) - if(y>=1)and(y<=h)then - if(x+text:len()>0)and(x<=w)then - local oldCache = cacheT[y] - local newCache - local nEnd = x + #text - 1 - - if(x < 1)then - local startN = 1 - x + 1 - local endN = w - x + 1 - text = sub(text, startN, endN) - elseif(nEnd > w)then - local endN = w - x + 1 - text = sub(text, 1, endN) - end - - if(x > 1)then - local endN = x - 1 - newCache = sub( oldCache, 1, endN )..text - else - newCache = text - end - if nEnd < w then - newCache = newCache .. sub(oldCache, nEnd + 1, w) - end - cacheT[y] = newCache - end - end - end - - local function setBG(x,y,colorStr) - if(y>=1)and(y<=h)then - if(x+colorStr:len()>0)and(x<=w)then - local oldCache = cacheBG[y] - local newCache - local nEnd = x + #colorStr - 1 - - if(x < 1)then - colorStr = sub(colorStr, 1 - x + 1, w - x + 1) - elseif(nEnd > w)then - colorStr = sub(colorStr, 1, w - x + 1) - end - - if(x > 1)then - newCache = sub( oldCache, 1, x - 1)..colorStr - else - newCache = colorStr - end - if nEnd < w then - newCache = newCache .. sub(oldCache, nEnd + 1, w) - end - cacheBG[y] = newCache - end - end - end - - local function setFG(x,y,colorStr) - if(y>=1)and(y<=h)then - if(x+colorStr:len()>0)and(x<=w)then - local oldCache = cacheFG[y] - local newCache - local nEnd = x + #colorStr - 1 - - if(x < 1)then - local startN = 1 - x + 1 - local endN = w - x + 1 - colorStr = sub(colorStr, startN, endN) - elseif(nEnd > w)then - local endN = w - x + 1 - colorStr = sub(colorStr, 1, endN) - end - - if(x > 1)then - local endN = x - 1 - newCache = sub( oldCache, 1, endN )..colorStr - else - newCache = colorStr - end - if nEnd < w then - newCache = newCache .. sub( oldCache, nEnd + 1, w ) - end - cacheFG[y] = newCache - end - end - end - - local drawHelper = { - setBG = function(x,y,colorStr) - setBG(x,y,colorStr) - end; - - setText = function(x,y,text) - setText(x,y,text) - end; - - setFG = function(x,y,colorStr) - setFG(x,y,colorStr) - end; - - drawBackgroundBox = function (x, y, w, h, bgCol) - for n = 1, h do - setBG(x, y + (n-1), tHex[bgCol]:rep(w)) - end - end; - drawForegroundBox = function (x, y, w, h, fgCol) - for n = 1, h do - setFG(x, y + (n-1), tHex[fgCol]:rep(w)) - end - end; - drawTextBox = function (x, y, w, h, symbol) - for n = 1, h do - setText(x, y + (n-1), symbol:rep(w)) - end - end; - writeText = function (x, y, text, bgCol, fgCol) - bgCol = bgCol or terminal.getBackgroundColor() - fgCol = fgCol or terminal.getTextColor() - setText(x, y, text) - setBG(x, y, tHex[bgCol]:rep(text:len())) - setFG(x, y, tHex[fgCol]:rep(text:len())) - end; - - update = function () - local xC, yC = terminal.getCursorPos() - local isBlinking = false - if(terminal.getCursorBlink~=nil)then isBlinking = terminal.getCursorBlink() end - terminal.setCursorBlink(false) - for n=1, h do - terminal.setCursorPos(1, n) - terminal.blit(cacheT[n], cacheFG[n], cacheBG[n]) - end - terminal.setCursorBlink(isBlinking) - terminal.setCursorPos(xC, yC) - end; - - setTerm = function (newTerm) - terminal = newTerm; - end; - } - return drawHelper -end -local drawHelper = basaltDrawHelper() - -local function BasaltEvents() - - local events = {} - local index = {} - - local event = { - registerEvent = function(self,_event,func) - if( events[_event] == nil)then events[_event] = {} index[_event] = 1 end - events[_event][index[_event]] = func - index[_event] = index[_event] + 1 - return index[_event] - 1 - end; - - removeEvent = function(self,_event,index) - events[_event][index[_event]] = nil - end; - - sendEvent = function(self,_event,...) - if(events[_event]~=nil)then - for k,v in pairs(events[_event])do - v(...) - end - end - end; - } - event.__index = event - return event -end - -local processes = {} -local process = {} -local processId = 0 - -function process:new(path,window,...) - local args = table.pack( ... ) - local newP = setmetatable({path=path},{__index = self}) - newP.window = window - newP.processId = processId - newP.coroutine = coroutine.create(function() - os.run({basalt=basalt}, path, table.unpack(args)) - end) - processes[processId] = newP - processId = processId + 1 - return newP -end - -function process:resume(event, ...) - term.redirect(self.window) - local ok, result = coroutine.resume( self.coroutine, event, ... ) - self.window = term.current() - if ok then - self.filter = result - else - basalt.debug( result ) - end -end - -function process:isDead() - if(self.coroutine~=nil)then - if(coroutine.status(self.coroutine)=="dead")then - table.remove(processes, self.processId) - return true - end - else - return true - end - return false -end - -function process:getStatus() - if(self.coroutine~=nil)then - return coroutine.status(self.coroutine) - end - return nil -end - -function process:start() - coroutine.resume(self.coroutine) -end - -local function Object(name) -- Base object - local typ = "Object" -- not changeable - --[[ - local horizontalAnchor = "left" - local verticalAnchor = "top" - local ignYOffset = false - local ignXOffset = false ]] - local value = nil - local zIndex = 1 - local hanchor = "left" - local vanchor = "top" - local ignOffset = false - local isVisible = false - - local visualsChanged = true - - local eventSystem = BasaltEvents() - - local object = { - x = 1, - y = 1, - w = 1, - h = 1, - bgcolor = colors.black, - fgcolor = colors.white, - name = name or "Object", - parent = nil, - - show = function(self) - isVisible = true - visualsChanged = true - return self - end; - - hide = function(self) - isVisible = false - visualsChanged = true - return self - end; - - isVisible = function(self) - return isVisible - end; - - getZIndex = function(self) - return zIndex; - end; - - setFocus = function(self) - if(self.parent~=nil)then - self.parent:setFocusedObject(self) - end - return self - end; - - setZIndex = function(self, index) - zIndex = index - if(self.parent~=nil)then - self.parent:removeObject(self) - self.parent:addObject(self) - end - return self - end; - - getType = function(self) - return typ - end; - - getName = function(self) - return self.name - end; - - remove = function(self) - if(self.parent~=nil)then - self.parent:removeObject(self) - end - return self - end; - - setParent = function(self, frame) - if(frame.getType~=nil and frame:getType()=="Frame")then - self:remove() - frame:addObject(self) - if(self.draw)then - self:show() - end - end - return self - end; - - setValue = function(self, _value) - value = _value - visualsChanged = true - self:valueChangedHandler() - return self - end; - - getValue = function(self) - return value - end; - - getVisualChanged = function(self) - return visualsChanged - end; - - setVisualChanged = function(self, change) - visualsChanged = change or true - return self - end; - - - getEventSystem = function(self) - return eventSystem - end; - - - getParent = function(self) - return self.parent - end; - - setPosition = function(self, xPos, yPos, rel) - if(rel)then - self.x, self.y = self.x + xPos, self.y + yPos - else - self.x, self.y = xPos, yPos - end - visualsChanged = true - return self - end; - - getPosition = function(self) - return self.x,self.y - end; - - getVisibility = function(self) - return isVisible - end; - - setVisibility = function(self, _isVisible) - isVisible = _isVisible or not isVisible - visualsChanged = true - return self - end; - - setSize = function(self, w, h) - self.w, self.h = w, h - visualsChanged = true - return self - end; - - getHeight = function(self) - return self.h - end; - - getWidth = function(self) - return self.w - end; - - setBackground = function(self, color) - self.bgcolor = color - visualsChanged = true - return self - end; - - getBackground = function(self) - return self.bgcolor - end; - - setForeground = function(self, color) - self.fgcolor = color - visualsChanged = true - return self - end; - - getForeground = function(self) - return self.fgcolor - end; - - draw = function(self) - if(isVisible)then - return true - end - return false - end; - - - getAbsolutePosition = function(self, x,y) -- relative position to absolute position - if(x==nil)then x = self.x end - if(y==nil)then y = self.y end - - if(self.parent~=nil)then - local fx,fy = self.parent:getAbsolutePosition(self.parent:getAnchorPosition()) - x=fx+x-1 - y=fy+y-1 - end - return x, y - end; - - getAnchorPosition = function(self,x, y, ignOff) - if(x==nil)then x = self.x end - if(y==nil)then y = self.y end - if(hanchor=="right")then - x = self.parent.w-x-self.w+2 - end - if(vanchor=="bottom")then - y = self.parent.h-y-self.h+2 - end - local xO, yO = self:getOffset() - if(ignOffset or ignOff)then - return x, y - end - return x+xO, y+yO - end; - - getOffset = function(self) - if(self.parent~=nil)and(ignOffset==false)then - return self.parent:getFrameOffset() - end - return 0, 0 - end; - - ignoreOffset = function(self, ignore) - ignOffset = ignore or true - return self - end; - - setAnchor = function(self,...) - for _,v in pairs(table.pack(...))do - if(v=="right")or(v=="left")then - hanchor = v - end - if(v=="top")or(v=="bottom")then - vanchor = v - end - end - visualsChanged = true - return self - end; - - getAnchor = function(self) - return hanchor, vanchor - end; - - onChange = function(self, func) - self:registerEvent("value_changed", func) - return self - end; - - onClick = function(self, func) - self:registerEvent("mouse_click", func) - return self - end; - - onEvent = function(self, func) - self:registerEvent("custom_event_handler", func) - return self - end; - - onClickUp = function(self, func) - self:registerEvent("mouse_up", func) - return self - end; - - onKey = function(self, func) - self:registerEvent("key", func) - self:registerEvent("char", func) - return self - end; - - onKeyUp = function(self, func) - self:registerEvent("key_up", func) - return self - end; - - onBackgroundKey = function(self, func) - self:registerEvent("background_key", func) - self:registerEvent("background_char", func) - return self - end; - - onBackgroundKeyUp = function(self, func) - self:registerEvent("background_key_up", func) - return self - end; - - isFocused = function(self) - if(self.parent~=nil)then - return self.parent:getFocusedObject()==self - end - return false - end; - - onGetFocus = function(self, func) - self:registerEvent("get_focus", func) - return self - end; - - onLoseFocus = function(self, func) - self:registerEvent("lose_focus", func) - return self - end; - - registerEvent = function(self,event, func) - return eventSystem:registerEvent(event, func) - end; - - removeEvent = function(self,event, index) - return eventSystem:removeEvent(event, index) - end; - - mouseHandler = function(self, event, button, x, y) - local objX,objY = self:getAbsolutePosition(self:getAnchorPosition()) - if(objX<=x)and(objX+self.w>x)and(objY<=y)and(objY+self.h>y)and(isVisible)then - if(self.parent~=nil)then self.parent:setFocusedObject(self) end - eventSystem:sendEvent(event, self, event, button, x, y) - return true - end - return false - end; - - keyHandler = function(self, event, key) - if(self:isFocused())then - eventSystem:sendEvent(event, self, event, key) - return true - end - return false - end; - - backgroundKeyHandler = function(self, event, key) - eventSystem:sendEvent("background_"..event, self, event, key) - end; - - valueChangedHandler = function(self) - eventSystem:sendEvent("value_changed", self) - end; - - eventHandler = function(self, event, p1, p2, p3, p4) - eventSystem:sendEvent("custom_event_handler", self, event, p1, p2, p3, p4) - end; - - getFocusHandler = function(self) - eventSystem:sendEvent("get_focus", self) - end; - - loseFocusHandler = function(self) - eventSystem:sendEvent("lose_focus", self) - end; - - - } - - object.__index = object - return object -end - -local function Button(name) -- Button - local base = Object(name) - local typ = "Button" - - base:setValue("Button") - base:setZIndex(5) - base.w = 8 - base.bgcolor = theme.ButtonBG - base.fgcolor = theme.ButtonFG - - local textHorizontalAlign = "center" - local textVerticalAlign = "center" - - local object = { - getType = function(self) - return typ - end; - setHorizontalAlign = function (self, pos) - textHorizontalAlign = pos - end; - - setVerticalAlign = function (self, pos) - textVerticalAlign = pos - end; - - setText = function(self,text) - base:setValue(text) - return self - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition(self.x,self.y) - local verticalAlign = getTextVerticalAlign(self.h, textVerticalAlign) - - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - self.parent:drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) - self.parent:drawTextBox(obx, oby, self.w, self.h, " ") - for n=1,self.h do - if(n==verticalAlign)then - self.parent:setText(obx, oby+(n-1), getTextHorizontalAlign(self:getValue(), self.w, textHorizontalAlign)) - end - end - end - end - end; - - } - return setmetatable(object, base) -end - -local function Program(name) - local base = Object(name) - local typ = "Program" - base:setZIndex(5) - local object - - local function basaltWindow(x, y, w, h) - local xCursor, yCursor = 1, 1 - local bgcolor, fgcolor = colors.black, colors.white - local cursorBlink = false - local visible = false - - local cacheT = {} - local cacheBG = {} - local cacheFG = {} - - local tPalette = {} - - local emptySpaceLine - local emptyColorLines = {} - - for i=0,15 do - local c = 2 ^ i - tPalette[c] = { parentTerminal.getPaletteColour( c ) } - end - - local function createEmptyLines() - emptySpaceLine = (" "):rep(w) - for n=0,15 do - local nColor = 2^n - local sHex = tHex[nColor] - emptyColorLines[nColor] = sHex:rep(w) - end - end - - local function recreateWindowArray() - createEmptyLines() - local emptyText = emptySpaceLine - local emptyFG = emptyColorLines[colors.white] - local emptyBG = emptyColorLines[colors.black] - for n=1,h do - cacheT[n] = sub(cacheT[n] == nil and emptyText or cacheT[n]..emptyText:sub(1,w-cacheT[n]:len()),1,w) - cacheFG[n] = sub(cacheFG[n] == nil and emptyFG or cacheFG[n]..emptyFG:sub(1,w-cacheFG[n]:len()),1,w) - cacheBG[n] = sub(cacheBG[n] == nil and emptyBG or cacheBG[n]..emptyBG:sub(1,w-cacheBG[n]:len()),1,w) - end - end - recreateWindowArray() - - local function updateCursor() - if xCursor >= 1 and yCursor >= 1 and xCursor <= w and yCursor <= h then - --parentTerminal.setCursorPos(xCursor + x - 1, yCursor + y - 1) - else - --parentTerminal.setCursorPos(0, 0) - end - --parentTerminal.setTextColor(fgcolor) - end - - local function internalBlit( sText, sTextColor, sBackgroundColor ) -- copy pasti strikes again (cc: window.lua) - local nStart = xCursor - local nEnd = nStart + #sText - 1 - if yCursor >= 1 and yCursor <= h then - if nStart <= w and nEnd >= 1 then - -- Modify line - if nStart == 1 and nEnd == w then - cacheT[yCursor] = sText - cacheFG[yCursor] = sTextColor - cacheBG[yCursor] = sBackgroundColor - else - local sClippedText, sClippedTextColor, sClippedBackgroundColor - if nStart < 1 then - local nClipStart = 1 - nStart + 1 - local nClipEnd = w - nStart + 1 - sClippedText = sub( sText, nClipStart, nClipEnd ) - sClippedTextColor = sub( sTextColor, nClipStart, nClipEnd ) - sClippedBackgroundColor = sub( sBackgroundColor, nClipStart, nClipEnd ) - elseif nEnd > w then - local nClipEnd = w - nStart + 1 - sClippedText = sub( sText, 1, nClipEnd ) - sClippedTextColor = sub( sTextColor, 1, nClipEnd ) - sClippedBackgroundColor = sub( sBackgroundColor, 1, nClipEnd ) - else - sClippedText = sText - sClippedTextColor = sTextColor - sClippedBackgroundColor = sBackgroundColor - end - - local sOldText = cacheT[yCursor] - local sOldTextColor = cacheFG[yCursor] - local sOldBackgroundColor = cacheBG[yCursor] - local sNewText, sNewTextColor, sNewBackgroundColor - if nStart > 1 then - local nOldEnd = nStart - 1 - sNewText = sub( sOldText, 1, nOldEnd ) .. sClippedText - sNewTextColor = sub( sOldTextColor, 1, nOldEnd ) .. sClippedTextColor - sNewBackgroundColor = sub( sOldBackgroundColor, 1, nOldEnd ) .. sClippedBackgroundColor - else - sNewText = sClippedText - sNewTextColor = sClippedTextColor - sNewBackgroundColor = sClippedBackgroundColor - end - if nEnd < w then - local nOldStart = nEnd + 1 - sNewText = sNewText .. sub( sOldText, nOldStart, w ) - sNewTextColor = sNewTextColor .. sub( sOldTextColor, nOldStart, w ) - sNewBackgroundColor = sNewBackgroundColor .. sub( sOldBackgroundColor, nOldStart, w ) - end - - cacheT[yCursor] = sNewText - cacheFG[yCursor] = sNewTextColor - cacheBG[yCursor] = sNewBackgroundColor - end - end - xCursor = nEnd + 1 - if(visible)then - updateCursor() - end - end - end - - local function setText(_x,_y,text) - if(text~=nil)then - local gText = cacheT[_y] - if(gText~=nil)then - cacheT[_y] = sub(gText:sub(1,_x-1)..text..gText:sub(_x+(text:len()),w),1,w) - end - end - end - - local function setBG(_x,_y,colorStr) - if(colorStr~=nil)then - local gBG = cacheBG[_y] - if(gBG~=nil)then - cacheBG[_y] = sub(gBG:sub(1,_x-1)..colorStr..gBG:sub(_x+(colorStr:len()),w),1,w) - end - end - end - - local function setFG(_x,_y,colorStr) - if(colorStr~=nil)then - local gFG = cacheFG[_y] - if(gFG~=nil)then - cacheFG[_y] = sub(gFG:sub(1,_x-1)..colorStr..gFG:sub(_x+(colorStr:len()),w),1,w) - end - end - end - - local setTextColor = function(color) - if type( color ) ~= "number" then - error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 ) - elseif tHex[color] == nil then - error( "Invalid color (got " .. color .. ")" , 2 ) - end - fgcolor = color - end - - local setBackgroundColor = function(color) - if type( color ) ~= "number" then - error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 ) - elseif tHex[color] == nil then - error( "Invalid color (got " .. color .. ")" , 2 ) - end - bgcolor = color - end - - local setPaletteColor = function( colour, r, g, b ) -- have to work on - if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end - - if tHex[colour] == nil then - error( "Invalid color (got " .. colour .. ")" , 2 ) - end - - local tCol - if type(r) == "number" and g == nil and b == nil then - tCol = { colours.rgb8( r ) } - tPalette[ colour ] = tCol - else - if type( r ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( r ) .. ")", 2 ) end - if type( g ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( g ) .. ")", 2 ) end - if type( b ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( b ) .. ")", 2 ) end - - tCol = tPalette[ colour ] - tCol[1] = r - tCol[2] = g - tCol[3] = b - end - end - - local getPaletteColor = function( colour ) - if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end - if tHex[colour] == nil then - error( "Invalid color (got " .. colour .. ")" , 2 ) - end - local tCol = tPalette[ colour ] - return tCol[1], tCol[2], tCol[3] - end - - local basaltwindow = { - setCursorPos = function(_x, _y) - if type( _x ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( _x ) .. ")", 2 ) end - if type( _y ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( _y ) .. ")", 2 ) end - xCursor = math.floor(_x) - yCursor = math.floor(_y) - if(visible)then - updateCursor() - end - end; - - getCursorPos = function() - return xCursor, yCursor - end; - - setCursorBlink = function(blink) - if type( blink ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( blink ) .. ")", 2 ) end - cursorBlink = blink - end; - - getCursorBlink = function() - return cursorBlink - end; - - - getPaletteColor = getPaletteColor, - getPaletteColour = getPaletteColor, - - setBackgroundColor = setBackgroundColor, - setBackgroundColour = setBackgroundColor, - - setTextColor = setTextColor, - setTextColour = setTextColor, - - setPaletteColor = setPaletteColor, - setPaletteColour = setPaletteColor, - - getBackgroundColor = function() - return bgcolor - end; - getBackgroundColour = function() - return bgcolor - end; - - getSize = function() - return w, h - end; - - getTextColor = function() - return fgcolor - end; - getTextColour = function() - return fgcolor - end; - - basalt_resize = function(_w, _h) - w, h = _w, _h - recreateWindowArray() - end; - - basalt_reposition = function(_x, _y) - x, y = _x, _y - end; - - basalt_setVisible = function(vis) - visible = vis - end; - - drawBackgroundBox = function (_x, _y, _w, _h, bgCol) - for n = 1, _h do - setBG(_x, _y + (n-1), tHex[bgCol]:rep(_w)) - end - end; - drawForegroundBox = function (_x, _y, _w, _h, fgCol) - for n = 1, _h do - setFG(_x, _y + (n-1), tHex[fgCol]:rep(_w)) - end - end; - drawTextBox = function (_x, _y, _w, _h, symbol) - for n = 1, _h do - setText(_x, _y + (n-1), symbol:rep(_w)) - end - end; - - writeText = function (_x, _y, text, bgCol, fgCol) - bgCol = bgCol or bgcolor - fgCol = fgCol or fgcolor - setText(x, _y, text) - setBG(_x, _y, tHex[bgCol]:rep(text:len())) - setFG(_x, _y, tHex[fgCol]:rep(text:len())) - end; - - basalt_update = function() - if(object.parent~=nil)then - for n=1, h do - object.parent:setText(x, y + (n-1), cacheT[n]) - object.parent:setBG(x, y + (n-1), cacheBG[n]) - object.parent:setFG(x, y + (n-1), cacheFG[n]) - end - end - end; - - scroll = function (offset) - if type( offset ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( offset ) .. ")", 2 ) end - if offset ~= 0 then - local sEmptyText = emptySpaceLine - local sEmptyTextColor = emptyColorLines[ fgcolor ] - local sEmptyBackgroundColor = emptyColorLines[ bgcolor ] - for newY=1,h do - local y = newY + offset - if y >= 1 and y <= h then - cacheT[newY] = cacheT[y] - cacheBG[newY] = cacheBG[y] - cacheFG[newY] = cacheFG[y] - else - cacheT[newY] = sEmptyText - cacheFG[newY] = sEmptyTextColor - cacheBG[newY] = sEmptyBackgroundColor - end - end - end - if(visible)then - updateCursor() - end - end; - - - isColor = function() - return parentTerminal.isColor() - end; - - isColour = function() - return parentTerminal.isColor() - end; - - write = function(text) - text = tostring(text) - if(visible)then - internalBlit( text, tHex[fgcolor]:rep(text:len()), tHex[bgcolor]:rep(text:len())) - end - end; - - clearLine = function() - if(visible)then - setText(1, yCursor, (" "):rep(w)) - setBG(1, yCursor, tHex[bgcolor]:rep(w)) - setFG(1, yCursor, tHex[fgcolor]:rep(w)) - end - if(visible)then - updateCursor() - end - end; - - clear = function() - for n=1,h do - setText(1, n, (" "):rep(w)) - setBG(1, n, tHex[bgcolor]:rep(w)) - setFG(1, n, tHex[fgcolor]:rep(w)) - end - if(visible)then - updateCursor() - end - end; - - blit = function(text, fgcol, bgcol) - if type( text ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( text ) .. ")", 2 ) end - if type( fgcol ) ~= "string" then error( "bad argument #2 (expected string, got " .. type( fgcol ) .. ")", 2 ) end - if type( bgcol ) ~= "string" then error( "bad argument #3 (expected string, got " .. type( bgcol ) .. ")", 2 ) end - if #fgcol ~= #text or #bgcol ~= #text then - error( "Arguments must be the same length", 2 ) - end - if(visible)then - --setText(xCursor, yCursor, text) - --setBG(xCursor, yCursor, bgcol) - --setFG(xCursor, yCursor, fgcol) - --xCursor = xCursor+text:len() - internalBlit(text, fgcol, bgcol) - end - end - - - } - - return basaltwindow - end - - base.w = 30 - base.h = 12 - local pWindow = basaltWindow(1, 1, base.w, base.h) - local curProcess - local paused = false - local queuedEvent = {} - - object = { - getType = function(self) - return typ - end; - - show = function(self) - base.show(self) - pWindow.setBackgroundColor(self.bgcolor) - pWindow.setTextColor(self.fgcolor) - pWindow.basalt_setVisible(true) - return self - end; - - hide = function(self) - base.hide(self) - pWindow.basalt_setVisible(false) - return self - end; - - setPosition = function(self, x, y, rel) - base.setPosition(self,x,y, rel) - pWindow.basalt_reposition(self:getAnchorPosition()) - return self - end; - - getBasaltWindow = function() - return pWindow - end; - - getBasaltProcess = function() - return curProcess - end; - - setSize = function(self, w, h) - base.setSize(self,w,h) - pWindow.basalt_resize(self.w, self.h) - return self - end; - - getStatus = function(self) - if(curProcess~=nil)then - return curProcess:getStatus() - end - return "inactive" - end; - - execute = function(self, path, ...) - curProcess = process:new(path, pWindow, ...) - pWindow.setBackgroundColor(colors.black) - pWindow.setTextColor(colors.white) - pWindow.clear() - pWindow.setCursorPos(1, 1) - curProcess:resume() - paused = false - return self - end; - - stop = function(self) - if(curProcess~=nil)then - if not(curProcess:isDead())then - curProcess:resume("terminate") - if(curProcess:isDead())then - if(self.parent~=nil)then - self.parent:setCursor(false) - end - end - end - end - return self - end; - - pause = function(self, p) - paused = p or (not paused) - if(curProcess~=nil)then - if not(curProcess:isDead())then - if not(paused)then - self:injectEvents(queuedEvent) - queuedEvent = {} - end - end - end - return self - end; - - isPaused = function(self) - return paused - end; - - injectEvent = function(self, event, p1, p2, p3, p4, ign) - if(curProcess~=nil)then - if not(curProcess:isDead())then - if (paused==false)or(ign)then - basalt.debug(event, p1) - curProcess:resume(event,p1,p2,p3,p4) - else - table.insert(queuedEvent, {event=event, args = {p1,p2,p3,p4}}) - end - end - end - return self - end; - - getQueuedEvents = function(self) - return queuedEvent - end; - - updateQueuedEvents = function(self, events) - queuedEvent = events or queuedEvent - return self - end; - - injectEvents = function(self, events) - if(curProcess~=nil)then - if not(curProcess:isDead())then - for _,v in pairs(events)do - curProcess:resume(v.event, table.unpack(v.args)) - end - end - end - return self - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self,event,button,x,y))then - if(curProcess==nil)then return false end - if not(curProcess:isDead())then - if not(paused)then - local absX,absY = self:getAbsolutePosition(self:getAnchorPosition()) - curProcess:resume(event, button, x-absX+1, y-absY+1) - end - end - return true - end - end; - - keyHandler = function(self, event, key) - if(self:isFocused())then - if(curProcess==nil)then return false end - if not(curProcess:isDead())then - if not(paused)then - base.keyHandler(self, event, key) - if(self.draw)then - curProcess:resume(event, key) - end - end - end - end - end; - - getFocusHandler = function(self) - base.getFocusHandler(self) - if(curProcess~=nil)then - if not(curProcess:isDead())then - if not(paused)then - if(self.parent~=nil)then - local xCur, yCur = pWindow.getCursorPos() - local obx, oby = self:getAnchorPosition() - if(self.parent~=nil)then - if(obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + self.w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + self.h - 1)then - self.parent:setCursor(pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor()) - end - end - end - end - end - end - end; - - loseFocusHandler = function(self) - base.loseFocusHandler(self) - if(curProcess~=nil)then - if not(curProcess:isDead())then - if(self.parent~=nil)then - self.parent:setCursor(false) - end - end - end - end; - - eventHandler = function(self, event, p1, p2, p3, p4) - if(curProcess==nil)then return end - if not(curProcess:isDead())then - if not(paused)then - if(event~="mouse_click")and(event~="mouse_up")and(event~="mouse_scroll")and(event~="mouse_drag")and(event~="key_up")and(event~="key")and(event~="char")and(event~="terminate")then - curProcess:resume(event,p1,p2,p3,p4) - end - if(self:isFocused())then - local obx, oby = self:getAnchorPosition() - local xCur, yCur = pWindow.getCursorPos() - if(self.parent~=nil)then - if(obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + self.w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + self.h - 1)then - self.parent:setCursor(pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor()) - end - end - - if(event=="terminate")and(self:isFocused())then - self:stop() - end - end - else - if(event~="mouse_click")and(event~="mouse_up")and(event~="mouse_scroll")and(event~="mouse_drag")and(event~="key_up")and(event~="key")and(event~="char")and(event~="terminate")then - table.insert(queuedEvent, {event=event, args = {p1,p2,p3,p4}}) - end - end - end - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - pWindow.basalt_reposition(obx,oby) - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - pWindow.basalt_update() - end - end - end; - - } - - return setmetatable(object, base) -end - -local function Label(name) -- Label - local base = Object(name) - local typ = "Label" - - base:setZIndex(3) - - local autoWidth = true - base:setValue("") - - local object = { - getType = function(self) - return typ - end; - setText = function(self,text) - text = tostring(text) - base:setValue(text) - if(autoWidth)then - self.w = text:len() - end - return self - end; - - setSize = function(self,w,h) - self.w, self.h = w, h - autoWidth = false - return self - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - self.parent:writeText(obx, oby, self:getValue(), self.bgcolor, self.fgcolor) - end - end - end; - - } - - return setmetatable(object, base) -end - -local function Pane(name) -- Pane - local base = Object(name) - local typ = "Pane" - - - local object = { - getType = function(self) - return typ - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - self.parent:drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) - end - end - end; - - } - - return setmetatable(object, base) -end - -local function Image(name) -- Pane - local base = Object(name) - local typ = "Image" - base:setZIndex(2) - local image - - local object = { - getType = function(self) - return typ - end; - - loadImage = function(self, path) - image = paintutils.loadImage(path) - return self - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - if(image~=nil)then - local obx, oby = self:getAnchorPosition() - - for yPos=1,math.min(#image, self.h) do - local line = image[yPos] - for xPos=1,math.min(#line, self.w) do - if line[xPos] > 0 then - self.parent:drawBackgroundBox(obx + xPos - 1, oby + yPos - 1, 1, 1, line[xPos]) - end - end - end - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function Checkbox(name) -- Checkbox - local base = Object(name) - local typ = "Checkbox" - - base:setZIndex(5) - base:setValue(false) - base.w = 1 - base.h = 1 - base.bgcolor = theme.CheckboxBG - base.fgcolor = theme.CheckboxFG - - local object = { - symbol = "\42", - - getType = function(self) - return typ - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self, event, button, x, y))then - if(event == "mouse_click")and(button == 1)then - if(self:getValue()~=true)and(self:getValue()~=false)then - self:setValue(false) - else - self:setValue(not self:getValue()) - end - end - return true - end - return false - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - local verticalAlign = getTextVerticalAlign(self.h, "center") - - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - for n=1,self.h do - if(n==verticalAlign)then - if(self:getValue()==true)then - self.parent:writeText(obx, oby+(n-1), getTextHorizontalAlign(self.symbol, self.w, "center"), self.bgcolor, self.fgcolor) - else - self.parent:writeText(obx, oby+(n-1), getTextHorizontalAlign(" ", self.w, "center"), self.bgcolor, self.fgcolor) - end - end - end - end - end - end; - - } - - return setmetatable(object, base) -end - -local function Input(name) -- Input - local base = Object(name) - local typ = "Input" - - local inputType = "text" - local inputLimit = 0 - base:setZIndex(5) - base:setValue("") - base.w = 10 - base.h = 1 - base.bgcolor = theme.InputBG - base.fgcolor = theme.InputFG - - local textX = 1 - local wIndex = 1 - - local defaultText = "" - local defaultBGCol - local defaultFGCol - local showingText = defaultText - local internalValueChange = false - - local object = { - - getType = function(self) - return typ - end; - - setInputType = function(self, iType) - if(iType=="password")or(iType=="number")or(iType=="text")then - inputType = iType - end - return self - end; - - setDefaultText = function(self, text, fCol, bCol) - defaultText = text - defaultBGCol = bCol or defaultBGCol - defaultFGCol = fCol or defaultFGCol - if(self:isFocused())then - showingText = "" - else - showingText = defaultText - end - return self - end; - - getInputType = function(self) - return inputType - end; - - setValue = function(self, val) - base.setValue(self, tostring(val)) - if not(internalValueChange)then - textX = tostring(val):len()+1 - end - return self - end; - - getValue = function(self) - local val = base.getValue(self) - return inputType == "number" and tonumber(val)or val - end; - - setInputLimit = function(self, limit) - inputLimit = tonumber(limit) or inputLimit - return self - end; - - getInputLimit = function(self) - return inputLimit - end; - - getFocusHandler = function(self) - base.getFocusHandler(self) - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - showingText = "" - if(self.parent~=nil)then - self.parent:setCursor(true, obx+textX-wIndex, oby, self.fgcolor) - end - end - end; - - loseFocusHandler = function(self) - base.loseFocusHandler(self) - if(self.parent~=nil)then - self.parent:setCursor(false) - showingText = defaultText - end - end; - - keyHandler = function(self, event, key) - if(base.keyHandler(self, event, key))then - internalValueChange = true - if(event=="key")then - if(key==259)then -- on backspace - local text = tostring(base.getValue()) - if(textX>1)then - self:setValue(text:sub(1,textX-2)..text:sub(textX,text:len())) - if(textX>1)then textX = textX-1 end - if(wIndex>1)then - if(textX tLength)then - textX = tLength+1 - end - if(textX<1)then textX = 1 end - if(textX=self.w+wIndex)then - wIndex = textX-self.w+1 - end - if(wIndex<1)then wIndex = 1 end - end - - if(key==263)then -- left arrow - textX = textX-1 - if(textX>=1)then - if(textX=self.w+wIndex)then - wIndex = textX - end - end - if(textX<1)then textX = 1 end - if(wIndex<1)then wIndex = 1 end - end - end - - if(event=="char")then - local text = base.getValue() - if(text:len() < inputLimit or inputLimit <= 0)then - if(inputType=="number")then - local cache = text - if (key==".")or(tonumber(key)~=nil)then - self:setValue(text:sub(1,textX-1)..key..text:sub(textX,text:len())) - textX = textX + 1 - end - if(tonumber(base.getValue())==nil)then - self:setValue(cache) - end - else - self:setValue(text:sub(1,textX-1)..key..text:sub(textX,text:len())) - textX = textX + 1 - end - if(textX>=self.w+wIndex)then wIndex = wIndex+1 end - end - end - local obx,oby = self:getAnchorPosition() - local val = tostring(base.getValue()) - local cursorX = (textX <= val:len() and textX-1 or val:len())-(wIndex-1) - - if(cursorX>self.x+self.w-1)then cursorX = self.x+self.w-1 end - if(self.parent~=nil)then - self.parent:setCursor(true, obx+cursorX, oby, self.fgcolor) - end - internalValueChange = false - end - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self, event, button, x, y))then - if(event == "mouse_click")and(button == 1)then - - end - return true - end - return false - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - local verticalAlign = getTextVerticalAlign(self.h, "center") - - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - for n=1,self.h do - if(n==verticalAlign)then - local val = tostring(base.getValue()) - local bCol = self.bgcolor - local fCol = self.fgcolor - local text - if(val:len()<=0)then text = showingText bCol = defaultBGCol or bCol fCol = defaultFGCol or fCol end - - text = showingText - if(val~="")then - text = val - end - text = text:sub(wIndex, self.w+wIndex-1) - local space = self.w-text:len() - if(space<0)then space = 0 end - if(inputType=="password")and(val~="")then - text = string.rep("*", text:len()) - end - text = text..string.rep(" ", space) - self.parent:writeText(obx, oby+(n-1), text, bCol, fCol) - end - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function Textfield(name) - local base = Object(name) - local typ = "Textfield" - local hIndex, wIndex, textX, textY = 1, 1, 1, 1 - - local lines = {""} - local selection = {} - local selectionColor = colors.lightBlue - - base.w = 20 - base.h = 8 - base.bgcolor = theme.textfieldBG - base.fgcolor = theme.textfieldFG - base:setZIndex(5) - - local object = { - getType = function(self) - return typ - end; - - getLines = function (self) - return lines - end; - - getLine = function (self, index) - return lines[index] or "" - end; - - editLine = function (self, index, text) - lines[index] = text or lines[index] - return self - end; - - addLine = function (self, text, index) - if(index~=nil)then - table.insert(lines, index, text) - else - table.insert(lines, text) - end - return self - end; - - removeLine = function(self, index) - table.remove(lines, index or #lines) - if(#lines <= 0)then - table.insert(lines, "") - end - return self - end; - - getTextCursor = function (self) - return textX, textY - end; - - getFocusHandler = function(self) - base.getFocusHandler(self) - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - if(self.parent~=nil)then - self.parent:setCursor(true, obx+textX-wIndex, oby+textY-hIndex, self.fgcolor) - end - end - end; - - loseFocusHandler = function(self) - base.loseFocusHandler(self) - if(self.parent~=nil)then - self.parent:setCursor(false) - end - end; - - keyHandler = function(self, event, key) - if(base.keyHandler(self, event, key))then - local obx,oby = self:getAnchorPosition() - if(event=="key")then - if(key==259)then -- on backspace - if(lines[textY]=="")then - if(textY>1)then - table.remove(lines,textY) - textX = lines[textY-1]:len()+1 - wIndex = textX-self.w+1 - if(wIndex<1)then wIndex = 1 end - textY = textY-1 - end - elseif(textX<=1)then - if(textY>1)then - textX = lines[textY-1]:len()+1 - wIndex = textX-self.w+1 - if(wIndex<1)then wIndex = 1 end - lines[textY-1] = lines[textY-1]..lines[textY] - table.remove(lines,textY) - textY = textY-1 - end - else - lines[textY] = lines[textY]:sub(1,textX-2)..lines[textY]:sub(textX,lines[textY]:len()) - if(textX>1)then textX = textX-1 end - if(wIndex>1)then - if(textX=self.h)then - hIndex = hIndex+1 - end - self:setValue("") - end - - if(key==265)then -- arrow up - if(textY>1)then - textY = textY-1 - if(textX>lines[textY]:len()+1)then textX = lines[textY]:len()+1 end - if(wIndex>1)then - if(textX1)then - if(textYlines[textY]:len()+1)then textX = lines[textY]:len()+1 end - - if(textY>=hIndex+self.h)then - hIndex = hIndex+1 - end - end - end - if(key==262)then -- arrow right - textX = textX+1 - if(textY<#lines)then - if(textX>lines[textY]:len()+1)then - textX = 1 - textY = textY+1 - end - elseif(textX > lines[textY]:len())then - textX = lines[textY]:len()+1 - end - if(textX<1)then textX = 1 end - if(textX=self.w+wIndex)then - wIndex = textX-self.w+1 - end - if(wIndex<1)then wIndex = 1 end - - end - if(key==263)then -- arrow left - textX = textX-1 - if(textX>=1)then - if(textX=self.w+wIndex)then - wIndex = textX - end - end - if(textY>1)then - if(textX<1)then - textY = textY-1 - textX = lines[textY]:len()+1 - wIndex = textX-self.w+1 - end - end - if(textX<1)then textX = 1 end - if(wIndex<1)then wIndex = 1 end - end - end - - if(event=="char")then - lines[textY] = lines[textY]:sub(1,textX-1)..key..lines[textY]:sub(textX,lines[textY]:len()) - textX = textX+1 - if(textX>=self.w+wIndex)then wIndex = wIndex+1 end - self:setValue("") - end - - local cursorX = (textX <= lines[textY]:len() and textX-1 or lines[textY]:len())-(wIndex-1) - if(cursorX>self.x+self.w-1)then cursorX = self.x+self.w-1 end - local cursorY = (textY-hIndex < self.h and textY-hIndex or textY-hIndex-1) - if(cursorX<1)then cursorX = 0 end - self.parent:setCursor(true, obx+cursorX, oby+cursorY, self.fgcolor) - return true - end - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self, event, button, x, y))then - local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) - local anchx,anchy = self:getAnchorPosition() - if(event=="mouse_click")then - if(lines[y-oby+hIndex]~=nil)then - textX = x-obx+wIndex - textY = y-oby+hIndex - if(textX>lines[textY]:len())then - textX = lines[textY]:len()+1 - end - if(textX#lines-(self.h-1))then hIndex = #lines-(self.h-1) end - - if(hIndex<1)then hIndex = 1 end - - if(self.parent~=nil)then - if(obx+textX-wIndex>=obx and obx+textX-wIndex <= obx+self.w)and(oby+textY-hIndex >= oby and oby+textY-hIndex <= oby+self.h)then - self.parent:setCursor(true, anchx+textX-wIndex, anchy+textY-hIndex) - else - self.parent:setCursor(false) - end - end - end - self:setVisualChanged() - return true - end - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - self.parent:drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) - for n=1, self.h do - local text = "" - if(lines[n+hIndex-1]~=nil)then - text = lines[n+hIndex-1] - end - text = text:sub(wIndex, self.w+wIndex-1) - local space = self.w-text:len() - if(space<0)then space = 0 end - text = text..string.rep(" ", space) - self.parent:setText(obx, oby+n-1, text) - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function List(name) - local base = Object(name) - local typ = "List" - base.w = 16 - base.h = 6 - base.bgcolor = theme.listBG - base.fgcolor = theme.listFG - base:setZIndex(5) - - local list = {} - local itemSelectedBG = theme.selectionBG - local itemSelectedFG = theme.selectionFG - local selectionColorActive = true - local align = "left" - local yOffset = 0 - local scrollable = true - - local object = { - getType = function(self) - return typ - end; - - addItem = function(self, text, bgCol, fgCol, ...) - table.insert(list, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - if(#list==1)then - self:setValue(list[1]) - end - return self - end; - - setIndexOffset = function(self, yOff) - yOffset = yOff - return self - end; - - getIndexOffset = function(self) - return yOffset - end; - - removeItem = function(self, index) - table.remove(list, index) - return self - end; - - getItem = function(self, index) - return list[index] - end; - - getItemIndex = function(self) - local selected = self:getValue() - for k,v in pairs(list)do - if(v==selected)then - return k - end - end - end; - - clear = function(self) - list = {} - self:setValue({}) - return self - end; - - getItemCount = function(self) - return #list - end; - - editItem = function(self, index, text, bgCol, fgCol, ...) - table.remove(list, index) - table.insert(list, index, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - return self - end; - - selectItem = function(self, index) - self:setValue(list[index] or {}) - return self - end; - - setSelectedItem = function(self, bgCol, fgCol, active) - itemSelectedBG = bgCol or self.bgcolor - itemSelectedFG = fgCol or self.fgcolor - selectionColorActive = active - return self - end; - - setScrollable = function(self, scroll) - scrollable = scroll - return self - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self, event, button, x, y))then - local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(event=="mouse_click")or(event=="mouse_drag")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw - if(button==1)then - if(#list>0)then - for n=1,self.h do - if(list[n+yOffset]~=nil)then - if(obx<=x)and(obx+self.w>x)and(oby+n-1==y)then - self:setValue(list[n+yOffset]) - end - end - end - end - end - end - - if(event=="mouse_scroll")and(scrollable)then - yOffset = yOffset+button - if(yOffset<0)then yOffset = 0 end - if(button>=1)then - if(#list>self.h)then - if(yOffset>#list-self.h)then - yOffset = #list-self.h - end - if(yOffset>=#list)then yOffset = #list-1 end - else - yOffset = yOffset-1 - end - end - end - self:setVisualChanged() - return true - end - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - for n=1, self.h do - if(list[n+yOffset]~=nil)then - if(list[n+yOffset]==self:getValue())then - if(selectionColorActive)then - self.parent:writeText(obx, oby+n-1, getTextHorizontalAlign(list[n+yOffset].text, self.w, align), itemSelectedBG, itemSelectedFG) - else - self.parent:writeText(obx, oby+n-1, getTextHorizontalAlign(list[n+yOffset].text, self.w, align), list[n+yOffset].bgCol, list[n+yOffset].fgCol) - end - else - self.parent:writeText(obx, oby+n-1, getTextHorizontalAlign(list[n+yOffset].text, self.w, align), list[n+yOffset].bgCol,list[n+yOffset].fgCol) - end - end - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function Menubar(name) - local base = Object(name) - local typ = "Menubar" - - base.w = 30 - base.h = 1 - base.bgcolor = colors.gray - base.fgcolor = colors.lightGray - base:setZIndex(5) - - local list = {} - local itemSelectedBG = theme.selectionBG - local itemSelectedFG = theme.selectionFG - local selectionColorActive = true - local align = "left" - local itemOffset = 0 - local space = 2 - local scrollable = false - - local function maxScroll() - local mScroll = 0 - local xPos = 1 - for n=1,#list do - if(xPos + list[n].text:len() + space*2 > base.w)then - mScroll = mScroll + 1 - end - xPos = xPos+list[n].text:len()+space*2 - - end - return mScroll - end - - local object = { - getType = function(self) - return typ - end; - - addItem = function(self, text, bgCol, fgCol, ...) - table.insert(list, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - if(#list==1)then - self:setValue(list[1]) - end - return self - end; - - getItemIndex = function(self) - local selected = self:getValue() - for k,v in pairs(list)do - if(v==selected)then - return k - end - end - end; - - clear = function(self) - list = {} - self:setValue({}) - return self - end; - - setSpace = function(self, _space) - space = _space or space - return self - end; - - setButtonOffset = function(self, offset) - itemOffset = offset or 0 - if(itemOffset<0)then - itemOffset = 0 - end - - local mScroll = maxScroll() - if(itemOffset>mScroll)then - itemOffset = mScroll - end - return self - end; - - setScrollable = function(self, scroll) - scrollable = scroll - return self - end; - - removeItem = function(self, index) - table.remove(list, index) - return self - end; - - getItem = function(self, index) - return list[index] - end; - - getItemCount = function(self) - return #list - end; - - editItem = function(self, index, text, bgCol, fgCol, ...) - table.remove(list, index) - table.insert(list, index, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - return self - end; - - selectItem = function(self, index) - self:setValue(list[index] or {}) - return self - end; - - setSelectedItem = function(self, bgCol, fgCol, active) - itemSelectedBG = bgCol or self.bgcolor - itemSelectedFG = fgCol or self.fgcolor - selectionColorActive = active - return self - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self,event,button,x,y))then - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(event=="mouse_click")then - local xPos = 1 - for n=1+itemOffset,#list do - if(list[n]~=nil)then - if(xPos + list[n].text:len() + space*2 <= self.w)then - if(obx + (xPos-1)<= x)and(obx + (xPos-1) + list[n].text:len() + space*2 > x)and(oby == y)then - self:setValue(list[n]) - end - xPos = xPos+list[n].text:len()+space*2 - else - break - end - end - end - - end - if(event=="mouse_scroll")and(scrollable)then - itemOffset = itemOffset+button - if(itemOffset<0)then - itemOffset = 0 - end - - local mScroll = maxScroll() - - if(itemOffset>mScroll)then - itemOffset = mScroll - end - end - end - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - local xPos = 1 - for n=1+itemOffset,#list do - if(list[n]~=nil)then - if(xPos + list[n].text:len() + space*2 <= self.w)then - if(list[n]==self:getValue())then - self.parent:writeText(obx + (xPos-1), oby, getTextHorizontalAlign((" "):rep(space)..list[n].text..(" "):rep(space), list[n].text:len()+space*2, align), itemSelectedBG or list[n].bgCol, itemSelectedFG or list[n].fgCol) - else - self.parent:writeText(obx + (xPos-1), oby, getTextHorizontalAlign((" "):rep(space)..list[n].text..(" "):rep(space), list[n].text:len()+space*2, align), list[n].bgCol, list[n].fgCol) - end - xPos = xPos+list[n].text:len()+space*2 - else - break - end - end - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function Dropdown(name) - local base = Object(name) - local typ = "Dropdown" - base.w = 12 - base.h = 1 - base.bgcolor = theme.dropdownBG - base.fgcolor = theme.dropdownFG - base:setZIndex(6) - - local list = {} - local itemSelectedBG = theme.selectionBG - local itemSelectedFG = theme.selectionFG - local selectionColorActive = true - local align = "left" - local yOffset = 0 - - local dropdownW = 16 - local dropdownH = 6 - local closedSymbol = "\16" - local openedSymbol = "\31" - local state = 1 - - local object = { - getType = function(self) - return typ - end; - - setIndexOffset = function(self, yOff) - yOffset = yOff - return self - end; - - getIndexOffset = function(self) - return yOffset - end; - - addItem = function(self, text, bgCol, fgCol, ...) - table.insert(list, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - if(#list==1)then - self:setValue(list[1]) - end - return self - end; - - removeItem = function(self, index) - table.remove(list, index) - return self - end; - - getItem = function(self, index) - return list[index] - end; - - getItemIndex = function(self) - local selected = self:getValue() - for k,v in pairs(list)do - if(v==selected)then - return k - end - end - end; - - clear = function(self) - list = {} - self:setValue({}) - return self - end; - - getItemCount = function(self) - return #list - end; - - editItem = function(self, index, text, bgCol, fgCol, ...) - table.remove(list, index) - table.insert(list, index, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - return self - end; - - selectItem = function(self, index) - self:setValue(list[index] or {}) - return self - end; - - setSelectedItem = function(self, bgCol, fgCol, active) - itemSelectedBG = bgCol or self.bgcolor - itemSelectedFG = fgCol or self.fgcolor - selectionColorActive = active - return self - end; - - setDropdownSize = function(self, w, h) - dropdownW, dropdownH = w, h - return self - end; - - mouseHandler = function(self, event, button, x, y) - if(state == 2)then - local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(event=="mouse_click")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw - if(button==1)then - if(#list>0)then - for n=1,dropdownH do - if(list[n+yOffset]~=nil)then - if(obx<=x)and(obx+dropdownW>x)and(oby+n==y)then - self:setValue(list[n+yOffset]) - return true - end - end - end - end - end - end - - if(event=="mouse_scroll")then - yOffset = yOffset+button - if(yOffset<0)then yOffset = 0 end - if(button==1)then - if(#list>dropdownH)then - if(yOffset>#list-dropdownH)then - yOffset = #list-dropdownH - end - else - yOffset = list-1 - end - end - return true - end - self:setVisualChanged() - end - if(base.mouseHandler(self, event, button, x, y))then - state = 2 - else - state = 1 - end - end; - - draw = function(self) - if(base.draw(self))then - local obx, oby = self:getAnchorPosition() - if(self.parent~=nil)then - self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - if(#list>=1)then - if(self:getValue().text~=nil)then - if(state==1)then - self.parent:writeText(obx, oby, getTextHorizontalAlign(self:getValue().text, self.w, align):sub(1,self.w-1)..closedSymbol, self.bgcolor, self.fgcolor) - else - self.parent:writeText(obx, oby, getTextHorizontalAlign(self:getValue().text, self.w, align):sub(1,self.w-1)..openedSymbol, self.bgcolor, self.fgcolor) - end - end - if(state==2)then - for n=1, dropdownH do - if(list[n+yOffset]~=nil)then - if(list[n+yOffset]==self:getValue())then - if(selectionColorActive)then - self.parent:writeText(obx, oby+n, getTextHorizontalAlign(list[n+yOffset].text, dropdownW, align), itemSelectedBG, itemSelectedFG) - else - self.parent:writeText(obx, oby+n, getTextHorizontalAlign(list[n+yOffset].text, dropdownW, align), list[n+yOffset].bgCol, list[n+yOffset].fgCol) - end - else - self.parent:writeText(obx, oby+n, getTextHorizontalAlign(list[n+yOffset].text, dropdownW, align), list[n+yOffset].bgCol,list[n+yOffset].fgCol) - end - end - end - end - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function Radio(name) - local base = Object(name) - local typ = "Radio" - base.w = 8 - base.bgcolor = theme.listBG - base.fgcolor = theme.listFG - base:setZIndex(5) - - local list = {} - local itemSelectedBG = theme.selectionBG - local itemSelectedFG = theme.selectionFG - local boxSelectedBG = base.bgcolor - local boxSelectedFG = base.fgcolor - local selectionColorActive = true - local symbol = "\7" - local align = "left" - - local object = { - getType = function(self) - return typ - end; - - addItem = function(self,text, x, y, bgCol, fgCol, ...) - table.insert(list, {x=x or 1, y=y or 1, text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - if(#list==1)then - self:setValue(list[1]) - end - return self - end; - - removeItem = function(self, index) - table.remove(list, index) - return self - end; - - getItem = function(self, index) - return list[index] - end; - - getItemIndex = function(self) - local selected = self:getValue() - for k,v in pairs(list)do - if(v==selected)then - return k - end - end - end; - - clear = function(self) - list = {} - self:setValue({}) - return self - end; - - getItemCount = function(self) - return #list - end; - - editItem = function(self, index, text, x, y, bgCol, fgCol, ...) - table.remove(list, index) - table.insert(list, index, {x=x or 1, y=y or 1, text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) - return self - end; - - selectItem = function(self, index) - self:setValue(list[index] or {}) - return self - end; - - setSelectedItem = function(self, bgCol, fgCol, boxBG, boxFG, active) - itemSelectedBG = bgCol or itemSelectedBG - itemSelectedFG = fgCol or itemSelectedFG - boxSelectedBG = boxBG or boxSelectedBG - boxSelectedFG = boxFG or boxSelectedFG - selectionColorActive = active - return self - end; - - mouseHandler = function(self, event, button, x, y) - local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(event=="mouse_click")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw - if(button==1)then - if(#list>0)then - for _,v in pairs(list)do - if(obx+v.x-1<=x)and(obx+v.x-1+v.text:len()+2>=x)and(oby+v.y-1==y)then - self:setValue(v) - if(self.parent~=nil)then self.parent:setFocusedObject(self) end - --eventSystem:sendEvent(event, self, event, button, x, y) - self:setVisualChanged() - return true - end - end - end - end - end - return false - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - for _,v in pairs(list)do - if(v==self:getValue())then - if(align=="left")then - self.parent:writeText(v.x+obx-1,v.y+oby-1,symbol,boxSelectedBG,boxSelectedFG) - self.parent:writeText(v.x+2+obx-1,v.y+oby-1,v.text,itemSelectedBG,itemSelectedFG) - end - else - self.parent:drawBackgroundBox(v.x+obx-1, v.y+oby-1,1,1,self.bgcolor) - self.parent:writeText(v.x+2+obx-1,v.y+oby-1,v.text,v.bgCol,v.fgCol) - end - end - end - end - end; - } - - return setmetatable(object, base) -end - -local function Timer(name) - local typ = "Timer" - - local timer = 0 - local savedRepeats = 0 - local repeats = 0 - local timerObj - local eventSystem = BasaltEvents() - - local object = { - name = name, - getType = function(self) - return typ - end; - - getZIndex = function(self) - return 1 - end; - - getName = function(self) - return self.name - end; - - setTime = function(self, _timer, _repeats) - timer = _timer or 0 - savedRepeats = _repeats or 1 - return self - end; - - start = function(self) - repeats = savedRepeats - timerObj = os.startTimer(timer) - return self - end; - - cancel = function(self) - if(timerObj~=nil)then - os.cancelTimer(timerObj) - end - return self - end; - - onCall = function(self, func) - eventSystem:registerEvent("timed_event", func) - return self - end; - - eventHandler = function(self, event, tObj) - if(event=="timer")and(tObj==timerObj)then - eventSystem:sendEvent("timed_event",self) - if(repeats>=1)then - repeats = repeats - 1 - if(repeats >= 1)then - timerObj = os.startTimer(timer) - end - elseif(repeats == -1)then - timerObj = os.startTimer(timer) - end - end - end; - } - object.__index = object - - return object -end - -local function Thread(name) - local object - local typ = "Thread" - - local func - local cRoutine - local isActive = false - - object = { - name = name, - getType = function(self) - return typ - end; - getZIndex = function(self) - return 1 - end; - getName = function(self) - return self.name - end; - - start = function(self, f) - if(f==nil)then error("function is nil") end - func = f - cRoutine = coroutine.create(func) - isActive = true - local ok, result = coroutine.resume(cRoutine) - if not(ok)then - if(result~="Terminated")then - error("Threaderror - "..result) - end - end - return self - end; - - getStatus = function(self, f) - if(cRoutine~=nil)then - return coroutine.status(cRoutine) - end - return nil - end; - - stop = function(self, f) - isActive = false - return self - end; - - eventHandler = function(self, event, p1,p2,p3) - if(isActive)then - if(coroutine.status(cRoutine)~="dead")then - local ok, result = coroutine.resume(cRoutine, event,p1,p2,p3) - if not(ok)then - if(result~="Terminated")then - error("Threaderror - "..result) - end - end - else - isActive = false - end - end - end; - - } - - object.__index = object - - return object -end - -local function Animation(name) - local object = {} - local typ = "Animation" - - local timerObj - - local animations = {} - local index = 1 - - local nextWaitTimer = 0 - local lastFunc - - local function onPlay() - if(animations[index]~=nil)then - animations[index].f(object, index) - end - index = index+1 - if(animations[index]~=nil)then - if(animations[index].t>0)then - timerObj = os.startTimer(animations[index].t) - else - onPlay() - end - end - end - - object = { - name = name, - getType = function(self) - return typ - end; - - getZIndex = function(self) - return 1 - end; - - getName = function(self) - return self.name - end; - - add = function(self, func, wait) - lastFunc = func - table.insert(animations, {f=func,t = wait or nextWaitTimer}) - return self - end; - - wait = function(self, wait) - nextWaitTimer = wait - return self - end; - - rep = function(self, reps) - for x=1,reps do - table.insert(animations, {f=lastFunc,t = nextWaitTimer}) - end - return self - end; - - clear = function(self) - animations = {} - lastFunc = nil - nextWaitTimer = 0 - index = 1 - return self - end; - - play = function(self) - index = 1 - if(animations[index]~=nil)then - if(animations[index].t>0)then - timerObj = os.startTimer(animations[index].t) - else - onPlay() - end - end - return self - end; - - cancel = function(self) - os.cancelTimer(timerObj) - return self - end; - - eventHandler = function(self, event, tObj) - if(event=="timer")and(tObj==timerObj)then - if(animations[index]~=nil)then - onPlay() - end - end - end; - } - object.__index = object - - return object -end - -local function Slider(name) - local base = Object(name) - local typ = "Slider" - - base.w = 8 - base.bgcolor = colors.lightGray - base.fgcolor = colors.gray - base:setValue(1) - - local barType = "horizontal" - local symbol = " " - local symbolColor=colors.black - local bgSymbol = "\140" - local maxValue = base.w - local index = 1 - - - - local object = { - getType = function(self) - return typ - end; - - setSymbol = function(self, _symbol) - symbol = _symbol:sub(1,1) - self:setVisualChanged() - return self - end; - - setBackgroundSymbol = function(self, _bgSymbol) - bgSymbol = string.sub(_bgSymbol,1,1) - self:setVisualChanged() - return self - end; - - setSymbolColor = function(self, col) - symbolColor = col - self:setVisualChanged() - return self - end; - - setBarType = function(self, _typ) - barType = _typ:lower() - return self - end; - - mouseHandler = function(self, event, button, x, y) - if(base.mouseHandler(self,event,button,x,y))then - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(barType=="horizontal")then - for _index=0,self.w-1 do - if(obx+_index==x)and(oby<=y)and(oby+self.h>y)then - index = _index+1 - self:setValue(maxValue/self.w*(index)) - self:setVisualChanged() - end - end - end - if(barType=="vertical")then - for _index=0,self.h-1 do - if(oby+_index==y)and(obx<=x)and(obx+self.w>x)then - index = _index+1 - self:setValue(maxValue/self.h*(index)) - self:setVisualChanged() - end - end - end - --[[if(event=="mouse_scroll")then - self:setValue(self:getValue() + (maxValue/(barType=="vertical" and self.h or self.w))*typ) - self:setVisualChanged() - end - if(self:getValue()>maxValue)then self:setValue(maxValue) end - if(self:getValue()y)then - index = math.min(_index+1, self.w-(symbolSize-1)) - self:setValue(maxValue/self.w*(index)) - self:setVisualChanged() - end - end - end - if(barType=="vertical")then - for _index=0,self.h do - if(oby+_index==y)and(obx<=x)and(obx+self.w>x)then - index = math.min(_index+1, self.h-(symbolSize-1)) - self:setValue(maxValue/self.h*(index)) - self:setVisualChanged() - end - end - end - end - if(event=="mouse_scroll")then - index = index+button - if(index<1)then index = 1 end - index = math.min(index, (barType=="vertical" and self.h or self.w)-(symbolSize-1)) - self:setValue(maxValue/(barType=="vertical" and self.h or self.w)*index) - end - return true - end - end; - - draw = function(self) - if(base.draw(self))then - if(self.parent~=nil)then - local obx, oby = self:getAnchorPosition() - if(barType=="horizontal")then - self.parent:writeText(obx, oby, bgSymbol:rep(index-1), self.bgcolor, self.fgcolor) - self.parent:writeText(obx + index - 1, oby, symbol:rep(symbolSize), symbolColor, symbolColor) - self.parent:writeText(obx + index+symbolSize-1, oby, bgSymbol:rep(self.w - (index+symbolSize-1)), self.bgcolor, self.fgcolor) - end - - if(barType=="vertical")then - for n = 0, self.h-1 do - - if(index==n+1)then - for curIndexOffset=0,math.min(symbolSize-1,self.h) do - self.parent:writeText(obx, oby+n+curIndexOffset, symbol, symbolColor, symbolColor) - end - else - if(n+1index-1+symbolSize)then - self.parent:writeText(obx, oby+n, bgSymbol, self.bgcolor, self.fgcolor) - end - end - end - end - end - end - end; - } - - return setmetatable(object, base) -end - - -local function Frame(name,parent) -- Frame - local base = Object(name) - local typ = "Frame" - local objects = {} - local objZIndex = {} - local object = {} - local focusedObject - base:setZIndex(10) - - local cursorBlink = false - local xCursor = 1 - local yCursor = 1 - local cursorColor = colors.white - - local xOffset, yOffset = 0, 0 - - if(parent~=nil)then - base.parent = parent - base.w, base.h = parent.w, parent.h - base.bgcolor = theme.FrameBG - base.fgcolor = theme.FrameFG - else - local termW, termH = parentTerminal.getSize() - base.w, base.h = termW, termH - base.bgcolor = theme.basaltBG - base.fgcolor = theme.basaltFG - end - - local function getObject(name) - for k,v in pairs(objects)do - for a,b in pairs(v)do - if(b.name == name)then - return v - end - end - end - end - - local function addObject(obj) - local zIndex = obj:getZIndex() - if(getObject(obj.name)~=nil)then return nil end - if(objects[zIndex]==nil)then - for x=1,#objZIndex+1 do - if(objZIndex[x]~=nil)then - if(zIndex == objZIndex[x])then break end - if(zIndex > objZIndex[x])then - table.insert(objZIndex,x,zIndex) - break - end - else - table.insert(objZIndex,zIndex) - end - end - if(#objZIndex<=0)then - table.insert(objZIndex,zIndex) - end - objects[zIndex] = {} - end - obj.parent = object - table.insert(objects[zIndex],obj) - return obj - end - - local function removeObject(obj) - for a,b in pairs(objects)do - for k,v in pairs(b)do - if(v==obj)then - table.remove(objects[a],k) - return; - end - end - end - end - - object = { - barActive = false, - barBackground = colors.gray, - barTextcolor = colors.black, - barText = "New Frame", - barTextAlign = "left", - isMoveable = false, - - getType = function(self) - return typ - end; - - setFocusedObject = function(self, obj) - for _,index in pairs(objZIndex)do - for _,v in pairs(objects[index])do - if(v == obj)then - if(focusedObject~=nil)then focusedObject:loseFocusHandler() end - focusedObject = obj - focusedObject:getFocusHandler() - end - end - end - return self - end; - - setOffset = function(self, xO, yO) - xOffset = xO ~= nil and math.floor(xO < 0 and math.abs(xO) or -xO) or xOffset - yOffset = yO ~= nil and math.floor(yO < 0 and math.abs(yO) or -yO) or yOffset - return self - end; - - getFrameOffset = function(self) - return xOffset, yOffset - end; - - removeFocusedObject = function(self) - if(focusedObject~=nil)then focusedObject:loseFocusHandler() end - focusedObject = nil - return self - end; - - getFocusedObject = function(self) - return focusedObject - end; - - show = function(self) - base:show() - if(self.parent==nil)then - activeFrame = self - end - return self - end; - - setCursor = function(self, _blink, _xCursor, _yCursor, color) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - cursorBlink = _blink or false - if(_xCursor~=nil)then xCursor = obx+_xCursor-1 end - if(_yCursor~=nil)then yCursor = oby+_yCursor-1 end - cursorColor = color or cursorColor - self:setVisualChanged() - return self - end; - - setMoveable = function(self, moveable) - self.isMoveable = moveable or not self.isMoveable - self:setVisualChanged() - return self; - end; - - - showBar = function(self, showIt) - self.barActive = showIt or not self.barActive - self:setVisualChanged() - return self - end; - - setBar = function(self, text, bgCol, fgCol) - self.barText = text or "" - self.barBackground = bgCol or self.barBackground - self.barTextcolor = fgCol or self.barTextcolor - self:setVisualChanged() - return self - end; - - setBarTextAlign = function(self, align) - self.barTextAlign = align or "left" - self:setVisualChanged() - return self - end; - - getVisualChanged = function(self) - local changed = base.getVisualChanged(self) - for _,index in pairs(objZIndex)do - if(objects[index]~=nil)then - for _,v in pairs(objects[index])do - if(v.getVisualChanged~=nil and v:getVisualChanged())then - changed = true - end - end - end - end - return changed - end; - - loseFocusHandler = function(self) - base.loseFocusHandler(self) - end; - - getFocusHandler = function(self) - base.getFocusHandler(self) - if(self.parent~=nil)then - self.parent:removeObject(self) - self.parent:addObject(self) - end - end; - - keyHandler = function(self, event, key) - if(focusedObject~=nil)then - if(focusedObject.keyHandler~=nil)then - if(focusedObject:keyHandler(event,key))then - return true - end - end - end - return false - end; - - backgroundKeyHandler = function(self, event, key) - base.backgroundKeyHandler(self, event, key) - for _,index in pairs(objZIndex)do - if(objects[index]~=nil)then - for _,v in pairs(objects[index])do - if(v.backgroundKeyHandler~=nil)then - v:backgroundKeyHandler(event,key) - end - end - end - end - end; - - eventHandler = function(self, event, p1, p2, p3, p4) - base.eventHandler(self, event, p1, p2, p3, p4) - for _,index in pairs(objZIndex)do - if(objects[index]~=nil)then - for _,v in pairs(objects[index])do - if(v.eventHandler~=nil)then - v:eventHandler(event,p1, p2, p3, p4) - end - end - end - end - if(event=="terminate")then - parentTerminal.clear() - parentTerminal.setCursorPos(1,1) - basalt.stop() - end - end; - - mouseHandler = function(self, event, button, x, y) - local xO, yO = self:getOffset() - xO = xO < 0 and math.abs(xO) or -xO - yO = yO < 0 and math.abs(yO) or -yO - if(self.drag)then - if(event=="mouse_drag")then - local parentX=1;local parentY=1 - if(self.parent~=nil)then - parentX,parentY = self.parent:getAbsolutePosition(self.parent:getAnchorPosition()) - end - self:setPosition(x+self.xToRem-(parentX-1) + xO,y-(parentY-1) + yO) - end - if(event=="mouse_up")then - self.drag = false - end - return true - end - - if(base.mouseHandler(self,event,button,x,y))then - local fx,fy = self:getAbsolutePosition(self:getAnchorPosition()) - for _,index in pairs(objZIndex)do - if(objects[index]~=nil)then - for _,v in rpairs(objects[index])do - if(v.mouseHandler~=nil)then - if(v:mouseHandler(event,button,x+xO,y+yO))then - return true - end - end - end - if(focusedObject~=nil)then focusedObject:loseFocusHandler() end - end - end - - if(self.isMoveable)then - if(x>=fx)and(x<=fx+self.w-1)and(y==fy)and(event=="mouse_click")then - self.drag = true - self.xToRem = fx-x - end - end - if(focusedObject~=nil)then focusedObject:loseFocusHandler() focusedObject = nil end - return true - end - return false - end; - - setText = function(self, x, y, text) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(y>=1)and(y<=self.h)then - if(self.parent~=nil)then - self.parent:setText(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( text, math.max(1 - x + 1, 1), self.w - x + 1 )) - else - drawHelper.setText(math.max(x+(obx-1),obx), oby+y-1, sub( text, math.max(1 - x + 1, 1), self.w - x + 1 )) - end - end - end; - - setBG = function(self, x, y, bgCol) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(y>=1)and(y<=self.h)then - if(self.parent~=nil)then - self.parent:setBG(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( bgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) - else - drawHelper.setBG(math.max(x+(obx-1),obx), oby+y-1, sub( bgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) - end - end - end; - - setFG = function(self, x, y, fgCol) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(y>=1)and(y<=self.h)then - if(self.parent~=nil)then - self.parent:setFG(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( fgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) - else - drawHelper.setFG(math.max(x+(obx-1),obx), oby+y-1, sub( fgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) - end - end - end; - - writeText = function(self, x, y, text, bgCol, fgCol) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - if(y>=1)and(y<=self.h)then - if(self.parent~=nil)then - self.parent:writeText(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( text, math.max(1 - x + 1, 1), self.w - x + 1 ), bgCol, fgCol) - else - drawHelper.writeText(math.max(x+(obx-1),obx), oby+y-1, sub( text, math.max(1 - x + 1, 1), self.w - x + 1 ), bgCol, fgCol) - end - end - end; - - drawBackgroundBox = function(self, x, y, w, h, bgCol) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - h = (y < 1 and (h+y > self.h and self.h or h+y-1) or (h+y > self.h and self.h-y+1 or h)) - w = (x < 1 and (w+x > self.w and self.w or w+x-1) or (w+x > self.w and self.w-x+1 or w)) - if(self.parent~=nil)then - self.parent:drawBackgroundBox(math.max(x+(obx-1),obx)-(self.parent.x-1), math.max(y+(oby-1),oby)-(self.parent.y-1), w, h, bgCol) - else - drawHelper.drawBackgroundBox(math.max(x+(obx-1),obx), math.max(y+(oby-1),oby), w, h, bgCol) - end - end; - - drawTextBox = function(self, x, y, w, h, symbol) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - h = (y < 1 and (h+y > self.h and self.h or h+y-1) or (h+y > self.h and self.h-y+1 or h)) - w = (x < 1 and (w+x > self.w and self.w or w+x-1) or (w+x > self.w and self.w-x+1 or w)) - if(self.parent~=nil)then - self.parent:drawTextBox(math.max(x+(obx-1),obx)-(self.parent.x-1), math.max(y+(oby-1),oby)-(self.parent.y-1), w, h, symbol:sub(1,1)) - else - drawHelper.drawTextBox(math.max(x+(obx-1),obx), math.max(y+(oby-1),oby), w, h, symbol:sub(1,1)) - end - end; - - drawForegroundBox = function(self, x, y, w, h, fgCol) - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - h = (y < 1 and (h+y > self.h and self.h or h+y-1) or (h+y > self.h and self.h-y+1 or h)) - w = (x < 1 and (w+x > self.w and self.w or w+x-1) or (w+x > self.w and self.w-x+1 or w)) - if(self.parent~=nil)then - self.parent:drawForegroundBox(math.max(x+(obx-1),obx)-(self.parent.x-1), math.max(y+(oby-1),oby)-(self.parent.y-1), w, h, fgCol) - else - drawHelper.drawForegroundBox(math.max(x+(obx-1),obx), math.max(y+(oby-1),oby), w, h, fgCol) - end - end; - - draw = function(self) - if(self:getVisualChanged())then - if(base.draw(self))then - local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) - local anchx, anchy = self:getAnchorPosition() - if(self.parent~=nil)then - self.parent:drawBackgroundBox(anchx, anchy, self.w, self.h, self.bgcolor) - self.parent:drawForegroundBox(anchx, anchy, self.w, self.h, self.fgcolor) - self.parent:drawTextBox(anchx, anchy, self.w, self.h, " ") - else - drawHelper.drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) - drawHelper.drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) - drawHelper.drawTextBox(obx, oby, self.w, self.h, " ") - end - parentTerminal.setCursorBlink(false) - if(self.barActive)then - if(self.parent~=nil)then - self.parent:writeText(anchx, anchy, getTextHorizontalAlign(self.barText, self.w, self.barTextAlign), self.barBackground, self.barTextcolor) - else - drawHelper.writeText(obx, oby, getTextHorizontalAlign(self.barText, self.w, self.barTextAlign), self.barBackground, self.barTextcolor) - end - end - - for _,index in rpairs(objZIndex)do - if(objects[index]~=nil)then - for _,v in pairs(objects[index])do - if(v.draw~=nil)then - v:draw() - end - end - end - end - - if(cursorBlink)then - parentTerminal.setTextColor(cursorColor) - parentTerminal.setCursorPos(xCursor, yCursor) - if(self.parent~=nil)then - parentTerminal.setCursorBlink(self:isFocused()) - else - parentTerminal.setCursorBlink(cursorBlink) - end - end - self:setVisualChanged(false) - end - end - end; - - addObject = function(self, obj) - return addObject(obj) - end; - - removeObject = function(self, obj) - return removeObject(obj) - end; - - addButton = function(self, name) - local obj = Button(name) - obj.name = name - return addObject(obj) - end; - - addLabel = function(self, name) - local obj = Label(name) - obj.name = name - obj.bgcolor = self.bgcolor - obj.fgcolor = self.fgcolor - return addObject(obj) - end; - - addCheckbox = function(self, name) - local obj = Checkbox(name) - obj.name = name - return addObject(obj) - end; - - addInput = function(self, name) - local obj = Input(name) - obj.name = name - return addObject(obj) - end; - - addProgram = function(self, name) - local obj = Program(name) - obj.name = name - return addObject(obj) - end; - - addTextfield = function(self, name) - local obj = Textfield(name) - obj.name = name - return addObject(obj) - end; - - addList = function(self, name) - local obj = List(name) - obj.name = name - return addObject(obj) - end; - - addDropdown = function(self, name) - local obj = Dropdown(name) - obj.name = name - return addObject(obj) - end; - - addRadio = function(self, name) - local obj = Radio(name) - obj.name = name - return addObject(obj) - end; - - addTimer = function(self, name) - local obj = Timer(name) - obj.name = name - return addObject(obj) - end; - - addAnimation = function(self, name) - local obj = Animation(name) - obj.name = name - return addObject(obj) - end; - - addSlider = function(self, name) - local obj = Slider(name) - obj.name = name - return addObject(obj) - end; - - addScrollbar = function(self, name) - local obj = Scrollbar(name) - obj.name = name - return addObject(obj) - end; - - addMenubar = function(self, name) - local obj = Menubar(name) - obj.name = name - return addObject(obj) - end; - - addThread = function(self, name) - local obj = Thread(name) - obj.name = name - return addObject(obj) - end; - - addPane = function(self, name) - local obj = Pane(name) - obj.name = name - return addObject(obj) - end; - - addImage = function(self, name) - local obj = Image(name) - obj.name = name - return addObject(obj) - end; - - addFrame = function(self, name) - local obj = Frame(name, self) - obj.name = name - return addObject(obj) - end; - } - setmetatable(object, base) - if(parent==nil)then - table.insert(frames, object) - end - return object -end - - -local updaterActive = false -local function basaltUpdateEvent(event, p1,p2,p3,p4) - if(event=="mouse_click")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end - if(event=="mouse_drag")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end - if(event=="mouse_up")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end - if(event=="mouse_scroll")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end - if(event=="key")or(event=="char")then activeFrame:keyHandler(event,p1) activeFrame:backgroundKeyHandler(event,p1) end - for _,v in pairs(frames)do - v:eventHandler(event, p1, p2, p3, p4) - end - if(updaterActive)then - activeFrame:draw() - drawHelper.update() - end -end - -function basalt.autoUpdate(isActive) - parentTerminal.clear() - updaterActive = isActive or true - activeFrame:draw() - drawHelper.update() - while updaterActive do - local event, p1,p2,p3,p4 = os.pullEventRaw() -- change to raw later - basaltUpdateEvent(event, p1,p2,p3,p4) - end -end - -function basalt.update(event, p1, p2, p3, p4) - if(event~="nil")then - basaltUpdateEvent(event, p1,p2,p3,p4) - else - activeFrame:draw() - drawHelper.update() - end -end - -function basalt.stop() - updaterActive = false -end - -function basalt.getFrame(name) - for k,v in pairs(frames)do - if(v.name==name)then - return v - end - end -end - -function basalt.getActiveFrame() - return activeFrame -end - -function basalt.setActiveFrame(frame) - if(frame:getType()=="Frame")then - activeFrame = frame - return true - end - return false -end - -function basalt.createFrame(name) - local frame = Frame(name) - return frame -end - -function basalt.removeFrame(name) - for k,v in pairs(frames)do - if(v.name==name)then - frames[k] = nil - return true - end - end - return false -end - -if(basalt.debugger)then - basalt.debugFrame = basalt.createFrame("basaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setBar("Debug",colors.black,colors.gray) - basalt.debugList = basalt.debugFrame:addList("debugList"):setSize(basalt.debugFrame.w - 2, basalt.debugFrame.h - 3):setPosition(2,3):show() - basalt.debugFrame:addButton("back"):setAnchor("right"):setSize(1,1):setText("\22"):onClick(function() basalt.oldFrame:show() end):setBackground(colors.red):show() - basalt.debugLabel = basalt.debugFrame:addLabel("debugLabel"):onClick(function() basalt.oldFrame = activeFrame basalt.debugFrame:show() end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottom"):show() -end - - -if(basalt.debugger)then - function basalt.debug(...) - local args = {...} - if(activeFrame.name~="basaltDebuggingFrame")then - if(activeFrame~=basalt.debugLabel.frame)then - basalt.debugLabel:setParent(activeFrame) - end - end - local str = "" - for k,v in pairs(args)do - str = str..tostring(v)..(#args~=k and ", " or "") - end - basalt.debugLabel:setText("[Debug] "..str) - basalt.debugList:addItem(str) - if(basalt.debugList:getItemCount()>basalt.debugList.h)then basalt.debugList:removeItem(1) end - - basalt.debugLabel:show() - end -end - -return basalt \ No newline at end of file +local a={basaltBG=colors.lightGray,basaltFG=colors.black,FrameBG=colors.gray,FrameFG=colors.black,ButtonBG=colors.gray,ButtonFG=colors.black,CheckboxBG=colors.gray,CheckboxFG=colors.black,InputBG=colors.gray,InputFG=colors.black,textfieldBG=colors.gray,textfieldFG=colors.black,listBG=colors.gray,listFG=colors.black,dropdownBG=colors.gray,dropdownFG=colors.black,radioBG=colors.gray,radioFG=colors.black,selectionBG=colors.black,selectionFG=colors.lightGray}local b={debugger=true,version=1}local c;local d={}local e={}local f=term.current()local g=string.sub;local function h(i,j,k)local i=string.sub(i,1,j)local l=j-string.len(i)if k=="right"then i=string.rep(" ",l)..i elseif k=="center"then i=string.rep(" ",math.floor(l/2))..i..string.rep(" ",math.floor(l/2))i=i..(string.len(i)=1 and M<=n then if L+i:len()>0 and L<=j then local N=v[M]local O;local P=L+#i-1;if L<1 then local Q=1-L+1;local R=j-L+1;i=g(i,Q,R)elseif P>j then local R=j-L+1;i=g(i,1,R)end;if L>1 then local R=L-1;O=g(N,1,R)..i else O=i end;if P=1 and M<=n then if L+T:len()>0 and L<=j then local N=w[M]local O;local P=L+#T-1;if L<1 then T=g(T,1-L+1,j-L+1)elseif P>j then T=g(T,1,j-L+1)end;if L>1 then O=g(N,1,L-1)..T else O=T end;if P=1 and M<=n then if L+T:len()>0 and L<=j then local N=x[M]local O;local P=L+#T-1;if L<1 then local Q=1-L+1;local R=j-L+1;T=g(T,Q,R)elseif P>j then local R=j-L+1;T=g(T,1,R)end;if L>1 then local R=L-1;O=g(N,1,R)..T else O=T end;if PL and aM<=M and aM+self.h>M and ar then if self.parent~=nil then self.parent:setFocusedObject(self)end;at:sendEvent(a5,self,a5,aK,L,M)return true end;return false end,keyHandler=function(self,a5,aN)if self:isFocused()then at:sendEvent(a5,self,a5,aN)return true end;return false end,backgroundKeyHandler=function(self,a5,aN)at:sendEvent("background_"..a5,self,a5,aN)end,valueChangedHandler=function(self)at:sendEvent("value_changed",self)end,eventHandler=function(self,a5,aO,aP,aQ,aR)at:sendEvent("custom_event_handler",self,a5,aO,aP,aQ,aR)end,getFocusHandler=function(self)at:sendEvent("get_focus",self)end,loseFocusHandler=function(self)at:sendEvent("lose_focus",self)end}au.__index=au;return au end;local function aS(ak)local aT=aj(ak)local al="Button"aT:setValue("Button")aT:setZIndex(5)aT.w=8;aT.bgcolor=a.ButtonBG;aT.fgcolor=a.ButtonFG;local aU="center"local aV="center"local au={getType=function(self)return al end,setHorizontalAlign=function(self,aW)aU=aW end,setVerticalAlign=function(self,aW)aV=aW end,setText=function(self,i)aT:setValue(i)return self end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition(self.x,self.y)local aZ=m(self.h,aV)self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)self.parent:drawForegroundBox(aX,aY,self.w,self.h,self.fgcolor)self.parent:drawTextBox(aX,aY,self.w,self.h," ")for l=1,self.h do if l==aZ then self.parent:setText(aX,aY+l-1,h(self:getValue(),self.w,aU))end end end end end}return setmetatable(au,aT)end;local function a_(ak)local aT=aj(ak)local al="Program"aT:setZIndex(5)local au;local function b0(L,M,j,n)local b1,b2=1,1;local b3,b4=colors.black,colors.white;local b5=false;local b6=false;local v={}local w={}local x={}local b7={}local B;local C={}for r=0,15 do local b8=2^r;b7[b8]={f.getPaletteColour(b8)}end;local function D()B=(" "):rep(j)for l=0,15 do local E=2^l;local F=s[E]C[E]=F:rep(j)end end;local function G()D()local H=B;local I=C[colors.white]local J=C[colors.black]for l=1,n do v[l]=g(v[l]==nil and H or v[l]..H:sub(1,j-v[l]:len()),1,j)x[l]=g(x[l]==nil and I or x[l]..I:sub(1,j-x[l]:len()),1,j)w[l]=g(w[l]==nil and J or w[l]..J:sub(1,j-w[l]:len()),1,j)end end;G()local function b9()if b1>=1 and b2>=1 and b1<=j and b2<=n then else end end;local function ba(bb,bc,bd)local be=b1;local P=be+#bb-1;if b2>=1 and b2<=n then if be<=j and P>=1 then if be==1 and P==j then v[b2]=bb;x[b2]=bc;w[b2]=bd else local bf,bg,bh;if be<1 then local bi=1-be+1;local bj=j-be+1;bf=g(bb,bi,bj)bg=g(bc,bi,bj)bh=g(bd,bi,bj)elseif P>j then local bj=j-be+1;bf=g(bb,1,bj)bg=g(bc,1,bj)bh=g(bd,1,bj)else bf=bb;bg=bc;bh=bd end;local bk=v[b2]local bl=x[b2]local bm=w[b2]local bn,bo,bp;if be>1 then local bq=be-1;bn=g(bk,1,bq)..bf;bo=g(bl,1,bq)..bg;bp=g(bm,1,bq)..bh else bn=bf;bo=bg;bp=bh end;if P=1 and M<=n then v[bO]=v[M]w[bO]=w[M]x[bO]=x[M]else v[bO]=bL;x[bO]=bM;w[bO]=bN end end end;if b6 then b9()end end,isColor=function()return f.isColor()end,isColour=function()return f.isColor()end,write=function(i)i=tostring(i)if b6 then ba(i,s[b4]:rep(i:len()),s[b3]:rep(i:len()))end end,clearLine=function()if b6 then K(1,b2,(" "):rep(j))S(1,b2,s[b3]:rep(j))U(1,b2,s[b4]:rep(j))end;if b6 then b9()end end,clear=function()for l=1,n do K(1,l,(" "):rep(j))S(1,l,s[b3]:rep(j))U(1,l,s[b4]:rep(j))end;if b6 then b9()end end,blit=function(i,bP,bQ)if type(i)~="string"then error("bad argument #1 (expected string, got "..type(i)..")",2)end;if type(bP)~="string"then error("bad argument #2 (expected string, got "..type(bP)..")",2)end;if type(bQ)~="string"then error("bad argument #3 (expected string, got "..type(bQ)..")",2)end;if#bP~=#i or#bQ~=#i then error("Arguments must be the same length",2)end;if b6 then ba(i,bP,bQ)end end}return bG end;aT.w=30;aT.h=12;local bR=b0(1,1,aT.w,aT.h)local bS;local bT=false;local bU={}au={getType=function(self)return al end,show=function(self)aT.show(self)bR.setBackgroundColor(self.bgcolor)bR.setTextColor(self.fgcolor)bR.basalt_setVisible(true)return self end,hide=function(self)aT.hide(self)bR.basalt_setVisible(false)return self end,setPosition=function(self,L,M,aA)aT.setPosition(self,L,M,aA)bR.basalt_reposition(self:getAnchorPosition())return self end,getBasaltWindow=function()return bR end,getBasaltProcess=function()return bS end,setSize=function(self,j,n)aT.setSize(self,j,n)bR.basalt_resize(self.w,self.h)return self end,getStatus=function(self)if bS~=nil then return bS:getStatus()end;return"inactive"end,execute=function(self,ad,...)bS=ab:new(ad,bR,...)bR.setBackgroundColor(colors.black)bR.setTextColor(colors.white)bR.clear()bR.setCursorPos(1,1)bS:resume()bT=false;return self end,stop=function(self)if bS~=nil then if not bS:isDead()then bS:resume("terminate")if bS:isDead()then if self.parent~=nil then self.parent:setCursor(false)end end end end;return self end,pause=function(self,bV)bT=bV or not bT;if bS~=nil then if not bS:isDead()then if not bT then self:injectEvents(bU)bU={}end end end;return self end,isPaused=function(self)return bT end,injectEvent=function(self,a5,aO,aP,aQ,aR,bW)if bS~=nil then if not bS:isDead()then if bT==false or bW then bS:resume(a5,aO,aP,aQ,aR)else table.insert(bU,{event=a5,args={aO,aP,aQ,aR}})end end end;return self end,getQueuedEvents=function(self)return bU end,updateQueuedEvents=function(self,a3)bU=a3 or bU;return self end,injectEvents=function(self,a3)if bS~=nil then if not bS:isDead()then for aJ,a9 in pairs(a3)do bS:resume(a9.event,table.unpack(a9.args))end end end;return self end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then if bS==nil then return false end;if not bS:isDead()then if not bT then local bX,bY=self:getAbsolutePosition(self:getAnchorPosition())bS:resume(a5,aK,L-bX+1,M-bY+1)end end;return true end end,keyHandler=function(self,a5,aN)aT.keyHandler(self,a5,aN)if self:isFocused()then if bS==nil then return false end;if not bS:isDead()then if not bT then if self.draw then bS:resume(a5,aN)end end end end end,getFocusHandler=function(self)aT.getFocusHandler(self)if bS~=nil then if not bS:isDead()then if not bT then if self.parent~=nil then local bZ,b_=bR.getCursorPos()local aX,aY=self:getAnchorPosition()if self.parent~=nil then if aX+bZ-1>=1 and aX+bZ-1<=aX+self.w-1 and b_+aY-1>=1 and b_+aY-1<=aY+self.h-1 then self.parent:setCursor(bR.getCursorBlink(),aX+bZ-1,b_+aY-1,bR.getTextColor())end end end end end end end,loseFocusHandler=function(self)aT.loseFocusHandler(self)if bS~=nil then if not bS:isDead()then if self.parent~=nil then self.parent:setCursor(false)end end end end,eventHandler=function(self,a5,aO,aP,aQ,aR)if bS==nil then return end;if not bS:isDead()then if not bT then if a5~="mouse_click"and a5~="mouse_up"and a5~="mouse_scroll"and a5~="mouse_drag"and a5~="key_up"and a5~="key"and a5~="char"and a5~="terminate"then bS:resume(a5,aO,aP,aQ,aR)end;if self:isFocused()then local aX,aY=self:getAnchorPosition()local bZ,b_=bR.getCursorPos()if self.parent~=nil then if aX+bZ-1>=1 and aX+bZ-1<=aX+self.w-1 and b_+aY-1>=1 and b_+aY-1<=aY+self.h-1 then self.parent:setCursor(bR.getCursorBlink(),aX+bZ-1,b_+aY-1,bR.getTextColor())end end;if a5=="terminate"and self:isFocused()then self:stop()end end else if a5~="mouse_click"and a5~="mouse_up"and a5~="mouse_scroll"and a5~="mouse_drag"and a5~="key_up"and a5~="key"and a5~="char"and a5~="terminate"then table.insert(bU,{event=a5,args={aO,aP,aQ,aR}})end end end end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()bR.basalt_reposition(aX,aY)self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)bR.basalt_update()end end end}return setmetatable(au,aT)end;local function c0(ak)local aT=aj(ak)local al="Label"aT:setZIndex(3)local c1=true;aT:setValue("")local au={getType=function(self)return al end,setText=function(self,i)i=tostring(i)aT:setValue(i)if c1 then self.w=i:len()end;return self end,setSize=function(self,j,n)self.w,self.h=j,n;c1=false;return self end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()self.parent:writeText(aX,aY,self:getValue(),self.bgcolor,self.fgcolor)end end end}return setmetatable(au,aT)end;local function c2(ak)local aT=aj(ak)local al="Pane"local au={getType=function(self)return al end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)self.parent:drawForegroundBox(aX,aY,self.w,self.h,self.fgcolor)end end end}return setmetatable(au,aT)end;local function c3(ak)local aT=aj(ak)local al="Image"aT:setZIndex(2)local c4;local au={getType=function(self)return al end,loadImage=function(self,ad)c4=paintutils.loadImage(ad)return self end,draw=function(self)if aT.draw(self)then if self.parent~=nil then if c4~=nil then local aX,aY=self:getAnchorPosition()for az=1,math.min(#c4,self.h)do local c5=c4[az]for ay=1,math.min(#c5,self.w)do if c5[ay]>0 then self.parent:drawBackgroundBox(aX+ay-1,aY+az-1,1,1,c5[ay])end end end end end end end}return setmetatable(au,aT)end;local function c6(ak)local aT=aj(ak)local al="Checkbox"aT:setZIndex(5)aT:setValue(false)aT.w=1;aT.h=1;aT.bgcolor=a.CheckboxBG;aT.fgcolor=a.CheckboxFG;local au={symbol="\42",getType=function(self)return al end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then if a5=="mouse_click"and aK==1 then if self:getValue()~=true and self:getValue()~=false then self:setValue(false)else self:setValue(not self:getValue())end end;return true end;return false end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()local aZ=m(self.h,"center")self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)for l=1,self.h do if l==aZ then if self:getValue()==true then self.parent:writeText(aX,aY+l-1,h(self.symbol,self.w,"center"),self.bgcolor,self.fgcolor)else self.parent:writeText(aX,aY+l-1,h(" ",self.w,"center"),self.bgcolor,self.fgcolor)end end end end end end}return setmetatable(au,aT)end;local function c7(ak)local aT=aj(ak)local al="Input"local c8="text"local c9=0;aT:setZIndex(5)aT:setValue("")aT.w=10;aT.h=1;aT.bgcolor=a.InputBG;aT.fgcolor=a.InputFG;local ca=1;local cb=1;local cc=""local cd;local ce;local cf=cc;local cg=false;local au={getType=function(self)return al end,setInputType=function(self,ch)if ch=="password"or ch=="number"or ch=="text"then c8=ch end;return self end,setDefaultText=function(self,i,ci,cj)cc=i;cd=cj or cd;ce=ci or ce;if self:isFocused()then cf=""else cf=cc end;return self end,getInputType=function(self)return c8 end,setValue=function(self,ck)aT.setValue(self,tostring(ck))if not cg then ca=tostring(ck):len()+1 end;return self end,getValue=function(self)local ck=aT.getValue(self)return c8=="number"and tonumber(ck)or ck end,setInputLimit=function(self,cl)c9=tonumber(cl)or c9;return self end,getInputLimit=function(self)return c9 end,getFocusHandler=function(self)aT.getFocusHandler(self)if self.parent~=nil then local aX,aY=self:getAnchorPosition()cf=""if self.parent~=nil then self.parent:setCursor(true,aX+ca-cb,aY,self.fgcolor)end end end,loseFocusHandler=function(self)aT.loseFocusHandler(self)if self.parent~=nil then self.parent:setCursor(false)cf=cc end end,keyHandler=function(self,a5,aN)if aT.keyHandler(self,a5,aN)then cg=true;if a5=="key"then if aN==259 then local i=tostring(aT.getValue())if ca>1 then self:setValue(i:sub(1,ca-2)..i:sub(ca,i:len()))if ca>1 then ca=ca-1 end;if cb>1 then if cacm then ca=cm+1 end;if ca<1 then ca=1 end;if ca=self.w+cb then cb=ca-self.w+1 end;if cb<1 then cb=1 end end;if aN==263 then ca=ca-1;if ca>=1 then if ca=self.w+cb then cb=ca end end;if ca<1 then ca=1 end;if cb<1 then cb=1 end end end;if a5=="char"then local i=aT.getValue()if i:len()=self.w+cb then cb=cb+1 end end end;local aX,aY=self:getAnchorPosition()local ck=tostring(aT.getValue())local co=(ca<=ck:len()and ca-1 or ck:len())-(cb-1)if co>self.x+self.w-1 then co=self.x+self.w-1 end;if self.parent~=nil then self.parent:setCursor(true,aX+co,aY,self.fgcolor)end;cg=false end end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then if a5=="mouse_click"and aK==1 then end;return true end;return false end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()local aZ=m(self.h,"center")self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)for l=1,self.h do if l==aZ then local ck=tostring(aT.getValue())local cj=self.bgcolor;local ci=self.fgcolor;local i;if ck:len()<=0 then i=cf;cj=cd or cj;ci=ce or ci end;i=cf;if ck~=""then i=ck end;i=i:sub(cb,self.w+cb-1)local cp=self.w-i:len()if cp<0 then cp=0 end;if c8=="password"and ck~=""then i=string.rep("*",i:len())end;i=i..string.rep(" ",cp)self.parent:writeText(aX,aY+l-1,i,cj,ci)end end end end end}return setmetatable(au,aT)end;local function cq(ak)local aT=aj(ak)local al="Textfield"local cr,cb,ca,cs=1,1,1,1;local ct={""}local cu={}local cv=colors.lightBlue;aT.w=20;aT.h=8;aT.bgcolor=a.textfieldBG;aT.fgcolor=a.textfieldFG;aT:setZIndex(5)local au={getType=function(self)return al end,getLines=function(self)return ct end,getLine=function(self,a4)return ct[a4]or""end,editLine=function(self,a4,i)ct[a4]=i or ct[a4]return self end,addLine=function(self,i,a4)if a4~=nil then table.insert(ct,a4,i)else table.insert(ct,i)end;return self end,removeLine=function(self,a4)table.remove(ct,a4 or#ct)if#ct<=0 then table.insert(ct,"")end;return self end,getTextCursor=function(self)return ca,cs end,getFocusHandler=function(self)aT.getFocusHandler(self)if self.parent~=nil then local aX,aY=self:getAnchorPosition()if self.parent~=nil then self.parent:setCursor(true,aX+ca-cb,aY+cs-cr,self.fgcolor)end end end,loseFocusHandler=function(self)aT.loseFocusHandler(self)if self.parent~=nil then self.parent:setCursor(false)end end,keyHandler=function(self,a5,aN)if aT.keyHandler(self,a5,aN)then local aX,aY=self:getAnchorPosition()if a5=="key"then if aN==259 then if ct[cs]==""then if cs>1 then table.remove(ct,cs)ca=ct[cs-1]:len()+1;cb=ca-self.w+1;if cb<1 then cb=1 end;cs=cs-1 end elseif ca<=1 then if cs>1 then ca=ct[cs-1]:len()+1;cb=ca-self.w+1;if cb<1 then cb=1 end;ct[cs-1]=ct[cs-1]..ct[cs]table.remove(ct,cs)cs=cs-1 end else ct[cs]=ct[cs]:sub(1,ca-2)..ct[cs]:sub(ca,ct[cs]:len())if ca>1 then ca=ca-1 end;if cb>1 then if ca=self.h then cr=cr+1 end;self:setValue("")end;if aN==265 then if cs>1 then cs=cs-1;if ca>ct[cs]:len()+1 then ca=ct[cs]:len()+1 end;if cb>1 then if ca1 then if csct[cs]:len()+1 then ca=ct[cs]:len()+1 end;if cs>=cr+self.h then cr=cr+1 end end end;if aN==262 then ca=ca+1;if cs<#ct then if ca>ct[cs]:len()+1 then ca=1;cs=cs+1 end elseif ca>ct[cs]:len()then ca=ct[cs]:len()+1 end;if ca<1 then ca=1 end;if ca=self.w+cb then cb=ca-self.w+1 end;if cb<1 then cb=1 end end;if aN==263 then ca=ca-1;if ca>=1 then if ca=self.w+cb then cb=ca end end;if cs>1 then if ca<1 then cs=cs-1;ca=ct[cs]:len()+1;cb=ca-self.w+1 end end;if ca<1 then ca=1 end;if cb<1 then cb=1 end end end;if a5=="char"then ct[cs]=ct[cs]:sub(1,ca-1)..aN..ct[cs]:sub(ca,ct[cs]:len())ca=ca+1;if ca>=self.w+cb then cb=cb+1 end;self:setValue("")end;local co=(ca<=ct[cs]:len()and ca-1 or ct[cs]:len())-(cb-1)if co>self.x+self.w-1 then co=self.x+self.w-1 end;local cw=cs-crct[cs]:len()then ca=ct[cs]:len()+1 end;if ca#ct-(self.h-1)then cr=#ct-(self.h-1)end;if cr<1 then cr=1 end;if self.parent~=nil then if aX+ca-cb>=aX and aX+ca-cb<=aX+self.w and(aY+cs-cr>=aY and aY+cs-cr<=aY+self.h)then self.parent:setCursor(true,cx+ca-cb,cy+cs-cr)else self.parent:setCursor(false)end end end;self:setVisualChanged()return true end end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)self.parent:drawForegroundBox(aX,aY,self.w,self.h,self.fgcolor)for l=1,self.h do local i=""if ct[l+cr-1]~=nil then i=ct[l+cr-1]end;i=i:sub(cb,self.w+cb-1)local cp=self.w-i:len()if cp<0 then cp=0 end;i=i..string.rep(" ",cp)self.parent:setText(aX,aY+l-1,i)end end end end}return setmetatable(au,aT)end;local function cz(ak)local aT=aj(ak)local al="List"aT.w=16;aT.h=6;aT.bgcolor=a.listBG;aT.fgcolor=a.listFG;aT:setZIndex(5)local cA={}local cB=a.selectionBG;local cC=a.selectionFG;local cD=true;local cE="left"local cF=0;local cG=true;local au={getType=function(self)return al end,addItem=function(self,i,W,X,...)table.insert(cA,{text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})if#cA==1 then self:setValue(cA[1])end;return self end,setIndexOffset=function(self,cH)cF=cH;return self end,getIndexOffset=function(self)return cF end,removeItem=function(self,a4)table.remove(cA,a4)return self end,getItem=function(self,a4)return cA[a4]end,getItemIndex=function(self)local cI=self:getValue()for a8,a9 in pairs(cA)do if a9==cI then return a8 end end end,clear=function(self)cA={}self:setValue({})return self end,getItemCount=function(self)return#cA end,editItem=function(self,a4,i,W,X,...)table.remove(cA,a4)table.insert(cA,a4,{text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})return self end,selectItem=function(self,a4)self:setValue(cA[a4]or{})return self end,setSelectedItem=function(self,W,X,cJ)cB=W or self.bgcolor;cC=X or self.fgcolor;cD=cJ;return self end,setScrollable=function(self,cK)cG=cK;return self end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if a5=="mouse_click"or a5=="mouse_drag"then if aK==1 then if#cA>0 then for l=1,self.h do if cA[l+cF]~=nil then if aX<=L and aX+self.w>L and aY+l-1==M then self:setValue(cA[l+cF])end end end end end end;if a5=="mouse_scroll"and cG then cF=cF+aK;if cF<0 then cF=0 end;if aK>=1 then if#cA>self.h then if cF>#cA-self.h then cF=#cA-self.h end;if cF>=#cA then cF=#cA-1 end else cF=cF-1 end end end;self:setVisualChanged()return true end end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)for l=1,self.h do if cA[l+cF]~=nil then if cA[l+cF]==self:getValue()then if cD then self.parent:writeText(aX,aY+l-1,h(cA[l+cF].text,self.w,cE),cB,cC)else self.parent:writeText(aX,aY+l-1,h(cA[l+cF].text,self.w,cE),cA[l+cF].bgCol,cA[l+cF].fgCol)end else self.parent:writeText(aX,aY+l-1,h(cA[l+cF].text,self.w,cE),cA[l+cF].bgCol,cA[l+cF].fgCol)end end end end end end}return setmetatable(au,aT)end;local function cL(ak)local aT=aj(ak)local al="Menubar"aT.w=30;aT.h=1;aT.bgcolor=colors.gray;aT.fgcolor=colors.lightGray;aT:setZIndex(5)local cA={}local cB=a.selectionBG;local cC=a.selectionFG;local cD=true;local cE="left"local cM=0;local cp=2;local cG=false;local function cN()local cO=0;local ay=1;for l=1,#cA do if ay+cA[l].text:len()+cp*2>aT.w then cO=cO+1 end;ay=ay+cA[l].text:len()+cp*2 end;return cO end;local au={getType=function(self)return al end,addItem=function(self,i,W,X,...)table.insert(cA,{text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})if#cA==1 then self:setValue(cA[1])end;return self end,getItemIndex=function(self)local cI=self:getValue()for a8,a9 in pairs(cA)do if a9==cI then return a8 end end end,clear=function(self)cA={}self:setValue({})return self end,setSpace=function(self,cP)cp=cP or cp;return self end,setButtonOffset=function(self,o)cM=o or 0;if cM<0 then cM=0 end;local cO=cN()if cM>cO then cM=cO end;return self end,setScrollable=function(self,cK)cG=cK;return self end,removeItem=function(self,a4)table.remove(cA,a4)return self end,getItem=function(self,a4)return cA[a4]end,getItemCount=function(self)return#cA end,editItem=function(self,a4,i,W,X,...)table.remove(cA,a4)table.insert(cA,a4,{text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})return self end,selectItem=function(self,a4)self:setValue(cA[a4]or{})return self end,setSelectedItem=function(self,W,X,cJ)cB=W or self.bgcolor;cC=X or self.fgcolor;cD=cJ;return self end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if a5=="mouse_click"then local ay=1;for l=1+cM,#cA do if cA[l]~=nil then if ay+cA[l].text:len()+cp*2<=self.w then if aX+ay-1<=L and aX+ay-1+cA[l].text:len()+cp*2>L and aY==M then self:setValue(cA[l])end;ay=ay+cA[l].text:len()+cp*2 else break end end end end;if a5=="mouse_scroll"and cG then cM=cM+aK;if cM<0 then cM=0 end;local cO=cN()if cM>cO then cM=cO end end end end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)local ay=1;for l=1+cM,#cA do if cA[l]~=nil then if ay+cA[l].text:len()+cp*2<=self.w then if cA[l]==self:getValue()then self.parent:writeText(aX+ay-1,aY,h((" "):rep(cp)..cA[l].text..(" "):rep(cp),cA[l].text:len()+cp*2,cE),cB or cA[l].bgCol,cC or cA[l].fgCol)else self.parent:writeText(aX+ay-1,aY,h((" "):rep(cp)..cA[l].text..(" "):rep(cp),cA[l].text:len()+cp*2,cE),cA[l].bgCol,cA[l].fgCol)end;ay=ay+cA[l].text:len()+cp*2 else break end end end end end end}return setmetatable(au,aT)end;local function cQ(ak)local aT=aj(ak)local al="Dropdown"aT.w=12;aT.h=1;aT.bgcolor=a.dropdownBG;aT.fgcolor=a.dropdownFG;aT:setZIndex(6)local cA={}local cB=a.selectionBG;local cC=a.selectionFG;local cD=true;local cE="left"local cF=0;local cR=16;local cS=6;local cT="\16"local cU="\31"local cV=1;local au={getType=function(self)return al end,setIndexOffset=function(self,cH)cF=cH;return self end,getIndexOffset=function(self)return cF end,addItem=function(self,i,W,X,...)table.insert(cA,{text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})if#cA==1 then self:setValue(cA[1])end;return self end,removeItem=function(self,a4)table.remove(cA,a4)return self end,getItem=function(self,a4)return cA[a4]end,getItemIndex=function(self)local cI=self:getValue()for a8,a9 in pairs(cA)do if a9==cI then return a8 end end end,clear=function(self)cA={}self:setValue({})return self end,getItemCount=function(self)return#cA end,editItem=function(self,a4,i,W,X,...)table.remove(cA,a4)table.insert(cA,a4,{text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})return self end,selectItem=function(self,a4)self:setValue(cA[a4]or{})return self end,setSelectedItem=function(self,W,X,cJ)cB=W or self.bgcolor;cC=X or self.fgcolor;cD=cJ;return self end,setDropdownSize=function(self,j,n)cR,cS=j,n;return self end,mouseHandler=function(self,a5,aK,L,M)if cV==2 then local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if a5=="mouse_click"then if aK==1 then if#cA>0 then for l=1,cS do if cA[l+cF]~=nil then if aX<=L and aX+cR>L and aY+l==M then self:setValue(cA[l+cF])return true end end end end end end;if a5=="mouse_scroll"then cF=cF+aK;if cF<0 then cF=0 end;if aK==1 then if#cA>cS then if cF>#cA-cS then cF=#cA-cS end else cF=cA-1 end end;return true end;self:setVisualChanged()end;if aT.mouseHandler(self,a5,aK,L,M)then cV=2 else cV=1 end end,draw=function(self)if aT.draw(self)then local aX,aY=self:getAnchorPosition()if self.parent~=nil then self.parent:drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)if#cA>=1 then if self:getValue().text~=nil then if cV==1 then self.parent:writeText(aX,aY,h(self:getValue().text,self.w,cE):sub(1,self.w-1)..cT,self.bgcolor,self.fgcolor)else self.parent:writeText(aX,aY,h(self:getValue().text,self.w,cE):sub(1,self.w-1)..cU,self.bgcolor,self.fgcolor)end end;if cV==2 then for l=1,cS do if cA[l+cF]~=nil then if cA[l+cF]==self:getValue()then if cD then self.parent:writeText(aX,aY+l,h(cA[l+cF].text,cR,cE),cB,cC)else self.parent:writeText(aX,aY+l,h(cA[l+cF].text,cR,cE),cA[l+cF].bgCol,cA[l+cF].fgCol)end else self.parent:writeText(aX,aY+l,h(cA[l+cF].text,cR,cE),cA[l+cF].bgCol,cA[l+cF].fgCol)end end end end end end end end}return setmetatable(au,aT)end;local function cW(ak)local aT=aj(ak)local al="Radio"aT.w=8;aT.bgcolor=a.listBG;aT.fgcolor=a.listFG;aT:setZIndex(5)local cA={}local cB=a.selectionBG;local cC=a.selectionFG;local cX=aT.bgcolor;local cY=aT.fgcolor;local cD=true;local Y="\7"local cE="left"local au={getType=function(self)return al end,addItem=function(self,i,L,M,W,X,...)table.insert(cA,{x=L or 1,y=M or 1,text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})if#cA==1 then self:setValue(cA[1])end;return self end,removeItem=function(self,a4)table.remove(cA,a4)return self end,getItem=function(self,a4)return cA[a4]end,getItemIndex=function(self)local cI=self:getValue()for a8,a9 in pairs(cA)do if a9==cI then return a8 end end end,clear=function(self)cA={}self:setValue({})return self end,getItemCount=function(self)return#cA end,editItem=function(self,a4,i,L,M,W,X,...)table.remove(cA,a4)table.insert(cA,a4,{x=L or 1,y=M or 1,text=i,bgCol=W or self.bgcolor,fgCol=X or self.fgcolor,args={...}})return self end,selectItem=function(self,a4)self:setValue(cA[a4]or{})return self end,setSelectedItem=function(self,W,X,cZ,c_,cJ)cB=W or cB;cC=X or cC;cX=cZ or cX;cY=c_ or cY;cD=cJ;return self end,mouseHandler=function(self,a5,aK,L,M)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if a5=="mouse_click"then if aK==1 then if#cA>0 then for aJ,a9 in pairs(cA)do if aX+a9.x-1<=L and aX+a9.x-1+a9.text:len()+2>=L and aY+a9.y-1==M then self:setValue(a9)if self.parent~=nil then self.parent:setFocusedObject(self)end;self:setVisualChanged()return true end end end end end;return false end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()for aJ,a9 in pairs(cA)do if a9==self:getValue()then if cE=="left"then self.parent:writeText(a9.x+aX-1,a9.y+aY-1,Y,cX,cY)self.parent:writeText(a9.x+2+aX-1,a9.y+aY-1,a9.text,cB,cC)end else self.parent:drawBackgroundBox(a9.x+aX-1,a9.y+aY-1,1,1,self.bgcolor)self.parent:writeText(a9.x+2+aX-1,a9.y+aY-1,a9.text,a9.bgCol,a9.fgCol)end end end end end}return setmetatable(au,aT)end;local function d0(ak)local al="Timer"local d1=0;local d2=0;local d3=0;local d4;local at=a2()local au={name=ak,getType=function(self)return al end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,setTime=function(self,d5,d6)d1=d5 or 0;d2=d6 or 1;return self end,start=function(self)d3=d2;d4=os.startTimer(d1)return self end,cancel=function(self)if d4~=nil then os.cancelTimer(d4)end;return self end,onCall=function(self,a7)at:registerEvent("timed_event",a7)return self end,eventHandler=function(self,a5,d7)if a5=="timer"and d7==d4 then at:sendEvent("timed_event",self)if d3>=1 then d3=d3-1;if d3>=1 then d4=os.startTimer(d1)end elseif d3==-1 then d4=os.startTimer(d1)end end end}au.__index=au;return au end;local function d8(ak)local au;local al="Thread"local a7;local d9;local da=false;au={name=ak,getType=function(self)return al end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,start=function(self,db)if db==nil then error("function is nil")end;a7=db;d9=coroutine.create(a7)da=true;local ah,ai=coroutine.resume(d9)if not ah then if ai~="Terminated"then error("Threaderror - "..ai)end end;return self end,getStatus=function(self,db)if d9~=nil then return coroutine.status(d9)end;return nil end,stop=function(self,db)da=false;return self end,eventHandler=function(self,a5,aO,aP,aQ)if da then if coroutine.status(d9)~="dead"then local ah,ai=coroutine.resume(d9,a5,aO,aP,aQ)if not ah then if ai~="Terminated"then error("Threaderror - "..ai)end end else da=false end end end}au.__index=au;return au end;local function dc(ak)local au={}local al="Animation"local d4;local dd={}local a4=1;local de=0;local df;local function dg()if dd[a4]~=nil then dd[a4].f(au,a4)end;a4=a4+1;if dd[a4]~=nil then if dd[a4].t>0 then d4=os.startTimer(dd[a4].t)else dg()end end end;au={name=ak,getType=function(self)return al end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,add=function(self,a7,dh)df=a7;table.insert(dd,{f=a7,t=dh or de})return self end,wait=function(self,dh)de=dh;return self end,rep=function(self,di)for L=1,di do table.insert(dd,{f=df,t=de})end;return self end,clear=function(self)dd={}df=nil;de=0;a4=1;return self end,play=function(self)a4=1;if dd[a4]~=nil then if dd[a4].t>0 then d4=os.startTimer(dd[a4].t)else dg()end end;return self end,cancel=function(self)os.cancelTimer(d4)return self end,eventHandler=function(self,a5,d7)if a5=="timer"and d7==d4 then if dd[a4]~=nil then dg()end end end}au.__index=au;return au end;local function dj(ak)local aT=aj(ak)local al="Slider"aT.w=8;aT.bgcolor=colors.lightGray;aT.fgcolor=colors.gray;aT:setValue(1)local dk="horizontal"local Y=" "local dl=colors.black;local dm="\140"local dn=aT.w;local a4=1;local au={getType=function(self)return al end,setSymbol=function(self,dp)Y=dp:sub(1,1)self:setVisualChanged()return self end,setBackgroundSymbol=function(self,dq)dm=string.sub(dq,1,1)self:setVisualChanged()return self end,setSymbolColor=function(self,dr)dl=dr;self:setVisualChanged()return self end,setBarType=function(self,ds)dk=ds:lower()return self end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if dk=="horizontal"then for dt=0,self.w-1 do if aX+dt==L and aY<=M and aY+self.h>M then a4=dt+1;self:setValue(dn/self.w*a4)self:setVisualChanged()end end end;if dk=="vertical"then for dt=0,self.h-1 do if aY+dt==M and aX<=L and aX+self.w>L then a4=dt+1;self:setValue(dn/self.h*a4)self:setVisualChanged()end end end end end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()if dk=="horizontal"then self.parent:writeText(aX,aY,dm:rep(a4-1),self.bgcolor,self.fgcolor)self.parent:writeText(aX+a4-1,aY,Y,dl,dl)self.parent:writeText(aX+a4,aY,dm:rep(self.w-a4),self.bgcolor,self.fgcolor)end;if dk=="vertical"then for l=0,self.h-1 do if l+1==a4 then self.parent:writeText(aX,aY+l,Y,dl,dl)else self.parent:writeText(aX,aY+l,dm,self.bgcolor,self.fgcolor)end end end end end end}return setmetatable(au,aT)end;local function du(ak)local aT=aj(ak)local al="Scrollbar"aT.w=1;aT.h=8;aT.bgcolor=colors.lightGray;aT.fgcolor=colors.gray;aT:setValue(1)aT:setZIndex(2)local dk="vertical"local Y=" "local dl=colors.black;local dm="\127"local dn=aT.h;local a4=1;local dv=1;local au={getType=function(self)return al end,setSymbol=function(self,dp)Y=dp:sub(1,1)self:setVisualChanged()return self end,setSymbolSize=function(self,dw)dv=tonumber(dw)or 1;if dk=="vertical"then self:setValue(a4-1*dn/(self.h-(dv-1))-dn/(self.h-(dv-1)))elseif dk=="horizontal"then self:setValue(a4-1*dn/(self.w-(dv-1))-dn/(self.w-(dv-1)))end;self:setVisualChanged()return self end,setMaxValue=function(self,ck)dn=ck;return self end,setBackgroundSymbol=function(self,dq)dm=string.sub(dq,1,1)self:setVisualChanged()return self end,setSymbolColor=function(self,dr)dl=dr;self:setVisualChanged()return self end,setBarType=function(self,ds)dk=ds:lower()return self end,mouseHandler=function(self,a5,aK,L,M)if aT.mouseHandler(self,a5,aK,L,M)then local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if(a5=="mouse_click"or a5=="mouse_drag")and aK==1 then if dk=="horizontal"then for dt=0,self.w do if aX+dt==L and aY<=M and aY+self.h>M then a4=math.min(dt+1,self.w-(dv-1))self:setValue(dn/self.w*a4)self:setVisualChanged()end end end;if dk=="vertical"then for dt=0,self.h do if aY+dt==M and aX<=L and aX+self.w>L then a4=math.min(dt+1,self.h-(dv-1))self:setValue(dn/self.h*a4)self:setVisualChanged()end end end end;if a5=="mouse_scroll"then a4=a4+aK;if a4<1 then a4=1 end;a4=math.min(a4,(dk=="vertical"and self.h or self.w)-(dv-1))self:setValue(dn/(dk=="vertical"and self.h or self.w)*a4)end;return true end end,draw=function(self)if aT.draw(self)then if self.parent~=nil then local aX,aY=self:getAnchorPosition()if dk=="horizontal"then self.parent:writeText(aX,aY,dm:rep(a4-1),self.bgcolor,self.fgcolor)self.parent:writeText(aX+a4-1,aY,Y:rep(dv),dl,dl)self.parent:writeText(aX+a4+dv-1,aY,dm:rep(self.w-(a4+dv-1)),self.bgcolor,self.fgcolor)end;if dk=="vertical"then for l=0,self.h-1 do if a4==l+1 then for dx=0,math.min(dv-1,self.h)do self.parent:writeText(aX,aY+l+dx,Y,dl,dl)end else if l+1a4-1+dv then self.parent:writeText(aX,aY+l,dm,self.bgcolor,self.fgcolor)end end end end end end end}return setmetatable(au,aT)end;local function dy(ak,dz)local aT=aj(ak)local al="Frame"local dA={}local dB={}local au={}local dC;aT:setZIndex(10)local b5=false;local b1=1;local b2=1;local dD=colors.white;local dE,cF=0,0;if dz~=nil then aT.parent=dz;aT.w,aT.h=dz.w,dz.h;aT.bgcolor=a.FrameBG;aT.fgcolor=a.FrameFG else local dF,dG=f.getSize()aT.w,aT.h=dF,dG;aT.bgcolor=a.basaltBG;aT.fgcolor=a.basaltFG end;local function dH(ak)for a8,a9 in pairs(dA)do for dI,bD in pairs(a9)do if bD.name==ak then return a9 end end end end;local function dJ(dK)local an=dK:getZIndex()if dH(dK.name)~=nil then return nil end;if dA[an]==nil then for L=1,#dB+1 do if dB[L]~=nil then if an==dB[L]then break end;if an>dB[L]then table.insert(dB,L,an)break end else table.insert(dB,an)end end;if#dB<=0 then table.insert(dB,an)end;dA[an]={}end;dK.parent=au;table.insert(dA[an],dK)return dK end;local function dL(dK)for dI,bD in pairs(dA)do for a8,a9 in pairs(bD)do if a9==dK then table.remove(dA[dI],a8)return end end end end;au={barActive=false,barBackground=colors.gray,barTextcolor=colors.black,barText="New Frame",barTextAlign="left",isMoveable=false,getType=function(self)return al end,setFocusedObject=function(self,dK)for aJ,a4 in pairs(dB)do for aJ,a9 in pairs(dA[a4])do if a9==dK then if dC~=nil then dC:loseFocusHandler()end;dC=dK;dC:getFocusHandler()end end end;return self end,setOffset=function(self,aG,aH)dE=aG~=nil and math.floor(aG<0 and math.abs(aG)or-aG)or dE;cF=aH~=nil and math.floor(aH<0 and math.abs(aH)or-aH)or cF;return self end,getFrameOffset=function(self)return dE,cF end,removeFocusedObject=function(self)if dC~=nil then dC:loseFocusHandler()end;dC=nil;return self end,getFocusedObject=function(self)return dC end,show=function(self)aT:show()if self.parent==nil then c=self end;return self end,setCursor=function(self,dM,dN,dO,aC)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())b5=dM or false;if dN~=nil then b1=aX+dN-1 end;if dO~=nil then b2=aY+dO-1 end;dD=aC or dD;self:setVisualChanged()return self end,setMoveable=function(self,dP)self.isMoveable=dP or not self.isMoveable;self:setVisualChanged()return self end,showBar=function(self,dQ)self.barActive=dQ or not self.barActive;self:setVisualChanged()return self end,setBar=function(self,i,W,X)self.barText=i or""self.barBackground=W or self.barBackground;self.barTextcolor=X or self.barTextcolor;self:setVisualChanged()return self end,setBarTextAlign=function(self,cE)self.barTextAlign=cE or"left"self:setVisualChanged()return self end,getVisualChanged=function(self)local dR=aT.getVisualChanged(self)for aJ,a4 in pairs(dB)do if dA[a4]~=nil then for aJ,a9 in pairs(dA[a4])do if a9.getVisualChanged~=nil and a9:getVisualChanged()then dR=true end end end end;return dR end,loseFocusHandler=function(self)aT.loseFocusHandler(self)end,getFocusHandler=function(self)aT.getFocusHandler(self)if self.parent~=nil then self.parent:removeObject(self)self.parent:addObject(self)end end,keyHandler=function(self,a5,aN)if dC~=nil then if dC.keyHandler~=nil then if dC:keyHandler(a5,aN)then return true end end end;return false end,backgroundKeyHandler=function(self,a5,aN)aT.backgroundKeyHandler(self,a5,aN)for aJ,a4 in pairs(dB)do if dA[a4]~=nil then for aJ,a9 in pairs(dA[a4])do if a9.backgroundKeyHandler~=nil then a9:backgroundKeyHandler(a5,aN)end end end end end,eventHandler=function(self,a5,aO,aP,aQ,aR)aT.eventHandler(self,a5,aO,aP,aQ,aR)for aJ,a4 in pairs(dB)do if dA[a4]~=nil then for aJ,a9 in pairs(dA[a4])do if a9.eventHandler~=nil then a9:eventHandler(a5,aO,aP,aQ,aR)end end end end;if a5=="terminate"then f.clear()f.setCursorPos(1,1)b.stop()end end,mouseHandler=function(self,a5,aK,L,M)local aG,aH=self:getOffset()aG=aG<0 and math.abs(aG)or-aG;aH=aH<0 and math.abs(aH)or-aH;if self.drag then if a5=="mouse_drag"then local dS=1;local dT=1;if self.parent~=nil then dS,dT=self.parent:getAbsolutePosition(self.parent:getAnchorPosition())end;self:setPosition(L+self.xToRem-(dS-1)+aG,M-(dT-1)+aH)end;if a5=="mouse_up"then self.drag=false end;return true end;if aT.mouseHandler(self,a5,aK,L,M)then local aD,aE=self:getAbsolutePosition(self:getAnchorPosition())for aJ,a4 in pairs(dB)do if dA[a4]~=nil then for aJ,a9 in p(dA[a4])do if a9.mouseHandler~=nil then if a9:mouseHandler(a5,aK,L+aG,M+aH)then return true end end end end end;if self.isMoveable then if L>=aD and L<=aD+self.w-1 and M==aE and a5=="mouse_click"then self.drag=true;self.xToRem=aD-L end end;if dC~=nil then dC:loseFocusHandler()dC=nil end;return true end;return false end,setText=function(self,L,M,i)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if M>=1 and M<=self.h then if self.parent~=nil then self.parent:setText(math.max(L+aX-1,aX)-(self.parent.x-1),aY+M-1-(self.parent.y-1),g(i,math.max(1-L+1,1),self.w-L+1))else V.setText(math.max(L+aX-1,aX),aY+M-1,g(i,math.max(1-L+1,1),self.w-L+1))end end end,setBG=function(self,L,M,W)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if M>=1 and M<=self.h then if self.parent~=nil then self.parent:setBG(math.max(L+aX-1,aX)-(self.parent.x-1),aY+M-1-(self.parent.y-1),g(W,math.max(1-L+1,1),self.w-L+1))else V.setBG(math.max(L+aX-1,aX),aY+M-1,g(W,math.max(1-L+1,1),self.w-L+1))end end end,setFG=function(self,L,M,X)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if M>=1 and M<=self.h then if self.parent~=nil then self.parent:setFG(math.max(L+aX-1,aX)-(self.parent.x-1),aY+M-1-(self.parent.y-1),g(X,math.max(1-L+1,1),self.w-L+1))else V.setFG(math.max(L+aX-1,aX),aY+M-1,g(X,math.max(1-L+1,1),self.w-L+1))end end end,writeText=function(self,L,M,i,W,X)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())if M>=1 and M<=self.h then if self.parent~=nil then self.parent:writeText(math.max(L+aX-1,aX)-(self.parent.x-1),aY+M-1-(self.parent.y-1),g(i,math.max(1-L+1,1),self.w-L+1),W,X)else V.writeText(math.max(L+aX-1,aX),aY+M-1,g(i,math.max(1-L+1,1),self.w-L+1),W,X)end end end,drawBackgroundBox=function(self,L,M,j,n,W)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())n=M<1 and(n+M>self.h and self.h or n+M-1)or(n+M>self.h and self.h-M+1 or n)j=L<1 and(j+L>self.w and self.w or j+L-1)or(j+L>self.w and self.w-L+1 or j)if self.parent~=nil then self.parent:drawBackgroundBox(math.max(L+aX-1,aX)-(self.parent.x-1),math.max(M+aY-1,aY)-(self.parent.y-1),j,n,W)else V.drawBackgroundBox(math.max(L+aX-1,aX),math.max(M+aY-1,aY),j,n,W)end end,drawTextBox=function(self,L,M,j,n,Y)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())n=M<1 and(n+M>self.h and self.h or n+M-1)or(n+M>self.h and self.h-M+1 or n)j=L<1 and(j+L>self.w and self.w or j+L-1)or(j+L>self.w and self.w-L+1 or j)if self.parent~=nil then self.parent:drawTextBox(math.max(L+aX-1,aX)-(self.parent.x-1),math.max(M+aY-1,aY)-(self.parent.y-1),j,n,Y:sub(1,1))else V.drawTextBox(math.max(L+aX-1,aX),math.max(M+aY-1,aY),j,n,Y:sub(1,1))end end,drawForegroundBox=function(self,L,M,j,n,X)local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())n=M<1 and(n+M>self.h and self.h or n+M-1)or(n+M>self.h and self.h-M+1 or n)j=L<1 and(j+L>self.w and self.w or j+L-1)or(j+L>self.w and self.w-L+1 or j)if self.parent~=nil then self.parent:drawForegroundBox(math.max(L+aX-1,aX)-(self.parent.x-1),math.max(M+aY-1,aY)-(self.parent.y-1),j,n,X)else V.drawForegroundBox(math.max(L+aX-1,aX),math.max(M+aY-1,aY),j,n,X)end end,draw=function(self)if self:getVisualChanged()then if aT.draw(self)then local aX,aY=self:getAbsolutePosition(self:getAnchorPosition())local cx,cy=self:getAnchorPosition()if self.parent~=nil then self.parent:drawBackgroundBox(cx,cy,self.w,self.h,self.bgcolor)self.parent:drawForegroundBox(cx,cy,self.w,self.h,self.fgcolor)self.parent:drawTextBox(cx,cy,self.w,self.h," ")else V.drawBackgroundBox(aX,aY,self.w,self.h,self.bgcolor)V.drawForegroundBox(aX,aY,self.w,self.h,self.fgcolor)V.drawTextBox(aX,aY,self.w,self.h," ")end;f.setCursorBlink(false)if self.barActive then if self.parent~=nil then self.parent:writeText(cx,cy,h(self.barText,self.w,self.barTextAlign),self.barBackground,self.barTextcolor)else V.writeText(aX,aY,h(self.barText,self.w,self.barTextAlign),self.barBackground,self.barTextcolor)end end;for aJ,a4 in p(dB)do if dA[a4]~=nil then for aJ,a9 in pairs(dA[a4])do if a9.draw~=nil then a9:draw()end end end end;if b5 then f.setTextColor(dD)f.setCursorPos(b1,b2)if self.parent~=nil then f.setCursorBlink(self:isFocused())else f.setCursorBlink(b5)end end;self:setVisualChanged(false)end end end,addObject=function(self,dK)return dJ(dK)end,removeObject=function(self,dK)return dL(dK)end,addButton=function(self,ak)local dK=aS(ak)dK.name=ak;return dJ(dK)end,addLabel=function(self,ak)local dK=c0(ak)dK.name=ak;dK.bgcolor=self.bgcolor;dK.fgcolor=self.fgcolor;return dJ(dK)end,addCheckbox=function(self,ak)local dK=c6(ak)dK.name=ak;return dJ(dK)end,addInput=function(self,ak)local dK=c7(ak)dK.name=ak;return dJ(dK)end,addProgram=function(self,ak)local dK=a_(ak)dK.name=ak;return dJ(dK)end,addTextfield=function(self,ak)local dK=cq(ak)dK.name=ak;return dJ(dK)end,addList=function(self,ak)local dK=cz(ak)dK.name=ak;return dJ(dK)end,addDropdown=function(self,ak)local dK=cQ(ak)dK.name=ak;return dJ(dK)end,addRadio=function(self,ak)local dK=cW(ak)dK.name=ak;return dJ(dK)end,addTimer=function(self,ak)local dK=d0(ak)dK.name=ak;return dJ(dK)end,addAnimation=function(self,ak)local dK=dc(ak)dK.name=ak;return dJ(dK)end,addSlider=function(self,ak)local dK=dj(ak)dK.name=ak;return dJ(dK)end,addScrollbar=function(self,ak)local dK=du(ak)dK.name=ak;return dJ(dK)end,addMenubar=function(self,ak)local dK=cL(ak)dK.name=ak;return dJ(dK)end,addThread=function(self,ak)local dK=d8(ak)dK.name=ak;return dJ(dK)end,addPane=function(self,ak)local dK=c2(ak)dK.name=ak;return dJ(dK)end,addImage=function(self,ak)local dK=c3(ak)dK.name=ak;return dJ(dK)end,addFrame=function(self,ak)local dK=dy(ak,self)dK.name=ak;return dJ(dK)end}setmetatable(au,aT)if dz==nil then table.insert(d,au)end;return au end;local dU=false;local function dV(a5,aO,aP,aQ,aR)if a5=="mouse_click"then c:mouseHandler(a5,aO,aP,aQ,aR)end;if a5=="mouse_drag"then c:mouseHandler(a5,aO,aP,aQ,aR)end;if a5=="mouse_up"then c:mouseHandler(a5,aO,aP,aQ,aR)end;if a5=="mouse_scroll"then c:mouseHandler(a5,aO,aP,aQ,aR)end;if a5=="key"or a5=="char"then c:keyHandler(a5,aO)c:backgroundKeyHandler(a5,aO)end;for aJ,a9 in pairs(d)do a9:eventHandler(a5,aO,aP,aQ,aR)end;if dU then c:draw()V.update()end end;function b.autoUpdate(da)f.clear()dU=da or true;c:draw()V.update()while dU do local a5,aO,aP,aQ,aR=os.pullEventRaw()dV(a5,aO,aP,aQ,aR)end end;function b.update(a5,aO,aP,aQ,aR)if a5~="nil"then dV(a5,aO,aP,aQ,aR)else c:draw()V.update()end end;function b.stop()dU=false end;function b.getFrame(ak)for a8,a9 in pairs(d)do if a9.name==ak then return a9 end end end;function b.getActiveFrame()return c end;function b.setActiveFrame(av)if av:getType()=="Frame"then c=av;return true end;return false end;function b.createFrame(ak)local av=dy(ak)return av end;function b.removeFrame(ak)for a8,a9 in pairs(d)do if a9.name==ak then d[a8]=nil;return true end end;return false end;if b.debugger then b.debugFrame=b.createFrame("basaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setBar("Debug",colors.black,colors.gray)b.debugList=b.debugFrame:addList("debugList"):setSize(b.debugFrame.w-2,b.debugFrame.h-3):setPosition(2,3):show()b.debugFrame:addButton("back"):setAnchor("right"):setSize(1,1):setText("\22"):onClick(function()b.oldFrame:show()end):setBackground(colors.red):show()b.debugLabel=b.debugFrame:addLabel("debugLabel"):onClick(function()b.oldFrame=c;b.debugFrame:show()end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottom"):show()end;if b.debugger then function b.debug(...)local af={...}if c.name~="basaltDebuggingFrame"then if c~=b.debugLabel.frame then b.debugLabel:setParent(c)end end;local dW=""for a8,a9 in pairs(af)do dW=dW..tostring(a9)..(#af~=a8 and", "or"")end;b.debugLabel:setText("[Debug] "..dW)b.debugList:addItem(dW)if b.debugList:getItemCount()>b.debugList.h then b.debugList:removeItem(1)end;b.debugLabel:show()end end;return b \ No newline at end of file diff --git a/source/basalt.lua b/source/basalt.lua new file mode 100644 index 0000000..9da3038 --- /dev/null +++ b/source/basalt.lua @@ -0,0 +1,3727 @@ +-- current version 1 + +local theme = { + basaltBG = colors.lightGray, + basaltFG = colors.black, + FrameBG = colors.gray, + FrameFG = colors.black, + ButtonBG = colors.gray, + ButtonFG = colors.black, + CheckboxBG = colors.gray, + CheckboxFG = colors.black, + InputBG = colors.gray, + InputFG = colors.black, + textfieldBG = colors.gray, + textfieldFG = colors.black, + listBG = colors.gray, + listFG = colors.black, + dropdownBG = colors.gray, + dropdownFG = colors.black, + radioBG = colors.gray, + radioFG = colors.black, + selectionBG = colors.black, + selectionFG = colors.lightGray, +} + +local basalt = {debugger=true, version=1} +local activeFrame +local frames = {} + +local keyModifier = {} +local parentTerminal = term.current() + +local sub = string.sub + +local function getTextHorizontalAlign(text, w, textAlign) + local text = string.sub(text, 1, w) + local n = w-string.len(text) + if(textAlign=="right")then + text = string.rep(" ", n)..text + elseif(textAlign=="center")then + text = string.rep(" ", math.floor(n/2))..text..string.rep(" ", math.floor(n/2)) + text = text..(string.len(text) < w and " " or "") + else + text = text..string.rep(" ", n) + end + return text +end + +local function getTextVerticalAlign(h,textAlign) +local offset = 0 + if(textAlign=="center")then + offset = math.ceil(h / 2) + if(offset<1)then offset = 1 end + end + if(textAlign=="bottom")then + offset = h + end + return offset +end + +local function rpairs(t) + return function(t, i) + i = i - 1 + if i ~= 0 then + return i, t[i] + end + end, t, #t + 1 +end + +local tHex = { -- copy paste is a very important feature + [ colors.white ] = "0", + [ colors.orange ] = "1", + [ colors.magenta ] = "2", + [ colors.lightBlue ] = "3", + [ colors.yellow ] = "4", + [ colors.lime ] = "5", + [ colors.pink ] = "6", + [ colors.gray ] = "7", + [ colors.lightGray ] = "8", + [ colors.cyan ] = "9", + [ colors.purple ] = "a", + [ colors.blue ] = "b", + [ colors.brown ] = "c", + [ colors.green ] = "d", + [ colors.red ] = "e", + [ colors.black ] = "f", +} + +local function basaltDrawHelper() + local terminal = parentTerminal + local w,h = terminal.getSize() + local cacheT = {} + local cacheBG = {} + local cacheFG = {} + + local _cacheT = {} + local _cacheBG = {} + local _cacheFG = {} + + local emptySpaceLine + local emptyColorLines = {} + + local function createEmptyLines() + emptySpaceLine = (" "):rep(w) + for n=0,15 do + local nColor = 2^n + local sHex = tHex[nColor] + emptyColorLines[nColor] = sHex:rep(w) + end + end + ---- + createEmptyLines() + + local function recreateWindowArray() + local emptyText = emptySpaceLine + local emptyFG = emptyColorLines[colors.white] + local emptyBG = emptyColorLines[colors.black] + for n=1,h do + cacheT[n] = sub(cacheT[n] == nil and emptyText or cacheT[n]..emptyText:sub(1,w-cacheT[n]:len()),1,w) + cacheFG[n] = sub(cacheFG[n] == nil and emptyFG or cacheFG[n]..emptyFG:sub(1,w-cacheFG[n]:len()),1,w) + cacheBG[n] = sub(cacheBG[n] == nil and emptyBG or cacheBG[n]..emptyBG:sub(1,w-cacheBG[n]:len()),1,w) + end + end + recreateWindowArray() + + local function setText(x,y,text) + if(y>=1)and(y<=h)then + if(x+text:len()>0)and(x<=w)then + local oldCache = cacheT[y] + local newCache + local nEnd = x + #text - 1 + + if(x < 1)then + local startN = 1 - x + 1 + local endN = w - x + 1 + text = sub(text, startN, endN) + elseif(nEnd > w)then + local endN = w - x + 1 + text = sub(text, 1, endN) + end + + if(x > 1)then + local endN = x - 1 + newCache = sub( oldCache, 1, endN )..text + else + newCache = text + end + if nEnd < w then + newCache = newCache .. sub(oldCache, nEnd + 1, w) + end + cacheT[y] = newCache + end + end + end + + local function setBG(x,y,colorStr) + if(y>=1)and(y<=h)then + if(x+colorStr:len()>0)and(x<=w)then + local oldCache = cacheBG[y] + local newCache + local nEnd = x + #colorStr - 1 + + if(x < 1)then + colorStr = sub(colorStr, 1 - x + 1, w - x + 1) + elseif(nEnd > w)then + colorStr = sub(colorStr, 1, w - x + 1) + end + + if(x > 1)then + newCache = sub( oldCache, 1, x - 1)..colorStr + else + newCache = colorStr + end + if nEnd < w then + newCache = newCache .. sub(oldCache, nEnd + 1, w) + end + cacheBG[y] = newCache + end + end + end + + local function setFG(x,y,colorStr) + if(y>=1)and(y<=h)then + if(x+colorStr:len()>0)and(x<=w)then + local oldCache = cacheFG[y] + local newCache + local nEnd = x + #colorStr - 1 + + if(x < 1)then + local startN = 1 - x + 1 + local endN = w - x + 1 + colorStr = sub(colorStr, startN, endN) + elseif(nEnd > w)then + local endN = w - x + 1 + colorStr = sub(colorStr, 1, endN) + end + + if(x > 1)then + local endN = x - 1 + newCache = sub( oldCache, 1, endN )..colorStr + else + newCache = colorStr + end + if nEnd < w then + newCache = newCache .. sub( oldCache, nEnd + 1, w ) + end + cacheFG[y] = newCache + end + end + end + + local drawHelper = { + setBG = function(x,y,colorStr) + setBG(x,y,colorStr) + end; + + setText = function(x,y,text) + setText(x,y,text) + end; + + setFG = function(x,y,colorStr) + setFG(x,y,colorStr) + end; + + drawBackgroundBox = function (x, y, w, h, bgCol) + for n = 1, h do + setBG(x, y + (n-1), tHex[bgCol]:rep(w)) + end + end; + drawForegroundBox = function (x, y, w, h, fgCol) + for n = 1, h do + setFG(x, y + (n-1), tHex[fgCol]:rep(w)) + end + end; + drawTextBox = function (x, y, w, h, symbol) + for n = 1, h do + setText(x, y + (n-1), symbol:rep(w)) + end + end; + writeText = function (x, y, text, bgCol, fgCol) + bgCol = bgCol or terminal.getBackgroundColor() + fgCol = fgCol or terminal.getTextColor() + setText(x, y, text) + setBG(x, y, tHex[bgCol]:rep(text:len())) + setFG(x, y, tHex[fgCol]:rep(text:len())) + end; + + update = function () + local xC, yC = terminal.getCursorPos() + local isBlinking = false + if(terminal.getCursorBlink~=nil)then isBlinking = terminal.getCursorBlink() end + terminal.setCursorBlink(false) + for n=1, h do + terminal.setCursorPos(1, n) + terminal.blit(cacheT[n], cacheFG[n], cacheBG[n]) + end + terminal.setCursorBlink(isBlinking) + terminal.setCursorPos(xC, yC) + end; + + setTerm = function (newTerm) + terminal = newTerm; + end; + } + return drawHelper +end +local drawHelper = basaltDrawHelper() + +local function BasaltEvents() + + local events = {} + local index = {} + + local event = { + registerEvent = function(self,_event,func) + if( events[_event] == nil)then events[_event] = {} index[_event] = 1 end + events[_event][index[_event]] = func + index[_event] = index[_event] + 1 + return index[_event] - 1 + end; + + removeEvent = function(self,_event,index) + events[_event][index[_event]] = nil + end; + + sendEvent = function(self,_event,...) + if(events[_event]~=nil)then + for k,v in pairs(events[_event])do + v(...) + end + end + end; + } + event.__index = event + return event +end + +local processes = {} +local process = {} +local processId = 0 + +function process:new(path,window,...) + local args = table.pack( ... ) + local newP = setmetatable({path=path},{__index = self}) + newP.window = window + newP.processId = processId + newP.coroutine = coroutine.create(function() + os.run({basalt=basalt}, path, table.unpack(args)) + end) + processes[processId] = newP + processId = processId + 1 + return newP +end + +function process:resume(event, ...) + term.redirect(self.window) + local ok, result = coroutine.resume( self.coroutine, event, ... ) + self.window = term.current() + if ok then + self.filter = result + else + basalt.debug( result ) + end +end + +function process:isDead() + if(self.coroutine~=nil)then + if(coroutine.status(self.coroutine)=="dead")then + table.remove(processes, self.processId) + return true + end + else + return true + end + return false +end + +function process:getStatus() + if(self.coroutine~=nil)then + return coroutine.status(self.coroutine) + end + return nil +end + +function process:start() + coroutine.resume(self.coroutine) +end + +local function Object(name) -- Base object + local typ = "Object" -- not changeable + --[[ + local horizontalAnchor = "left" + local verticalAnchor = "top" + local ignYOffset = false + local ignXOffset = false ]] + local value = nil + local zIndex = 1 + local hanchor = "left" + local vanchor = "top" + local ignOffset = false + local isVisible = false + + local visualsChanged = true + + local eventSystem = BasaltEvents() + + local object = { + x = 1, + y = 1, + w = 1, + h = 1, + bgcolor = colors.black, + fgcolor = colors.white, + name = name or "Object", + parent = nil, + + show = function(self) + isVisible = true + visualsChanged = true + return self + end; + + hide = function(self) + isVisible = false + visualsChanged = true + return self + end; + + isVisible = function(self) + return isVisible + end; + + getZIndex = function(self) + return zIndex; + end; + + setFocus = function(self) + if(self.parent~=nil)then + self.parent:setFocusedObject(self) + end + return self + end; + + setZIndex = function(self, index) + zIndex = index + if(self.parent~=nil)then + self.parent:removeObject(self) + self.parent:addObject(self) + end + return self + end; + + getType = function(self) + return typ + end; + + getName = function(self) + return self.name + end; + + remove = function(self) + if(self.parent~=nil)then + self.parent:removeObject(self) + end + return self + end; + + setParent = function(self, frame) + if(frame.getType~=nil and frame:getType()=="Frame")then + self:remove() + frame:addObject(self) + if(self.draw)then + self:show() + end + end + return self + end; + + setValue = function(self, _value) + value = _value + visualsChanged = true + self:valueChangedHandler() + return self + end; + + getValue = function(self) + return value + end; + + getVisualChanged = function(self) + return visualsChanged + end; + + setVisualChanged = function(self, change) + visualsChanged = change or true + return self + end; + + + getEventSystem = function(self) + return eventSystem + end; + + + getParent = function(self) + return self.parent + end; + + setPosition = function(self, xPos, yPos, rel) + if(rel)then + self.x, self.y = self.x + xPos, self.y + yPos + else + self.x, self.y = xPos, yPos + end + visualsChanged = true + return self + end; + + getPosition = function(self) + return self.x,self.y + end; + + getVisibility = function(self) + return isVisible + end; + + setVisibility = function(self, _isVisible) + isVisible = _isVisible or not isVisible + visualsChanged = true + return self + end; + + setSize = function(self, w, h) + self.w, self.h = w, h + visualsChanged = true + return self + end; + + getHeight = function(self) + return self.h + end; + + getWidth = function(self) + return self.w + end; + + setBackground = function(self, color) + self.bgcolor = color + visualsChanged = true + return self + end; + + getBackground = function(self) + return self.bgcolor + end; + + setForeground = function(self, color) + self.fgcolor = color + visualsChanged = true + return self + end; + + getForeground = function(self) + return self.fgcolor + end; + + draw = function(self) + if(isVisible)then + return true + end + return false + end; + + + getAbsolutePosition = function(self, x,y) -- relative position to absolute position + if(x==nil)then x = self.x end + if(y==nil)then y = self.y end + + if(self.parent~=nil)then + local fx,fy = self.parent:getAbsolutePosition(self.parent:getAnchorPosition()) + x=fx+x-1 + y=fy+y-1 + end + return x, y + end; + + getAnchorPosition = function(self,x, y, ignOff) + if(x==nil)then x = self.x end + if(y==nil)then y = self.y end + if(hanchor=="right")then + x = self.parent.w-x-self.w+2 + end + if(vanchor=="bottom")then + y = self.parent.h-y-self.h+2 + end + local xO, yO = self:getOffset() + if(ignOffset or ignOff)then + return x, y + end + return x+xO, y+yO + end; + + getOffset = function(self) + if(self.parent~=nil)and(ignOffset==false)then + return self.parent:getFrameOffset() + end + return 0, 0 + end; + + ignoreOffset = function(self, ignore) + ignOffset = ignore or true + return self + end; + + setAnchor = function(self,...) + for _,v in pairs(table.pack(...))do + if(v=="right")or(v=="left")then + hanchor = v + end + if(v=="top")or(v=="bottom")then + vanchor = v + end + end + visualsChanged = true + return self + end; + + getAnchor = function(self) + return hanchor, vanchor + end; + + onChange = function(self, func) + self:registerEvent("value_changed", func) + return self + end; + + onClick = function(self, func) + self:registerEvent("mouse_click", func) + return self + end; + + onEvent = function(self, func) + self:registerEvent("custom_event_handler", func) + return self + end; + + onClickUp = function(self, func) + self:registerEvent("mouse_up", func) + return self + end; + + onKey = function(self, func) + self:registerEvent("key", func) + self:registerEvent("char", func) + return self + end; + + onKeyUp = function(self, func) + self:registerEvent("key_up", func) + return self + end; + + onBackgroundKey = function(self, func) + self:registerEvent("background_key", func) + self:registerEvent("background_char", func) + return self + end; + + onBackgroundKeyUp = function(self, func) + self:registerEvent("background_key_up", func) + return self + end; + + isFocused = function(self) + if(self.parent~=nil)then + return self.parent:getFocusedObject()==self + end + return false + end; + + onGetFocus = function(self, func) + self:registerEvent("get_focus", func) + return self + end; + + onLoseFocus = function(self, func) + self:registerEvent("lose_focus", func) + return self + end; + + registerEvent = function(self,event, func) + return eventSystem:registerEvent(event, func) + end; + + removeEvent = function(self,event, index) + return eventSystem:removeEvent(event, index) + end; + + mouseHandler = function(self, event, button, x, y) + local objX,objY = self:getAbsolutePosition(self:getAnchorPosition()) + if(objX<=x)and(objX+self.w>x)and(objY<=y)and(objY+self.h>y)and(isVisible)then + if(self.parent~=nil)then self.parent:setFocusedObject(self) end + eventSystem:sendEvent(event, self, event, button, x, y) + return true + end + return false + end; + + keyHandler = function(self, event, key) + if(self:isFocused())then + eventSystem:sendEvent(event, self, event, key) + return true + end + return false + end; + + backgroundKeyHandler = function(self, event, key) + eventSystem:sendEvent("background_"..event, self, event, key) + end; + + valueChangedHandler = function(self) + eventSystem:sendEvent("value_changed", self) + end; + + eventHandler = function(self, event, p1, p2, p3, p4) + eventSystem:sendEvent("custom_event_handler", self, event, p1, p2, p3, p4) + end; + + getFocusHandler = function(self) + eventSystem:sendEvent("get_focus", self) + end; + + loseFocusHandler = function(self) + eventSystem:sendEvent("lose_focus", self) + end; + + + } + + object.__index = object + return object +end + +local function Button(name) -- Button + local base = Object(name) + local typ = "Button" + + base:setValue("Button") + base:setZIndex(5) + base.w = 8 + base.bgcolor = theme.ButtonBG + base.fgcolor = theme.ButtonFG + + local textHorizontalAlign = "center" + local textVerticalAlign = "center" + + local object = { + getType = function(self) + return typ + end; + setHorizontalAlign = function (self, pos) + textHorizontalAlign = pos + end; + + setVerticalAlign = function (self, pos) + textVerticalAlign = pos + end; + + setText = function(self,text) + base:setValue(text) + return self + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition(self.x,self.y) + local verticalAlign = getTextVerticalAlign(self.h, textVerticalAlign) + + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + self.parent:drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) + self.parent:drawTextBox(obx, oby, self.w, self.h, " ") + for n=1,self.h do + if(n==verticalAlign)then + self.parent:setText(obx, oby+(n-1), getTextHorizontalAlign(self:getValue(), self.w, textHorizontalAlign)) + end + end + end + end + end; + + } + return setmetatable(object, base) +end + +local function Program(name) + local base = Object(name) + local typ = "Program" + base:setZIndex(5) + local object + + local function basaltWindow(x, y, w, h) + local xCursor, yCursor = 1, 1 + local bgcolor, fgcolor = colors.black, colors.white + local cursorBlink = false + local visible = false + + local cacheT = {} + local cacheBG = {} + local cacheFG = {} + + local tPalette = {} + + local emptySpaceLine + local emptyColorLines = {} + + for i=0,15 do + local c = 2 ^ i + tPalette[c] = { parentTerminal.getPaletteColour( c ) } + end + + local function createEmptyLines() + emptySpaceLine = (" "):rep(w) + for n=0,15 do + local nColor = 2^n + local sHex = tHex[nColor] + emptyColorLines[nColor] = sHex:rep(w) + end + end + + local function recreateWindowArray() + createEmptyLines() + local emptyText = emptySpaceLine + local emptyFG = emptyColorLines[colors.white] + local emptyBG = emptyColorLines[colors.black] + for n=1,h do + cacheT[n] = sub(cacheT[n] == nil and emptyText or cacheT[n]..emptyText:sub(1,w-cacheT[n]:len()),1,w) + cacheFG[n] = sub(cacheFG[n] == nil and emptyFG or cacheFG[n]..emptyFG:sub(1,w-cacheFG[n]:len()),1,w) + cacheBG[n] = sub(cacheBG[n] == nil and emptyBG or cacheBG[n]..emptyBG:sub(1,w-cacheBG[n]:len()),1,w) + end + end + recreateWindowArray() + + local function updateCursor() + if xCursor >= 1 and yCursor >= 1 and xCursor <= w and yCursor <= h then + --parentTerminal.setCursorPos(xCursor + x - 1, yCursor + y - 1) + else + --parentTerminal.setCursorPos(0, 0) + end + --parentTerminal.setTextColor(fgcolor) + end + + local function internalBlit( sText, sTextColor, sBackgroundColor ) -- copy pasti strikes again (cc: window.lua) + local nStart = xCursor + local nEnd = nStart + #sText - 1 + if yCursor >= 1 and yCursor <= h then + if nStart <= w and nEnd >= 1 then + -- Modify line + if nStart == 1 and nEnd == w then + cacheT[yCursor] = sText + cacheFG[yCursor] = sTextColor + cacheBG[yCursor] = sBackgroundColor + else + local sClippedText, sClippedTextColor, sClippedBackgroundColor + if nStart < 1 then + local nClipStart = 1 - nStart + 1 + local nClipEnd = w - nStart + 1 + sClippedText = sub( sText, nClipStart, nClipEnd ) + sClippedTextColor = sub( sTextColor, nClipStart, nClipEnd ) + sClippedBackgroundColor = sub( sBackgroundColor, nClipStart, nClipEnd ) + elseif nEnd > w then + local nClipEnd = w - nStart + 1 + sClippedText = sub( sText, 1, nClipEnd ) + sClippedTextColor = sub( sTextColor, 1, nClipEnd ) + sClippedBackgroundColor = sub( sBackgroundColor, 1, nClipEnd ) + else + sClippedText = sText + sClippedTextColor = sTextColor + sClippedBackgroundColor = sBackgroundColor + end + + local sOldText = cacheT[yCursor] + local sOldTextColor = cacheFG[yCursor] + local sOldBackgroundColor = cacheBG[yCursor] + local sNewText, sNewTextColor, sNewBackgroundColor + if nStart > 1 then + local nOldEnd = nStart - 1 + sNewText = sub( sOldText, 1, nOldEnd ) .. sClippedText + sNewTextColor = sub( sOldTextColor, 1, nOldEnd ) .. sClippedTextColor + sNewBackgroundColor = sub( sOldBackgroundColor, 1, nOldEnd ) .. sClippedBackgroundColor + else + sNewText = sClippedText + sNewTextColor = sClippedTextColor + sNewBackgroundColor = sClippedBackgroundColor + end + if nEnd < w then + local nOldStart = nEnd + 1 + sNewText = sNewText .. sub( sOldText, nOldStart, w ) + sNewTextColor = sNewTextColor .. sub( sOldTextColor, nOldStart, w ) + sNewBackgroundColor = sNewBackgroundColor .. sub( sOldBackgroundColor, nOldStart, w ) + end + + cacheT[yCursor] = sNewText + cacheFG[yCursor] = sNewTextColor + cacheBG[yCursor] = sNewBackgroundColor + end + end + xCursor = nEnd + 1 + if(visible)then + updateCursor() + end + end + end + + local function setText(_x,_y,text) + if(text~=nil)then + local gText = cacheT[_y] + if(gText~=nil)then + cacheT[_y] = sub(gText:sub(1,_x-1)..text..gText:sub(_x+(text:len()),w),1,w) + end + end + end + + local function setBG(_x,_y,colorStr) + if(colorStr~=nil)then + local gBG = cacheBG[_y] + if(gBG~=nil)then + cacheBG[_y] = sub(gBG:sub(1,_x-1)..colorStr..gBG:sub(_x+(colorStr:len()),w),1,w) + end + end + end + + local function setFG(_x,_y,colorStr) + if(colorStr~=nil)then + local gFG = cacheFG[_y] + if(gFG~=nil)then + cacheFG[_y] = sub(gFG:sub(1,_x-1)..colorStr..gFG:sub(_x+(colorStr:len()),w),1,w) + end + end + end + + local setTextColor = function(color) + if type( color ) ~= "number" then + error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 ) + elseif tHex[color] == nil then + error( "Invalid color (got " .. color .. ")" , 2 ) + end + fgcolor = color + end + + local setBackgroundColor = function(color) + if type( color ) ~= "number" then + error( "bad argument #1 (expected number, got " .. type( color ) .. ")", 2 ) + elseif tHex[color] == nil then + error( "Invalid color (got " .. color .. ")" , 2 ) + end + bgcolor = color + end + + local setPaletteColor = function( colour, r, g, b ) -- have to work on + if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end + + if tHex[colour] == nil then + error( "Invalid color (got " .. colour .. ")" , 2 ) + end + + local tCol + if type(r) == "number" and g == nil and b == nil then + tCol = { colours.rgb8( r ) } + tPalette[ colour ] = tCol + else + if type( r ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( r ) .. ")", 2 ) end + if type( g ) ~= "number" then error( "bad argument #3 (expected number, got " .. type( g ) .. ")", 2 ) end + if type( b ) ~= "number" then error( "bad argument #4 (expected number, got " .. type( b ) .. ")", 2 ) end + + tCol = tPalette[ colour ] + tCol[1] = r + tCol[2] = g + tCol[3] = b + end + end + + local getPaletteColor = function( colour ) + if type( colour ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( colour ) .. ")", 2 ) end + if tHex[colour] == nil then + error( "Invalid color (got " .. colour .. ")" , 2 ) + end + local tCol = tPalette[ colour ] + return tCol[1], tCol[2], tCol[3] + end + + local basaltwindow = { + setCursorPos = function(_x, _y) + if type( _x ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( _x ) .. ")", 2 ) end + if type( _y ) ~= "number" then error( "bad argument #2 (expected number, got " .. type( _y ) .. ")", 2 ) end + xCursor = math.floor(_x) + yCursor = math.floor(_y) + if(visible)then + updateCursor() + end + end; + + getCursorPos = function() + return xCursor, yCursor + end; + + setCursorBlink = function(blink) + if type( blink ) ~= "boolean" then error( "bad argument #1 (expected boolean, got " .. type( blink ) .. ")", 2 ) end + cursorBlink = blink + end; + + getCursorBlink = function() + return cursorBlink + end; + + + getPaletteColor = getPaletteColor, + getPaletteColour = getPaletteColor, + + setBackgroundColor = setBackgroundColor, + setBackgroundColour = setBackgroundColor, + + setTextColor = setTextColor, + setTextColour = setTextColor, + + setPaletteColor = setPaletteColor, + setPaletteColour = setPaletteColor, + + getBackgroundColor = function() + return bgcolor + end; + getBackgroundColour = function() + return bgcolor + end; + + getSize = function() + return w, h + end; + + getTextColor = function() + return fgcolor + end; + getTextColour = function() + return fgcolor + end; + + basalt_resize = function(_w, _h) + w, h = _w, _h + recreateWindowArray() + end; + + basalt_reposition = function(_x, _y) + x, y = _x, _y + end; + + basalt_setVisible = function(vis) + visible = vis + end; + + drawBackgroundBox = function (_x, _y, _w, _h, bgCol) + for n = 1, _h do + setBG(_x, _y + (n-1), tHex[bgCol]:rep(_w)) + end + end; + drawForegroundBox = function (_x, _y, _w, _h, fgCol) + for n = 1, _h do + setFG(_x, _y + (n-1), tHex[fgCol]:rep(_w)) + end + end; + drawTextBox = function (_x, _y, _w, _h, symbol) + for n = 1, _h do + setText(_x, _y + (n-1), symbol:rep(_w)) + end + end; + + writeText = function (_x, _y, text, bgCol, fgCol) + bgCol = bgCol or bgcolor + fgCol = fgCol or fgcolor + setText(x, _y, text) + setBG(_x, _y, tHex[bgCol]:rep(text:len())) + setFG(_x, _y, tHex[fgCol]:rep(text:len())) + end; + + basalt_update = function() + if(object.parent~=nil)then + for n=1, h do + object.parent:setText(x, y + (n-1), cacheT[n]) + object.parent:setBG(x, y + (n-1), cacheBG[n]) + object.parent:setFG(x, y + (n-1), cacheFG[n]) + end + end + end; + + scroll = function (offset) + if type( offset ) ~= "number" then error( "bad argument #1 (expected number, got " .. type( offset ) .. ")", 2 ) end + if offset ~= 0 then + local sEmptyText = emptySpaceLine + local sEmptyTextColor = emptyColorLines[ fgcolor ] + local sEmptyBackgroundColor = emptyColorLines[ bgcolor ] + for newY=1,h do + local y = newY + offset + if y >= 1 and y <= h then + cacheT[newY] = cacheT[y] + cacheBG[newY] = cacheBG[y] + cacheFG[newY] = cacheFG[y] + else + cacheT[newY] = sEmptyText + cacheFG[newY] = sEmptyTextColor + cacheBG[newY] = sEmptyBackgroundColor + end + end + end + if(visible)then + updateCursor() + end + end; + + + isColor = function() + return parentTerminal.isColor() + end; + + isColour = function() + return parentTerminal.isColor() + end; + + write = function(text) + text = tostring(text) + if(visible)then + internalBlit( text, tHex[fgcolor]:rep(text:len()), tHex[bgcolor]:rep(text:len())) + end + end; + + clearLine = function() + if(visible)then + setText(1, yCursor, (" "):rep(w)) + setBG(1, yCursor, tHex[bgcolor]:rep(w)) + setFG(1, yCursor, tHex[fgcolor]:rep(w)) + end + if(visible)then + updateCursor() + end + end; + + clear = function() + for n=1,h do + setText(1, n, (" "):rep(w)) + setBG(1, n, tHex[bgcolor]:rep(w)) + setFG(1, n, tHex[fgcolor]:rep(w)) + end + if(visible)then + updateCursor() + end + end; + + blit = function(text, fgcol, bgcol) + if type( text ) ~= "string" then error( "bad argument #1 (expected string, got " .. type( text ) .. ")", 2 ) end + if type( fgcol ) ~= "string" then error( "bad argument #2 (expected string, got " .. type( fgcol ) .. ")", 2 ) end + if type( bgcol ) ~= "string" then error( "bad argument #3 (expected string, got " .. type( bgcol ) .. ")", 2 ) end + if #fgcol ~= #text or #bgcol ~= #text then + error( "Arguments must be the same length", 2 ) + end + if(visible)then + --setText(xCursor, yCursor, text) + --setBG(xCursor, yCursor, bgcol) + --setFG(xCursor, yCursor, fgcol) + --xCursor = xCursor+text:len() + internalBlit(text, fgcol, bgcol) + end + end + + + } + + return basaltwindow + end + + base.w = 30 + base.h = 12 + local pWindow = basaltWindow(1, 1, base.w, base.h) + local curProcess + local paused = false + local queuedEvent = {} + + object = { + getType = function(self) + return typ + end; + + show = function(self) + base.show(self) + pWindow.setBackgroundColor(self.bgcolor) + pWindow.setTextColor(self.fgcolor) + pWindow.basalt_setVisible(true) + return self + end; + + hide = function(self) + base.hide(self) + pWindow.basalt_setVisible(false) + return self + end; + + setPosition = function(self, x, y, rel) + base.setPosition(self,x,y, rel) + pWindow.basalt_reposition(self:getAnchorPosition()) + return self + end; + + getBasaltWindow = function() + return pWindow + end; + + getBasaltProcess = function() + return curProcess + end; + + setSize = function(self, w, h) + base.setSize(self,w,h) + pWindow.basalt_resize(self.w, self.h) + return self + end; + + getStatus = function(self) + if(curProcess~=nil)then + return curProcess:getStatus() + end + return "inactive" + end; + + execute = function(self, path, ...) + curProcess = process:new(path, pWindow, ...) + pWindow.setBackgroundColor(colors.black) + pWindow.setTextColor(colors.white) + pWindow.clear() + pWindow.setCursorPos(1, 1) + curProcess:resume() + paused = false + return self + end; + + stop = function(self) + if(curProcess~=nil)then + if not(curProcess:isDead())then + curProcess:resume("terminate") + if(curProcess:isDead())then + if(self.parent~=nil)then + self.parent:setCursor(false) + end + end + end + end + return self + end; + + pause = function(self, p) + paused = p or (not paused) + if(curProcess~=nil)then + if not(curProcess:isDead())then + if not(paused)then + self:injectEvents(queuedEvent) + queuedEvent = {} + end + end + end + return self + end; + + isPaused = function(self) + return paused + end; + + injectEvent = function(self, event, p1, p2, p3, p4, ign) + if(curProcess~=nil)then + if not(curProcess:isDead())then + if (paused==false)or(ign)then + curProcess:resume(event,p1,p2,p3,p4) + else + table.insert(queuedEvent, {event=event, args = {p1,p2,p3,p4}}) + end + end + end + return self + end; + + getQueuedEvents = function(self) + return queuedEvent + end; + + updateQueuedEvents = function(self, events) + queuedEvent = events or queuedEvent + return self + end; + + injectEvents = function(self, events) + if(curProcess~=nil)then + if not(curProcess:isDead())then + for _,v in pairs(events)do + curProcess:resume(v.event, table.unpack(v.args)) + end + end + end + return self + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self,event,button,x,y))then + if(curProcess==nil)then return false end + if not(curProcess:isDead())then + if not(paused)then + local absX,absY = self:getAbsolutePosition(self:getAnchorPosition()) + curProcess:resume(event, button, x-absX+1, y-absY+1) + end + end + return true + end + end; + + keyHandler = function(self, event, key) + base.keyHandler(self, event, key) + if(self:isFocused())then + if(curProcess==nil)then return false end + if not(curProcess:isDead())then + if not(paused)then + if(self.draw)then + curProcess:resume(event, key) + end + end + end + end + end; + + getFocusHandler = function(self) + base.getFocusHandler(self) + if(curProcess~=nil)then + if not(curProcess:isDead())then + if not(paused)then + if(self.parent~=nil)then + local xCur, yCur = pWindow.getCursorPos() + local obx, oby = self:getAnchorPosition() + if(self.parent~=nil)then + if(obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + self.w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + self.h - 1)then + self.parent:setCursor(pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor()) + end + end + end + end + end + end + end; + + loseFocusHandler = function(self) + base.loseFocusHandler(self) + if(curProcess~=nil)then + if not(curProcess:isDead())then + if(self.parent~=nil)then + self.parent:setCursor(false) + end + end + end + end; + + eventHandler = function(self, event, p1, p2, p3, p4) + if(curProcess==nil)then return end + if not(curProcess:isDead())then + if not(paused)then + if(event~="mouse_click")and(event~="mouse_up")and(event~="mouse_scroll")and(event~="mouse_drag")and(event~="key_up")and(event~="key")and(event~="char")and(event~="terminate")then + curProcess:resume(event,p1,p2,p3,p4) + end + if(self:isFocused())then + local obx, oby = self:getAnchorPosition() + local xCur, yCur = pWindow.getCursorPos() + if(self.parent~=nil)then + if(obx + xCur - 1 >= 1 and obx + xCur - 1 <= obx + self.w - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + self.h - 1)then + self.parent:setCursor(pWindow.getCursorBlink(), obx + xCur - 1, yCur + oby - 1, pWindow.getTextColor()) + end + end + + if(event=="terminate")and(self:isFocused())then + self:stop() + end + end + else + if(event~="mouse_click")and(event~="mouse_up")and(event~="mouse_scroll")and(event~="mouse_drag")and(event~="key_up")and(event~="key")and(event~="char")and(event~="terminate")then + table.insert(queuedEvent, {event=event, args = {p1,p2,p3,p4}}) + end + end + end + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + pWindow.basalt_reposition(obx,oby) + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + pWindow.basalt_update() + end + end + end; + + } + + return setmetatable(object, base) +end + +local function Label(name) -- Label + local base = Object(name) + local typ = "Label" + + base:setZIndex(3) + + local autoWidth = true + base:setValue("") + + local object = { + getType = function(self) + return typ + end; + setText = function(self,text) + text = tostring(text) + base:setValue(text) + if(autoWidth)then + self.w = text:len() + end + return self + end; + + setSize = function(self,w,h) + self.w, self.h = w, h + autoWidth = false + return self + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + self.parent:writeText(obx, oby, self:getValue(), self.bgcolor, self.fgcolor) + end + end + end; + + } + + return setmetatable(object, base) +end + +local function Pane(name) -- Pane + local base = Object(name) + local typ = "Pane" + + + local object = { + getType = function(self) + return typ + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + self.parent:drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) + end + end + end; + + } + + return setmetatable(object, base) +end + +local function Image(name) -- Pane + local base = Object(name) + local typ = "Image" + base:setZIndex(2) + local image + + local object = { + getType = function(self) + return typ + end; + + loadImage = function(self, path) + image = paintutils.loadImage(path) + return self + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + if(image~=nil)then + local obx, oby = self:getAnchorPosition() + + for yPos=1,math.min(#image, self.h) do + local line = image[yPos] + for xPos=1,math.min(#line, self.w) do + if line[xPos] > 0 then + self.parent:drawBackgroundBox(obx + xPos - 1, oby + yPos - 1, 1, 1, line[xPos]) + end + end + end + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function Checkbox(name) -- Checkbox + local base = Object(name) + local typ = "Checkbox" + + base:setZIndex(5) + base:setValue(false) + base.w = 1 + base.h = 1 + base.bgcolor = theme.CheckboxBG + base.fgcolor = theme.CheckboxFG + + local object = { + symbol = "\42", + + getType = function(self) + return typ + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self, event, button, x, y))then + if(event == "mouse_click")and(button == 1)then + if(self:getValue()~=true)and(self:getValue()~=false)then + self:setValue(false) + else + self:setValue(not self:getValue()) + end + end + return true + end + return false + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + local verticalAlign = getTextVerticalAlign(self.h, "center") + + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + for n=1,self.h do + if(n==verticalAlign)then + if(self:getValue()==true)then + self.parent:writeText(obx, oby+(n-1), getTextHorizontalAlign(self.symbol, self.w, "center"), self.bgcolor, self.fgcolor) + else + self.parent:writeText(obx, oby+(n-1), getTextHorizontalAlign(" ", self.w, "center"), self.bgcolor, self.fgcolor) + end + end + end + end + end + end; + + } + + return setmetatable(object, base) +end + +local function Input(name) -- Input + local base = Object(name) + local typ = "Input" + + local inputType = "text" + local inputLimit = 0 + base:setZIndex(5) + base:setValue("") + base.w = 10 + base.h = 1 + base.bgcolor = theme.InputBG + base.fgcolor = theme.InputFG + + local textX = 1 + local wIndex = 1 + + local defaultText = "" + local defaultBGCol + local defaultFGCol + local showingText = defaultText + local internalValueChange = false + + local object = { + + getType = function(self) + return typ + end; + + setInputType = function(self, iType) + if(iType=="password")or(iType=="number")or(iType=="text")then + inputType = iType + end + return self + end; + + setDefaultText = function(self, text, fCol, bCol) + defaultText = text + defaultBGCol = bCol or defaultBGCol + defaultFGCol = fCol or defaultFGCol + if(self:isFocused())then + showingText = "" + else + showingText = defaultText + end + return self + end; + + getInputType = function(self) + return inputType + end; + + setValue = function(self, val) + base.setValue(self, tostring(val)) + if not(internalValueChange)then + textX = tostring(val):len()+1 + end + return self + end; + + getValue = function(self) + local val = base.getValue(self) + return inputType == "number" and tonumber(val)or val + end; + + setInputLimit = function(self, limit) + inputLimit = tonumber(limit) or inputLimit + return self + end; + + getInputLimit = function(self) + return inputLimit + end; + + getFocusHandler = function(self) + base.getFocusHandler(self) + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + showingText = "" + if(self.parent~=nil)then + self.parent:setCursor(true, obx+textX-wIndex, oby, self.fgcolor) + end + end + end; + + loseFocusHandler = function(self) + base.loseFocusHandler(self) + if(self.parent~=nil)then + self.parent:setCursor(false) + showingText = defaultText + end + end; + + keyHandler = function(self, event, key) + if(base.keyHandler(self, event, key))then + internalValueChange = true + if(event=="key")then + if(key==259)then -- on backspace + local text = tostring(base.getValue()) + if(textX>1)then + self:setValue(text:sub(1,textX-2)..text:sub(textX,text:len())) + if(textX>1)then textX = textX-1 end + if(wIndex>1)then + if(textX tLength)then + textX = tLength+1 + end + if(textX<1)then textX = 1 end + if(textX=self.w+wIndex)then + wIndex = textX-self.w+1 + end + if(wIndex<1)then wIndex = 1 end + end + + if(key==263)then -- left arrow + textX = textX-1 + if(textX>=1)then + if(textX=self.w+wIndex)then + wIndex = textX + end + end + if(textX<1)then textX = 1 end + if(wIndex<1)then wIndex = 1 end + end + end + + if(event=="char")then + local text = base.getValue() + if(text:len() < inputLimit or inputLimit <= 0)then + if(inputType=="number")then + local cache = text + if (key==".")or(tonumber(key)~=nil)then + self:setValue(text:sub(1,textX-1)..key..text:sub(textX,text:len())) + textX = textX + 1 + end + if(tonumber(base.getValue())==nil)then + self:setValue(cache) + end + else + self:setValue(text:sub(1,textX-1)..key..text:sub(textX,text:len())) + textX = textX + 1 + end + if(textX>=self.w+wIndex)then wIndex = wIndex+1 end + end + end + local obx,oby = self:getAnchorPosition() + local val = tostring(base.getValue()) + local cursorX = (textX <= val:len() and textX-1 or val:len())-(wIndex-1) + + if(cursorX>self.x+self.w-1)then cursorX = self.x+self.w-1 end + if(self.parent~=nil)then + self.parent:setCursor(true, obx+cursorX, oby, self.fgcolor) + end + internalValueChange = false + end + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self, event, button, x, y))then + if(event == "mouse_click")and(button == 1)then + + end + return true + end + return false + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + local verticalAlign = getTextVerticalAlign(self.h, "center") + + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + for n=1,self.h do + if(n==verticalAlign)then + local val = tostring(base.getValue()) + local bCol = self.bgcolor + local fCol = self.fgcolor + local text + if(val:len()<=0)then text = showingText bCol = defaultBGCol or bCol fCol = defaultFGCol or fCol end + + text = showingText + if(val~="")then + text = val + end + text = text:sub(wIndex, self.w+wIndex-1) + local space = self.w-text:len() + if(space<0)then space = 0 end + if(inputType=="password")and(val~="")then + text = string.rep("*", text:len()) + end + text = text..string.rep(" ", space) + self.parent:writeText(obx, oby+(n-1), text, bCol, fCol) + end + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function Textfield(name) + local base = Object(name) + local typ = "Textfield" + local hIndex, wIndex, textX, textY = 1, 1, 1, 1 + + local lines = {""} + local selection = {} + local selectionColor = colors.lightBlue + + base.w = 20 + base.h = 8 + base.bgcolor = theme.textfieldBG + base.fgcolor = theme.textfieldFG + base:setZIndex(5) + + local object = { + getType = function(self) + return typ + end; + + getLines = function (self) + return lines + end; + + getLine = function (self, index) + return lines[index] or "" + end; + + editLine = function (self, index, text) + lines[index] = text or lines[index] + return self + end; + + addLine = function (self, text, index) + if(index~=nil)then + table.insert(lines, index, text) + else + table.insert(lines, text) + end + return self + end; + + removeLine = function(self, index) + table.remove(lines, index or #lines) + if(#lines <= 0)then + table.insert(lines, "") + end + return self + end; + + getTextCursor = function (self) + return textX, textY + end; + + getFocusHandler = function(self) + base.getFocusHandler(self) + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + if(self.parent~=nil)then + self.parent:setCursor(true, obx+textX-wIndex, oby+textY-hIndex, self.fgcolor) + end + end + end; + + loseFocusHandler = function(self) + base.loseFocusHandler(self) + if(self.parent~=nil)then + self.parent:setCursor(false) + end + end; + + keyHandler = function(self, event, key) + if(base.keyHandler(self, event, key))then + local obx,oby = self:getAnchorPosition() + if(event=="key")then + if(key==259)then -- on backspace + if(lines[textY]=="")then + if(textY>1)then + table.remove(lines,textY) + textX = lines[textY-1]:len()+1 + wIndex = textX-self.w+1 + if(wIndex<1)then wIndex = 1 end + textY = textY-1 + end + elseif(textX<=1)then + if(textY>1)then + textX = lines[textY-1]:len()+1 + wIndex = textX-self.w+1 + if(wIndex<1)then wIndex = 1 end + lines[textY-1] = lines[textY-1]..lines[textY] + table.remove(lines,textY) + textY = textY-1 + end + else + lines[textY] = lines[textY]:sub(1,textX-2)..lines[textY]:sub(textX,lines[textY]:len()) + if(textX>1)then textX = textX-1 end + if(wIndex>1)then + if(textX=self.h)then + hIndex = hIndex+1 + end + self:setValue("") + end + + if(key==265)then -- arrow up + if(textY>1)then + textY = textY-1 + if(textX>lines[textY]:len()+1)then textX = lines[textY]:len()+1 end + if(wIndex>1)then + if(textX1)then + if(textYlines[textY]:len()+1)then textX = lines[textY]:len()+1 end + + if(textY>=hIndex+self.h)then + hIndex = hIndex+1 + end + end + end + if(key==262)then -- arrow right + textX = textX+1 + if(textY<#lines)then + if(textX>lines[textY]:len()+1)then + textX = 1 + textY = textY+1 + end + elseif(textX > lines[textY]:len())then + textX = lines[textY]:len()+1 + end + if(textX<1)then textX = 1 end + if(textX=self.w+wIndex)then + wIndex = textX-self.w+1 + end + if(wIndex<1)then wIndex = 1 end + + end + if(key==263)then -- arrow left + textX = textX-1 + if(textX>=1)then + if(textX=self.w+wIndex)then + wIndex = textX + end + end + if(textY>1)then + if(textX<1)then + textY = textY-1 + textX = lines[textY]:len()+1 + wIndex = textX-self.w+1 + end + end + if(textX<1)then textX = 1 end + if(wIndex<1)then wIndex = 1 end + end + end + + if(event=="char")then + lines[textY] = lines[textY]:sub(1,textX-1)..key..lines[textY]:sub(textX,lines[textY]:len()) + textX = textX+1 + if(textX>=self.w+wIndex)then wIndex = wIndex+1 end + self:setValue("") + end + + local cursorX = (textX <= lines[textY]:len() and textX-1 or lines[textY]:len())-(wIndex-1) + if(cursorX>self.x+self.w-1)then cursorX = self.x+self.w-1 end + local cursorY = (textY-hIndex < self.h and textY-hIndex or textY-hIndex-1) + if(cursorX<1)then cursorX = 0 end + self.parent:setCursor(true, obx+cursorX, oby+cursorY, self.fgcolor) + return true + end + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self, event, button, x, y))then + local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) + local anchx,anchy = self:getAnchorPosition() + if(event=="mouse_click")then + if(lines[y-oby+hIndex]~=nil)then + textX = x-obx+wIndex + textY = y-oby+hIndex + if(textX>lines[textY]:len())then + textX = lines[textY]:len()+1 + end + if(textX#lines-(self.h-1))then hIndex = #lines-(self.h-1) end + + if(hIndex<1)then hIndex = 1 end + + if(self.parent~=nil)then + if(obx+textX-wIndex>=obx and obx+textX-wIndex <= obx+self.w)and(oby+textY-hIndex >= oby and oby+textY-hIndex <= oby+self.h)then + self.parent:setCursor(true, anchx+textX-wIndex, anchy+textY-hIndex) + else + self.parent:setCursor(false) + end + end + end + self:setVisualChanged() + return true + end + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + self.parent:drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) + for n=1, self.h do + local text = "" + if(lines[n+hIndex-1]~=nil)then + text = lines[n+hIndex-1] + end + text = text:sub(wIndex, self.w+wIndex-1) + local space = self.w-text:len() + if(space<0)then space = 0 end + text = text..string.rep(" ", space) + self.parent:setText(obx, oby+n-1, text) + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function List(name) + local base = Object(name) + local typ = "List" + base.w = 16 + base.h = 6 + base.bgcolor = theme.listBG + base.fgcolor = theme.listFG + base:setZIndex(5) + + local list = {} + local itemSelectedBG = theme.selectionBG + local itemSelectedFG = theme.selectionFG + local selectionColorActive = true + local align = "left" + local yOffset = 0 + local scrollable = true + + local object = { + getType = function(self) + return typ + end; + + addItem = function(self, text, bgCol, fgCol, ...) + table.insert(list, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + if(#list==1)then + self:setValue(list[1]) + end + return self + end; + + setIndexOffset = function(self, yOff) + yOffset = yOff + return self + end; + + getIndexOffset = function(self) + return yOffset + end; + + removeItem = function(self, index) + table.remove(list, index) + return self + end; + + getItem = function(self, index) + return list[index] + end; + + getItemIndex = function(self) + local selected = self:getValue() + for k,v in pairs(list)do + if(v==selected)then + return k + end + end + end; + + clear = function(self) + list = {} + self:setValue({}) + return self + end; + + getItemCount = function(self) + return #list + end; + + editItem = function(self, index, text, bgCol, fgCol, ...) + table.remove(list, index) + table.insert(list, index, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + return self + end; + + selectItem = function(self, index) + self:setValue(list[index] or {}) + return self + end; + + setSelectedItem = function(self, bgCol, fgCol, active) + itemSelectedBG = bgCol or self.bgcolor + itemSelectedFG = fgCol or self.fgcolor + selectionColorActive = active + return self + end; + + setScrollable = function(self, scroll) + scrollable = scroll + return self + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self, event, button, x, y))then + local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(event=="mouse_click")or(event=="mouse_drag")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw + if(button==1)then + if(#list>0)then + for n=1,self.h do + if(list[n+yOffset]~=nil)then + if(obx<=x)and(obx+self.w>x)and(oby+n-1==y)then + self:setValue(list[n+yOffset]) + end + end + end + end + end + end + + if(event=="mouse_scroll")and(scrollable)then + yOffset = yOffset+button + if(yOffset<0)then yOffset = 0 end + if(button>=1)then + if(#list>self.h)then + if(yOffset>#list-self.h)then + yOffset = #list-self.h + end + if(yOffset>=#list)then yOffset = #list-1 end + else + yOffset = yOffset-1 + end + end + end + self:setVisualChanged() + return true + end + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + for n=1, self.h do + if(list[n+yOffset]~=nil)then + if(list[n+yOffset]==self:getValue())then + if(selectionColorActive)then + self.parent:writeText(obx, oby+n-1, getTextHorizontalAlign(list[n+yOffset].text, self.w, align), itemSelectedBG, itemSelectedFG) + else + self.parent:writeText(obx, oby+n-1, getTextHorizontalAlign(list[n+yOffset].text, self.w, align), list[n+yOffset].bgCol, list[n+yOffset].fgCol) + end + else + self.parent:writeText(obx, oby+n-1, getTextHorizontalAlign(list[n+yOffset].text, self.w, align), list[n+yOffset].bgCol,list[n+yOffset].fgCol) + end + end + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function Menubar(name) + local base = Object(name) + local typ = "Menubar" + + base.w = 30 + base.h = 1 + base.bgcolor = colors.gray + base.fgcolor = colors.lightGray + base:setZIndex(5) + + local list = {} + local itemSelectedBG = theme.selectionBG + local itemSelectedFG = theme.selectionFG + local selectionColorActive = true + local align = "left" + local itemOffset = 0 + local space = 2 + local scrollable = false + + local function maxScroll() + local mScroll = 0 + local xPos = 1 + for n=1,#list do + if(xPos + list[n].text:len() + space*2 > base.w)then + mScroll = mScroll + 1 + end + xPos = xPos+list[n].text:len()+space*2 + + end + return mScroll + end + + local object = { + getType = function(self) + return typ + end; + + addItem = function(self, text, bgCol, fgCol, ...) + table.insert(list, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + if(#list==1)then + self:setValue(list[1]) + end + return self + end; + + getItemIndex = function(self) + local selected = self:getValue() + for k,v in pairs(list)do + if(v==selected)then + return k + end + end + end; + + clear = function(self) + list = {} + self:setValue({}) + return self + end; + + setSpace = function(self, _space) + space = _space or space + return self + end; + + setButtonOffset = function(self, offset) + itemOffset = offset or 0 + if(itemOffset<0)then + itemOffset = 0 + end + + local mScroll = maxScroll() + if(itemOffset>mScroll)then + itemOffset = mScroll + end + return self + end; + + setScrollable = function(self, scroll) + scrollable = scroll + return self + end; + + removeItem = function(self, index) + table.remove(list, index) + return self + end; + + getItem = function(self, index) + return list[index] + end; + + getItemCount = function(self) + return #list + end; + + editItem = function(self, index, text, bgCol, fgCol, ...) + table.remove(list, index) + table.insert(list, index, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + return self + end; + + selectItem = function(self, index) + self:setValue(list[index] or {}) + return self + end; + + setSelectedItem = function(self, bgCol, fgCol, active) + itemSelectedBG = bgCol or self.bgcolor + itemSelectedFG = fgCol or self.fgcolor + selectionColorActive = active + return self + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self,event,button,x,y))then + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(event=="mouse_click")then + local xPos = 1 + for n=1+itemOffset,#list do + if(list[n]~=nil)then + if(xPos + list[n].text:len() + space*2 <= self.w)then + if(obx + (xPos-1)<= x)and(obx + (xPos-1) + list[n].text:len() + space*2 > x)and(oby == y)then + self:setValue(list[n]) + end + xPos = xPos+list[n].text:len()+space*2 + else + break + end + end + end + + end + if(event=="mouse_scroll")and(scrollable)then + itemOffset = itemOffset+button + if(itemOffset<0)then + itemOffset = 0 + end + + local mScroll = maxScroll() + + if(itemOffset>mScroll)then + itemOffset = mScroll + end + end + end + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + local xPos = 1 + for n=1+itemOffset,#list do + if(list[n]~=nil)then + if(xPos + list[n].text:len() + space*2 <= self.w)then + if(list[n]==self:getValue())then + self.parent:writeText(obx + (xPos-1), oby, getTextHorizontalAlign((" "):rep(space)..list[n].text..(" "):rep(space), list[n].text:len()+space*2, align), itemSelectedBG or list[n].bgCol, itemSelectedFG or list[n].fgCol) + else + self.parent:writeText(obx + (xPos-1), oby, getTextHorizontalAlign((" "):rep(space)..list[n].text..(" "):rep(space), list[n].text:len()+space*2, align), list[n].bgCol, list[n].fgCol) + end + xPos = xPos+list[n].text:len()+space*2 + else + break + end + end + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function Dropdown(name) + local base = Object(name) + local typ = "Dropdown" + base.w = 12 + base.h = 1 + base.bgcolor = theme.dropdownBG + base.fgcolor = theme.dropdownFG + base:setZIndex(6) + + local list = {} + local itemSelectedBG = theme.selectionBG + local itemSelectedFG = theme.selectionFG + local selectionColorActive = true + local align = "left" + local yOffset = 0 + + local dropdownW = 16 + local dropdownH = 6 + local closedSymbol = "\16" + local openedSymbol = "\31" + local state = 1 + + local object = { + getType = function(self) + return typ + end; + + setIndexOffset = function(self, yOff) + yOffset = yOff + return self + end; + + getIndexOffset = function(self) + return yOffset + end; + + addItem = function(self, text, bgCol, fgCol, ...) + table.insert(list, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + if(#list==1)then + self:setValue(list[1]) + end + return self + end; + + removeItem = function(self, index) + table.remove(list, index) + return self + end; + + getItem = function(self, index) + return list[index] + end; + + getItemIndex = function(self) + local selected = self:getValue() + for k,v in pairs(list)do + if(v==selected)then + return k + end + end + end; + + clear = function(self) + list = {} + self:setValue({}) + return self + end; + + getItemCount = function(self) + return #list + end; + + editItem = function(self, index, text, bgCol, fgCol, ...) + table.remove(list, index) + table.insert(list, index, {text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + return self + end; + + selectItem = function(self, index) + self:setValue(list[index] or {}) + return self + end; + + setSelectedItem = function(self, bgCol, fgCol, active) + itemSelectedBG = bgCol or self.bgcolor + itemSelectedFG = fgCol or self.fgcolor + selectionColorActive = active + return self + end; + + setDropdownSize = function(self, w, h) + dropdownW, dropdownH = w, h + return self + end; + + mouseHandler = function(self, event, button, x, y) + if(state == 2)then + local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(event=="mouse_click")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw + if(button==1)then + if(#list>0)then + for n=1,dropdownH do + if(list[n+yOffset]~=nil)then + if(obx<=x)and(obx+dropdownW>x)and(oby+n==y)then + self:setValue(list[n+yOffset]) + return true + end + end + end + end + end + end + + if(event=="mouse_scroll")then + yOffset = yOffset+button + if(yOffset<0)then yOffset = 0 end + if(button==1)then + if(#list>dropdownH)then + if(yOffset>#list-dropdownH)then + yOffset = #list-dropdownH + end + else + yOffset = list-1 + end + end + return true + end + self:setVisualChanged() + end + if(base.mouseHandler(self, event, button, x, y))then + state = 2 + else + state = 1 + end + end; + + draw = function(self) + if(base.draw(self))then + local obx, oby = self:getAnchorPosition() + if(self.parent~=nil)then + self.parent:drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + if(#list>=1)then + if(self:getValue().text~=nil)then + if(state==1)then + self.parent:writeText(obx, oby, getTextHorizontalAlign(self:getValue().text, self.w, align):sub(1,self.w-1)..closedSymbol, self.bgcolor, self.fgcolor) + else + self.parent:writeText(obx, oby, getTextHorizontalAlign(self:getValue().text, self.w, align):sub(1,self.w-1)..openedSymbol, self.bgcolor, self.fgcolor) + end + end + if(state==2)then + for n=1, dropdownH do + if(list[n+yOffset]~=nil)then + if(list[n+yOffset]==self:getValue())then + if(selectionColorActive)then + self.parent:writeText(obx, oby+n, getTextHorizontalAlign(list[n+yOffset].text, dropdownW, align), itemSelectedBG, itemSelectedFG) + else + self.parent:writeText(obx, oby+n, getTextHorizontalAlign(list[n+yOffset].text, dropdownW, align), list[n+yOffset].bgCol, list[n+yOffset].fgCol) + end + else + self.parent:writeText(obx, oby+n, getTextHorizontalAlign(list[n+yOffset].text, dropdownW, align), list[n+yOffset].bgCol,list[n+yOffset].fgCol) + end + end + end + end + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function Radio(name) + local base = Object(name) + local typ = "Radio" + base.w = 8 + base.bgcolor = theme.listBG + base.fgcolor = theme.listFG + base:setZIndex(5) + + local list = {} + local itemSelectedBG = theme.selectionBG + local itemSelectedFG = theme.selectionFG + local boxSelectedBG = base.bgcolor + local boxSelectedFG = base.fgcolor + local selectionColorActive = true + local symbol = "\7" + local align = "left" + + local object = { + getType = function(self) + return typ + end; + + addItem = function(self,text, x, y, bgCol, fgCol, ...) + table.insert(list, {x=x or 1, y=y or 1, text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + if(#list==1)then + self:setValue(list[1]) + end + return self + end; + + removeItem = function(self, index) + table.remove(list, index) + return self + end; + + getItem = function(self, index) + return list[index] + end; + + getItemIndex = function(self) + local selected = self:getValue() + for k,v in pairs(list)do + if(v==selected)then + return k + end + end + end; + + clear = function(self) + list = {} + self:setValue({}) + return self + end; + + getItemCount = function(self) + return #list + end; + + editItem = function(self, index, text, x, y, bgCol, fgCol, ...) + table.remove(list, index) + table.insert(list, index, {x=x or 1, y=y or 1, text=text, bgCol = bgCol or self.bgcolor, fgCol = fgCol or self.fgcolor, args={...}}) + return self + end; + + selectItem = function(self, index) + self:setValue(list[index] or {}) + return self + end; + + setSelectedItem = function(self, bgCol, fgCol, boxBG, boxFG, active) + itemSelectedBG = bgCol or itemSelectedBG + itemSelectedFG = fgCol or itemSelectedFG + boxSelectedBG = boxBG or boxSelectedBG + boxSelectedFG = boxFG or boxSelectedFG + selectionColorActive = active + return self + end; + + mouseHandler = function(self, event, button, x, y) + local obx,oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(event=="mouse_click")then -- remove mouse_drag if i want to make objects moveable uwuwuwuw + if(button==1)then + if(#list>0)then + for _,v in pairs(list)do + if(obx+v.x-1<=x)and(obx+v.x-1+v.text:len()+2>=x)and(oby+v.y-1==y)then + self:setValue(v) + if(self.parent~=nil)then self.parent:setFocusedObject(self) end + --eventSystem:sendEvent(event, self, event, button, x, y) + self:setVisualChanged() + return true + end + end + end + end + end + return false + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + for _,v in pairs(list)do + if(v==self:getValue())then + if(align=="left")then + self.parent:writeText(v.x+obx-1,v.y+oby-1,symbol,boxSelectedBG,boxSelectedFG) + self.parent:writeText(v.x+2+obx-1,v.y+oby-1,v.text,itemSelectedBG,itemSelectedFG) + end + else + self.parent:drawBackgroundBox(v.x+obx-1, v.y+oby-1,1,1,self.bgcolor) + self.parent:writeText(v.x+2+obx-1,v.y+oby-1,v.text,v.bgCol,v.fgCol) + end + end + end + end + end; + } + + return setmetatable(object, base) +end + +local function Timer(name) + local typ = "Timer" + + local timer = 0 + local savedRepeats = 0 + local repeats = 0 + local timerObj + local eventSystem = BasaltEvents() + + local object = { + name = name, + getType = function(self) + return typ + end; + + getZIndex = function(self) + return 1 + end; + + getName = function(self) + return self.name + end; + + setTime = function(self, _timer, _repeats) + timer = _timer or 0 + savedRepeats = _repeats or 1 + return self + end; + + start = function(self) + repeats = savedRepeats + timerObj = os.startTimer(timer) + return self + end; + + cancel = function(self) + if(timerObj~=nil)then + os.cancelTimer(timerObj) + end + return self + end; + + onCall = function(self, func) + eventSystem:registerEvent("timed_event", func) + return self + end; + + eventHandler = function(self, event, tObj) + if(event=="timer")and(tObj==timerObj)then + eventSystem:sendEvent("timed_event",self) + if(repeats>=1)then + repeats = repeats - 1 + if(repeats >= 1)then + timerObj = os.startTimer(timer) + end + elseif(repeats == -1)then + timerObj = os.startTimer(timer) + end + end + end; + } + object.__index = object + + return object +end + +local function Thread(name) + local object + local typ = "Thread" + + local func + local cRoutine + local isActive = false + + object = { + name = name, + getType = function(self) + return typ + end; + getZIndex = function(self) + return 1 + end; + getName = function(self) + return self.name + end; + + start = function(self, f) + if(f==nil)then error("function is nil") end + func = f + cRoutine = coroutine.create(func) + isActive = true + local ok, result = coroutine.resume(cRoutine) + if not(ok)then + if(result~="Terminated")then + error("Threaderror - "..result) + end + end + return self + end; + + getStatus = function(self, f) + if(cRoutine~=nil)then + return coroutine.status(cRoutine) + end + return nil + end; + + stop = function(self, f) + isActive = false + return self + end; + + eventHandler = function(self, event, p1,p2,p3) + if(isActive)then + if(coroutine.status(cRoutine)~="dead")then + local ok, result = coroutine.resume(cRoutine, event,p1,p2,p3) + if not(ok)then + if(result~="Terminated")then + error("Threaderror - "..result) + end + end + else + isActive = false + end + end + end; + + } + + object.__index = object + + return object +end + +local function Animation(name) + local object = {} + local typ = "Animation" + + local timerObj + + local animations = {} + local index = 1 + + local nextWaitTimer = 0 + local lastFunc + + local function onPlay() + if(animations[index]~=nil)then + animations[index].f(object, index) + end + index = index+1 + if(animations[index]~=nil)then + if(animations[index].t>0)then + timerObj = os.startTimer(animations[index].t) + else + onPlay() + end + end + end + + object = { + name = name, + getType = function(self) + return typ + end; + + getZIndex = function(self) + return 1 + end; + + getName = function(self) + return self.name + end; + + add = function(self, func, wait) + lastFunc = func + table.insert(animations, {f=func,t = wait or nextWaitTimer}) + return self + end; + + wait = function(self, wait) + nextWaitTimer = wait + return self + end; + + rep = function(self, reps) + for x=1,reps do + table.insert(animations, {f=lastFunc,t = nextWaitTimer}) + end + return self + end; + + clear = function(self) + animations = {} + lastFunc = nil + nextWaitTimer = 0 + index = 1 + return self + end; + + play = function(self) + index = 1 + if(animations[index]~=nil)then + if(animations[index].t>0)then + timerObj = os.startTimer(animations[index].t) + else + onPlay() + end + end + return self + end; + + cancel = function(self) + os.cancelTimer(timerObj) + return self + end; + + eventHandler = function(self, event, tObj) + if(event=="timer")and(tObj==timerObj)then + if(animations[index]~=nil)then + onPlay() + end + end + end; + } + object.__index = object + + return object +end + +local function Slider(name) + local base = Object(name) + local typ = "Slider" + + base.w = 8 + base.bgcolor = colors.lightGray + base.fgcolor = colors.gray + base:setValue(1) + + local barType = "horizontal" + local symbol = " " + local symbolColor=colors.black + local bgSymbol = "\140" + local maxValue = base.w + local index = 1 + + + + local object = { + getType = function(self) + return typ + end; + + setSymbol = function(self, _symbol) + symbol = _symbol:sub(1,1) + self:setVisualChanged() + return self + end; + + setBackgroundSymbol = function(self, _bgSymbol) + bgSymbol = string.sub(_bgSymbol,1,1) + self:setVisualChanged() + return self + end; + + setSymbolColor = function(self, col) + symbolColor = col + self:setVisualChanged() + return self + end; + + setBarType = function(self, _typ) + barType = _typ:lower() + return self + end; + + mouseHandler = function(self, event, button, x, y) + if(base.mouseHandler(self,event,button,x,y))then + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(barType=="horizontal")then + for _index=0,self.w-1 do + if(obx+_index==x)and(oby<=y)and(oby+self.h>y)then + index = _index+1 + self:setValue(maxValue/self.w*(index)) + self:setVisualChanged() + end + end + end + if(barType=="vertical")then + for _index=0,self.h-1 do + if(oby+_index==y)and(obx<=x)and(obx+self.w>x)then + index = _index+1 + self:setValue(maxValue/self.h*(index)) + self:setVisualChanged() + end + end + end + --[[if(event=="mouse_scroll")then + self:setValue(self:getValue() + (maxValue/(barType=="vertical" and self.h or self.w))*typ) + self:setVisualChanged() + end + if(self:getValue()>maxValue)then self:setValue(maxValue) end + if(self:getValue()y)then + index = math.min(_index+1, self.w-(symbolSize-1)) + self:setValue(maxValue/self.w*(index)) + self:setVisualChanged() + end + end + end + if(barType=="vertical")then + for _index=0,self.h do + if(oby+_index==y)and(obx<=x)and(obx+self.w>x)then + index = math.min(_index+1, self.h-(symbolSize-1)) + self:setValue(maxValue/self.h*(index)) + self:setVisualChanged() + end + end + end + end + if(event=="mouse_scroll")then + index = index+button + if(index<1)then index = 1 end + index = math.min(index, (barType=="vertical" and self.h or self.w)-(symbolSize-1)) + self:setValue(maxValue/(barType=="vertical" and self.h or self.w)*index) + end + return true + end + end; + + draw = function(self) + if(base.draw(self))then + if(self.parent~=nil)then + local obx, oby = self:getAnchorPosition() + if(barType=="horizontal")then + self.parent:writeText(obx, oby, bgSymbol:rep(index-1), self.bgcolor, self.fgcolor) + self.parent:writeText(obx + index - 1, oby, symbol:rep(symbolSize), symbolColor, symbolColor) + self.parent:writeText(obx + index+symbolSize-1, oby, bgSymbol:rep(self.w - (index+symbolSize-1)), self.bgcolor, self.fgcolor) + end + + if(barType=="vertical")then + for n = 0, self.h-1 do + + if(index==n+1)then + for curIndexOffset=0,math.min(symbolSize-1,self.h) do + self.parent:writeText(obx, oby+n+curIndexOffset, symbol, symbolColor, symbolColor) + end + else + if(n+1index-1+symbolSize)then + self.parent:writeText(obx, oby+n, bgSymbol, self.bgcolor, self.fgcolor) + end + end + end + end + end + end + end; + } + + return setmetatable(object, base) +end + + +local function Frame(name,parent) -- Frame + local base = Object(name) + local typ = "Frame" + local objects = {} + local objZIndex = {} + local object = {} + local focusedObject + base:setZIndex(10) + + local cursorBlink = false + local xCursor = 1 + local yCursor = 1 + local cursorColor = colors.white + + local xOffset, yOffset = 0, 0 + + if(parent~=nil)then + base.parent = parent + base.w, base.h = parent.w, parent.h + base.bgcolor = theme.FrameBG + base.fgcolor = theme.FrameFG + else + local termW, termH = parentTerminal.getSize() + base.w, base.h = termW, termH + base.bgcolor = theme.basaltBG + base.fgcolor = theme.basaltFG + end + + local function getObject(name) + for k,v in pairs(objects)do + for a,b in pairs(v)do + if(b.name == name)then + return v + end + end + end + end + + local function addObject(obj) + local zIndex = obj:getZIndex() + if(getObject(obj.name)~=nil)then return nil end + if(objects[zIndex]==nil)then + for x=1,#objZIndex+1 do + if(objZIndex[x]~=nil)then + if(zIndex == objZIndex[x])then break end + if(zIndex > objZIndex[x])then + table.insert(objZIndex,x,zIndex) + break + end + else + table.insert(objZIndex,zIndex) + end + end + if(#objZIndex<=0)then + table.insert(objZIndex,zIndex) + end + objects[zIndex] = {} + end + obj.parent = object + table.insert(objects[zIndex],obj) + return obj + end + + local function removeObject(obj) + for a,b in pairs(objects)do + for k,v in pairs(b)do + if(v==obj)then + table.remove(objects[a],k) + return; + end + end + end + end + + object = { + barActive = false, + barBackground = colors.gray, + barTextcolor = colors.black, + barText = "New Frame", + barTextAlign = "left", + isMoveable = false, + + getType = function(self) + return typ + end; + + setFocusedObject = function(self, obj) + for _,index in pairs(objZIndex)do + for _,v in pairs(objects[index])do + if(v == obj)then + if(focusedObject~=nil)then focusedObject:loseFocusHandler() end + focusedObject = obj + focusedObject:getFocusHandler() + end + end + end + return self + end; + + setOffset = function(self, xO, yO) + xOffset = xO ~= nil and math.floor(xO < 0 and math.abs(xO) or -xO) or xOffset + yOffset = yO ~= nil and math.floor(yO < 0 and math.abs(yO) or -yO) or yOffset + return self + end; + + getFrameOffset = function(self) + return xOffset, yOffset + end; + + removeFocusedObject = function(self) + if(focusedObject~=nil)then focusedObject:loseFocusHandler() end + focusedObject = nil + return self + end; + + getFocusedObject = function(self) + return focusedObject + end; + + show = function(self) + base:show() + if(self.parent==nil)then + activeFrame = self + end + return self + end; + + setCursor = function(self, _blink, _xCursor, _yCursor, color) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + cursorBlink = _blink or false + if(_xCursor~=nil)then xCursor = obx+_xCursor-1 end + if(_yCursor~=nil)then yCursor = oby+_yCursor-1 end + cursorColor = color or cursorColor + self:setVisualChanged() + return self + end; + + setMoveable = function(self, moveable) + self.isMoveable = moveable or not self.isMoveable + self:setVisualChanged() + return self; + end; + + + showBar = function(self, showIt) + self.barActive = showIt or not self.barActive + self:setVisualChanged() + return self + end; + + setBar = function(self, text, bgCol, fgCol) + self.barText = text or "" + self.barBackground = bgCol or self.barBackground + self.barTextcolor = fgCol or self.barTextcolor + self:setVisualChanged() + return self + end; + + setBarTextAlign = function(self, align) + self.barTextAlign = align or "left" + self:setVisualChanged() + return self + end; + + getVisualChanged = function(self) + local changed = base.getVisualChanged(self) + for _,index in pairs(objZIndex)do + if(objects[index]~=nil)then + for _,v in pairs(objects[index])do + if(v.getVisualChanged~=nil and v:getVisualChanged())then + changed = true + end + end + end + end + return changed + end; + + loseFocusHandler = function(self) + base.loseFocusHandler(self) + end; + + getFocusHandler = function(self) + base.getFocusHandler(self) + if(self.parent~=nil)then + self.parent:removeObject(self) + self.parent:addObject(self) + end + end; + + keyHandler = function(self, event, key) + if(focusedObject~=nil)then + if(focusedObject.keyHandler~=nil)then + if(focusedObject:keyHandler(event,key))then + return true + end + end + end + return false + end; + + backgroundKeyHandler = function(self, event, key) + base.backgroundKeyHandler(self, event, key) + for _,index in pairs(objZIndex)do + if(objects[index]~=nil)then + for _,v in pairs(objects[index])do + if(v.backgroundKeyHandler~=nil)then + v:backgroundKeyHandler(event,key) + end + end + end + end + end; + + eventHandler = function(self, event, p1, p2, p3, p4) + base.eventHandler(self, event, p1, p2, p3, p4) + for _,index in pairs(objZIndex)do + if(objects[index]~=nil)then + for _,v in pairs(objects[index])do + if(v.eventHandler~=nil)then + v:eventHandler(event,p1, p2, p3, p4) + end + end + end + end + if(event=="terminate")then + parentTerminal.clear() + parentTerminal.setCursorPos(1,1) + basalt.stop() + end + end; + + mouseHandler = function(self, event, button, x, y) + local xO, yO = self:getOffset() + xO = xO < 0 and math.abs(xO) or -xO + yO = yO < 0 and math.abs(yO) or -yO + if(self.drag)then + if(event=="mouse_drag")then + local parentX=1;local parentY=1 + if(self.parent~=nil)then + parentX,parentY = self.parent:getAbsolutePosition(self.parent:getAnchorPosition()) + end + self:setPosition(x+self.xToRem-(parentX-1) + xO,y-(parentY-1) + yO) + end + if(event=="mouse_up")then + self.drag = false + end + return true + end + + if(base.mouseHandler(self,event,button,x,y))then + local fx,fy = self:getAbsolutePosition(self:getAnchorPosition()) + for _,index in pairs(objZIndex)do + if(objects[index]~=nil)then + for _,v in rpairs(objects[index])do + if(v.mouseHandler~=nil)then + if(v:mouseHandler(event,button,x+xO,y+yO))then + return true + end + end + end + end + end + + if(self.isMoveable)then + if(x>=fx)and(x<=fx+self.w-1)and(y==fy)and(event=="mouse_click")then + self.drag = true + self.xToRem = fx-x + end + end + if(focusedObject~=nil)then focusedObject:loseFocusHandler() focusedObject = nil end + return true + end + return false + end; + + setText = function(self, x, y, text) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(y>=1)and(y<=self.h)then + if(self.parent~=nil)then + self.parent:setText(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( text, math.max(1 - x + 1, 1), self.w - x + 1 )) + else + drawHelper.setText(math.max(x+(obx-1),obx), oby+y-1, sub( text, math.max(1 - x + 1, 1), self.w - x + 1 )) + end + end + end; + + setBG = function(self, x, y, bgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(y>=1)and(y<=self.h)then + if(self.parent~=nil)then + self.parent:setBG(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( bgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) + else + drawHelper.setBG(math.max(x+(obx-1),obx), oby+y-1, sub( bgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) + end + end + end; + + setFG = function(self, x, y, fgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(y>=1)and(y<=self.h)then + if(self.parent~=nil)then + self.parent:setFG(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( fgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) + else + drawHelper.setFG(math.max(x+(obx-1),obx), oby+y-1, sub( fgCol, math.max(1 - x + 1, 1), self.w - x + 1 )) + end + end + end; + + writeText = function(self, x, y, text, bgCol, fgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if(y>=1)and(y<=self.h)then + if(self.parent~=nil)then + self.parent:writeText(math.max(x+(obx-1),obx)-(self.parent.x-1), oby+y-1-(self.parent.y-1), sub( text, math.max(1 - x + 1, 1), self.w - x + 1 ), bgCol, fgCol) + else + drawHelper.writeText(math.max(x+(obx-1),obx), oby+y-1, sub( text, math.max(1 - x + 1, 1), self.w - x + 1 ), bgCol, fgCol) + end + end + end; + + drawBackgroundBox = function(self, x, y, w, h, bgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + h = (y < 1 and (h+y > self.h and self.h or h+y-1) or (h+y > self.h and self.h-y+1 or h)) + w = (x < 1 and (w+x > self.w and self.w or w+x-1) or (w+x > self.w and self.w-x+1 or w)) + if(self.parent~=nil)then + self.parent:drawBackgroundBox(math.max(x+(obx-1),obx)-(self.parent.x-1), math.max(y+(oby-1),oby)-(self.parent.y-1), w, h, bgCol) + else + drawHelper.drawBackgroundBox(math.max(x+(obx-1),obx), math.max(y+(oby-1),oby), w, h, bgCol) + end + end; + + drawTextBox = function(self, x, y, w, h, symbol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + h = (y < 1 and (h+y > self.h and self.h or h+y-1) or (h+y > self.h and self.h-y+1 or h)) + w = (x < 1 and (w+x > self.w and self.w or w+x-1) or (w+x > self.w and self.w-x+1 or w)) + if(self.parent~=nil)then + self.parent:drawTextBox(math.max(x+(obx-1),obx)-(self.parent.x-1), math.max(y+(oby-1),oby)-(self.parent.y-1), w, h, symbol:sub(1,1)) + else + drawHelper.drawTextBox(math.max(x+(obx-1),obx), math.max(y+(oby-1),oby), w, h, symbol:sub(1,1)) + end + end; + + drawForegroundBox = function(self, x, y, w, h, fgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + h = (y < 1 and (h+y > self.h and self.h or h+y-1) or (h+y > self.h and self.h-y+1 or h)) + w = (x < 1 and (w+x > self.w and self.w or w+x-1) or (w+x > self.w and self.w-x+1 or w)) + if(self.parent~=nil)then + self.parent:drawForegroundBox(math.max(x+(obx-1),obx)-(self.parent.x-1), math.max(y+(oby-1),oby)-(self.parent.y-1), w, h, fgCol) + else + drawHelper.drawForegroundBox(math.max(x+(obx-1),obx), math.max(y+(oby-1),oby), w, h, fgCol) + end + end; + + draw = function(self) + if(self:getVisualChanged())then + if(base.draw(self))then + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + local anchx, anchy = self:getAnchorPosition() + if(self.parent~=nil)then + self.parent:drawBackgroundBox(anchx, anchy, self.w, self.h, self.bgcolor) + self.parent:drawForegroundBox(anchx, anchy, self.w, self.h, self.fgcolor) + self.parent:drawTextBox(anchx, anchy, self.w, self.h, " ") + else + drawHelper.drawBackgroundBox(obx, oby, self.w, self.h, self.bgcolor) + drawHelper.drawForegroundBox(obx, oby, self.w, self.h, self.fgcolor) + drawHelper.drawTextBox(obx, oby, self.w, self.h, " ") + end + parentTerminal.setCursorBlink(false) + if(self.barActive)then + if(self.parent~=nil)then + self.parent:writeText(anchx, anchy, getTextHorizontalAlign(self.barText, self.w, self.barTextAlign), self.barBackground, self.barTextcolor) + else + drawHelper.writeText(obx, oby, getTextHorizontalAlign(self.barText, self.w, self.barTextAlign), self.barBackground, self.barTextcolor) + end + end + + for _,index in rpairs(objZIndex)do + if(objects[index]~=nil)then + for _,v in pairs(objects[index])do + if(v.draw~=nil)then + v:draw() + end + end + end + end + + if(cursorBlink)then + parentTerminal.setTextColor(cursorColor) + parentTerminal.setCursorPos(xCursor, yCursor) + if(self.parent~=nil)then + parentTerminal.setCursorBlink(self:isFocused()) + else + parentTerminal.setCursorBlink(cursorBlink) + end + end + self:setVisualChanged(false) + end + end + end; + + addObject = function(self, obj) + return addObject(obj) + end; + + removeObject = function(self, obj) + return removeObject(obj) + end; + + addButton = function(self, name) + local obj = Button(name) + obj.name = name + return addObject(obj) + end; + + addLabel = function(self, name) + local obj = Label(name) + obj.name = name + obj.bgcolor = self.bgcolor + obj.fgcolor = self.fgcolor + return addObject(obj) + end; + + addCheckbox = function(self, name) + local obj = Checkbox(name) + obj.name = name + return addObject(obj) + end; + + addInput = function(self, name) + local obj = Input(name) + obj.name = name + return addObject(obj) + end; + + addProgram = function(self, name) + local obj = Program(name) + obj.name = name + return addObject(obj) + end; + + addTextfield = function(self, name) + local obj = Textfield(name) + obj.name = name + return addObject(obj) + end; + + addList = function(self, name) + local obj = List(name) + obj.name = name + return addObject(obj) + end; + + addDropdown = function(self, name) + local obj = Dropdown(name) + obj.name = name + return addObject(obj) + end; + + addRadio = function(self, name) + local obj = Radio(name) + obj.name = name + return addObject(obj) + end; + + addTimer = function(self, name) + local obj = Timer(name) + obj.name = name + return addObject(obj) + end; + + addAnimation = function(self, name) + local obj = Animation(name) + obj.name = name + return addObject(obj) + end; + + addSlider = function(self, name) + local obj = Slider(name) + obj.name = name + return addObject(obj) + end; + + addScrollbar = function(self, name) + local obj = Scrollbar(name) + obj.name = name + return addObject(obj) + end; + + addMenubar = function(self, name) + local obj = Menubar(name) + obj.name = name + return addObject(obj) + end; + + addThread = function(self, name) + local obj = Thread(name) + obj.name = name + return addObject(obj) + end; + + addPane = function(self, name) + local obj = Pane(name) + obj.name = name + return addObject(obj) + end; + + addImage = function(self, name) + local obj = Image(name) + obj.name = name + return addObject(obj) + end; + + addFrame = function(self, name) + local obj = Frame(name, self) + obj.name = name + return addObject(obj) + end; + } + setmetatable(object, base) + if(parent==nil)then + table.insert(frames, object) + end + return object +end + + +local updaterActive = false +local function basaltUpdateEvent(event, p1,p2,p3,p4) + if(event=="mouse_click")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end + if(event=="mouse_drag")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end + if(event=="mouse_up")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end + if(event=="mouse_scroll")then activeFrame:mouseHandler(event,p1,p2,p3,p4) end + if(event=="key")or(event=="char")then activeFrame:keyHandler(event,p1) activeFrame:backgroundKeyHandler(event,p1) end + for _,v in pairs(frames)do + v:eventHandler(event, p1, p2, p3, p4) + end + if(updaterActive)then + activeFrame:draw() + drawHelper.update() + end +end + +function basalt.autoUpdate(isActive) + parentTerminal.clear() + updaterActive = isActive or true + activeFrame:draw() + drawHelper.update() + while updaterActive do + local event, p1,p2,p3,p4 = os.pullEventRaw() -- change to raw later + basaltUpdateEvent(event, p1,p2,p3,p4) + end +end + +function basalt.update(event, p1, p2, p3, p4) + if(event~="nil")then + basaltUpdateEvent(event, p1,p2,p3,p4) + else + activeFrame:draw() + drawHelper.update() + end +end + +function basalt.stop() + updaterActive = false +end + +function basalt.getFrame(name) + for k,v in pairs(frames)do + if(v.name==name)then + return v + end + end +end + +function basalt.getActiveFrame() + return activeFrame +end + +function basalt.setActiveFrame(frame) + if(frame:getType()=="Frame")then + activeFrame = frame + return true + end + return false +end + +function basalt.createFrame(name) + local frame = Frame(name) + return frame +end + +function basalt.removeFrame(name) + for k,v in pairs(frames)do + if(v.name==name)then + frames[k] = nil + return true + end + end + return false +end + +if(basalt.debugger)then + basalt.debugFrame = basalt.createFrame("basaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setBar("Debug",colors.black,colors.gray) + basalt.debugList = basalt.debugFrame:addList("debugList"):setSize(basalt.debugFrame.w - 2, basalt.debugFrame.h - 3):setPosition(2,3):show() + basalt.debugFrame:addButton("back"):setAnchor("right"):setSize(1,1):setText("\22"):onClick(function() basalt.oldFrame:show() end):setBackground(colors.red):show() + basalt.debugLabel = basalt.debugFrame:addLabel("debugLabel"):onClick(function() basalt.oldFrame = activeFrame basalt.debugFrame:show() end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottom"):show() +end + + +if(basalt.debugger)then + function basalt.debug(...) + local args = {...} + if(activeFrame.name~="basaltDebuggingFrame")then + if(activeFrame~=basalt.debugLabel.frame)then + basalt.debugLabel:setParent(activeFrame) + end + end + local str = "" + for k,v in pairs(args)do + str = str..tostring(v)..(#args~=k and ", " or "") + end + basalt.debugLabel:setText("[Debug] "..str) + basalt.debugList:addItem(str) + if(basalt.debugList:getItemCount()>basalt.debugList.h)then basalt.debugList:removeItem(1) end + + basalt.debugLabel:show() + end +end + +return basalt \ No newline at end of file