-- 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