diff --git a/basalt-source.lua b/basalt-source.lua new file mode 100644 index 0000000..316fac4 --- /dev/null +++ b/basalt-source.lua @@ -0,0 +1,4529 @@ +local basalt = { debugger = true, version = 1 } +local activeFrame +local frames = {} +local keyModifier = {} +local parentTerminal = term.current() + +local sub = string.sub + +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", +} +-- 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, +} +------------------------------------------------------------------------------------- +-- Wojbies API 5.0 - Bigfont - functions to write bigger font using drawing sybols -- +------------------------------------------------------------------------------------- +-- Copyright (c) 2015-2022 Wojbie (wojbie@wojbie.net) +-- Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: +-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +-- 4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +-- 5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +-- NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY. + +local rawFont = {{"\32\32\32\137\156\148\158\159\148\135\135\144\159\139\32\136\157\32\159\139\32\32\143\32\32\143\32\32\32\32\32\32\32\32\147\148\150\131\148\32\32\32\151\140\148\151\140\147", "\32\32\32\149\132\149\136\156\149\144\32\133\139\159\129\143\159\133\143\159\133\138\32\133\138\32\133\32\32\32\32\32\32\150\150\129\137\156\129\32\32\32\133\131\129\133\131\132", "\32\32\32\130\131\32\130\131\32\32\129\32\32\32\32\130\131\32\130\131\32\32\32\32\143\143\143\32\32\32\32\32\32\130\129\32\130\135\32\32\32\32\131\32\32\131\32\131", "\139\144\32\32\143\148\135\130\144\149\32\149\150\151\149\158\140\129\32\32\32\135\130\144\135\130\144\32\149\32\32\139\32\159\148\32\32\32\32\159\32\144\32\148\32\147\131\132", "\159\135\129\131\143\149\143\138\144\138\32\133\130\149\149\137\155\149\159\143\144\147\130\132\32\149\32\147\130\132\131\159\129\139\151\129\148\32\32\139\131\135\133\32\144\130\151\32", "\32\32\32\32\32\32\130\135\32\130\32\129\32\129\129\131\131\32\130\131\129\140\141\132\32\129\32\32\129\32\32\32\32\32\32\32\131\131\129\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\159\154\133\133\133\144\152\141\132\133\151\129\136\153\32\32\154\32\159\134\129\130\137\144\159\32\144\32\148\32\32\32\32\32\32\32\32\32\32\32\151\129", "\32\32\32\32\133\32\32\32\32\145\145\132\141\140\132\151\129\144\150\146\129\32\32\32\138\144\32\32\159\133\136\131\132\131\151\129\32\144\32\131\131\129\32\144\32\151\129\32", "\32\32\32\32\129\32\32\32\32\130\130\32\32\129\32\129\32\129\130\129\129\32\32\32\32\130\129\130\129\32\32\32\32\32\32\32\32\133\32\32\32\32\32\129\32\129\32\32", "\150\156\148\136\149\32\134\131\148\134\131\148\159\134\149\136\140\129\152\131\32\135\131\149\150\131\148\150\131\148\32\148\32\32\148\32\32\152\129\143\143\144\130\155\32\134\131\148", "\157\129\149\32\149\32\152\131\144\144\131\148\141\140\149\144\32\149\151\131\148\32\150\32\150\131\148\130\156\133\32\144\32\32\144\32\130\155\32\143\143\144\32\152\129\32\134\32", "\130\131\32\131\131\129\131\131\129\130\131\32\32\32\129\130\131\32\130\131\32\32\129\32\130\131\32\130\129\32\32\129\32\32\133\32\32\32\129\32\32\32\130\32\32\32\129\32", "\150\140\150\137\140\148\136\140\132\150\131\132\151\131\148\136\147\129\136\147\129\150\156\145\138\143\149\130\151\32\32\32\149\138\152\129\149\32\32\157\152\149\157\144\149\150\131\148", "\149\143\142\149\32\149\149\32\149\149\32\144\149\32\149\149\32\32\149\32\32\149\32\149\149\32\149\32\149\32\144\32\149\149\130\148\149\32\32\149\32\149\149\130\149\149\32\149", "\130\131\129\129\32\129\131\131\32\130\131\32\131\131\32\131\131\129\129\32\32\130\131\32\129\32\129\130\131\32\130\131\32\129\32\129\131\131\129\129\32\129\129\32\129\130\131\32", "\136\140\132\150\131\148\136\140\132\153\140\129\131\151\129\149\32\149\149\32\149\149\32\149\137\152\129\137\152\129\131\156\133\149\131\32\150\32\32\130\148\32\152\137\144\32\32\32", "\149\32\32\149\159\133\149\32\149\144\32\149\32\149\32\149\32\149\150\151\129\138\155\149\150\130\148\32\149\32\152\129\32\149\32\32\32\150\32\32\149\32\32\32\32\32\32\32", "\129\32\32\130\129\129\129\32\129\130\131\32\32\129\32\130\131\32\32\129\32\129\32\129\129\32\129\32\129\32\131\131\129\130\131\32\32\32\129\130\131\32\32\32\32\140\140\132", "\32\154\32\159\143\32\149\143\32\159\143\32\159\144\149\159\143\32\159\137\145\159\143\144\149\143\32\32\145\32\32\32\145\149\32\144\32\149\32\143\159\32\143\143\32\159\143\32", "\32\32\32\152\140\149\151\32\149\149\32\145\149\130\149\157\140\133\32\149\32\154\143\149\151\32\149\32\149\32\144\32\149\149\153\32\32\149\32\149\133\149\149\32\149\149\32\149", "\32\32\32\130\131\129\131\131\32\130\131\32\130\131\129\130\131\129\32\129\32\140\140\129\129\32\129\32\129\32\137\140\129\130\32\129\32\130\32\129\32\129\129\32\129\130\131\32", "\144\143\32\159\144\144\144\143\32\159\143\144\159\138\32\144\32\144\144\32\144\144\32\144\144\32\144\144\32\144\143\143\144\32\150\129\32\149\32\130\150\32\134\137\134\134\131\148", "\136\143\133\154\141\149\151\32\129\137\140\144\32\149\32\149\32\149\154\159\133\149\148\149\157\153\32\154\143\149\159\134\32\130\148\32\32\149\32\32\151\129\32\32\32\32\134\32", "\133\32\32\32\32\133\129\32\32\131\131\32\32\130\32\130\131\129\32\129\32\130\131\129\129\32\129\140\140\129\131\131\129\32\130\129\32\129\32\130\129\32\32\32\32\32\129\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32", "\32\32\32\32\145\32\159\139\32\151\131\132\155\143\132\134\135\145\32\149\32\158\140\129\130\130\32\152\147\155\157\134\32\32\144\144\32\32\32\32\32\32\152\131\155\131\131\129", "\32\32\32\32\149\32\149\32\145\148\131\32\149\32\149\140\157\132\32\148\32\137\155\149\32\32\32\149\154\149\137\142\32\153\153\32\131\131\149\131\131\129\149\135\145\32\32\32", "\32\32\32\32\129\32\130\135\32\131\131\129\134\131\132\32\129\32\32\129\32\131\131\32\32\32\32\130\131\129\32\32\32\32\129\129\32\32\32\32\32\32\130\131\129\32\32\32", "\150\150\32\32\148\32\134\32\32\132\32\32\134\32\32\144\32\144\150\151\149\32\32\32\32\32\32\145\32\32\152\140\144\144\144\32\133\151\129\133\151\129\132\151\129\32\145\32", "\130\129\32\131\151\129\141\32\32\142\32\32\32\32\32\149\32\149\130\149\149\32\143\32\32\32\32\142\132\32\154\143\133\157\153\132\151\150\148\151\158\132\151\150\148\144\130\148", "\32\32\32\140\140\132\32\32\32\32\32\32\32\32\32\151\131\32\32\129\129\32\32\32\32\134\32\32\32\32\32\32\32\129\129\32\129\32\129\129\130\129\129\32\129\130\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\150\151\129\150\131\132\140\143\144\143\141\145\137\140\148\141\141\144\157\142\32\159\140\32\151\134\32\157\141\32", "\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\151\151\32\154\143\132\157\140\32\157\140\32\157\140\32\157\140\32\32\149\32\32\149\32\32\149\32\32\149\32", "\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\131\129\32\134\32\131\131\129\131\131\129\131\131\129\131\131\129\130\131\32\130\131\32\130\131\32\130\131\32", "\151\131\148\152\137\145\155\140\144\152\142\145\153\140\132\153\137\32\154\142\144\155\159\132\150\156\148\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\136\32\151\140\132", "\151\32\149\151\155\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\152\137\144\157\129\149\149\32\149\149\32\149\149\32\149\149\32\149\130\150\32\32\157\129\149\32\149", "\131\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\129\32\130\131\32\133\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\159\159\144\152\140\144\156\143\32\159\141\129\153\140\132\157\141\32\130\145\32\32\147\32\136\153\32\130\146\32", "\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\149\157\134\154\143\132\157\140\133\157\140\133\157\140\133\157\140\133\32\149\32\32\149\32\32\149\32\32\149\32", "\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\130\131\32\134\32\130\131\129\130\131\129\130\131\129\130\131\129\32\129\32\32\129\32\32\129\32\32\129\32", "\159\134\144\137\137\32\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\32\132\32\159\143\32\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\138\32\146\130\144", "\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\131\147\129\138\134\149\149\32\149\149\32\149\149\32\149\149\32\149\154\143\149\32\157\129\154\143\149", "\130\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\129\130\131\129\130\131\129\130\131\129\140\140\129\130\131\32\140\140\129" }, {[[000110000110110000110010101000000010000000100101]], [[000000110110000000000010101000000010000000100101]], [[000000000000000000000000000000000000000000000000]], [[100010110100000010000110110000010100000100000110]], [[000000110000000010110110000110000000000000110000]], [[000000000000000000000000000000000000000000000000]], [[000000110110000010000000100000100000000000000010]], [[000000000110110100010000000010000000000000000100]], [[000000000000000000000000000000000000000000000000]], [[010000000000100110000000000000000000000110010000]], [[000000000000000000000000000010000000010110000000]], [[000000000000000000000000000000000000000000000000]], [[011110110000000100100010110000000100000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110000110110000000000000000000010100100010000000]], [[000010000000000000110110000000000100010010000000]], [[000000000000000000000000000000000000000000000000]], [[010110010110100110110110010000000100000110110110]], [[000000000000000000000110000000000110000000000000]], [[000000000000000000000000000000000000000000000000]], [[010100010110110000000000000000110000000010000000]], [[110110000000000000110000110110100000000010000000]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[100100100100100100100100100100100100100100100100]], [[000000110100110110000010000011110000000000011000]], [[000000000100000000000010000011000110000000001000]], [[000000000000000000000000000000000000000000000000]], [[010000100100000000000000000100000000010010110000]], [[000000000000000000000000000000110110110110110000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110000000110110110110110110110110]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000110110000110010000000000000000010010]], [[000010000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110110110110000000000000]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110000000000000000010000]], [[000000000000000000000000100000000000000110000110]], [[000000000000000000000000000000000000000000000000]] }} + +--### Genarate fonts using 3x3 chars per a character. (1 character is 6x9 pixels) +local fonts = {} +local firstFont = {} +do + local char = 0 + local height = #rawFont[1] + local length = #rawFont[1][1] + for i = 1, height, 3 do + for j = 1, length, 3 do + local thisChar = string.char(char) + + local temp = {} + temp[1] = rawFont[1][i]:sub(j, j + 2) + temp[2] = rawFont[1][i + 1]:sub(j, j + 2) + temp[3] = rawFont[1][i + 2]:sub(j, j + 2) + + local temp2 = {} + temp2[1] = rawFont[2][i]:sub(j, j + 2) + temp2[2] = rawFont[2][i + 1]:sub(j, j + 2) + temp2[3] = rawFont[2][i + 2]:sub(j, j + 2) + + firstFont[thisChar] = {temp, temp2} + char = char + 1 + end + end + fonts[1] = firstFont +end + +local function generateFontSize(size,yeld) + local inverter = {["0"] = "1", ["1"] = "0"} --:gsub("[01]",inverter) + if size<= #fonts then return true end + for f = #fonts+1, size do + --automagicly make bigger fonts using firstFont and fonts[f-1]. + local nextFont = {} + local lastFont = fonts[f - 1] + for char = 0, 255 do + local thisChar = string.char(char) + --sleep(0) print(f,thisChar) + + local temp = {} + local temp2 = {} + + local templateChar = lastFont[thisChar][1] + local templateBack = lastFont[thisChar][2] + for i = 1, #templateChar do + local line1, line2, line3, back1, back2, back3 = {}, {}, {}, {}, {}, {} + for j = 1, #templateChar[1] do + local currentChar = firstFont[templateChar[i]:sub(j, j)][1] + table.insert(line1, currentChar[1]) + table.insert(line2, currentChar[2]) + table.insert(line3, currentChar[3]) + + local currentBack = firstFont[templateChar[i]:sub(j, j)][2] + if templateBack[i]:sub(j, j) == "1" then + table.insert(back1, (currentBack[1]:gsub("[01]", inverter))) + table.insert(back2, (currentBack[2]:gsub("[01]", inverter))) + table.insert(back3, (currentBack[3]:gsub("[01]", inverter))) + else + table.insert(back1, currentBack[1]) + table.insert(back2, currentBack[2]) + table.insert(back3, currentBack[3]) + end + end + table.insert(temp, table.concat(line1)) + table.insert(temp, table.concat(line2)) + table.insert(temp, table.concat(line3)) + table.insert(temp2, table.concat(back1)) + table.insert(temp2, table.concat(back2)) + table.insert(temp2, table.concat(back3)) + end + + nextFont[thisChar] = {temp, temp2} + if yeld then yeld = "Font"..f.."Yeld"..char os.queueEvent(yeld) os.pullEvent(yeld) end + end + fonts[f] = nextFont + end + return true +end + +generateFontSize(3,false) + +local function makeText(nSize, sString, nFC, nBC, bBlit) + if not type(sString) == "string" then error("Not a String",3) end --this should never happend with expects in place. + print(tHex, nFC) + local cFC = type(nFC) == "string" and nFC:sub(1, 1) or tHex[nFC] or error("Wrong Front Color",3) + local cBC = type(nBC) == "string" and nBC:sub(1, 1) or tHex[nBC] or error("Wrong Back Color",3) + local font = fonts[nSize] or error("Wrong font size selected",3) + if sString == "" then return {{""}, {""}, {""}} end + + local input = {} + for i in sString:gmatch('.') do table.insert(input, i) end + + local tText = {} + local height = #font[input[1]][1] + + + for nLine = 1, height do + local outLine = {} + for i = 1, #input do + outLine[i] = font[input[i]] and font[input[i]][1][nLine] or "" + end + tText[nLine] = table.concat(outLine) + end + + local tFront = {} + local tBack = {} + local tFrontSub = {["0"] = cFC, ["1"] = cBC} + local tBackSub = {["0"] = cBC, ["1"] = cFC} + + for nLine = 1, height do + local front = {} + local back = {} + for i = 1, #input do + local template = font[input[i]] and font[input[i]][2][nLine] or "" + front[i] = template:gsub("[01]", bBlit and {["0"] = nFC:sub(i, i), ["1"] = nBC:sub(i, i)} or tFrontSub) + back[i] = template:gsub("[01]", bBlit and {["0"] = nBC:sub(i, i), ["1"] = nFC:sub(i, i)} or tBackSub) + end + tFront[nLine] = table.concat(front) + tBack[nLine] = table.concat(back) + end + + return {tText, tFront, tBack} +end +local function basaltDrawHelper() + local terminal = parentTerminal + local width, height = terminal.getSize() + local cacheT = {} + local cacheBG = {} + local cacheFG = {} + + local _cacheT = {} + local _cacheBG = {} + local _cacheFG = {} + + local emptySpaceLine + local emptyColorLines = {} + + local function createEmptyLines() + emptySpaceLine = (" "):rep(width) + for n = 0, 15 do + local nColor = 2 ^ n + local sHex = tHex[nColor] + emptyColorLines[nColor] = sHex:rep(width) + end + end + ---- + createEmptyLines() + + local function recreateWindowArray() + local emptyText = emptySpaceLine + local emptyFG = emptyColorLines[colors.white] + local emptyBG = emptyColorLines[colors.black] + for currentY = 1, height do + cacheT[currentY] = sub(cacheT[currentY] == nil and emptyText or cacheT[currentY] .. emptyText:sub(1, width - cacheT[currentY]:len()), 1, width) + cacheFG[currentY] = sub(cacheFG[currentY] == nil and emptyFG or cacheFG[currentY] .. emptyFG:sub(1, width - cacheFG[currentY]:len()), 1, width) + cacheBG[currentY] = sub(cacheBG[currentY] == nil and emptyBG or cacheBG[currentY] .. emptyBG:sub(1, width - cacheBG[currentY]:len()), 1, width) + end + end + recreateWindowArray() + + local function setText(x, y, text) + if (y >= 1) and (y <= height) then + if (x + text:len() > 0) and (x <= width) then + local oldCache = cacheT[y] + local newCache + local nEnd = x + #text - 1 + + if (x < 1) then + local startN = 1 - x + 1 + local endN = width - x + 1 + text = sub(text, startN, endN) + elseif (nEnd > width) then + local endN = width - 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 < width then + newCache = newCache .. sub(oldCache, nEnd + 1, width) + end + cacheT[y] = newCache + end + end + end + + local function setBG(x, y, colorStr) + if (y >= 1) and (y <= height) then + if (x + colorStr:len() > 0) and (x <= width) then + local oldCache = cacheBG[y] + local newCache + local nEnd = x + #colorStr - 1 + + if (x < 1) then + colorStr = sub(colorStr, 1 - x + 1, width - x + 1) + elseif (nEnd > width) then + colorStr = sub(colorStr, 1, width - x + 1) + end + + if (x > 1) then + newCache = sub(oldCache, 1, x - 1) .. colorStr + else + newCache = colorStr + end + if nEnd < width then + newCache = newCache .. sub(oldCache, nEnd + 1, width) + end + cacheBG[y] = newCache + end + end + end + + local function setFG(x, y, colorStr) + if (y >= 1) and (y <= height) then + if (x + colorStr:len() > 0) and (x <= width) then + local oldCache = cacheFG[y] + local newCache + local nEnd = x + #colorStr - 1 + + if (x < 1) then + local startN = 1 - x + 1 + local endN = width - x + 1 + colorStr = sub(colorStr, startN, endN) + elseif (nEnd > width) then + local endN = width - 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 < width then + newCache = newCache .. sub(oldCache, nEnd + 1, width) + 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, width, height, bgCol) + for n = 1, height do + setBG(x, y + (n - 1), tHex[bgCol]:rep(width)) + end + end; + drawForegroundBox = function(x, y, width, height, fgCol) + for n = 1, height do + setFG(x, y + (n - 1), tHex[fgCol]:rep(width)) + end + end; + drawTextBox = function(x, y, width, height, symbol) + for n = 1, height do + setText(x, y + (n - 1), symbol:rep(width)) + 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, height do + terminal.setCursorPos(1, n) + terminal.blit(cacheT[n], cacheFG[n], cacheBG[n]) + end + terminal.setBackgroundColor(colors.black) + 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 _, value in pairs(events[_event]) do + value(...) + 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 getTextHorizontalAlign(text, width, textAlign, replaceChar) + text = string.sub(text, 1, width) + local offset = width - string.len(text) + if (textAlign == "right") then + text = string.rep(replaceChar or " ", offset) .. text + elseif (textAlign == "center") then + text = string.rep(replaceChar or " ", math.floor(offset / 2)) .. text .. string.rep(replaceChar or " ", math.floor(offset / 2)) + text = text .. (string.len(text) < width and (replaceChar or " ") or "") + else + text = text .. string.rep(replaceChar or " ", offset) + 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 + if(offset<1)then offset=1 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 function shrink(bLittleData, bgColor) + + -- shrinkSystem is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ + local relations = { [0] = { 8, 4, 3, 6, 5 }, { 4, 14, 8, 7 }, { 6, 10, 8, 7 }, { 9, 11, 8, 0 }, { 1, 14, 8, 0 }, { 13, 12, 8, 0 }, { 2, 10, 8, 0 }, { 15, 8, 10, 11, 12, 14 }, + { 0, 7, 1, 9, 2, 13 }, { 3, 11, 8, 7 }, { 2, 6, 7, 15 }, { 9, 3, 7, 15 }, { 13, 5, 7, 15 }, { 5, 12, 8, 7 }, { 1, 4, 7, 15 }, { 7, 10, 11, 12, 14 } } + + local colourNum, exponents, colourChar = {}, {}, {} + for i = 0, 15 do + exponents[2 ^ i] = i + end + do + local hex = "0123456789abcdef" + for i = 1, 16 do + colourNum[hex:sub(i, i)] = i - 1 + colourNum[i - 1] = hex:sub(i, i) + colourChar[hex:sub(i, i)] = 2 ^ (i - 1) + colourChar[2 ^ (i - 1)] = hex:sub(i, i) + + local thisRel = relations[i - 1] + for i = 1, #thisRel do + thisRel[i] = 2 ^ thisRel[i] + end + end + end + + local function getBestColourMatch(usage) + local lastCol = relations[exponents[usage[#usage][1]]] + if(lastCol~=nil)then + for j = 1, #lastCol do + local thisRelation = lastCol[j] + for i = 1, #usage - 1 do + if usage[i][1] == thisRelation then + return i + end + end + end + end + return 1 + end + + local function colsToChar(pattern, totals) + if not totals then + local newPattern = {} + totals = {} + for i = 1, 6 do + local thisVal = pattern[i] + local thisTot = totals[thisVal] + totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal + end + pattern = newPattern + end + + local usage = {} + for key, value in pairs(totals) do + usage[#usage + 1] = { key, value } + end + + if #usage > 1 then + -- Reduce the chunk to two colours: + while #usage > 2 do + table.sort(usage, function(a, b) + return a[2] > b[2] + end) + local matchToInd, usageLen = getBestColourMatch(usage), #usage + local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1] + for i = 1, 6 do + if pattern[i] == matchFrom then + pattern[i] = matchTo + usage[matchToInd][2] = usage[matchToInd][2] + 1 + end + end + usage[usageLen] = nil + end + + -- Convert to character. Adapted from oli414's function: + -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/ + local data = 128 + for i = 1, #pattern - 1 do + if pattern[i] ~= pattern[6] then + data = data + 2 ^ (i - 1) + end + end + return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]] + else + -- Solid colour character: + return "\128", colourChar[pattern[1]], colourChar[pattern[1]] + end + end + + local results, width, height, bgCol = { {}, {}, {} }, 0, #bLittleData + #bLittleData % 3, bgColor or colors.black + for i = 1, #bLittleData do + if #bLittleData[i] > width then + width = #bLittleData[i] + end + end + + for y = 0, height - 1, 3 do + local cRow, tRow, bRow, counter = {}, {}, {}, 1 + + for x = 0, width - 1, 2 do + -- Grab a 2x3 chunk: + local pattern, totals = {}, {} + + for yy = 1, 3 do + for xx = 1, 2 do + pattern[#pattern + 1] = (bLittleData[y + yy] and bLittleData[y + yy][x + xx]) and (bLittleData[y + yy][x + xx] == 0 and bgCol or bLittleData[y + yy][x + xx]) or bgCol + totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1 + end + end + + cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals) + counter = counter + 1 + end + + results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow) + end + + results.width, results.height = #results[1][1], #results[1] + + return results +end +local function Object(name) + -- Base object + local objectType = "Object" -- not changeable + --[[ + local horizontalAnchor = "left" + local verticalAnchor = "top" + local ignYOffset = false + local ignXOffset = false ]] + local value + 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, + width = 1, + height = 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 objectType + 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) + if (value ~= _value) then + value = _value + visualsChanged = true + self:valueChangedHandler() + end + 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, width, height) + self.width, self.height = width, height + visualsChanged = true + return self + end; + + getHeight = function(self) + return self.height + end; + + getWidth = function(self) + return self.width + end; + + getSize = function(self) + return self.width, self.height + 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.width - x - self.width + 2 + end + if (vanchor == "bottom") then + y = self.parent.height - y - self.height + 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 _, value in pairs(table.pack(...)) do + if (value == "right") or (value == "left") then + hanchor = value + end + if (value == "top") or (value == "bottom") then + vanchor = value + 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; + + sendEvent = function(self, event, ...) + return eventSystem:sendEvent(event, self, ...) + end; + + mouseClickHandler = function(self, event, button, x, y) + local objX, objY = self:getAbsolutePosition(self:getAnchorPosition()) + if (objX <= x) and (objX + self.width > x) and (objY <= y) and (objY + self.height > 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 Animation(name) + local object = {} + local objectType = "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 objectType + 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 Button(name) + -- Button + local base = Object(name) + local objectType = "Button" + + base:setValue("Button") + base:setZIndex(5) + base.width = 8 + base.bgColor = theme.ButtonBG + base.fgColor = theme.ButtonFG + + local textHorizontalAlign = "center" + local textVerticalAlign = "center" + + local object = { + getType = function(self) + return objectType + 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() + local verticalAlign = getTextVerticalAlign(self.height, textVerticalAlign) + + self.parent:drawBackgroundBox(obx, oby, self.width, self.height, self.bgColor) + self.parent:drawForegroundBox(obx, oby, self.width, self.height, self.fgColor) + self.parent:drawTextBox(obx, oby, self.width, self.height, " ") + for n = 1, self.height do + if (n == verticalAlign) then + self.parent:setText(obx, oby + (n - 1), getTextHorizontalAlign(self:getValue(), self.width, textHorizontalAlign)) + end + end + end + end + end; + + } + return setmetatable(object, base) +end +local function Checkbox(name) + -- Checkbox + local base = Object(name) + local objectType = "Checkbox" + + base:setZIndex(5) + base:setValue(false) + base.width = 1 + base.height = 1 + base.bgColor = theme.CheckboxBG + base.fgColor = theme.CheckboxFG + + local object = { + symbol = "\42", + + getType = function(self) + return objectType + end; + + mouseClickHandler = function(self, event, button, x, y) + if (base.mouseClickHandler(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.height, "center") + + self.parent:drawBackgroundBox(obx, oby, self.width, self.height, self.bgColor) + for n = 1, self.height do + if (n == verticalAlign) then + if (self:getValue() == true) then + self.parent:writeText(obx, oby + (n - 1), getTextHorizontalAlign(self.symbol, self.width, "center"), self.bgColor, self.fgColor) + else + self.parent:writeText(obx, oby + (n - 1), getTextHorizontalAlign(" ", self.width, "center"), self.bgColor, self.fgColor) + end + end + end + end + end + end; + + } + + return setmetatable(object, base) +end +local function Dropdown(name) + local base = Object(name) + local objectType = "Dropdown" + base.width = 12 + base.height = 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 objectType + 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 = { ... } }) + 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 key, value in pairs(list) do + if (value == selected) then + return key + 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, width, height) + dropdownW, dropdownH = width, height + return self + end; + + mouseClickHandler = 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.mouseClickHandler(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.width, self.height, self.bgColor) + if (#list >= 1) then + if (self:getValue() ~= nil) then + if (self:getValue().text ~= nil) then + if (state == 1) then + self.parent:writeText(obx, oby, getTextHorizontalAlign(self:getValue().text, self.width, align):sub(1, self.width - 1) .. closedSymbol, self.bgColor, self.fgColor) + else + self.parent:writeText(obx, oby, getTextHorizontalAlign(self:getValue().text, self.width, align):sub(1, self.width - 1) .. openedSymbol, self.bgColor, self.fgColor) + end + 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 Image(name) + -- Pane + local base = Object(name) + local objectType = "Image" + base:setZIndex(2) + local image + local shrinkedImage + local imageGotShrinked = false + + local function shrink() + + -- shrinkSystem is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ + local relations = { [0] = { 8, 4, 3, 6, 5 }, { 4, 14, 8, 7 }, { 6, 10, 8, 7 }, { 9, 11, 8, 0 }, { 1, 14, 8, 0 }, { 13, 12, 8, 0 }, { 2, 10, 8, 0 }, { 15, 8, 10, 11, 12, 14 }, + { 0, 7, 1, 9, 2, 13 }, { 3, 11, 8, 7 }, { 2, 6, 7, 15 }, { 9, 3, 7, 15 }, { 13, 5, 7, 15 }, { 5, 12, 8, 7 }, { 1, 4, 7, 15 }, { 7, 10, 11, 12, 14 } } + + local colourNum, exponents, colourChar = {}, {}, {} + for i = 0, 15 do + exponents[2 ^ i] = i + end + do + local hex = "0123456789abcdef" + for i = 1, 16 do + colourNum[hex:sub(i, i)] = i - 1 + colourNum[i - 1] = hex:sub(i, i) + colourChar[hex:sub(i, i)] = 2 ^ (i - 1) + colourChar[2 ^ (i - 1)] = hex:sub(i, i) + + local thisRel = relations[i - 1] + for i = 1, #thisRel do + thisRel[i] = 2 ^ thisRel[i] + end + end + end + + local function getBestColourMatch(usage) + local lastCol = relations[exponents[usage[#usage][1]]] + + for j = 1, #lastCol do + local thisRelation = lastCol[j] + for i = 1, #usage - 1 do + if usage[i][1] == thisRelation then + return i + end + end + end + + return 1 + end + + local function colsToChar(pattern, totals) + if not totals then + local newPattern = {} + totals = {} + for i = 1, 6 do + local thisVal = pattern[i] + local thisTot = totals[thisVal] + totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal + end + pattern = newPattern + end + + local usage = {} + for key, value in pairs(totals) do + usage[#usage + 1] = { key, value } + end + + if #usage > 1 then + -- Reduce the chunk to two colours: + while #usage > 2 do + table.sort(usage, function(a, b) + return a[2] > b[2] + end) + local matchToInd, usageLen = getBestColourMatch(usage), #usage + local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1] + for i = 1, 6 do + if pattern[i] == matchFrom then + pattern[i] = matchTo + usage[matchToInd][2] = usage[matchToInd][2] + 1 + end + end + usage[usageLen] = nil + end + + -- Convert to character. Adapted from oli414's function: + -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/ + local data = 128 + for i = 1, #pattern - 1 do + if pattern[i] ~= pattern[6] then + data = data + 2 ^ (i - 1) + end + end + return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]] + else + -- Solid colour character: + return "\128", colourChar[pattern[1]], colourChar[pattern[1]] + end + end + + local results, width, height, bgCol = { {}, {}, {} }, 0, #image + #image % 3, base.bgColor or colors.black + for i = 1, #image do + if #image[i] > width then + width = #image[i] + end + end + + for y = 0, height - 1, 3 do + local cRow, tRow, bRow, counter = {}, {}, {}, 1 + + for x = 0, width - 1, 2 do + -- Grab a 2x3 chunk: + local pattern, totals = {}, {} + + for yy = 1, 3 do + for xx = 1, 2 do + pattern[#pattern + 1] = (image[y + yy] and image[y + yy][x + xx]) and (image[y + yy][x + xx] == 0 and bgCol or image[y + yy][x + xx]) or bgCol + totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1 + end + end + + cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals) + counter = counter + 1 + end + + results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow) + end + + results.width, results.height = #results[1][1], #results[1] + + shrinkedImage = results + end + + local object = { + getType = function(self) + return objectType + end; + + loadImage = function(self, path) + image = paintutils.loadImage(path) + imageGotShrinked = false + return self + end; + + loadBlittleImage = function(self, path) -- not done yet + --image = paintutils.loadImage(path) + imageGotShrinked = true + return self + end; + + shrink = function(self) + shrink() + imageGotShrinked = true + 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() + if (imageGotShrinked) then + -- this is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ + local t, tC, bC = shrinkedImage[1], shrinkedImage[2], shrinkedImage[3] + for i = 1, shrinkedImage.height do + local tI = t[i] + if type(tI) == "string" then + self.parent:setText(obx, oby + i - 1, tI) + self.parent:setFG(obx, oby + i - 1, tC[i]) + self.parent:setBG(obx, oby + i - 1, bC[i]) + elseif type(tI) == "table" then + self.parent:setText(obx, oby + i - 1, tI[2]) + self.parent:setFG(obx, oby + i - 1, tC[i]) + self.parent:setBG(obx, oby + i - 1, bC[i]) + end + end + else + for yPos = 1, math.min(#image, self.height) do + local line = image[yPos] + for xPos = 1, math.min(#line, self.width) 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 + end; + } + + return setmetatable(object, base) +end +local function Input(name) + -- Input + local base = Object(name) + local objectType = "Input" + + local inputType = "text" + local inputLimit = 0 + base:setZIndex(5) + base:setValue("") + base.width = 10 + base.height = 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 objectType + 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 == keys.backspace) 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 < wIndex) then + wIndex = wIndex - 1 + end + end + end + end + if (key == keys.enter) then + -- on enter + if (self.parent ~= nil) then + --self.parent:removeFocusedObject(self) + end + end + if (key == keys.right) then + -- right arrow + local tLength = tostring(base.getValue()):len() + textX = textX + 1 + + if (textX > tLength) then + textX = tLength + 1 + end + if (textX < 1) then + textX = 1 + end + if (textX < wIndex) or (textX >= self.width + wIndex) then + wIndex = textX - self.width + 1 + end + if (wIndex < 1) then + wIndex = 1 + end + end + + if (key == keys.left) then + -- left arrow + textX = textX - 1 + if (textX >= 1) then + if (textX < wIndex) or (textX >= self.width + 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.width + 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.width - 1) then + cursorX = self.x + self.width - 1 + end + if (self.parent ~= nil) then + self.parent:setCursor(true, obx + cursorX, oby, self.fgColor) + end + internalValueChange = false + end + end; + + mouseClickHandler = function(self, event, button, x, y) + if (base.mouseClickHandler(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.height, "center") + + self.parent:drawBackgroundBox(obx, oby, self.width, self.height, self.bgColor) + for n = 1, self.height 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.width + wIndex - 1) + local space = self.width - 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 Label(name) + -- Label + local base = Object(name) + local objectType = "Label" + + base:setZIndex(3) + base.fgColor = colors.white + base.bgcolor = colors.black + + local autoWidth = true + base:setValue("") + + local textHorizontalAlign = "left" + local textVerticalAlign = "top" + local fontsize = 0 + + local object = { + getType = function(self) + return objectType + end; + setText = function(self, text) + text = tostring(text) + base:setValue(text) + if (autoWidth) then + self.width = text:len() + end + return self + end; + + setTextAlign = function(self, hor, vert) + textHorizontalAlign = hor or textHorizontalAlign + textVerticalAlign = vert or textVerticalAlign + self:setVisualChanged() + return self + end; + + setFontSize = function(self, size) + if(size>0)and(size<=4)then + fontsize = size-1 or 0 + end + return self + end; + + getFontSize = function(self) + return fontsize+1 + end; + + setSize = function(self, width, height) + base.setSize(self, width, height) + autoWidth = false + self:setVisualChanged() + return self + end; + + draw = function(self) + if (base.draw(self)) then + if (self.parent ~= nil) then + local obx, oby = self:getAnchorPosition() + local verticalAlign = getTextVerticalAlign(self.height, textVerticalAlign) + self.parent:drawBackgroundBox(obx, oby, self.width, self.height, self.bgColor) + self.parent:drawForegroundBox(obx, oby, self.width, self.height, self.fgColor) + self.parent:drawTextBox(obx, oby, self.width, self.height, " ") + if(fontsize==0)then + for n = 1, self.height do + if (n == verticalAlign) then + self.parent:writeText(obx, oby + (n - 1), getTextHorizontalAlign(self:getValue(), self.width, textHorizontalAlign), self.bgColor, self.fgColor) + end + end + else + local tData = makeText(fontsize, self:getValue(), self.fgColor, self.bgColor) + for n = 1, self.height do + if (n == verticalAlign) then + local oX, oY = self.parent:getSize() + local cX, cY = #tData[1][1], #tData[1] + obx = obx or math.floor((oX - cX) / 2) + 1 + oby = oby or math.floor((oY - cY) / 2) + 1 + + for i = 1, cY do + self.parent:setFG(obx, oby + i + n - 2, getTextHorizontalAlign(tData[2][i], self.width, textHorizontalAlign)) + self.parent:setBG(obx, oby + i + n - 2, getTextHorizontalAlign(tData[3][i], self.width, textHorizontalAlign, tHex[self.bgColor])) + self.parent:setText(obx, oby + i + n - 2, getTextHorizontalAlign(tData[1][i], self.width, textHorizontalAlign)) + end + end + end + end + end + end + end; + + } + + return setmetatable(object, base) +end +local function List(name) + local base = Object(name) + local objectType = "List" + base.width = 16 + base.height = 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 objectType + 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 key, value in pairs(list) do + if (value == selected) then + return key + 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; + + mouseClickHandler = function(self, event, button, x, y) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if (obx <= x) and (obx + self.width > x) and (oby <= y) and (oby + self.height > y) and (self:isVisible()) then + 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.height do + if (list[n + yOffset] ~= nil) then + if (obx <= x) and (obx + self.width > x) and (oby + n - 1 == y) then + self:setValue(list[n + yOffset]) + self:getEventSystem():sendEvent("mouse_click", self, "mouse_click", 0, x, y, 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.height) then + if (yOffset > #list - self.height) then + yOffset = #list - self.height + 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.width, self.height, self.bgColor) + for n = 1, self.height 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.width, align), itemSelectedBG, itemSelectedFG) + else + self.parent:writeText(obx, oby + n - 1, getTextHorizontalAlign(list[n + yOffset].text, self.width, align), list[n + yOffset].bgCol, list[n + yOffset].fgCol) + end + else + self.parent:writeText(obx, oby + n - 1, getTextHorizontalAlign(list[n + yOffset].text, self.width, 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 objectType = "Menubar" + local object = {} + + base.width = 30 + base.height = 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 > object.w) then + mScroll = mScroll + list[n].text:len() + space * 2 + end + xPos = xPos + list[n].text:len() + space * 2 + + end + return mScroll + end + + object = { + getType = function(self) + return objectType + 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 key, value in pairs(list) do + if (value == selected) then + return key + 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; + + mouseClickHandler = function(self, event, button, x, y) + local objX, objY = self:getAbsolutePosition(self:getAnchorPosition()) + if (objX <= x) and (objX + self.width > x) and (objY <= y) and (objY + self.height > y) and (self:isVisible()) then + if (self.parent ~= nil) then + self.parent:setFocusedObject(self) + end + 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.width) then + if (objX + (xPos - 1) <= x) and (objX + (xPos - 1) + list[n].text:len() + space * 2 > x) and (objY == y) then + self:setValue(list[n]) + self:getEventSystem():sendEvent("mouse_click", self, "mouse_click", 0, x, y, 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 + return true + end + return false + 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.width, self.height, self.bgColor) + local xPos = 0 + for _, value in pairs(list) do + if (xPos + value.text:len() + space * 2 <= self.width) then + if (value == self:getValue()) then + self.parent:writeText(obx + (xPos - 1) + (-itemOffset), oby, getTextHorizontalAlign((" "):rep(space) .. value.text .. (" "):rep(space), value.text:len() + space * 2, align), itemSelectedBG or value.bgCol, itemSelectedFG or value.fgCol) + else + self.parent:writeText(obx + (xPos - 1) + (-itemOffset), oby, getTextHorizontalAlign((" "):rep(space) .. value.text .. (" "):rep(space), value.text:len() + space * 2, align), value.bgCol, value.fgCol) + end + xPos = xPos + value.text:len() + space * 2 + else + if (xPos < self.width + itemOffset) then + if (value == self:getValue()) then + self.parent:writeText(obx + (xPos - 1) + (-itemOffset), oby, getTextHorizontalAlign((" "):rep(space) .. value.text .. (" "):rep(space), value.text:len() + space * 2, align):sub(1, self.width + itemOffset - xPos), itemSelectedBG or value.bgCol, itemSelectedFG or value.fgCol) + else + self.parent:writeText(obx + (xPos - 1) + (-itemOffset), oby, getTextHorizontalAlign((" "):rep(space) .. value.text .. (" "):rep(space), value.text:len() + space * 2, align):sub(1, self.width + itemOffset - xPos), value.bgCol, value.fgCol) + end + xPos = xPos + value.text:len() + space * 2 + end + end + end + end + end + end; + } + + return setmetatable(object, base) +end +local function Pane(name) + -- Pane + local base = Object(name) + local objectType = "Pane" + + local object = { + getType = function(self) + return objectType + 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.width, self.height, self.bgColor) + self.parent:drawForegroundBox(obx, oby, self.width, self.height, self.bgColor) + end + end + end; + + } + + return setmetatable(object, base) +end +local function Program(name) + local base = Object(name) + local objectType = "Program" + base:setZIndex(5) + local object + + local function createBasaltWindow(x, y, width, height) + 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(width) + for n = 0, 15 do + local nColor = 2 ^ n + local sHex = tHex[nColor] + emptyColorLines[nColor] = sHex:rep(width) + end + end + + local function recreateWindowArray() + createEmptyLines() + local emptyText = emptySpaceLine + local emptyFG = emptyColorLines[colors.white] + local emptyBG = emptyColorLines[colors.black] + for n = 1, height do + cacheT[n] = sub(cacheT[n] == nil and emptyText or cacheT[n] .. emptyText:sub(1, width - cacheT[n]:len()), 1, width) + cacheFG[n] = sub(cacheFG[n] == nil and emptyFG or cacheFG[n] .. emptyFG:sub(1, width - cacheFG[n]:len()), 1, width) + cacheBG[n] = sub(cacheBG[n] == nil and emptyBG or cacheBG[n] .. emptyBG:sub(1, width - cacheBG[n]:len()), 1, width) + end + end + recreateWindowArray() + + local function updateCursor() + if xCursor >= 1 and yCursor >= 1 and xCursor <= width and yCursor <= height 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 <= height then + if nStart <= width and nEnd >= 1 then + -- Modify line + if nStart == 1 and nEnd == width 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 = width - nStart + 1 + sClippedText = sub(sText, nClipStart, nClipEnd) + sClippedTextColor = sub(sTextColor, nClipStart, nClipEnd) + sClippedBackgroundColor = sub(sBackgroundColor, nClipStart, nClipEnd) + elseif nEnd > width then + local nClipEnd = width - 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 < width then + local nOldStart = nEnd + 1 + sNewText = sNewText .. sub(sOldText, nOldStart, width) + sNewTextColor = sNewTextColor .. sub(sOldTextColor, nOldStart, width) + sNewBackgroundColor = sNewBackgroundColor .. sub(sOldBackgroundColor, nOldStart, width) + 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()), width), 1, width) + 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()), width), 1, width) + 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()), width), 1, width) + 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 width, height + end; + + getTextColor = function() + return fgColor + end; + getTextColour = function() + return fgColor + end; + + basalt_resize = function(_width, _height) + width, height = _width, _height + recreateWindowArray() + end; + + basalt_reposition = function(_x, _y) + x, y = _x, _y + end; + + basalt_setVisible = function(vis) + visible = vis + end; + + drawBackgroundBox = function(_x, _y, _width, _height, bgCol) + for n = 1, _height do + setBG(_x, _y + (n - 1), tHex[bgCol]:rep(_width)) + end + end; + drawForegroundBox = function(_x, _y, _width, _height, fgCol) + for n = 1, _height do + setFG(_x, _y + (n - 1), tHex[fgCol]:rep(_width)) + end + end; + drawTextBox = function(_x, _y, _width, _height, symbol) + for n = 1, _height do + setText(_x, _y + (n - 1), symbol:rep(_width)) + 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, height 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, height do + local y = newY + offset + if y >= 1 and y <= height 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(width)) + setBG(1, yCursor, tHex[bgColor]:rep(width)) + setFG(1, yCursor, tHex[fgColor]:rep(width)) + end + if (visible) then + updateCursor() + end + end; + + clear = function() + for n = 1, height do + setText(1, n, (" "):rep(width)) + setBG(1, n, tHex[bgColor]:rep(width)) + setFG(1, n, tHex[fgColor]:rep(width)) + 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.width = 30 + base.height = 12 + local pWindow = createBasaltWindow(1, 1, base.width, base.height) + local curProcess + local paused = false + local queuedEvent = {} + + object = { + getType = function(self) + return objectType + 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, width, height) + base.setSize(self, width, height) + pWindow.basalt_resize(self.width, self.height) + 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 _, value in pairs(events) do + curProcess:resume(value.event, table.unpack(value.args)) + end + end + end + return self + end; + + mouseClickHandler = function(self, event, button, x, y) + if (base.mouseClickHandler(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(nil, nil, true)) + 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.width - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + self.height - 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.width - 1 and yCur + oby - 1 >= 1 and yCur + oby - 1 <= oby + self.height - 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.width, self.height, self.bgColor) + pWindow.basalt_update() + end + end + end; + + } + + return setmetatable(object, base) +end +local function Progressbar(name) + -- Checkbox + local base = Object(name) + local objectType = "Progressbar" + + local progress = 0 + + base:setZIndex(5) + base:setValue(false) + base.width = 25 + base.height = 1 + base.bgColor = theme.CheckboxBG + base.fgColor = theme.CheckboxFG + + local activeBarColor = colors.black + local activeBarSymbol = "" + local activeBarSymbolCol = colors.white + local bgBarSymbol = "" + local direction = 0 + + local object = { + + getType = function(self) + return objectType + end; + + setDirection = function(self, dir) + direction = dir + return self + end; + + setProgressBar = function(self, color, symbol, symbolcolor) + activeBarColor = color or activeBarColor + activeBarSymbol = symbol or activeBarSymbol + activeBarSymbolCol = symbolcolor or activeBarSymbolCol + return self + end; + + setBackgroundSymbol = function(self, symbol) + bgBarSymbol = symbol:sub(1, 1) + return self + end; + + setProgress = function(self, value) + if (value >= 0) and (value <= 100) and (progress ~= value) then + progress = value + self:setValue(progress) + if (progress == 100) then + self:progressDoneHandler() + end + end + return self + end; + + getProgress = function(self) + return progress + end; + + onProgressDone = function(self, f) + self:registerEvent("progress_done", f) + return self + end; + + progressDoneHandler = function(self) + self:sendEvent("progress_done") + 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.width, self.height, self.bgColor) + self.parent:drawForegroundBox(obx, oby, self.width, self.height, self.fgColor) + self.parent:drawTextBox(obx, oby, self.width, self.height, bgBarSymbol) + if (direction == 1) then + self.parent:drawBackgroundBox(obx, oby, self.width, self.height / 100 * progress, activeBarColor) + self.parent:drawForegroundBox(obx, oby, self.width, self.height / 100 * progress, activeBarSymbolCol) + self.parent:drawTextBox(obx, oby, self.width, self.height / 100 * progress, activeBarSymbol) + elseif (direction == 2) then + self.parent:drawBackgroundBox(obx, oby + math.ceil(self.height - self.height / 100 * progress), self.width, self.height / 100 * progress, activeBarColor) + self.parent:drawForegroundBox(obx, oby + math.ceil(self.height - self.height / 100 * progress), self.width, self.height / 100 * progress, activeBarSymbolCol) + self.parent:drawTextBox(obx, oby + math.ceil(self.height - self.height / 100 * progress), self.width, self.height / 100 * progress, activeBarSymbol) + elseif (direction == 3) then + self.parent:drawBackgroundBox(obx + math.ceil(self.width - self.width / 100 * progress), oby, self.width / 100 * progress, self.height, activeBarColor) + self.parent:drawForegroundBox(obx + math.ceil(self.width - self.width / 100 * progress), oby, self.width / 100 * progress, self.height, activeBarSymbolCol) + self.parent:drawTextBox(obx + math.ceil(self.width - self.width / 100 * progress), oby, self.width / 100 * progress, self.height, activeBarSymbol) + else + self.parent:drawBackgroundBox(obx, oby, self.width / 100 * progress, self.height, activeBarColor) + self.parent:drawForegroundBox(obx, oby, self.width / 100 * progress, self.height, activeBarSymbolCol) + self.parent:drawTextBox(obx, oby, self.width / 100 * progress, self.height, activeBarSymbol) + end + end + end + end; + + } + + return setmetatable(object, base) +end +local function Radio(name) + local base = Object(name) + local objectType = "Radio" + base.width = 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 objectType + 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 key, value in pairs(list) do + if (value == selected) then + return key + 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; + + mouseClickHandler = 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 _, value in pairs(list) do + if (obx + value.x - 1 <= x) and (obx + value.x - 1 + value.text:len() + 2 >= x) and (oby + value.y - 1 == y) then + self:setValue(value) + 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 _, value in pairs(list) do + if (value == self:getValue()) then + if (align == "left") then + self.parent:writeText(value.x + obx - 1, value.y + oby - 1, symbol, boxSelectedBG, boxSelectedFG) + self.parent:writeText(value.x + 2 + obx - 1, value.y + oby - 1, value.text, itemSelectedBG, itemSelectedFG) + end + else + self.parent:drawBackgroundBox(value.x + obx - 1, value.y + oby - 1, 1, 1, self.bgColor) + self.parent:writeText(value.x + 2 + obx - 1, value.y + oby - 1, value.text, value.bgCol, value.fgCol) + end + end + end + end + end; + } + + return setmetatable(object, base) +end +local function Scrollbar(name) + local base = Object(name) + local objectType = "Scrollbar" + + base.width = 1 + base.height = 8 + base.bgColor = colors.lightGray + base.fgColor = colors.gray + base:setValue(1) + base:setZIndex(2) + + local barType = "vertical" + local symbol = " " + local symbolColor = colors.black + local bgSymbol = "\127" + local maxValue = base.height + local index = 1 + local symbolSize = 1 + + local object = { + getType = function(self) + return objectType + end; + + setSymbol = function(self, _symbol) + symbol = _symbol:sub(1, 1) + self:setVisualChanged() + return self + end; + + setSymbolSize = function(self, size) + symbolSize = tonumber(size) or 1 + if (barType == "vertical") then + self:setValue(index - 1 * (maxValue / (self.height - (symbolSize - 1))) - (maxValue / (self.height - (symbolSize - 1)))) + elseif (barType == "horizontal") then + self:setValue(index - 1 * (maxValue / (self.width - (symbolSize - 1))) - (maxValue / (self.width - (symbolSize - 1)))) + end + self:setVisualChanged() + return self + end; + + setMaxValue = function(self, val) + maxValue = val + 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; + + mouseClickHandler = function(self, event, button, x, y) + if (base.mouseClickHandler(self, event, button, x, y)) then + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if ((event == "mouse_click") or (event == "mouse_drag")) and (button == 1) then + if (barType == "horizontal") then + for _index = 0, self.width do + if (obx + _index == x) and (oby <= y) and (oby + self.height > y) then + index = math.min(_index + 1, self.width - (symbolSize - 1)) + self:setValue(maxValue / self.width * (index)) + self:setVisualChanged() + end + end + end + if (barType == "vertical") then + for _index = 0, self.height do + if (oby + _index == y) and (obx <= x) and (obx + self.width > x) then + index = math.min(_index + 1, self.height - (symbolSize - 1)) + self:setValue(maxValue / self.height * (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.height or self.width) - (symbolSize - 1)) + self:setValue(maxValue / (barType == "vertical" and self.height or self.width) * 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.width - (index + symbolSize - 1)), self.bgColor, self.fgColor) + end + + if (barType == "vertical") then + for n = 0, self.height - 1 do + + if (index == n + 1) then + for curIndexOffset = 0, math.min(symbolSize - 1, self.height) do + self.parent:writeText(obx, oby + n + curIndexOffset, symbol, symbolColor, symbolColor) + end + else + if (n + 1 < index) or (n + 1 > index - 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 Slider(name) + local base = Object(name) + local objectType = "Slider" + + base.width = 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.width + local index = 1 + + local object = { + getType = function(self) + return objectType + 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; + + mouseClickHandler = function(self, event, button, x, y) + if (base.mouseClickHandler(self, event, button, x, y)) then + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if (barType == "horizontal") then + for _index = 0, self.width - 1 do + if (obx + _index == x) and (oby <= y) and (oby + self.height > y) then + index = _index + 1 + self:setValue(maxValue / self.width * (index)) + self:setVisualChanged() + end + end + end + if (barType == "vertical") then + for _index = 0, self.height - 1 do + if (oby + _index == y) and (obx <= x) and (obx + self.width > x) then + index = _index + 1 + self:setValue(maxValue / self.height * (index)) + self:setVisualChanged() + end + end + end + --[[if(event=="mouse_scroll")then + self:setValue(self:getValue() + (maxValue/(barType=="vertical" and self.height or self.width))*typ) + self:setVisualChanged() + end + if(self:getValue()>maxValue)then self:setValue(maxValue) end + if(self:getValue() 1) then + table.remove(lines, textY) + textX = lines[textY - 1]:len() + 1 + wIndex = textX - self.width + 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.width + 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 < wIndex) then + wIndex = wIndex - 1 + end + end + end + if (textY < hIndex) then + hIndex = hIndex - 1 + end + self:setValue("") + end + + if (key == keys.delete) then + -- on delete + if (textX > lines[textY]:len()) then + if (lines[textY + 1] ~= nil) then + lines[textY] = lines[textY] .. lines[textY + 1] + table.remove(lines, textY + 1) + end + else + lines[textY] = lines[textY]:sub(1, textX - 1) .. lines[textY]:sub(textX + 1, lines[textY]:len()) + end + end + + if (key == keys.enter) then + -- on enter + table.insert(lines, textY + 1, lines[textY]:sub(textX, lines[textY]:len())) + lines[textY] = lines[textY]:sub(1, textX - 1) + textY = textY + 1 + textX = 1 + wIndex = 1 + if (textY - hIndex >= self.height) then + hIndex = hIndex + 1 + end + self:setValue("") + end + + if (key == keys.up) 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 (textX < wIndex) then + wIndex = textX - self.width + 1 + if (wIndex < 1) then + wIndex = 1 + end + end + end + if (hIndex > 1) then + if (textY < hIndex) then + hIndex = hIndex - 1 + end + end + end + end + if (key == keys.down) then + -- arrow down + if (textY < #lines) then + textY = textY + 1 + if (textX > lines[textY]:len() + 1) then + textX = lines[textY]:len() + 1 + end + + if (textY >= hIndex + self.height) then + hIndex = hIndex + 1 + end + end + end + if (key == keys.right) 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 < wIndex) or (textX >= self.width + wIndex) then + wIndex = textX - self.width + 1 + end + if (wIndex < 1) then + wIndex = 1 + end + + end + if (key == keys.left) then + -- arrow left + textX = textX - 1 + if (textX >= 1) then + if (textX < wIndex) or (textX >= self.width + 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.width + 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.width + 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.width - 1) then + cursorX = self.x + self.width - 1 + end + local cursorY = (textY - hIndex < self.height 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; + + mouseClickHandler = function(self, event, button, x, y) + if (base.mouseClickHandler(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 < wIndex) then + wIndex = textX - 1 + if (wIndex < 1) then + wIndex = 1 + end + end + if (self.parent ~= nil) then + self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex) + end + end + end + if (event == "mouse_drag") 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 < wIndex) then + wIndex = textX - 1 + if (wIndex < 1) then + wIndex = 1 + end + end + if (self.parent ~= nil) then + self.parent:setCursor(true, anchx + textX - wIndex, anchy + textY - hIndex) + end + end + end + + if (event == "mouse_scroll") then + hIndex = hIndex + button + if (hIndex > #lines - (self.height - 1)) then + hIndex = #lines - (self.height - 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.width) and (oby + textY - hIndex >= oby and oby + textY - hIndex <= oby + self.height) 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.width, self.height, self.bgColor) + self.parent:drawForegroundBox(obx, oby, self.width, self.height, self.fgColor) + for n = 1, self.height do + local text = "" + if (lines[n + hIndex - 1] ~= nil) then + text = lines[n + hIndex - 1] + end + text = text:sub(wIndex, self.width + wIndex - 1) + local space = self.width - 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 Thread(name) + local object + local objectType = "Thread" + + local func + local cRoutine + local isActive = false + + object = { + name = name, + getType = function(self) + return objectType + end; + getZIndex = function(self) + return 1 + end; + getName = function(self) + return self.name + end; + + start = function(self, f) + if (f == nil) then + error("Function provided to thread 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("Thread Error Occurred - " .. 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("Thread Error Occurred - " .. result) + end + end + else + isActive = false + end + end + end; + + } + + object.__index = object + + return object +end +local function Timer(name) + local objectType = "Timer" + + local timer = 0 + local savedRepeats = 0 + local repeats = 0 + local timerObj + local eventSystem = BasaltEvents() + + local object = { + name = name, + getType = function(self) + return objectType + 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 Frame(name, parent) + -- Frame + local base = Object(name) + local objectType = "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.width, base.height = parent.w, parent.h + base.bgColor = theme.FrameBG + base.fgColor = theme.FrameFG + else + local termW, termH = parentTerminal.getSize() + base.width, base.height = termW, termH + base.bgColor = theme.basaltBG + base.fgColor = theme.basaltFG + end + + local function getObject(name) + for _, value in pairs(objects) do + for _, b in pairs(value) do + if (b.name == name) then + return value + 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 key, value in pairs(b) do + if (value == obj) then + table.remove(objects[a], key) + return true; + end + end + end + return false + end + + object = { + barActive = false, + barBackground = colors.gray, + barTextcolor = colors.black, + barText = "New Frame", + barTextAlign = "left", + isMoveable = false, + + getType = function(self) + return objectType + end; + + setFocusedObject = function(self, obj) + for _, index in pairs(objZIndex) do + for _, value in pairs(objects[index]) do + if (value == 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 _, value in pairs(objects[index]) do + if (value.getVisualChanged ~= nil and value: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 _, value in pairs(objects[index]) do + if (value.backgroundKeyHandler ~= nil) then + value: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 _, value in pairs(objects[index]) do + if (value.eventHandler ~= nil) then + value:eventHandler(event, p1, p2, p3, p4) + end + end + end + end + if (event == "terminate") then + parentTerminal.clear() + parentTerminal.setCursorPos(1, 1) + basalt.stop() + end + end; + + mouseClickHandler = 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.mouseClickHandler(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 _, value in rpairs(objects[index]) do + if (value.mouseClickHandler ~= nil) then + if (value:mouseClickHandler(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.width - 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.height) 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.width - x + 1)) + else + drawHelper.setText(math.max(x + (obx - 1), obx), oby + y - 1, sub(text, math.max(1 - x + 1, 1), self.width - x + 1)) + end + end + end; + + setBG = function(self, x, y, bgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if (y >= 1) and (y <= self.height) 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.width - x + 1)) + else + drawHelper.setBG(math.max(x + (obx - 1), obx), oby + y - 1, sub(bgCol, math.max(1 - x + 1, 1), self.width - x + 1)) + end + end + end; + + setFG = function(self, x, y, fgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + if (y >= 1) and (y <= self.height) 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.width - x + 1)) + else + drawHelper.setFG(math.max(x + (obx - 1), obx), oby + y - 1, sub(fgCol, math.max(1 - x + 1, 1), self.width - 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.height) 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.width - 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.width - x + 1), bgCol, fgCol) + end + end + end; + + drawBackgroundBox = function(self, x, y, width, height, bgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + height = (y < 1 and (height + y > self.height and self.height or height + y - 1) or (height + y > self.height and self.height - y + 1 or height)) + width = (x < 1 and (width + x > self.width and self.width or width + x - 1) or (width + x > self.width and self.width - x + 1 or width)) + 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), width, height, bgCol) + else + drawHelper.drawBackgroundBox(math.max(x + (obx - 1), obx), math.max(y + (oby - 1), oby), width, height, bgCol) + end + end; + + drawTextBox = function(self, x, y, width, height, symbol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + height = (y < 1 and (height + y > self.height and self.height or height + y - 1) or (height + y > self.height and self.height - y + 1 or height)) + width = (x < 1 and (width + x > self.width and self.width or width + x - 1) or (width + x > self.width and self.width - x + 1 or width)) + 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), width, height, symbol:sub(1, 1)) + else + drawHelper.drawTextBox(math.max(x + (obx - 1), obx), math.max(y + (oby - 1), oby), width, height, symbol:sub(1, 1)) + end + end; + + drawForegroundBox = function(self, x, y, width, height, fgCol) + local obx, oby = self:getAbsolutePosition(self:getAnchorPosition()) + height = (y < 1 and (height + y > self.height and self.height or height + y - 1) or (height + y > self.height and self.height - y + 1 or height)) + width = (x < 1 and (width + x > self.width and self.width or width + x - 1) or (width + x > self.width and self.width - x + 1 or width)) + 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), width, height, fgCol) + else + drawHelper.drawForegroundBox(math.max(x + (obx - 1), obx), math.max(y + (oby - 1), oby), width, height, 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.width, self.height, self.bgColor) + self.parent:drawForegroundBox(anchx, anchy, self.width, self.height, self.fgColor) + self.parent:drawTextBox(anchx, anchy, self.width, self.height, " ") + else + drawHelper.drawBackgroundBox(obx, oby, self.width, self.height, self.bgColor) + drawHelper.drawForegroundBox(obx, oby, self.width, self.height, self.fgColor) + drawHelper.drawTextBox(obx, oby, self.width, self.height, " ") + end + parentTerminal.setCursorBlink(false) + if (self.barActive) then + if (self.parent ~= nil) then + self.parent:writeText(anchx, anchy, getTextHorizontalAlign(self.barText, self.width, self.barTextAlign), self.barBackground, self.barTextcolor) + else + drawHelper.writeText(obx, oby, getTextHorizontalAlign(self.barText, self.width, self.barTextAlign), self.barBackground, self.barTextcolor) + end + end + + for _, index in rpairs(objZIndex) do + if (objects[index] ~= nil) then + for _, value in pairs(objects[index]) do + if (value.draw ~= nil) then + value: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; + + getObject = function(self, obj) + return getObject(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; + + addProgressbar = function(self, name) + local obj = Progressbar(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:mouseClickHandler(event, p1, p2, p3, p4) + end + if (event == "mouse_drag") then + activeFrame:mouseClickHandler(event, p1, p2, p3, p4) + end + if (event == "mouse_up") then + activeFrame:mouseClickHandler(event, p1, p2, p3, p4) + end + if (event == "mouse_scroll") then + activeFrame:mouseClickHandler(event, p1, p2, p3, p4) + end + if (event == "key") or (event == "char") then + activeFrame:keyHandler(event, p1) + activeFrame:backgroundKeyHandler(event, p1) + end + for _, value in pairs(frames) do + value: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 _, value in pairs(frames) do + if (value.name == name) then + return value + 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 key, value in pairs(frames) do + if (value.name == name) then + frames[key] = 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.width - 2, basalt.debugFrame.height - 3):setPosition(2, 3):setScrollable(true):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"):setZIndex(20):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 key, value in pairs(args) do + str = str .. tostring(value) .. (#args ~= key and ", " or "") + end + basalt.debugLabel:setText("[Debug] " .. str) + basalt.debugList:addItem(str) + if (basalt.debugList:getItemCount() > 50) then + basalt.debugList:removeItem(1) + end + basalt.debugList:setValue(basalt.debugList:getItem(basalt.debugList:getItemCount())) + basalt.debugLabel:show() + end +end + +return basalt diff --git a/basalt.lua b/basalt.lua index 20f9126..31ecf44 100644 --- a/basalt.lua +++ b/basalt.lua @@ -1 +1 @@ -local a={debugger=true,version=1}local b;local c={}local d={}local e=term.current()local f=string.sub;local g={[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 h={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 function i()local j=e;local k,l=j.getSize()local m={}local n={}local o={}local p={}local q={}local r={}local s;local t={}local function u()s=(" "):rep(k)for v=0,15 do local w=2^v;local x=g[w]t[w]=x:rep(k)end end;u()local function y()local z=s;local A=t[colors.white]local B=t[colors.black]for C=1,l do m[C]=f(m[C]==nil and z or m[C]..z:sub(1,k-m[C]:len()),1,k)o[C]=f(o[C]==nil and A or o[C]..A:sub(1,k-o[C]:len()),1,k)n[C]=f(n[C]==nil and B or n[C]..B:sub(1,k-n[C]:len()),1,k)end end;y()local function D(E,F,G)if F>=1 and F<=l then if E+G:len()>0 and E<=k then local H=m[F]local I;local J=E+#G-1;if E<1 then local K=1-E+1;local L=k-E+1;G=f(G,K,L)elseif J>k then local L=k-E+1;G=f(G,1,L)end;if E>1 then local L=E-1;I=f(H,1,L)..G else I=G end;if J=1 and F<=l then if E+N:len()>0 and E<=k then local H=n[F]local I;local J=E+#N-1;if E<1 then N=f(N,1-E+1,k-E+1)elseif J>k then N=f(N,1,k-E+1)end;if E>1 then I=f(H,1,E-1)..N else I=N end;if J=1 and F<=l then if E+N:len()>0 and E<=k then local H=o[F]local I;local J=E+#N-1;if E<1 then local K=1-E+1;local L=k-E+1;N=f(N,K,L)elseif J>k then local L=k-E+1;N=f(N,1,L)end;if E>1 then local L=E-1;I=f(H,1,L)..N else I=N end;if JE and aM<=F and aM+self.height>F and as then if self.parent~=nil then self.parent:setFocusedObject(self)end;au:sendEvent(_,self,_,aK,E,F)return true end;return false end,keyHandler=function(self,_,aN)if self:isFocused()then au:sendEvent(_,self,_,aN)return true end;return false end,backgroundKeyHandler=function(self,_,aN)au:sendEvent("background_".._,self,_,aN)end,valueChangedHandler=function(self)au:sendEvent("value_changed",self)end,eventHandler=function(self,_,aO,aP,aQ,aR)au:sendEvent("custom_event_handler",self,_,aO,aP,aQ,aR)end,getFocusHandler=function(self)au:sendEvent("get_focus",self)end,loseFocusHandler=function(self)au:sendEvent("lose_focus",self)end}av.__index=av;return av end;local function aS(am)local av={}local an="Animation"local aT;local aU={}local Z=1;local aV=0;local aW;local function aX()if aU[Z]~=nil then aU[Z].f(av,Z)end;Z=Z+1;if aU[Z]~=nil then if aU[Z].t>0 then aT=os.startTimer(aU[Z].t)else aX()end end end;av={name=am,getType=function(self)return an end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,add=function(self,a1,aY)aW=a1;table.insert(aU,{f=a1,t=aY or aV})return self end,wait=function(self,aY)aV=aY;return self end,rep=function(self,aZ)for E=1,aZ do table.insert(aU,{f=aW,t=aV})end;return self end,clear=function(self)aU={}aW=nil;aV=0;Z=1;return self end,play=function(self)Z=1;if aU[Z]~=nil then if aU[Z].t>0 then aT=os.startTimer(aU[Z].t)else aX()end end;return self end,cancel=function(self)os.cancelTimer(aT)return self end,eventHandler=function(self,_,a_)if _=="timer"and a_==aT then if aU[Z]~=nil then aX()end end end}av.__index=av;return av end;local function b0(am)local b1=al(am)local an="Button"b1:setValue("Button")b1:setZIndex(5)b1.width=8;b1.bgColor=h.ButtonBG;b1.fgColor=h.ButtonFG;local b2="center"local b3="center"local av={getType=function(self)return an end,setHorizontalAlign=function(self,b4)b2=b4 end,setVerticalAlign=function(self,b4)b3=b4 end,setText=function(self,G)b1:setValue(G)return self end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()local b7=ag(self.height,b3)self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(b5,b6,self.width,self.height,self.fgColor)self.parent:drawTextBox(b5,b6,self.width,self.height," ")for v=1,self.height do if v==b7 then self.parent:setText(b5,b6+v-1,ad(self:getValue(),self.width,b2))end end end end end}return setmetatable(av,b1)end;local function b8(am)local b1=al(am)local an="Checkbox"b1:setZIndex(5)b1:setValue(false)b1.width=1;b1.height=1;b1.bgColor=h.CheckboxBG;b1.fgColor=h.CheckboxFG;local av={symbol="\42",getType=function(self)return an end,mouseClickHandler=function(self,_,aK,E,F)if b1.mouseClickHandler(self,_,aK,E,F)then if _=="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 b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()local b7=ag(self.height,"center")self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)for v=1,self.height do if v==b7 then if self:getValue()==true then self.parent:writeText(b5,b6+v-1,ad(self.symbol,self.width,"center"),self.bgColor,self.fgColor)else self.parent:writeText(b5,b6+v-1,ad(" ",self.width,"center"),self.bgColor,self.fgColor)end end end end end end}return setmetatable(av,b1)end;local function b9(am)local b1=al(am)local an="Dropdown"b1.width=12;b1.height=1;b1.bgColor=h.dropdownBG;b1.fgColor=h.dropdownFG;b1:setZIndex(6)local ba={}local bb=h.selectionBG;local bc=h.selectionFG;local bd=true;local be="left"local bf=0;local bg=16;local bh=6;local bi="\16"local bj="\31"local bk=1;local av={getType=function(self)return an end,setIndexOffset=function(self,bl)bf=bl;return self end,getIndexOffset=function(self)return bf end,addItem=function(self,G,Q,R,...)table.insert(ba,{text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})return self end,removeItem=function(self,Z)table.remove(ba,Z)return self end,getItem=function(self,Z)return ba[Z]end,getItemIndex=function(self)local bm=self:getValue()for aN,a3 in pairs(ba)do if a3==bm then return aN end end end,clear=function(self)ba={}self:setValue({})return self end,getItemCount=function(self)return#ba end,editItem=function(self,Z,G,Q,R,...)table.remove(ba,Z)table.insert(ba,Z,{text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})return self end,selectItem=function(self,Z)self:setValue(ba[Z]or{})return self end,setSelectedItem=function(self,Q,R,bn)bb=Q or self.bgColor;bc=R or self.fgColor;bd=bn;return self end,setDropdownSize=function(self,k,l)bg,bh=k,l;return self end,mouseClickHandler=function(self,_,aK,E,F)if bk==2 then local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if _=="mouse_click"then if aK==1 then if#ba>0 then for v=1,bh do if ba[v+bf]~=nil then if b5<=E and b5+bg>E and b6+v==F then self:setValue(ba[v+bf])return true end end end end end end;if _=="mouse_scroll"then bf=bf+aK;if bf<0 then bf=0 end;if aK==1 then if#ba>bh then if bf>#ba-bh then bf=#ba-bh end else bf=ba-1 end end;return true end;self:setVisualChanged()end;if b1.mouseClickHandler(self,_,aK,E,F)then bk=2 else bk=1 end end,draw=function(self)if b1.draw(self)then local b5,b6=self:getAnchorPosition()if self.parent~=nil then self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)if#ba>=1 then if self:getValue()~=nil then if self:getValue().text~=nil then if bk==1 then self.parent:writeText(b5,b6,ad(self:getValue().text,self.width,be):sub(1,self.width-1)..bi,self.bgColor,self.fgColor)else self.parent:writeText(b5,b6,ad(self:getValue().text,self.width,be):sub(1,self.width-1)..bj,self.bgColor,self.fgColor)end end end;if bk==2 then for v=1,bh do if ba[v+bf]~=nil then if ba[v+bf]==self:getValue()then if bd then self.parent:writeText(b5,b6+v,ad(ba[v+bf].text,bg,be),bb,bc)else self.parent:writeText(b5,b6+v,ad(ba[v+bf].text,bg,be),ba[v+bf].bgCol,ba[v+bf].fgCol)end else self.parent:writeText(b5,b6+v,ad(ba[v+bf].text,bg,be),ba[v+bf].bgCol,ba[v+bf].fgCol)end end end end end end end end}return setmetatable(av,b1)end;local function bo(am)local b1=al(am)local an="Image"b1:setZIndex(2)local bp;local bq;local br=false;local function bs()local bt={[0]={8,4,3,6,5},{4,14,8,7},{6,10,8,7},{9,11,8,0},{1,14,8,0},{13,12,8,0},{2,10,8,0},{15,8,10,11,12,14},{0,7,1,9,2,13},{3,11,8,7},{2,6,7,15},{9,3,7,15},{13,5,7,15},{5,12,8,7},{1,4,7,15},{7,10,11,12,14}}local bu,bv,bw={},{},{}for ak=0,15 do bv[2^ak]=ak end;do local bx="0123456789abcdef"for ak=1,16 do bu[bx:sub(ak,ak)]=ak-1;bu[ak-1]=bx:sub(ak,ak)bw[bx:sub(ak,ak)]=2^(ak-1)bw[2^(ak-1)]=bx:sub(ak,ak)local by=bt[ak-1]for ak=1,#by do by[ak]=2^by[ak]end end end;local function bz(bA)local bB=bt[bv[bA[#bA][1]]]for bC=1,#bB do local bD=bB[bC]for ak=1,#bA-1 do if bA[ak][1]==bD then return ak end end end;return 1 end;local function bE(bF,bG)if not bG then local bH={}bG={}for ak=1,6 do local bI=bF[ak]local bJ=bG[bI]bG[bI],bH[ak]=bJ and bJ+1 or 1,bI end;bF=bH end;local bA={}for aN,a3 in pairs(bG)do bA[#bA+1]={aN,a3}end;if#bA>1 then while#bA>2 do table.sort(bA,function(bK,bL)return bK[2]>bL[2]end)local bM,bN=bz(bA),#bA;local bO,bP=bA[bN][1],bA[bM][1]for ak=1,6 do if bF[ak]==bO then bF[ak]=bP;bA[bM][2]=bA[bM][2]+1 end end;bA[bN]=nil end;local bQ=128;for ak=1,#bF-1 do if bF[ak]~=bF[6]then bQ=bQ+2^(ak-1)end end;return string.char(bQ),bw[bA[1][1]==bF[6]and bA[2][1]or bA[1][1]],bw[bF[6]]else return"\128",bw[bF[1]],bw[bF[1]]end end;local bR,k,l,Q={{},{},{}},0,#bp+#bp%3,b1.bgColor or colors.black;for ak=1,#bp do if#bp[ak]>k then k=#bp[ak]end end;for F=0,l-1,3 do local bS,bT,bU,bV={},{},{},1;for E=0,k-1,2 do local bF,bG={},{}for bW=1,3 do for bX=1,2 do bF[#bF+1]=bp[F+bW]and bp[F+bW][E+bX]and(bp[F+bW][E+bX]==0 and Q or bp[F+bW][E+bX])or Q;bG[bF[#bF]]=bG[bF[#bF]]and bG[bF[#bF]]+1 or 1 end end;bS[bV],bT[bV],bU[bV]=bE(bF,bG)bV=bV+1 end;bR[1][#bR[1]+1],bR[2][#bR[2]+1],bR[3][#bR[3]+1]=table.concat(bS),table.concat(bT),table.concat(bU)end;bR.width,bR.height=#bR[1][1],#bR[1]bq=bR end;local av={getType=function(self)return an end,loadImage=function(self,a7)bp=paintutils.loadImage(a7)br=false;return self end,loadBlittleImage=function(self,a7)br=true;return self end,shrink=function(self)bs()br=true;return self end,draw=function(self)if b1.draw(self)then if self.parent~=nil then if bp~=nil then local b5,b6=self:getAnchorPosition()if br then local aj,bY,bZ=bq[1],bq[2],bq[3]for ak=1,bq.height do local b_=aj[ak]if type(b_)=="string"then self.parent:setText(b5,b6+ak-1,b_)self.parent:setFG(b5,b6+ak-1,bY[ak])self.parent:setBG(b5,b6+ak-1,bZ[ak])elseif type(b_)=="table"then self.parent:setText(b5,b6+ak-1,b_[2])self.parent:setFG(b5,b6+ak-1,bY[ak])self.parent:setBG(b5,b6+ak-1,bZ[ak])end end else for aA=1,math.min(#bp,self.height)do local c0=bp[aA]for az=1,math.min(#c0,self.width)do if c0[az]>0 then self.parent:drawBackgroundBox(b5+az-1,b6+aA-1,1,1,c0[az])end end end end end end end end}return setmetatable(av,b1)end;local function c1(am)local b1=al(am)local an="Input"local c2="text"local c3=0;b1:setZIndex(5)b1:setValue("")b1.width=10;b1.height=1;b1.bgColor=h.InputBG;b1.fgColor=h.InputFG;local c4=1;local c5=1;local c6=""local c7;local c8;local c9=c6;local ca=false;local av={getType=function(self)return an end,setInputType=function(self,cb)if cb=="password"or cb=="number"or cb=="text"then c2=cb end;return self end,setDefaultText=function(self,G,cc,cd)c6=G;c7=cd or c7;c8=cc or c8;if self:isFocused()then c9=""else c9=c6 end;return self end,getInputType=function(self)return c2 end,setValue=function(self,ce)b1.setValue(self,tostring(ce))if not ca then c4=tostring(ce):len()+1 end;return self end,getValue=function(self)local ce=b1.getValue(self)return c2=="number"and tonumber(ce)or ce end,setInputLimit=function(self,cf)c3=tonumber(cf)or c3;return self end,getInputLimit=function(self)return c3 end,getFocusHandler=function(self)b1.getFocusHandler(self)if self.parent~=nil then local b5,b6=self:getAnchorPosition()c9=""if self.parent~=nil then self.parent:setCursor(true,b5+c4-c5,b6,self.fgColor)end end end,loseFocusHandler=function(self)b1.loseFocusHandler(self)if self.parent~=nil then self.parent:setCursor(false)c9=c6 end end,keyHandler=function(self,_,aN)if b1.keyHandler(self,_,aN)then ca=true;if _=="key"then if aN==keys.backspace then local G=tostring(b1.getValue())if c4>1 then self:setValue(G:sub(1,c4-2)..G:sub(c4,G:len()))if c4>1 then c4=c4-1 end;if c5>1 then if c4cg then c4=cg+1 end;if c4<1 then c4=1 end;if c4=self.width+c5 then c5=c4-self.width+1 end;if c5<1 then c5=1 end end;if aN==keys.left then c4=c4-1;if c4>=1 then if c4=self.width+c5 then c5=c4 end end;if c4<1 then c4=1 end;if c5<1 then c5=1 end end end;if _=="char"then local G=b1.getValue()if G:len()=self.width+c5 then c5=c5+1 end end end;local b5,b6=self:getAnchorPosition()local ce=tostring(b1.getValue())local ci=(c4<=ce:len()and c4-1 or ce:len())-(c5-1)if ci>self.x+self.width-1 then ci=self.x+self.width-1 end;if self.parent~=nil then self.parent:setCursor(true,b5+ci,b6,self.fgColor)end;ca=false end end,mouseClickHandler=function(self,_,aK,E,F)if b1.mouseClickHandler(self,_,aK,E,F)then if _=="mouse_click"and aK==1 then end;return true end;return false end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()local b7=ag(self.height,"center")self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)for v=1,self.height do if v==b7 then local ce=tostring(b1.getValue())local cd=self.bgColor;local cc=self.fgColor;local G;if ce:len()<=0 then G=c9;cd=c7 or cd;cc=c8 or cc end;G=c9;if ce~=""then G=ce end;G=G:sub(c5,self.width+c5-1)local cj=self.width-G:len()if cj<0 then cj=0 end;if c2=="password"and ce~=""then G=string.rep("*",G:len())end;G=G..string.rep(" ",cj)self.parent:writeText(b5,b6+v-1,G,cd,cc)end end end end end}return setmetatable(av,b1)end;local function ck(am)local b1=al(am)local an="Label"b1:setZIndex(3)local cl=true;b1:setValue("")local b2="left"local b3="top"local av={getType=function(self)return an end,setText=function(self,G)G=tostring(G)b1:setValue(G)if cl then self.width=G:len()end;return self end,setTextAlign=function(self,cm,cn)b2=cm or b2;b3=cn or b3;self:setVisualChanged()return self end,setSize=function(self,k,l)b1.setSize(self,k,l)cl=false;self:setVisualChanged()return self end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()local b7=ag(self.height,b3)self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(b5,b6,self.width,self.height,self.fgColor)self.parent:drawTextBox(b5,b6,self.width,self.height," ")for v=1,self.height do if v==b7 then self.parent:writeText(b5,b6+v-1,ad(self:getValue(),self.width,b2),self.bgColor,self.fgColor)end end end end end}return setmetatable(av,b1)end;local function co(am)local b1=al(am)local an="List"b1.width=16;b1.height=6;b1.bgColor=h.listBG;b1.fgColor=h.listFG;b1:setZIndex(5)local ba={}local bb=h.selectionBG;local bc=h.selectionFG;local bd=true;local be="left"local bf=0;local cp=true;local av={getType=function(self)return an end,addItem=function(self,G,Q,R,...)table.insert(ba,{text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})if#ba==1 then self:setValue(ba[1])end;return self end,setIndexOffset=function(self,bl)bf=bl;return self end,getIndexOffset=function(self)return bf end,removeItem=function(self,Z)table.remove(ba,Z)return self end,getItem=function(self,Z)return ba[Z]end,getItemIndex=function(self)local bm=self:getValue()for aN,a3 in pairs(ba)do if a3==bm then return aN end end end,clear=function(self)ba={}self:setValue({})return self end,getItemCount=function(self)return#ba end,editItem=function(self,Z,G,Q,R,...)table.remove(ba,Z)table.insert(ba,Z,{text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})return self end,selectItem=function(self,Z)self:setValue(ba[Z]or{})return self end,setSelectedItem=function(self,Q,R,bn)bb=Q or self.bgColor;bc=R or self.fgColor;bd=bn;return self end,setScrollable=function(self,cq)cp=cq;return self end,mouseClickHandler=function(self,_,aK,E,F)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if b5<=E and b5+self.width>E and b6<=F and b6+self.height>F and self:isVisible()then if _=="mouse_click"or _=="mouse_drag"then if aK==1 then if#ba>0 then for v=1,self.height do if ba[v+bf]~=nil then if b5<=E and b5+self.width>E and b6+v-1==F then self:setValue(ba[v+bf])self:getEventSystem():sendEvent("mouse_click",self,"mouse_click",0,E,F,ba[v+bf])end end end end end end;if _=="mouse_scroll"and cp then bf=bf+aK;if bf<0 then bf=0 end;if aK>=1 then if#ba>self.height then if bf>#ba-self.height then bf=#ba-self.height end;if bf>=#ba then bf=#ba-1 end else bf=bf-1 end end end;self:setVisualChanged()return true end end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)for v=1,self.height do if ba[v+bf]~=nil then if ba[v+bf]==self:getValue()then if bd then self.parent:writeText(b5,b6+v-1,ad(ba[v+bf].text,self.width,be),bb,bc)else self.parent:writeText(b5,b6+v-1,ad(ba[v+bf].text,self.width,be),ba[v+bf].bgCol,ba[v+bf].fgCol)end else self.parent:writeText(b5,b6+v-1,ad(ba[v+bf].text,self.width,be),ba[v+bf].bgCol,ba[v+bf].fgCol)end end end end end end}return setmetatable(av,b1)end;local function cr(am)local b1=al(am)local an="Menubar"local av={}b1.width=30;b1.height=1;b1.bgColor=colors.gray;b1.fgColor=colors.lightGray;b1:setZIndex(5)local ba={}local bb=h.selectionBG;local bc=h.selectionFG;local bd=true;local be="left"local cs=0;local cj=2;local cp=false;local function ct()local cu=0;local az=1;for v=1,#ba do if az+ba[v].text:len()+cj*2>av.w then cu=cu+ba[v].text:len()+cj*2 end;az=az+ba[v].text:len()+cj*2 end;return cu end;av={getType=function(self)return an end,addItem=function(self,G,Q,R,...)table.insert(ba,{text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})if#ba==1 then self:setValue(ba[1])end;return self end,getItemIndex=function(self)local bm=self:getValue()for aN,a3 in pairs(ba)do if a3==bm then return aN end end end,clear=function(self)ba={}self:setValue({})return self end,setSpace=function(self,cv)cj=cv or cj;return self end,setButtonOffset=function(self,af)cs=af or 0;if cs<0 then cs=0 end;local cu=ct()if cs>cu then cs=cu end;return self end,setScrollable=function(self,cq)cp=cq;return self end,removeItem=function(self,Z)table.remove(ba,Z)return self end,getItem=function(self,Z)return ba[Z]end,getItemCount=function(self)return#ba end,editItem=function(self,Z,G,Q,R,...)table.remove(ba,Z)table.insert(ba,Z,{text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})return self end,selectItem=function(self,Z)self:setValue(ba[Z]or{})return self end,setSelectedItem=function(self,Q,R,bn)bb=Q or self.bgColor;bc=R or self.fgColor;bd=bn;return self end,mouseClickHandler=function(self,_,aK,E,F)local aL,aM=self:getAbsolutePosition(self:getAnchorPosition())if aL<=E and aL+self.width>E and aM<=F and aM+self.height>F and self:isVisible()then if self.parent~=nil then self.parent:setFocusedObject(self)end;if _=="mouse_click"then local az=1;for v=1+cs,#ba do if ba[v]~=nil then if az+ba[v].text:len()+cj*2<=self.width then if aL+az-1<=E and aL+az-1+ba[v].text:len()+cj*2>E and aM==F then self:setValue(ba[v])self:getEventSystem():sendEvent("mouse_click",self,"mouse_click",0,E,F,ba[v])end;az=az+ba[v].text:len()+cj*2 else break end end end end;if _=="mouse_scroll"and cp then cs=cs+aK;if cs<0 then cs=0 end;local cu=ct()if cs>cu then cs=cu end end;return true end;return false end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)local az=0;for a2,a3 in pairs(ba)do if az+a3.text:len()+cj*2<=self.width then if a3==self:getValue()then self.parent:writeText(b5+az-1+-cs,b6,ad((" "):rep(cj)..a3.text..(" "):rep(cj),a3.text:len()+cj*2,be),bb or a3.bgCol,bc or a3.fgCol)else self.parent:writeText(b5+az-1+-cs,b6,ad((" "):rep(cj)..a3.text..(" "):rep(cj),a3.text:len()+cj*2,be),a3.bgCol,a3.fgCol)end;az=az+a3.text:len()+cj*2 else if az=1 and cA>=1 and cz<=k and cA<=l then else end end;local function cI(cJ,cK,cL)local cM=cz;local J=cM+#cJ-1;if cA>=1 and cA<=l then if cM<=k and J>=1 then if cM==1 and J==k then m[cA]=cJ;o[cA]=cK;n[cA]=cL else local cN,cO,cP;if cM<1 then local cQ=1-cM+1;local cR=k-cM+1;cN=f(cJ,cQ,cR)cO=f(cK,cQ,cR)cP=f(cL,cQ,cR)elseif J>k then local cR=k-cM+1;cN=f(cJ,1,cR)cO=f(cK,1,cR)cP=f(cL,1,cR)else cN=cJ;cO=cK;cP=cL end;local cS=m[cA]local cT=o[cA]local cU=n[cA]local cV,cW,cX;if cM>1 then local cY=cM-1;cV=f(cS,1,cY)..cN;cW=f(cT,1,cY)..cO;cX=f(cU,1,cY)..cP else cV=cN;cW=cO;cX=cP end;if J=1 and F<=l then m[dk]=m[F]n[dk]=n[F]o[dk]=o[F]else m[dk]=dh;o[dk]=di;n[dk]=dj end end end;if cE then cH()end end,isColor=function()return e.isColor()end,isColour=function()return e.isColor()end,write=function(G)G=tostring(G)if cE then cI(G,g[cC]:rep(G:len()),g[cB]:rep(G:len()))end end,clearLine=function()if cE then D(1,cA,(" "):rep(k))M(1,cA,g[cB]:rep(k))O(1,cA,g[cC]:rep(k))end;if cE then cH()end end,clear=function()for v=1,l do D(1,v,(" "):rep(k))M(1,v,g[cB]:rep(k))O(1,v,g[cC]:rep(k))end;if cE then cH()end end,blit=function(G,dl,dm)if type(G)~="string"then error("bad argument #1 (expected string, got "..type(G)..")",2)end;if type(dl)~="string"then error("bad argument #2 (expected string, got "..type(dl)..")",2)end;if type(dm)~="string"then error("bad argument #3 (expected string, got "..type(dm)..")",2)end;if#dl~=#G or#dm~=#G then error("Arguments must be the same length",2)end;if cE then cI(G,dl,dm)end end}return dc end;b1.width=30;b1.height=12;local dn=cy(1,1,b1.width,b1.height)local dp;local dq=false;local dr={}av={getType=function(self)return an end,show=function(self)b1.show(self)dn.setBackgroundColor(self.bgColor)dn.setTextColor(self.fgColor)dn.basalt_setVisible(true)return self end,hide=function(self)b1.hide(self)dn.basalt_setVisible(false)return self end,setPosition=function(self,E,F,aB)b1.setPosition(self,E,F,aB)dn.basalt_reposition(self:getAnchorPosition())return self end,getBasaltWindow=function()return dn end,getBasaltProcess=function()return dp end,setSize=function(self,k,l)b1.setSize(self,k,l)dn.basalt_resize(self.width,self.height)return self end,getStatus=function(self)if dp~=nil then return dp:getStatus()end;return"inactive"end,execute=function(self,a7,...)dp=a5:new(a7,dn,...)dn.setBackgroundColor(colors.black)dn.setTextColor(colors.white)dn.clear()dn.setCursorPos(1,1)dp:resume()dq=false;return self end,stop=function(self)if dp~=nil then if not dp:isDead()then dp:resume("terminate")if dp:isDead()then if self.parent~=nil then self.parent:setCursor(false)end end end end;return self end,pause=function(self,ds)dq=ds or not dq;if dp~=nil then if not dp:isDead()then if not dq then self:injectEvents(dr)dr={}end end end;return self end,isPaused=function(self)return dq end,injectEvent=function(self,_,aO,aP,aQ,aR,dt)if dp~=nil then if not dp:isDead()then if dq==false or dt then dp:resume(_,aO,aP,aQ,aR)else table.insert(dr,{event=_,args={aO,aP,aQ,aR}})end end end;return self end,getQueuedEvents=function(self)return dr end,updateQueuedEvents=function(self,Y)dr=Y or dr;return self end,injectEvents=function(self,Y)if dp~=nil then if not dp:isDead()then for a2,a3 in pairs(Y)do dp:resume(a3.event,table.unpack(a3.args))end end end;return self end,mouseClickHandler=function(self,_,aK,E,F)if b1.mouseClickHandler(self,_,aK,E,F)then if dp==nil then return false end;if not dp:isDead()then if not dq then local du,dv=self:getAbsolutePosition(self:getAnchorPosition(nil,nil,true))dp:resume(_,aK,E-du+1,F-dv+1)end end;return true end end,keyHandler=function(self,_,aN)b1.keyHandler(self,_,aN)if self:isFocused()then if dp==nil then return false end;if not dp:isDead()then if not dq then if self.draw then dp:resume(_,aN)end end end end end,getFocusHandler=function(self)b1.getFocusHandler(self)if dp~=nil then if not dp:isDead()then if not dq then if self.parent~=nil then local dw,dx=dn.getCursorPos()local b5,b6=self:getAnchorPosition()if self.parent~=nil then if b5+dw-1>=1 and b5+dw-1<=b5+self.width-1 and dx+b6-1>=1 and dx+b6-1<=b6+self.height-1 then self.parent:setCursor(dn.getCursorBlink(),b5+dw-1,dx+b6-1,dn.getTextColor())end end end end end end end,loseFocusHandler=function(self)b1.loseFocusHandler(self)if dp~=nil then if not dp:isDead()then if self.parent~=nil then self.parent:setCursor(false)end end end end,eventHandler=function(self,_,aO,aP,aQ,aR)if dp==nil then return end;if not dp:isDead()then if not dq then if _~="mouse_click"and _~="mouse_up"and _~="mouse_scroll"and _~="mouse_drag"and _~="key_up"and _~="key"and _~="char"and _~="terminate"then dp:resume(_,aO,aP,aQ,aR)end;if self:isFocused()then local b5,b6=self:getAnchorPosition()local dw,dx=dn.getCursorPos()if self.parent~=nil then if b5+dw-1>=1 and b5+dw-1<=b5+self.width-1 and dx+b6-1>=1 and dx+b6-1<=b6+self.height-1 then self.parent:setCursor(dn.getCursorBlink(),b5+dw-1,dx+b6-1,dn.getTextColor())end end;if _=="terminate"and self:isFocused()then self:stop()end end else if _~="mouse_click"and _~="mouse_up"and _~="mouse_scroll"and _~="mouse_drag"and _~="key_up"and _~="key"and _~="char"and _~="terminate"then table.insert(dr,{event=_,args={aO,aP,aQ,aR}})end end end end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()dn.basalt_reposition(b5,b6)self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)dn.basalt_update()end end end}return setmetatable(av,b1)end;local function dy(am)local b1=al(am)local an="Progressbar"local dz=0;b1:setZIndex(5)b1:setValue(false)b1.width=25;b1.height=1;b1.bgColor=h.CheckboxBG;b1.fgColor=h.CheckboxFG;local dA=colors.black;local dB=""local dC=colors.white;local dD=""local dE=0;local av={getType=function(self)return an end,setDirection=function(self,dF)dE=dF;return self end,setProgressBar=function(self,aD,S,dG)dA=aD or dA;dB=S or dB;dC=dG or dC;return self end,setBackgroundSymbol=function(self,S)dD=S:sub(1,1)return self end,setProgress=function(self,a3)if a3>=0 and a3<=100 and dz~=a3 then dz=a3;self:setValue(dz)if dz==100 then self:progressDoneHandler()end end;return self end,getProgress=function(self)return dz end,onProgressDone=function(self,dH)self:registerEvent("progress_done",dH)return self end,progressDoneHandler=function(self)self:sendEvent("progress_done")end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(b5,b6,self.width,self.height,self.fgColor)self.parent:drawTextBox(b5,b6,self.width,self.height,dD)if dE==1 then self.parent:drawBackgroundBox(b5,b6,self.width,self.height/100*dz,dA)self.parent:drawForegroundBox(b5,b6,self.width,self.height/100*dz,dC)self.parent:drawTextBox(b5,b6,self.width,self.height/100*dz,dB)elseif dE==2 then self.parent:drawBackgroundBox(b5,b6+math.ceil(self.height-self.height/100*dz),self.width,self.height/100*dz,dA)self.parent:drawForegroundBox(b5,b6+math.ceil(self.height-self.height/100*dz),self.width,self.height/100*dz,dC)self.parent:drawTextBox(b5,b6+math.ceil(self.height-self.height/100*dz),self.width,self.height/100*dz,dB)elseif dE==3 then self.parent:drawBackgroundBox(b5+math.ceil(self.width-self.width/100*dz),b6,self.width/100*dz,self.height,dA)self.parent:drawForegroundBox(b5+math.ceil(self.width-self.width/100*dz),b6,self.width/100*dz,self.height,dC)self.parent:drawTextBox(b5+math.ceil(self.width-self.width/100*dz),b6,self.width/100*dz,self.height,dB)else self.parent:drawBackgroundBox(b5,b6,self.width/100*dz,self.height,dA)self.parent:drawForegroundBox(b5,b6,self.width/100*dz,self.height,dC)self.parent:drawTextBox(b5,b6,self.width/100*dz,self.height,dB)end end end end}return setmetatable(av,b1)end;local function dI(am)local b1=al(am)local an="Radio"b1.width=8;b1.bgColor=h.listBG;b1.fgColor=h.listFG;b1:setZIndex(5)local ba={}local bb=h.selectionBG;local bc=h.selectionFG;local dJ=b1.bgColor;local dK=b1.fgColor;local bd=true;local S="\7"local be="left"local av={getType=function(self)return an end,addItem=function(self,G,E,F,Q,R,...)table.insert(ba,{x=E or 1,y=F or 1,text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})if#ba==1 then self:setValue(ba[1])end;return self end,removeItem=function(self,Z)table.remove(ba,Z)return self end,getItem=function(self,Z)return ba[Z]end,getItemIndex=function(self)local bm=self:getValue()for aN,a3 in pairs(ba)do if a3==bm then return aN end end end,clear=function(self)ba={}self:setValue({})return self end,getItemCount=function(self)return#ba end,editItem=function(self,Z,G,E,F,Q,R,...)table.remove(ba,Z)table.insert(ba,Z,{x=E or 1,y=F or 1,text=G,bgCol=Q or self.bgColor,fgCol=R or self.fgColor,args={...}})return self end,selectItem=function(self,Z)self:setValue(ba[Z]or{})return self end,setSelectedItem=function(self,Q,R,dL,dM,bn)bb=Q or bb;bc=R or bc;dJ=dL or dJ;dK=dM or dK;bd=bn;return self end,mouseClickHandler=function(self,_,aK,E,F)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if _=="mouse_click"then if aK==1 then if#ba>0 then for a2,a3 in pairs(ba)do if b5+a3.x-1<=E and b5+a3.x-1+a3.text:len()+2>=E and b6+a3.y-1==F then self:setValue(a3)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 b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()for a2,a3 in pairs(ba)do if a3==self:getValue()then if be=="left"then self.parent:writeText(a3.x+b5-1,a3.y+b6-1,S,dJ,dK)self.parent:writeText(a3.x+2+b5-1,a3.y+b6-1,a3.text,bb,bc)end else self.parent:drawBackgroundBox(a3.x+b5-1,a3.y+b6-1,1,1,self.bgColor)self.parent:writeText(a3.x+2+b5-1,a3.y+b6-1,a3.text,a3.bgCol,a3.fgCol)end end end end end}return setmetatable(av,b1)end;local function dN(am)local b1=al(am)local an="Scrollbar"b1.width=1;b1.height=8;b1.bgColor=colors.lightGray;b1.fgColor=colors.gray;b1:setValue(1)b1:setZIndex(2)local dO="vertical"local S=" "local dP=colors.black;local dQ="\127"local dR=b1.height;local Z=1;local dS=1;local av={getType=function(self)return an end,setSymbol=function(self,dT)S=dT:sub(1,1)self:setVisualChanged()return self end,setSymbolSize=function(self,dU)dS=tonumber(dU)or 1;if dO=="vertical"then self:setValue(Z-1*dR/(self.height-(dS-1))-dR/(self.height-(dS-1)))elseif dO=="horizontal"then self:setValue(Z-1*dR/(self.width-(dS-1))-dR/(self.width-(dS-1)))end;self:setVisualChanged()return self end,setMaxValue=function(self,ce)dR=ce;return self end,setBackgroundSymbol=function(self,dV)dQ=string.sub(dV,1,1)self:setVisualChanged()return self end,setSymbolColor=function(self,dW)dP=dW;self:setVisualChanged()return self end,setBarType=function(self,dX)dO=dX:lower()return self end,mouseClickHandler=function(self,_,aK,E,F)if b1.mouseClickHandler(self,_,aK,E,F)then local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if(_=="mouse_click"or _=="mouse_drag")and aK==1 then if dO=="horizontal"then for dY=0,self.width do if b5+dY==E and b6<=F and b6+self.height>F then Z=math.min(dY+1,self.width-(dS-1))self:setValue(dR/self.width*Z)self:setVisualChanged()end end end;if dO=="vertical"then for dY=0,self.height do if b6+dY==F and b5<=E and b5+self.width>E then Z=math.min(dY+1,self.height-(dS-1))self:setValue(dR/self.height*Z)self:setVisualChanged()end end end end;if _=="mouse_scroll"then Z=Z+aK;if Z<1 then Z=1 end;Z=math.min(Z,(dO=="vertical"and self.height or self.width)-(dS-1))self:setValue(dR/(dO=="vertical"and self.height or self.width)*Z)end;return true end end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()if dO=="horizontal"then self.parent:writeText(b5,b6,dQ:rep(Z-1),self.bgColor,self.fgColor)self.parent:writeText(b5+Z-1,b6,S:rep(dS),dP,dP)self.parent:writeText(b5+Z+dS-1,b6,dQ:rep(self.width-(Z+dS-1)),self.bgColor,self.fgColor)end;if dO=="vertical"then for v=0,self.height-1 do if Z==v+1 then for dZ=0,math.min(dS-1,self.height)do self.parent:writeText(b5,b6+v+dZ,S,dP,dP)end else if v+1Z-1+dS then self.parent:writeText(b5,b6+v,dQ,self.bgColor,self.fgColor)end end end end end end end}return setmetatable(av,b1)end;local function d_(am)local b1=al(am)local an="Slider"b1.width=8;b1.bgColor=colors.lightGray;b1.fgColor=colors.gray;b1:setValue(1)local dO="horizontal"local S=" "local dP=colors.black;local dQ="\140"local dR=b1.width;local Z=1;local av={getType=function(self)return an end,setSymbol=function(self,dT)S=dT:sub(1,1)self:setVisualChanged()return self end,setBackgroundSymbol=function(self,dV)dQ=string.sub(dV,1,1)self:setVisualChanged()return self end,setSymbolColor=function(self,dW)dP=dW;self:setVisualChanged()return self end,setBarType=function(self,dX)dO=dX:lower()return self end,mouseClickHandler=function(self,_,aK,E,F)if b1.mouseClickHandler(self,_,aK,E,F)then local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if dO=="horizontal"then for dY=0,self.width-1 do if b5+dY==E and b6<=F and b6+self.height>F then Z=dY+1;self:setValue(dR/self.width*Z)self:setVisualChanged()end end end;if dO=="vertical"then for dY=0,self.height-1 do if b6+dY==F and b5<=E and b5+self.width>E then Z=dY+1;self:setValue(dR/self.height*Z)self:setVisualChanged()end end end end end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()if dO=="horizontal"then self.parent:writeText(b5,b6,dQ:rep(Z-1),self.bgColor,self.fgColor)self.parent:writeText(b5+Z-1,b6,S,dP,dP)self.parent:writeText(b5+Z,b6,dQ:rep(self.width-Z),self.bgColor,self.fgColor)end;if dO=="vertical"then for v=0,self.height-1 do if v+1==Z then self.parent:writeText(b5,b6+v,S,dP,dP)else self.parent:writeText(b5,b6+v,dQ,self.bgColor,self.fgColor)end end end end end end}return setmetatable(av,b1)end;local function e0(am)local b1=al(am)local an="Switch"b1.width=3;b1.height=1;b1.bgColor=colors.lightGray;b1.fgColor=colors.gray;b1:setValue(false)b1:setZIndex(5)local av={getType=function(self)return an end,mouseClickHandler=function(self,_,aK,E,F)if b1.mouseClickHandler(self,_,aK,E,F)then local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if(_=="mouse_click"or _=="mouse_drag")and aK==1 then end;return true end end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()end end end}return setmetatable(av,b1)end;local function e1(am)local b1=al(am)local an="Textfield"local e2,c5,c4,e3=1,1,1,1;local e4={""}local e5={[colors.purple]={"break"}}b1.width=20;b1.height=8;b1.bgColor=h.textfieldBG;b1.fgColor=h.textfieldFG;b1:setZIndex(5)local av={getType=function(self)return an end,getLines=function(self)return e4 end,getLine=function(self,Z)return e4[Z]or""end,editLine=function(self,Z,G)e4[Z]=G or e4[Z]return self end,addLine=function(self,G,Z)if Z~=nil then table.insert(e4,Z,G)else table.insert(e4,G)end;return self end,addKeyword=function(self,e6,aD)end,removeLine=function(self,Z)table.remove(e4,Z or#e4)if#e4<=0 then table.insert(e4,"")end;return self end,getTextCursor=function(self)return c4,e3 end,getFocusHandler=function(self)b1.getFocusHandler(self)if self.parent~=nil then local b5,b6=self:getAnchorPosition()if self.parent~=nil then self.parent:setCursor(true,b5+c4-c5,b6+e3-e2,self.fgColor)end end end,loseFocusHandler=function(self)b1.loseFocusHandler(self)if self.parent~=nil then self.parent:setCursor(false)end end,keyHandler=function(self,_,aN)if b1.keyHandler(self,_,aN)then local b5,b6=self:getAnchorPosition()if _=="key"then if aN==keys.backspace then if e4[e3]==""then if e3>1 then table.remove(e4,e3)c4=e4[e3-1]:len()+1;c5=c4-self.width+1;if c5<1 then c5=1 end;e3=e3-1 end elseif c4<=1 then if e3>1 then c4=e4[e3-1]:len()+1;c5=c4-self.width+1;if c5<1 then c5=1 end;e4[e3-1]=e4[e3-1]..e4[e3]table.remove(e4,e3)e3=e3-1 end else e4[e3]=e4[e3]:sub(1,c4-2)..e4[e3]:sub(c4,e4[e3]:len())if c4>1 then c4=c4-1 end;if c5>1 then if c4e4[e3]:len()then if e4[e3+1]~=nil then e4[e3]=e4[e3]..e4[e3+1]table.remove(e4,e3+1)end else e4[e3]=e4[e3]:sub(1,c4-1)..e4[e3]:sub(c4+1,e4[e3]:len())end end;if aN==keys.enter then table.insert(e4,e3+1,e4[e3]:sub(c4,e4[e3]:len()))e4[e3]=e4[e3]:sub(1,c4-1)e3=e3+1;c4=1;c5=1;if e3-e2>=self.height then e2=e2+1 end;self:setValue("")end;if aN==keys.up then if e3>1 then e3=e3-1;if c4>e4[e3]:len()+1 then c4=e4[e3]:len()+1 end;if c5>1 then if c41 then if e3e4[e3]:len()+1 then c4=e4[e3]:len()+1 end;if e3>=e2+self.height then e2=e2+1 end end end;if aN==keys.right then c4=c4+1;if e3<#e4 then if c4>e4[e3]:len()+1 then c4=1;e3=e3+1 end elseif c4>e4[e3]:len()then c4=e4[e3]:len()+1 end;if c4<1 then c4=1 end;if c4=self.width+c5 then c5=c4-self.width+1 end;if c5<1 then c5=1 end end;if aN==keys.left then c4=c4-1;if c4>=1 then if c4=self.width+c5 then c5=c4 end end;if e3>1 then if c4<1 then e3=e3-1;c4=e4[e3]:len()+1;c5=c4-self.width+1 end end;if c4<1 then c4=1 end;if c5<1 then c5=1 end end end;if _=="char"then e4[e3]=e4[e3]:sub(1,c4-1)..aN..e4[e3]:sub(c4,e4[e3]:len())c4=c4+1;if c4>=self.width+c5 then c5=c5+1 end;self:setValue("")end;local ci=(c4<=e4[e3]:len()and c4-1 or e4[e3]:len())-(c5-1)if ci>self.x+self.width-1 then ci=self.x+self.width-1 end;local e7=e3-e2e4[e3]:len()then c4=e4[e3]:len()+1 end;if c4e4[e3]:len()then c4=e4[e3]:len()+1 end;if c4#e4-(self.height-1)then e2=#e4-(self.height-1)end;if e2<1 then e2=1 end;if self.parent~=nil then if b5+c4-c5>=b5 and b5+c4-c5<=b5+self.width and(b6+e3-e2>=b6 and b6+e3-e2<=b6+self.height)then self.parent:setCursor(true,e8+c4-c5,e9+e3-e2)else self.parent:setCursor(false)end end end;self:setVisualChanged()return true end end,draw=function(self)if b1.draw(self)then if self.parent~=nil then local b5,b6=self:getAnchorPosition()self.parent:drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(b5,b6,self.width,self.height,self.fgColor)for v=1,self.height do local G=""if e4[v+e2-1]~=nil then G=e4[v+e2-1]end;G=G:sub(c5,self.width+c5-1)local cj=self.width-G:len()if cj<0 then cj=0 end;G=G..string.rep(" ",cj)self.parent:setText(b5,b6+v-1,G)end end end end}return setmetatable(av,b1)end;local function ea(am)local av;local an="Thread"local a1;local eb;local ec=false;av={name=am,getType=function(self)return an end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,start=function(self,dH)if dH==nil then error("Function provided to thread is nil")end;a1=dH;eb=coroutine.create(a1)ec=true;local ab,ac=coroutine.resume(eb)if not ab then if ac~="Terminated"then error("Thread Error Occurred - "..ac)end end;return self end,getStatus=function(self,dH)if eb~=nil then return coroutine.status(eb)end;return nil end,stop=function(self,dH)ec=false;return self end,eventHandler=function(self,_,aO,aP,aQ)if ec then if coroutine.status(eb)~="dead"then local ab,ac=coroutine.resume(eb,_,aO,aP,aQ)if not ab then if ac~="Terminated"then error("Thread Error Occurred - "..ac)end end else ec=false end end end}av.__index=av;return av end;local function ed(am)local an="Timer"local ee=0;local ef=0;local eg=0;local aT;local au=X()local av={name=am,getType=function(self)return an end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,setTime=function(self,eh,ei)ee=eh or 0;ef=ei or 1;return self end,start=function(self)eg=ef;aT=os.startTimer(ee)return self end,cancel=function(self)if aT~=nil then os.cancelTimer(aT)end;return self end,onCall=function(self,a1)au:registerEvent("timed_event",a1)return self end,eventHandler=function(self,_,a_)if _=="timer"and a_==aT then au:sendEvent("timed_event",self)if eg>=1 then eg=eg-1;if eg>=1 then aT=os.startTimer(ee)end elseif eg==-1 then aT=os.startTimer(ee)end end end}av.__index=av;return av end;local function ej(am,ek)local b1=al(am)local an="Frame"local el={}local em={}local av={}local en;b1:setZIndex(10)local cD=false;local cz=1;local cA=1;local eo=colors.white;local ep,bf=0,0;if ek~=nil then b1.parent=ek;b1.width,b1.height=ek.w,ek.h;b1.bgColor=h.FrameBG;b1.fgColor=h.FrameFG else local eq,er=e.getSize()b1.width,b1.height=eq,er;b1.bgColor=h.basaltBG;b1.fgColor=h.basaltFG end;local function es(am)for a2,a3 in pairs(el)do for a2,bL in pairs(a3)do if bL.name==am then return a3 end end end end;local function et(eu)local ao=eu:getZIndex()if es(eu.name)~=nil then return nil end;if el[ao]==nil then for E=1,#em+1 do if em[E]~=nil then if ao==em[E]then break end;if ao>em[E]then table.insert(em,E,ao)break end else table.insert(em,ao)end end;if#em<=0 then table.insert(em,ao)end;el[ao]={}end;eu.parent=av;table.insert(el[ao],eu)return eu end;local function ev(eu)for bK,bL in pairs(el)do for aN,a3 in pairs(bL)do if a3==eu then table.remove(el[bK],aN)return true end end end;return false end;av={barActive=false,barBackground=colors.gray,barTextcolor=colors.black,barText="New Frame",barTextAlign="left",isMoveable=false,getType=function(self)return an end,setFocusedObject=function(self,eu)for a2,Z in pairs(em)do for a2,a3 in pairs(el[Z])do if a3==eu then if en~=nil then en:loseFocusHandler()end;en=eu;en:getFocusHandler()end end end;return self end,setOffset=function(self,aH,aI)ep=aH~=nil and math.floor(aH<0 and math.abs(aH)or-aH)or ep;bf=aI~=nil and math.floor(aI<0 and math.abs(aI)or-aI)or bf;return self end,getFrameOffset=function(self)return ep,bf end,removeFocusedObject=function(self)if en~=nil then en:loseFocusHandler()end;en=nil;return self end,getFocusedObject=function(self)return en end,show=function(self)b1:show()if self.parent==nil then b=self end;return self end,setCursor=function(self,ew,ex,ey,aD)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())cD=ew or false;if ex~=nil then cz=b5+ex-1 end;if ey~=nil then cA=b6+ey-1 end;eo=aD or eo;self:setVisualChanged()return self end,setMoveable=function(self,ez)self.isMoveable=ez or not self.isMoveable;self:setVisualChanged()return self end,showBar=function(self,eA)self.barActive=eA or not self.barActive;self:setVisualChanged()return self end,setBar=function(self,G,Q,R)self.barText=G or""self.barBackground=Q or self.barBackground;self.barTextcolor=R or self.barTextcolor;self:setVisualChanged()return self end,setBarTextAlign=function(self,be)self.barTextAlign=be or"left"self:setVisualChanged()return self end,getVisualChanged=function(self)local eB=b1.getVisualChanged(self)for a2,Z in pairs(em)do if el[Z]~=nil then for a2,a3 in pairs(el[Z])do if a3.getVisualChanged~=nil and a3:getVisualChanged()then eB=true end end end end;return eB end,loseFocusHandler=function(self)b1.loseFocusHandler(self)end,getFocusHandler=function(self)b1.getFocusHandler(self)if self.parent~=nil then self.parent:removeObject(self)self.parent:addObject(self)end end,keyHandler=function(self,_,aN)if en~=nil then if en.keyHandler~=nil then if en:keyHandler(_,aN)then return true end end end;return false end,backgroundKeyHandler=function(self,_,aN)b1.backgroundKeyHandler(self,_,aN)for a2,Z in pairs(em)do if el[Z]~=nil then for a2,a3 in pairs(el[Z])do if a3.backgroundKeyHandler~=nil then a3:backgroundKeyHandler(_,aN)end end end end end,eventHandler=function(self,_,aO,aP,aQ,aR)b1.eventHandler(self,_,aO,aP,aQ,aR)for a2,Z in pairs(em)do if el[Z]~=nil then for a2,a3 in pairs(el[Z])do if a3.eventHandler~=nil then a3:eventHandler(_,aO,aP,aQ,aR)end end end end;if _=="terminate"then e.clear()e.setCursorPos(1,1)a.stop()end end,mouseClickHandler=function(self,_,aK,E,F)local aH,aI=self:getOffset()aH=aH<0 and math.abs(aH)or-aH;aI=aI<0 and math.abs(aI)or-aI;if self.drag then if _=="mouse_drag"then local eC=1;local eD=1;if self.parent~=nil then eC,eD=self.parent:getAbsolutePosition(self.parent:getAnchorPosition())end;self:setPosition(E+self.xToRem-(eC-1)+aH,F-(eD-1)+aI)end;if _=="mouse_up"then self.drag=false end;return true end;if b1.mouseClickHandler(self,_,aK,E,F)then local aE,aF=self:getAbsolutePosition(self:getAnchorPosition())for a2,Z in pairs(em)do if el[Z]~=nil then for a2,a3 in ai(el[Z])do if a3.mouseClickHandler~=nil then if a3:mouseClickHandler(_,aK,E+aH,F+aI)then return true end end end end end;if self.isMoveable then if E>=aE and E<=aE+self.width-1 and F==aF and _=="mouse_click"then self.drag=true;self.xToRem=aE-E end end;if en~=nil then en:loseFocusHandler()en=nil end;return true end;return false end,setText=function(self,E,F,G)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if F>=1 and F<=self.height then if self.parent~=nil then self.parent:setText(math.max(E+b5-1,b5)-(self.parent.x-1),b6+F-1-(self.parent.y-1),f(G,math.max(1-E+1,1),self.width-E+1))else P.setText(math.max(E+b5-1,b5),b6+F-1,f(G,math.max(1-E+1,1),self.width-E+1))end end end,setBG=function(self,E,F,Q)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if F>=1 and F<=self.height then if self.parent~=nil then self.parent:setBG(math.max(E+b5-1,b5)-(self.parent.x-1),b6+F-1-(self.parent.y-1),f(Q,math.max(1-E+1,1),self.width-E+1))else P.setBG(math.max(E+b5-1,b5),b6+F-1,f(Q,math.max(1-E+1,1),self.width-E+1))end end end,setFG=function(self,E,F,R)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if F>=1 and F<=self.height then if self.parent~=nil then self.parent:setFG(math.max(E+b5-1,b5)-(self.parent.x-1),b6+F-1-(self.parent.y-1),f(R,math.max(1-E+1,1),self.width-E+1))else P.setFG(math.max(E+b5-1,b5),b6+F-1,f(R,math.max(1-E+1,1),self.width-E+1))end end end,writeText=function(self,E,F,G,Q,R)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())if F>=1 and F<=self.height then if self.parent~=nil then self.parent:writeText(math.max(E+b5-1,b5)-(self.parent.x-1),b6+F-1-(self.parent.y-1),f(G,math.max(1-E+1,1),self.width-E+1),Q,R)else P.writeText(math.max(E+b5-1,b5),b6+F-1,f(G,math.max(1-E+1,1),self.width-E+1),Q,R)end end end,drawBackgroundBox=function(self,E,F,k,l,Q)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())l=F<1 and(l+F>self.height and self.height or l+F-1)or(l+F>self.height and self.height-F+1 or l)k=E<1 and(k+E>self.width and self.width or k+E-1)or(k+E>self.width and self.width-E+1 or k)if self.parent~=nil then self.parent:drawBackgroundBox(math.max(E+b5-1,b5)-(self.parent.x-1),math.max(F+b6-1,b6)-(self.parent.y-1),k,l,Q)else P.drawBackgroundBox(math.max(E+b5-1,b5),math.max(F+b6-1,b6),k,l,Q)end end,drawTextBox=function(self,E,F,k,l,S)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())l=F<1 and(l+F>self.height and self.height or l+F-1)or(l+F>self.height and self.height-F+1 or l)k=E<1 and(k+E>self.width and self.width or k+E-1)or(k+E>self.width and self.width-E+1 or k)if self.parent~=nil then self.parent:drawTextBox(math.max(E+b5-1,b5)-(self.parent.x-1),math.max(F+b6-1,b6)-(self.parent.y-1),k,l,S:sub(1,1))else P.drawTextBox(math.max(E+b5-1,b5),math.max(F+b6-1,b6),k,l,S:sub(1,1))end end,drawForegroundBox=function(self,E,F,k,l,R)local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())l=F<1 and(l+F>self.height and self.height or l+F-1)or(l+F>self.height and self.height-F+1 or l)k=E<1 and(k+E>self.width and self.width or k+E-1)or(k+E>self.width and self.width-E+1 or k)if self.parent~=nil then self.parent:drawForegroundBox(math.max(E+b5-1,b5)-(self.parent.x-1),math.max(F+b6-1,b6)-(self.parent.y-1),k,l,R)else P.drawForegroundBox(math.max(E+b5-1,b5),math.max(F+b6-1,b6),k,l,R)end end,draw=function(self)if self:getVisualChanged()then if b1.draw(self)then local b5,b6=self:getAbsolutePosition(self:getAnchorPosition())local e8,e9=self:getAnchorPosition()if self.parent~=nil then self.parent:drawBackgroundBox(e8,e9,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(e8,e9,self.width,self.height,self.fgColor)self.parent:drawTextBox(e8,e9,self.width,self.height," ")else P.drawBackgroundBox(b5,b6,self.width,self.height,self.bgColor)P.drawForegroundBox(b5,b6,self.width,self.height,self.fgColor)P.drawTextBox(b5,b6,self.width,self.height," ")end;e.setCursorBlink(false)if self.barActive then if self.parent~=nil then self.parent:writeText(e8,e9,ad(self.barText,self.width,self.barTextAlign),self.barBackground,self.barTextcolor)else P.writeText(b5,b6,ad(self.barText,self.width,self.barTextAlign),self.barBackground,self.barTextcolor)end end;for a2,Z in ai(em)do if el[Z]~=nil then for a2,a3 in pairs(el[Z])do if a3.draw~=nil then a3:draw()end end end end;if cD then e.setTextColor(eo)e.setCursorPos(cz,cA)if self.parent~=nil then e.setCursorBlink(self:isFocused())else e.setCursorBlink(cD)end end;self:setVisualChanged(false)end end end,addObject=function(self,eu)return et(eu)end,removeObject=function(self,eu)return ev(eu)end,getObject=function(self,eu)return es(eu)end,addButton=function(self,am)local eu=b0(am)eu.name=am;return et(eu)end,addLabel=function(self,am)local eu=ck(am)eu.name=am;eu.bgColor=self.bgColor;eu.fgColor=self.fgColor;return et(eu)end,addCheckbox=function(self,am)local eu=b8(am)eu.name=am;return et(eu)end,addInput=function(self,am)local eu=c1(am)eu.name=am;return et(eu)end,addProgram=function(self,am)local eu=cx(am)eu.name=am;return et(eu)end,addTextfield=function(self,am)local eu=e1(am)eu.name=am;return et(eu)end,addList=function(self,am)local eu=co(am)eu.name=am;return et(eu)end,addDropdown=function(self,am)local eu=b9(am)eu.name=am;return et(eu)end,addRadio=function(self,am)local eu=dI(am)eu.name=am;return et(eu)end,addTimer=function(self,am)local eu=ed(am)eu.name=am;return et(eu)end,addAnimation=function(self,am)local eu=aS(am)eu.name=am;return et(eu)end,addSlider=function(self,am)local eu=d_(am)eu.name=am;return et(eu)end,addScrollbar=function(self,am)local eu=dN(am)eu.name=am;return et(eu)end,addMenubar=function(self,am)local eu=cr(am)eu.name=am;return et(eu)end,addThread=function(self,am)local eu=ea(am)eu.name=am;return et(eu)end,addPane=function(self,am)local eu=cw(am)eu.name=am;return et(eu)end,addImage=function(self,am)local eu=bo(am)eu.name=am;return et(eu)end,addProgressbar=function(self,am)local eu=dy(am)eu.name=am;return et(eu)end,addFrame=function(self,am)local eu=ej(am,self)eu.name=am;return et(eu)end}setmetatable(av,b1)if ek==nil then table.insert(c,av)end;return av end;local eE=false;local function eF(_,aO,aP,aQ,aR)if _=="mouse_click"then b:mouseClickHandler(_,aO,aP,aQ,aR)end;if _=="mouse_drag"then b:mouseClickHandler(_,aO,aP,aQ,aR)end;if _=="mouse_up"then b:mouseClickHandler(_,aO,aP,aQ,aR)end;if _=="mouse_scroll"then b:mouseClickHandler(_,aO,aP,aQ,aR)end;if _=="key"or _=="char"then b:keyHandler(_,aO)b:backgroundKeyHandler(_,aO)end;for a2,a3 in pairs(c)do a3:eventHandler(_,aO,aP,aQ,aR)end;if eE then b:draw()P.update()end end;function a.autoUpdate(ec)e.clear()eE=ec or true;b:draw()P.update()while eE do local _,aO,aP,aQ,aR=os.pullEventRaw()eF(_,aO,aP,aQ,aR)end end;function a.update(_,aO,aP,aQ,aR)if _~="nil"then eF(_,aO,aP,aQ,aR)else b:draw()P.update()end end;function a.stop()eE=false end;function a.getFrame(am)for a2,a3 in pairs(c)do if a3.name==am then return a3 end end end;function a.getActiveFrame()return b end;function a.setActiveFrame(aw)if aw:getType()=="Frame"then b=aw;return true end;return false end;function a.createFrame(am)local aw=ej(am)return aw end;function a.removeFrame(am)for aN,a3 in pairs(c)do if a3.name==am then c[aN]=nil;return true end end;return false end;if a.debugger then a.debugFrame=a.createFrame("basaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setBar("Debug",colors.black,colors.gray)a.debugList=a.debugFrame:addList("debugList"):setSize(a.debugFrame.width-2,a.debugFrame.height-3):setPosition(2,3):setScrollable(true):show()a.debugFrame:addButton("back"):setAnchor("right"):setSize(1,1):setText("\22"):onClick(function()a.oldFrame:show()end):setBackground(colors.red):show()a.debugLabel=a.debugFrame:addLabel("debugLabel"):onClick(function()a.oldFrame=b;a.debugFrame:show()end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottom"):setZIndex(20):show()end;if a.debugger then function a.debug(...)local a9={...}if b.name~="basaltDebuggingFrame"then if b~=a.debugLabel.frame then a.debugLabel:setParent(b)end end;local eG=""for aN,a3 in pairs(a9)do eG=eG..tostring(a3)..(#a9~=aN and", "or"")end;a.debugLabel:setText("[Debug] "..eG)a.debugList:addItem(eG)if a.debugList:getItemCount()>50 then a.debugList:removeItem(1)end;a.debugList:setValue(a.debugList:getItem(a.debugList:getItemCount()))a.debugLabel:show()end end;return a \ No newline at end of file +local a={debugger=true,version=1}local b;local c={}local d={}local e=term.current()local f=string.sub;local g={[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 h={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 i={{"\32\32\32\137\156\148\158\159\148\135\135\144\159\139\32\136\157\32\159\139\32\32\143\32\32\143\32\32\32\32\32\32\32\32\147\148\150\131\148\32\32\32\151\140\148\151\140\147","\32\32\32\149\132\149\136\156\149\144\32\133\139\159\129\143\159\133\143\159\133\138\32\133\138\32\133\32\32\32\32\32\32\150\150\129\137\156\129\32\32\32\133\131\129\133\131\132","\32\32\32\130\131\32\130\131\32\32\129\32\32\32\32\130\131\32\130\131\32\32\32\32\143\143\143\32\32\32\32\32\32\130\129\32\130\135\32\32\32\32\131\32\32\131\32\131","\139\144\32\32\143\148\135\130\144\149\32\149\150\151\149\158\140\129\32\32\32\135\130\144\135\130\144\32\149\32\32\139\32\159\148\32\32\32\32\159\32\144\32\148\32\147\131\132","\159\135\129\131\143\149\143\138\144\138\32\133\130\149\149\137\155\149\159\143\144\147\130\132\32\149\32\147\130\132\131\159\129\139\151\129\148\32\32\139\131\135\133\32\144\130\151\32","\32\32\32\32\32\32\130\135\32\130\32\129\32\129\129\131\131\32\130\131\129\140\141\132\32\129\32\32\129\32\32\32\32\32\32\32\131\131\129\32\32\32\32\32\32\32\32\32","\32\32\32\32\149\32\159\154\133\133\133\144\152\141\132\133\151\129\136\153\32\32\154\32\159\134\129\130\137\144\159\32\144\32\148\32\32\32\32\32\32\32\32\32\32\32\151\129","\32\32\32\32\133\32\32\32\32\145\145\132\141\140\132\151\129\144\150\146\129\32\32\32\138\144\32\32\159\133\136\131\132\131\151\129\32\144\32\131\131\129\32\144\32\151\129\32","\32\32\32\32\129\32\32\32\32\130\130\32\32\129\32\129\32\129\130\129\129\32\32\32\32\130\129\130\129\32\32\32\32\32\32\32\32\133\32\32\32\32\32\129\32\129\32\32","\150\156\148\136\149\32\134\131\148\134\131\148\159\134\149\136\140\129\152\131\32\135\131\149\150\131\148\150\131\148\32\148\32\32\148\32\32\152\129\143\143\144\130\155\32\134\131\148","\157\129\149\32\149\32\152\131\144\144\131\148\141\140\149\144\32\149\151\131\148\32\150\32\150\131\148\130\156\133\32\144\32\32\144\32\130\155\32\143\143\144\32\152\129\32\134\32","\130\131\32\131\131\129\131\131\129\130\131\32\32\32\129\130\131\32\130\131\32\32\129\32\130\131\32\130\129\32\32\129\32\32\133\32\32\32\129\32\32\32\130\32\32\32\129\32","\150\140\150\137\140\148\136\140\132\150\131\132\151\131\148\136\147\129\136\147\129\150\156\145\138\143\149\130\151\32\32\32\149\138\152\129\149\32\32\157\152\149\157\144\149\150\131\148","\149\143\142\149\32\149\149\32\149\149\32\144\149\32\149\149\32\32\149\32\32\149\32\149\149\32\149\32\149\32\144\32\149\149\130\148\149\32\32\149\32\149\149\130\149\149\32\149","\130\131\129\129\32\129\131\131\32\130\131\32\131\131\32\131\131\129\129\32\32\130\131\32\129\32\129\130\131\32\130\131\32\129\32\129\131\131\129\129\32\129\129\32\129\130\131\32","\136\140\132\150\131\148\136\140\132\153\140\129\131\151\129\149\32\149\149\32\149\149\32\149\137\152\129\137\152\129\131\156\133\149\131\32\150\32\32\130\148\32\152\137\144\32\32\32","\149\32\32\149\159\133\149\32\149\144\32\149\32\149\32\149\32\149\150\151\129\138\155\149\150\130\148\32\149\32\152\129\32\149\32\32\32\150\32\32\149\32\32\32\32\32\32\32","\129\32\32\130\129\129\129\32\129\130\131\32\32\129\32\130\131\32\32\129\32\129\32\129\129\32\129\32\129\32\131\131\129\130\131\32\32\32\129\130\131\32\32\32\32\140\140\132","\32\154\32\159\143\32\149\143\32\159\143\32\159\144\149\159\143\32\159\137\145\159\143\144\149\143\32\32\145\32\32\32\145\149\32\144\32\149\32\143\159\32\143\143\32\159\143\32","\32\32\32\152\140\149\151\32\149\149\32\145\149\130\149\157\140\133\32\149\32\154\143\149\151\32\149\32\149\32\144\32\149\149\153\32\32\149\32\149\133\149\149\32\149\149\32\149","\32\32\32\130\131\129\131\131\32\130\131\32\130\131\129\130\131\129\32\129\32\140\140\129\129\32\129\32\129\32\137\140\129\130\32\129\32\130\32\129\32\129\129\32\129\130\131\32","\144\143\32\159\144\144\144\143\32\159\143\144\159\138\32\144\32\144\144\32\144\144\32\144\144\32\144\144\32\144\143\143\144\32\150\129\32\149\32\130\150\32\134\137\134\134\131\148","\136\143\133\154\141\149\151\32\129\137\140\144\32\149\32\149\32\149\154\159\133\149\148\149\157\153\32\154\143\149\159\134\32\130\148\32\32\149\32\32\151\129\32\32\32\32\134\32","\133\32\32\32\32\133\129\32\32\131\131\32\32\130\32\130\131\129\32\129\32\130\131\129\129\32\129\140\140\129\131\131\129\32\130\129\32\129\32\130\129\32\32\32\32\32\129\32","\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32","\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32","\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32","\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32","\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32","\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32","\32\32\32\32\145\32\159\139\32\151\131\132\155\143\132\134\135\145\32\149\32\158\140\129\130\130\32\152\147\155\157\134\32\32\144\144\32\32\32\32\32\32\152\131\155\131\131\129","\32\32\32\32\149\32\149\32\145\148\131\32\149\32\149\140\157\132\32\148\32\137\155\149\32\32\32\149\154\149\137\142\32\153\153\32\131\131\149\131\131\129\149\135\145\32\32\32","\32\32\32\32\129\32\130\135\32\131\131\129\134\131\132\32\129\32\32\129\32\131\131\32\32\32\32\130\131\129\32\32\32\32\129\129\32\32\32\32\32\32\130\131\129\32\32\32","\150\150\32\32\148\32\134\32\32\132\32\32\134\32\32\144\32\144\150\151\149\32\32\32\32\32\32\145\32\32\152\140\144\144\144\32\133\151\129\133\151\129\132\151\129\32\145\32","\130\129\32\131\151\129\141\32\32\142\32\32\32\32\32\149\32\149\130\149\149\32\143\32\32\32\32\142\132\32\154\143\133\157\153\132\151\150\148\151\158\132\151\150\148\144\130\148","\32\32\32\140\140\132\32\32\32\32\32\32\32\32\32\151\131\32\32\129\129\32\32\32\32\134\32\32\32\32\32\32\32\129\129\32\129\32\129\129\130\129\129\32\129\130\131\32","\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\150\151\129\150\131\132\140\143\144\143\141\145\137\140\148\141\141\144\157\142\32\159\140\32\151\134\32\157\141\32","\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\151\151\32\154\143\132\157\140\32\157\140\32\157\140\32\157\140\32\32\149\32\32\149\32\32\149\32\32\149\32","\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\131\129\32\134\32\131\131\129\131\131\129\131\131\129\131\131\129\130\131\32\130\131\32\130\131\32\130\131\32","\151\131\148\152\137\145\155\140\144\152\142\145\153\140\132\153\137\32\154\142\144\155\159\132\150\156\148\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\136\32\151\140\132","\151\32\149\151\155\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\152\137\144\157\129\149\149\32\149\149\32\149\149\32\149\149\32\149\130\150\32\32\157\129\149\32\149","\131\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\129\32\130\131\32\133\131\32","\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\159\159\144\152\140\144\156\143\32\159\141\129\153\140\132\157\141\32\130\145\32\32\147\32\136\153\32\130\146\32","\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\149\157\134\154\143\132\157\140\133\157\140\133\157\140\133\157\140\133\32\149\32\32\149\32\32\149\32\32\149\32","\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\130\131\32\134\32\130\131\129\130\131\129\130\131\129\130\131\129\32\129\32\32\129\32\32\129\32\32\129\32","\159\134\144\137\137\32\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\32\132\32\159\143\32\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\138\32\146\130\144","\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\131\147\129\138\134\149\149\32\149\149\32\149\149\32\149\149\32\149\154\143\149\32\157\129\154\143\149","\130\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\129\130\131\129\130\131\129\130\131\129\140\140\129\130\131\32\140\140\129"},{[[000110000110110000110010101000000010000000100101]],[[000000110110000000000010101000000010000000100101]],[[000000000000000000000000000000000000000000000000]],[[100010110100000010000110110000010100000100000110]],[[000000110000000010110110000110000000000000110000]],[[000000000000000000000000000000000000000000000000]],[[000000110110000010000000100000100000000000000010]],[[000000000110110100010000000010000000000000000100]],[[000000000000000000000000000000000000000000000000]],[[010000000000100110000000000000000000000110010000]],[[000000000000000000000000000010000000010110000000]],[[000000000000000000000000000000000000000000000000]],[[011110110000000100100010110000000100000000000000]],[[000000000000000000000000000000000000000000000000]],[[000000000000000000000000000000000000000000000000]],[[110000110110000000000000000000010100100010000000]],[[000010000000000000110110000000000100010010000000]],[[000000000000000000000000000000000000000000000000]],[[010110010110100110110110010000000100000110110110]],[[000000000000000000000110000000000110000000000000]],[[000000000000000000000000000000000000000000000000]],[[010100010110110000000000000000110000000010000000]],[[110110000000000000110000110110100000000010000000]],[[000000000000000000000000000000000000000000000000]],[[000100011111000100011111000100011111000100011111]],[[000000000000100100100100011011011011111111111111]],[[000000000000000000000000000000000000000000000000]],[[000100011111000100011111000100011111000100011111]],[[000000000000100100100100011011011011111111111111]],[[100100100100100100100100100100100100100100100100]],[[000000110100110110000010000011110000000000011000]],[[000000000100000000000010000011000110000000001000]],[[000000000000000000000000000000000000000000000000]],[[010000100100000000000000000100000000010010110000]],[[000000000000000000000000000000110110110110110000]],[[000000000000000000000000000000000000000000000000]],[[110110110110110110000000110110110110110110110110]],[[000000000000000000000110000000000000000000000000]],[[000000000000000000000000000000000000000000000000]],[[000000000000110110000110010000000000000000010010]],[[000010000000000000000000000000000000000000000000]],[[000000000000000000000000000000000000000000000000]],[[110110110110110110110000110110110110000000000000]],[[000000000000000000000110000000000000000000000000]],[[000000000000000000000000000000000000000000000000]],[[110110110110110110110000110000000000000000010000]],[[000000000000000000000000100000000000000110000110]],[[000000000000000000000000000000000000000000000000]]}}local j={}local k={}do local l=0;local m=#i[1]local n=#i[1][1]for o=1,m,3 do for p=1,n,3 do local q=string.char(l)local r={}r[1]=i[1][o]:sub(p,p+2)r[2]=i[1][o+1]:sub(p,p+2)r[3]=i[1][o+2]:sub(p,p+2)local s={}s[1]=i[2][o]:sub(p,p+2)s[2]=i[2][o+1]:sub(p,p+2)s[3]=i[2][o+2]:sub(p,p+2)k[q]={r,s}l=l+1 end end;j[1]=k end;local function t(u,v)local w={["0"]="1",["1"]="0"}if u<=#j then return true end;for x=#j+1,u do local y={}local z=j[x-1]for l=0,255 do local q=string.char(l)local r={}local s={}local A=z[q][1]local B=z[q][2]for o=1,#A do local C,D,E,F,G,H={},{},{},{},{},{}for p=1,#A[1]do local I=k[A[o]:sub(p,p)][1]table.insert(C,I[1])table.insert(D,I[2])table.insert(E,I[3])local J=k[A[o]:sub(p,p)][2]if B[o]:sub(p,p)=="1"then table.insert(F,J[1]:gsub("[01]",w))table.insert(G,J[2]:gsub("[01]",w))table.insert(H,J[3]:gsub("[01]",w))else table.insert(F,J[1])table.insert(G,J[2])table.insert(H,J[3])end end;table.insert(r,table.concat(C))table.insert(r,table.concat(D))table.insert(r,table.concat(E))table.insert(s,table.concat(F))table.insert(s,table.concat(G))table.insert(s,table.concat(H))end;y[q]={r,s}if v then v="Font"..x.."Yeld"..l;os.queueEvent(v)os.pullEvent(v)end end;j[x]=y end;return true end;t(3,false)local function K(L,M,N,O,P)if not type(M)=="string"then error("Not a String",3)end;print(g,N)local Q=type(N)=="string"and N:sub(1,1)or g[N]or error("Wrong Front Color",3)local R=type(O)=="string"and O:sub(1,1)or g[O]or error("Wrong Back Color",3)local S=j[L]or error("Wrong font size selected",3)if M==""then return{{""},{""},{""}}end;local T={}for o in M:gmatch('.')do table.insert(T,o)end;local U={}local m=#S[T[1]][1]for V=1,m do local W={}for o=1,#T do W[o]=S[T[o]]and S[T[o]][1][V]or""end;U[V]=table.concat(W)end;local X={}local Y={}local Z={["0"]=Q,["1"]=R}local _={["0"]=R,["1"]=Q}for V=1,m do local a0={}local a1={}for o=1,#T do local a2=S[T[o]]and S[T[o]][2][V]or""a0[o]=a2:gsub("[01]",P and{["0"]=N:sub(o,o),["1"]=O:sub(o,o)}or Z)a1[o]=a2:gsub("[01]",P and{["0"]=O:sub(o,o),["1"]=N:sub(o,o)}or _)end;X[V]=table.concat(a0)Y[V]=table.concat(a1)end;return{U,X,Y}end;local function a3()local a4=e;local a5,m=a4.getSize()local a6={}local a7={}local a8={}local a9={}local aa={}local ab={}local ac;local ad={}local function ae()ac=(" "):rep(a5)for af=0,15 do local ag=2^af;local ah=g[ag]ad[ag]=ah:rep(a5)end end;ae()local function ai()local aj=ac;local ak=ad[colors.white]local al=ad[colors.black]for am=1,m do a6[am]=f(a6[am]==nil and aj or a6[am]..aj:sub(1,a5-a6[am]:len()),1,a5)a8[am]=f(a8[am]==nil and ak or a8[am]..ak:sub(1,a5-a8[am]:len()),1,a5)a7[am]=f(a7[am]==nil and al or a7[am]..al:sub(1,a5-a7[am]:len()),1,a5)end end;ai()local function an(ao,ap,aq)if ap>=1 and ap<=m then if ao+aq:len()>0 and ao<=a5 then local ar=a6[ap]local as;local at=ao+#aq-1;if ao<1 then local au=1-ao+1;local av=a5-ao+1;aq=f(aq,au,av)elseif at>a5 then local av=a5-ao+1;aq=f(aq,1,av)end;if ao>1 then local av=ao-1;as=f(ar,1,av)..aq else as=aq end;if at=1 and ap<=m then if ao+ax:len()>0 and ao<=a5 then local ar=a7[ap]local as;local at=ao+#ax-1;if ao<1 then ax=f(ax,1-ao+1,a5-ao+1)elseif at>a5 then ax=f(ax,1,a5-ao+1)end;if ao>1 then as=f(ar,1,ao-1)..ax else as=ax end;if at=1 and ap<=m then if ao+ax:len()>0 and ao<=a5 then local ar=a8[ap]local as;local at=ao+#ax-1;if ao<1 then local au=1-ao+1;local av=a5-ao+1;ax=f(ax,au,av)elseif at>a5 then local av=a5-ao+1;ax=f(ax,1,av)end;if ao>1 then local av=ao-1;as=f(ar,1,av)..ax else as=ax end;if at1 then while#bf>2 do table.sort(bf,function(bp,bq)return bp[2]>bq[2]end)local br,bs=be(bf),#bf;local bt,bu=bf[bs][1],bf[br][1]for o=1,6 do if bj[o]==bt then bj[o]=bu;bf[br][2]=bf[br][2]+1 end end;bf[bs]=nil end;local bv=128;for o=1,#bj-1 do if bj[o]~=bj[6]then bv=bv+2^(o-1)end end;return string.char(bv),bb[bf[1][1]==bj[6]and bf[2][1]or bf[1][1]],bb[bj[6]]else return"\128",bb[bj[1]],bb[bj[1]]end end;local bw,a5,m,aA={{},{},{}},0,#b6+#b6%3,b7 or colors.black;for o=1,#b6 do if#b6[o]>a5 then a5=#b6[o]end end;for ap=0,m-1,3 do local bx,by,bz,bA={},{},{},1;for ao=0,a5-1,2 do local bj,bk={},{}for bB=1,3 do for bC=1,2 do bj[#bj+1]=b6[ap+bB]and b6[ap+bB][ao+bC]and(b6[ap+bB][ao+bC]==0 and aA or b6[ap+bB][ao+bC])or aA;bk[bj[#bj]]=bk[bj[#bj]]and bk[bj[#bj]]+1 or 1 end end;bx[bA],by[bA],bz[bA]=bi(bj,bk)bA=bA+1 end;bw[1][#bw[1]+1],bw[2][#bw[2]+1],bw[3][#bw[3]+1]=table.concat(bx),table.concat(by),table.concat(bz)end;bw.width,bw.height=#bw[1][1],#bw[1]return bw end;local function bD(bE)local bF="Object"local aO;local bG=1;local bH="left"local bI="top"local bJ=false;local bK=false;local bL=true;local bM=aH()local bN={x=1,y=1,width=1,height=1,bgColor=colors.black,fgColor=colors.white,name=bE or"Object",parent=nil,show=function(self)bK=true;bL=true;return self end,hide=function(self)bK=false;bL=true;return self end,isVisible=function(self)return bK end,getZIndex=function(self)return bG end,setFocus=function(self)if self.parent~=nil then self.parent:setFocusedObject(self)end;return self end,setZIndex=function(self,aJ)bG=aJ;if self.parent~=nil then self.parent:removeObject(self)self.parent:addObject(self)end;return self end,getType=function(self)return bF 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,bO)if bO.getType~=nil and bO:getType()=="Frame"then self:remove()bO:addObject(self)if self.draw then self:show()end end;return self end,setValue=function(self,bP)if aO~=bP then aO=bP;bL=true;self:valueChangedHandler()end;return self end,getValue=function(self)return aO end,getVisualChanged=function(self)return bL end,setVisualChanged=function(self,bQ)bL=bQ or true;return self end,getEventSystem=function(self)return bM end,getParent=function(self)return self.parent end,setPosition=function(self,bR,bS,bT)if bT then self.x,self.y=self.x+bR,self.y+bS else self.x,self.y=bR,bS end;bL=true;return self end,getPosition=function(self)return self.x,self.y end,getVisibility=function(self)return bK end,setVisibility=function(self,bU)bK=bU or not bK;bL=true;return self end,setSize=function(self,a5,m)self.width,self.height=a5,m;bL=true;return self end,getHeight=function(self)return self.height end,getWidth=function(self)return self.width end,getSize=function(self)return self.width,self.height end,setBackground=function(self,bV)self.bgColor=bV;bL=true;return self end,getBackground=function(self)return self.bgColor end,setForeground=function(self,bV)self.fgColor=bV;bL=true;return self end,getForeground=function(self)return self.fgColor end,draw=function(self)if bK then return true end;return false end,getAbsolutePosition=function(self,ao,ap)if ao==nil then ao=self.x end;if ap==nil then ap=self.y end;if self.parent~=nil then local bW,bX=self.parent:getAbsolutePosition(self.parent:getAnchorPosition())ao=bW+ao-1;ap=bX+ap-1 end;return ao,ap end,getAnchorPosition=function(self,ao,ap,bY)if ao==nil then ao=self.x end;if ap==nil then ap=self.y end;if bH=="right"then ao=self.parent.width-ao-self.width+2 end;if bI=="bottom"then ap=self.parent.height-ap-self.height+2 end;local bZ,b_=self:getOffset()if bJ or bY then return ao,ap end;return ao+bZ,ap+b_ end,getOffset=function(self)if self.parent~=nil and bJ==false then return self.parent:getFrameOffset()end;return 0,0 end,ignoreOffset=function(self,c0)bJ=c0 or true;return self end,setAnchor=function(self,...)for aN,aO in pairs(table.pack(...))do if aO=="right"or aO=="left"then bH=aO end;if aO=="top"or aO=="bottom"then bI=aO end end;bL=true;return self end,getAnchor=function(self)return bH,bI end,onChange=function(self,aM)self:registerEvent("value_changed",aM)return self end,onClick=function(self,aM)self:registerEvent("mouse_click",aM)return self end,onEvent=function(self,aM)self:registerEvent("custom_event_handler",aM)return self end,onClickUp=function(self,aM)self:registerEvent("mouse_up",aM)return self end,onKey=function(self,aM)self:registerEvent("key",aM)self:registerEvent("char",aM)return self end,onKeyUp=function(self,aM)self:registerEvent("key_up",aM)return self end,onBackgroundKey=function(self,aM)self:registerEvent("background_key",aM)self:registerEvent("background_char",aM)return self end,onBackgroundKeyUp=function(self,aM)self:registerEvent("background_key_up",aM)return self end,isFocused=function(self)if self.parent~=nil then return self.parent:getFocusedObject()==self end;return false end,onGetFocus=function(self,aM)self:registerEvent("get_focus",aM)return self end,onLoseFocus=function(self,aM)self:registerEvent("lose_focus",aM)return self end,registerEvent=function(self,aK,aM)return bM:registerEvent(aK,aM)end,removeEvent=function(self,aK,aJ)return bM:removeEvent(aK,aJ)end,sendEvent=function(self,aK,...)return bM:sendEvent(aK,self,...)end,mouseClickHandler=function(self,aK,c1,ao,ap)local c2,c3=self:getAbsolutePosition(self:getAnchorPosition())if c2<=ao and c2+self.width>ao and c3<=ap and c3+self.height>ap and bK then if self.parent~=nil then self.parent:setFocusedObject(self)end;bM:sendEvent(aK,self,aK,c1,ao,ap)return true end;return false end,keyHandler=function(self,aK,bo)if self:isFocused()then bM:sendEvent(aK,self,aK,bo)return true end;return false end,backgroundKeyHandler=function(self,aK,bo)bM:sendEvent("background_"..aK,self,aK,bo)end,valueChangedHandler=function(self)bM:sendEvent("value_changed",self)end,eventHandler=function(self,aK,c4,c5,c6,c7)bM:sendEvent("custom_event_handler",self,aK,c4,c5,c6,c7)end,getFocusHandler=function(self)bM:sendEvent("get_focus",self)end,loseFocusHandler=function(self)bM:sendEvent("lose_focus",self)end}bN.__index=bN;return bN end;local function c8(bE)local bN={}local bF="Animation"local c9;local ca={}local aJ=1;local cb=0;local cc;local function cd()if ca[aJ]~=nil then ca[aJ].f(bN,aJ)end;aJ=aJ+1;if ca[aJ]~=nil then if ca[aJ].t>0 then c9=os.startTimer(ca[aJ].t)else cd()end end end;bN={name=bE,getType=function(self)return bF end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,add=function(self,aM,ce)cc=aM;table.insert(ca,{f=aM,t=ce or cb})return self end,wait=function(self,ce)cb=ce;return self end,rep=function(self,cf)for ao=1,cf do table.insert(ca,{f=cc,t=cb})end;return self end,clear=function(self)ca={}cc=nil;cb=0;aJ=1;return self end,play=function(self)aJ=1;if ca[aJ]~=nil then if ca[aJ].t>0 then c9=os.startTimer(ca[aJ].t)else cd()end end;return self end,cancel=function(self)os.cancelTimer(c9)return self end,eventHandler=function(self,aK,cg)if aK=="timer"and cg==c9 then if ca[aJ]~=nil then cd()end end end}bN.__index=bN;return bN end;local function ch(bE)local ci=bD(bE)local bF="Button"ci:setValue("Button")ci:setZIndex(5)ci.width=8;ci.bgColor=h.ButtonBG;ci.fgColor=h.ButtonFG;local cj="center"local ck="center"local bN={getType=function(self)return bF end,setHorizontalAlign=function(self,cl)cj=cl end,setVerticalAlign=function(self,cl)ck=cl end,setText=function(self,aq)ci:setValue(aq)return self end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()local co=b1(self.height,ck)self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(cm,cn,self.width,self.height,self.fgColor)self.parent:drawTextBox(cm,cn,self.width,self.height," ")for af=1,self.height do if af==co then self.parent:setText(cm,cn+af-1,aY(self:getValue(),self.width,cj))end end end end end}return setmetatable(bN,ci)end;local function cp(bE)local ci=bD(bE)local bF="Checkbox"ci:setZIndex(5)ci:setValue(false)ci.width=1;ci.height=1;ci.bgColor=h.CheckboxBG;ci.fgColor=h.CheckboxFG;local bN={symbol="\42",getType=function(self)return bF end,mouseClickHandler=function(self,aK,c1,ao,ap)if ci.mouseClickHandler(self,aK,c1,ao,ap)then if aK=="mouse_click"and c1==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 ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()local co=b1(self.height,"center")self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)for af=1,self.height do if af==co then if self:getValue()==true then self.parent:writeText(cm,cn+af-1,aY(self.symbol,self.width,"center"),self.bgColor,self.fgColor)else self.parent:writeText(cm,cn+af-1,aY(" ",self.width,"center"),self.bgColor,self.fgColor)end end end end end end}return setmetatable(bN,ci)end;local function cq(bE)local ci=bD(bE)local bF="Dropdown"ci.width=12;ci.height=1;ci.bgColor=h.dropdownBG;ci.fgColor=h.dropdownFG;ci:setZIndex(6)local cr={}local cs=h.selectionBG;local ct=h.selectionFG;local cu=true;local cv="left"local cw=0;local cx=16;local cy=6;local cz="\16"local cA="\31"local cB=1;local bN={getType=function(self)return bF end,setIndexOffset=function(self,cC)cw=cC;return self end,getIndexOffset=function(self)return cw end,addItem=function(self,aq,aA,aB,...)table.insert(cr,{text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})return self end,removeItem=function(self,aJ)table.remove(cr,aJ)return self end,getItem=function(self,aJ)return cr[aJ]end,getItemIndex=function(self)local cD=self:getValue()for bo,aO in pairs(cr)do if aO==cD then return bo end end end,clear=function(self)cr={}self:setValue({})return self end,getItemCount=function(self)return#cr end,editItem=function(self,aJ,aq,aA,aB,...)table.remove(cr,aJ)table.insert(cr,aJ,{text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})return self end,selectItem=function(self,aJ)self:setValue(cr[aJ]or{})return self end,setSelectedItem=function(self,aA,aB,cE)cs=aA or self.bgColor;ct=aB or self.fgColor;cu=cE;return self end,setDropdownSize=function(self,a5,m)cx,cy=a5,m;return self end,mouseClickHandler=function(self,aK,c1,ao,ap)if cB==2 then local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if aK=="mouse_click"then if c1==1 then if#cr>0 then for af=1,cy do if cr[af+cw]~=nil then if cm<=ao and cm+cx>ao and cn+af==ap then self:setValue(cr[af+cw])return true end end end end end end;if aK=="mouse_scroll"then cw=cw+c1;if cw<0 then cw=0 end;if c1==1 then if#cr>cy then if cw>#cr-cy then cw=#cr-cy end else cw=cr-1 end end;return true end;self:setVisualChanged()end;if ci.mouseClickHandler(self,aK,c1,ao,ap)then cB=2 else cB=1 end end,draw=function(self)if ci.draw(self)then local cm,cn=self:getAnchorPosition()if self.parent~=nil then self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)if#cr>=1 then if self:getValue()~=nil then if self:getValue().text~=nil then if cB==1 then self.parent:writeText(cm,cn,aY(self:getValue().text,self.width,cv):sub(1,self.width-1)..cz,self.bgColor,self.fgColor)else self.parent:writeText(cm,cn,aY(self:getValue().text,self.width,cv):sub(1,self.width-1)..cA,self.bgColor,self.fgColor)end end end;if cB==2 then for af=1,cy do if cr[af+cw]~=nil then if cr[af+cw]==self:getValue()then if cu then self.parent:writeText(cm,cn+af,aY(cr[af+cw].text,cx,cv),cs,ct)else self.parent:writeText(cm,cn+af,aY(cr[af+cw].text,cx,cv),cr[af+cw].bgCol,cr[af+cw].fgCol)end else self.parent:writeText(cm,cn+af,aY(cr[af+cw].text,cx,cv),cr[af+cw].bgCol,cr[af+cw].fgCol)end end end end end end end end}return setmetatable(bN,ci)end;local function cF(bE)local ci=bD(bE)local bF="Image"ci:setZIndex(2)local cG;local cH;local cI=false;local function b5()local b8={[0]={8,4,3,6,5},{4,14,8,7},{6,10,8,7},{9,11,8,0},{1,14,8,0},{13,12,8,0},{2,10,8,0},{15,8,10,11,12,14},{0,7,1,9,2,13},{3,11,8,7},{2,6,7,15},{9,3,7,15},{13,5,7,15},{5,12,8,7},{1,4,7,15},{7,10,11,12,14}}local b9,ba,bb={},{},{}for o=0,15 do ba[2^o]=o end;do local bc="0123456789abcdef"for o=1,16 do b9[bc:sub(o,o)]=o-1;b9[o-1]=bc:sub(o,o)bb[bc:sub(o,o)]=2^(o-1)bb[2^(o-1)]=bc:sub(o,o)local bd=b8[o-1]for o=1,#bd do bd[o]=2^bd[o]end end end;local function be(bf)local bg=b8[ba[bf[#bf][1]]]for p=1,#bg do local bh=bg[p]for o=1,#bf-1 do if bf[o][1]==bh then return o end end end;return 1 end;local function bi(bj,bk)if not bk then local bl={}bk={}for o=1,6 do local bm=bj[o]local bn=bk[bm]bk[bm],bl[o]=bn and bn+1 or 1,bm end;bj=bl end;local bf={}for bo,aO in pairs(bk)do bf[#bf+1]={bo,aO}end;if#bf>1 then while#bf>2 do table.sort(bf,function(bp,bq)return bp[2]>bq[2]end)local br,bs=be(bf),#bf;local bt,bu=bf[bs][1],bf[br][1]for o=1,6 do if bj[o]==bt then bj[o]=bu;bf[br][2]=bf[br][2]+1 end end;bf[bs]=nil end;local bv=128;for o=1,#bj-1 do if bj[o]~=bj[6]then bv=bv+2^(o-1)end end;return string.char(bv),bb[bf[1][1]==bj[6]and bf[2][1]or bf[1][1]],bb[bj[6]]else return"\128",bb[bj[1]],bb[bj[1]]end end;local bw,a5,m,aA={{},{},{}},0,#cG+#cG%3,ci.bgColor or colors.black;for o=1,#cG do if#cG[o]>a5 then a5=#cG[o]end end;for ap=0,m-1,3 do local bx,by,bz,bA={},{},{},1;for ao=0,a5-1,2 do local bj,bk={},{}for bB=1,3 do for bC=1,2 do bj[#bj+1]=cG[ap+bB]and cG[ap+bB][ao+bC]and(cG[ap+bB][ao+bC]==0 and aA or cG[ap+bB][ao+bC])or aA;bk[bj[#bj]]=bk[bj[#bj]]and bk[bj[#bj]]+1 or 1 end end;bx[bA],by[bA],bz[bA]=bi(bj,bk)bA=bA+1 end;bw[1][#bw[1]+1],bw[2][#bw[2]+1],bw[3][#bw[3]+1]=table.concat(bx),table.concat(by),table.concat(bz)end;bw.width,bw.height=#bw[1][1],#bw[1]cH=bw end;local bN={getType=function(self)return bF end,loadImage=function(self,aS)cG=paintutils.loadImage(aS)cI=false;return self end,loadBlittleImage=function(self,aS)cI=true;return self end,shrink=function(self)b5()cI=true;return self end,draw=function(self)if ci.draw(self)then if self.parent~=nil then if cG~=nil then local cm,cn=self:getAnchorPosition()if cI then local b4,cJ,cK=cH[1],cH[2],cH[3]for o=1,cH.height do local cL=b4[o]if type(cL)=="string"then self.parent:setText(cm,cn+o-1,cL)self.parent:setFG(cm,cn+o-1,cJ[o])self.parent:setBG(cm,cn+o-1,cK[o])elseif type(cL)=="table"then self.parent:setText(cm,cn+o-1,cL[2])self.parent:setFG(cm,cn+o-1,cJ[o])self.parent:setBG(cm,cn+o-1,cK[o])end end else for bS=1,math.min(#cG,self.height)do local cM=cG[bS]for bR=1,math.min(#cM,self.width)do if cM[bR]>0 then self.parent:drawBackgroundBox(cm+bR-1,cn+bS-1,1,1,cM[bR])end end end end end end end end}return setmetatable(bN,ci)end;local function cN(bE)local ci=bD(bE)local bF="Input"local cO="text"local cP=0;ci:setZIndex(5)ci:setValue("")ci.width=10;ci.height=1;ci.bgColor=h.InputBG;ci.fgColor=h.InputFG;local cQ=1;local cR=1;local cS=""local cT;local cU;local cV=cS;local cW=false;local bN={getType=function(self)return bF end,setInputType=function(self,cX)if cX=="password"or cX=="number"or cX=="text"then cO=cX end;return self end,setDefaultText=function(self,aq,cY,cZ)cS=aq;cT=cZ or cT;cU=cY or cU;if self:isFocused()then cV=""else cV=cS end;return self end,getInputType=function(self)return cO end,setValue=function(self,c_)ci.setValue(self,tostring(c_))if not cW then cQ=tostring(c_):len()+1 end;return self end,getValue=function(self)local c_=ci.getValue(self)return cO=="number"and tonumber(c_)or c_ end,setInputLimit=function(self,d0)cP=tonumber(d0)or cP;return self end,getInputLimit=function(self)return cP end,getFocusHandler=function(self)ci.getFocusHandler(self)if self.parent~=nil then local cm,cn=self:getAnchorPosition()cV=""if self.parent~=nil then self.parent:setCursor(true,cm+cQ-cR,cn,self.fgColor)end end end,loseFocusHandler=function(self)ci.loseFocusHandler(self)if self.parent~=nil then self.parent:setCursor(false)cV=cS end end,keyHandler=function(self,aK,bo)if ci.keyHandler(self,aK,bo)then cW=true;if aK=="key"then if bo==keys.backspace then local aq=tostring(ci.getValue())if cQ>1 then self:setValue(aq:sub(1,cQ-2)..aq:sub(cQ,aq:len()))if cQ>1 then cQ=cQ-1 end;if cR>1 then if cQd1 then cQ=d1+1 end;if cQ<1 then cQ=1 end;if cQ=self.width+cR then cR=cQ-self.width+1 end;if cR<1 then cR=1 end end;if bo==keys.left then cQ=cQ-1;if cQ>=1 then if cQ=self.width+cR then cR=cQ end end;if cQ<1 then cQ=1 end;if cR<1 then cR=1 end end end;if aK=="char"then local aq=ci.getValue()if aq:len()=self.width+cR then cR=cR+1 end end end;local cm,cn=self:getAnchorPosition()local c_=tostring(ci.getValue())local d3=(cQ<=c_:len()and cQ-1 or c_:len())-(cR-1)if d3>self.x+self.width-1 then d3=self.x+self.width-1 end;if self.parent~=nil then self.parent:setCursor(true,cm+d3,cn,self.fgColor)end;cW=false end end,mouseClickHandler=function(self,aK,c1,ao,ap)if ci.mouseClickHandler(self,aK,c1,ao,ap)then if aK=="mouse_click"and c1==1 then end;return true end;return false end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()local co=b1(self.height,"center")self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)for af=1,self.height do if af==co then local c_=tostring(ci.getValue())local cZ=self.bgColor;local cY=self.fgColor;local aq;if c_:len()<=0 then aq=cV;cZ=cT or cZ;cY=cU or cY end;aq=cV;if c_~=""then aq=c_ end;aq=aq:sub(cR,self.width+cR-1)local d4=self.width-aq:len()if d4<0 then d4=0 end;if cO=="password"and c_~=""then aq=string.rep("*",aq:len())end;aq=aq..string.rep(" ",d4)self.parent:writeText(cm,cn+af-1,aq,cZ,cY)end end end end end}return setmetatable(bN,ci)end;local function d5(bE)local ci=bD(bE)local bF="Label"ci:setZIndex(3)ci.fgColor=colors.white;ci.bgcolor=colors.black;local d6=true;ci:setValue("")local cj="left"local ck="top"local d7=0;local bN={getType=function(self)return bF end,setText=function(self,aq)aq=tostring(aq)ci:setValue(aq)if d6 then self.width=aq:len()end;return self end,setTextAlign=function(self,d8,d9)cj=d8 or cj;ck=d9 or ck;self:setVisualChanged()return self end,setFontSize=function(self,u)if u>0 and u<=4 then d7=u-1 or 0 end;return self end,getFontSize=function(self)return d7+1 end,setSize=function(self,a5,m)ci.setSize(self,a5,m)d6=false;self:setVisualChanged()return self end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()local co=b1(self.height,ck)self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(cm,cn,self.width,self.height,self.fgColor)self.parent:drawTextBox(cm,cn,self.width,self.height," ")if d7==0 then for af=1,self.height do if af==co then self.parent:writeText(cm,cn+af-1,aY(self:getValue(),self.width,cj),self.bgColor,self.fgColor)end end else local da=K(d7,self:getValue(),self.fgColor,self.bgColor)for af=1,self.height do if af==co then local db,dc=self.parent:getSize()local dd,de=#da[1][1],#da[1]cm=cm or math.floor((db-dd)/2)+1;cn=cn or math.floor((dc-de)/2)+1;for o=1,de do self.parent:setFG(cm,cn+o+af-2,aY(da[2][o],self.width,cj))self.parent:setBG(cm,cn+o+af-2,aY(da[3][o],self.width,cj,g[self.bgColor]))self.parent:setText(cm,cn+o+af-2,aY(da[1][o],self.width,cj))end end end end end end end}return setmetatable(bN,ci)end;local function df(bE)local ci=bD(bE)local bF="List"ci.width=16;ci.height=6;ci.bgColor=h.listBG;ci.fgColor=h.listFG;ci:setZIndex(5)local cr={}local cs=h.selectionBG;local ct=h.selectionFG;local cu=true;local cv="left"local cw=0;local dg=true;local bN={getType=function(self)return bF end,addItem=function(self,aq,aA,aB,...)table.insert(cr,{text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})if#cr==1 then self:setValue(cr[1])end;return self end,setIndexOffset=function(self,cC)cw=cC;return self end,getIndexOffset=function(self)return cw end,removeItem=function(self,aJ)table.remove(cr,aJ)return self end,getItem=function(self,aJ)return cr[aJ]end,getItemIndex=function(self)local cD=self:getValue()for bo,aO in pairs(cr)do if aO==cD then return bo end end end,clear=function(self)cr={}self:setValue({})return self end,getItemCount=function(self)return#cr end,editItem=function(self,aJ,aq,aA,aB,...)table.remove(cr,aJ)table.insert(cr,aJ,{text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})return self end,selectItem=function(self,aJ)self:setValue(cr[aJ]or{})return self end,setSelectedItem=function(self,aA,aB,cE)cs=aA or self.bgColor;ct=aB or self.fgColor;cu=cE;return self end,setScrollable=function(self,dh)dg=dh;return self end,mouseClickHandler=function(self,aK,c1,ao,ap)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if cm<=ao and cm+self.width>ao and cn<=ap and cn+self.height>ap and self:isVisible()then if aK=="mouse_click"or aK=="mouse_drag"then if c1==1 then if#cr>0 then for af=1,self.height do if cr[af+cw]~=nil then if cm<=ao and cm+self.width>ao and cn+af-1==ap then self:setValue(cr[af+cw])self:getEventSystem():sendEvent("mouse_click",self,"mouse_click",0,ao,ap,cr[af+cw])end end end end end end;if aK=="mouse_scroll"and dg then cw=cw+c1;if cw<0 then cw=0 end;if c1>=1 then if#cr>self.height then if cw>#cr-self.height then cw=#cr-self.height end;if cw>=#cr then cw=#cr-1 end else cw=cw-1 end end end;self:setVisualChanged()return true end end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)for af=1,self.height do if cr[af+cw]~=nil then if cr[af+cw]==self:getValue()then if cu then self.parent:writeText(cm,cn+af-1,aY(cr[af+cw].text,self.width,cv),cs,ct)else self.parent:writeText(cm,cn+af-1,aY(cr[af+cw].text,self.width,cv),cr[af+cw].bgCol,cr[af+cw].fgCol)end else self.parent:writeText(cm,cn+af-1,aY(cr[af+cw].text,self.width,cv),cr[af+cw].bgCol,cr[af+cw].fgCol)end end end end end end}return setmetatable(bN,ci)end;local function di(bE)local ci=bD(bE)local bF="Menubar"local bN={}ci.width=30;ci.height=1;ci.bgColor=colors.gray;ci.fgColor=colors.lightGray;ci:setZIndex(5)local cr={}local cs=h.selectionBG;local ct=h.selectionFG;local cu=true;local cv="left"local dj=0;local d4=2;local dg=false;local function dk()local dl=0;local bR=1;for af=1,#cr do if bR+cr[af].text:len()+d4*2>bN.w then dl=dl+cr[af].text:len()+d4*2 end;bR=bR+cr[af].text:len()+d4*2 end;return dl end;bN={getType=function(self)return bF end,addItem=function(self,aq,aA,aB,...)table.insert(cr,{text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})if#cr==1 then self:setValue(cr[1])end;return self end,getItemIndex=function(self)local cD=self:getValue()for bo,aO in pairs(cr)do if aO==cD then return bo end end end,clear=function(self)cr={}self:setValue({})return self end,setSpace=function(self,dm)d4=dm or d4;return self end,setButtonOffset=function(self,b0)dj=b0 or 0;if dj<0 then dj=0 end;local dl=dk()if dj>dl then dj=dl end;return self end,setScrollable=function(self,dh)dg=dh;return self end,removeItem=function(self,aJ)table.remove(cr,aJ)return self end,getItem=function(self,aJ)return cr[aJ]end,getItemCount=function(self)return#cr end,editItem=function(self,aJ,aq,aA,aB,...)table.remove(cr,aJ)table.insert(cr,aJ,{text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})return self end,selectItem=function(self,aJ)self:setValue(cr[aJ]or{})return self end,setSelectedItem=function(self,aA,aB,cE)cs=aA or self.bgColor;ct=aB or self.fgColor;cu=cE;return self end,mouseClickHandler=function(self,aK,c1,ao,ap)local c2,c3=self:getAbsolutePosition(self:getAnchorPosition())if c2<=ao and c2+self.width>ao and c3<=ap and c3+self.height>ap and self:isVisible()then if self.parent~=nil then self.parent:setFocusedObject(self)end;if aK=="mouse_click"then local bR=1;for af=1+dj,#cr do if cr[af]~=nil then if bR+cr[af].text:len()+d4*2<=self.width then if c2+bR-1<=ao and c2+bR-1+cr[af].text:len()+d4*2>ao and c3==ap then self:setValue(cr[af])self:getEventSystem():sendEvent("mouse_click",self,"mouse_click",0,ao,ap,cr[af])end;bR=bR+cr[af].text:len()+d4*2 else break end end end end;if aK=="mouse_scroll"and dg then dj=dj+c1;if dj<0 then dj=0 end;local dl=dk()if dj>dl then dj=dl end end;return true end;return false end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)local bR=0;for aN,aO in pairs(cr)do if bR+aO.text:len()+d4*2<=self.width then if aO==self:getValue()then self.parent:writeText(cm+bR-1+-dj,cn,aY((" "):rep(d4)..aO.text..(" "):rep(d4),aO.text:len()+d4*2,cv),cs or aO.bgCol,ct or aO.fgCol)else self.parent:writeText(cm+bR-1+-dj,cn,aY((" "):rep(d4)..aO.text..(" "):rep(d4),aO.text:len()+d4*2,cv),aO.bgCol,aO.fgCol)end;bR=bR+aO.text:len()+d4*2 else if bR=1 and ds>=1 and dr<=a5 and ds<=m then else end end;local function dz(dA,dB,dC)local dD=dr;local at=dD+#dA-1;if ds>=1 and ds<=m then if dD<=a5 and at>=1 then if dD==1 and at==a5 then a6[ds]=dA;a8[ds]=dB;a7[ds]=dC else local dE,dF,dG;if dD<1 then local dH=1-dD+1;local dI=a5-dD+1;dE=f(dA,dH,dI)dF=f(dB,dH,dI)dG=f(dC,dH,dI)elseif at>a5 then local dI=a5-dD+1;dE=f(dA,1,dI)dF=f(dB,1,dI)dG=f(dC,1,dI)else dE=dA;dF=dB;dG=dC end;local dJ=a6[ds]local dK=a8[ds]local dL=a7[ds]local dM,dN,dO;if dD>1 then local dP=dD-1;dM=f(dJ,1,dP)..dE;dN=f(dK,1,dP)..dF;dO=f(dL,1,dP)..dG else dM=dE;dN=dF;dO=dG end;if at=1 and ap<=m then a6[eb]=a6[ap]a7[eb]=a7[ap]a8[eb]=a8[ap]else a6[eb]=e8;a8[eb]=e9;a7[eb]=ea end end end;if dv then dy()end end,isColor=function()return e.isColor()end,isColour=function()return e.isColor()end,write=function(aq)aq=tostring(aq)if dv then dz(aq,g[dt]:rep(aq:len()),g[b7]:rep(aq:len()))end end,clearLine=function()if dv then an(1,ds,(" "):rep(a5))aw(1,ds,g[b7]:rep(a5))ay(1,ds,g[dt]:rep(a5))end;if dv then dy()end end,clear=function()for af=1,m do an(1,af,(" "):rep(a5))aw(1,af,g[b7]:rep(a5))ay(1,af,g[dt]:rep(a5))end;if dv then dy()end end,blit=function(aq,ec,ed)if type(aq)~="string"then error("bad argument #1 (expected string, got "..type(aq)..")",2)end;if type(ec)~="string"then error("bad argument #2 (expected string, got "..type(ec)..")",2)end;if type(ed)~="string"then error("bad argument #3 (expected string, got "..type(ed)..")",2)end;if#ec~=#aq or#ed~=#aq then error("Arguments must be the same length",2)end;if dv then dz(aq,ec,ed)end end}return e3 end;ci.width=30;ci.height=12;local ee=dq(1,1,ci.width,ci.height)local ef;local eg=false;local eh={}bN={getType=function(self)return bF end,show=function(self)ci.show(self)ee.setBackgroundColor(self.bgColor)ee.setTextColor(self.fgColor)ee.basalt_setVisible(true)return self end,hide=function(self)ci.hide(self)ee.basalt_setVisible(false)return self end,setPosition=function(self,ao,ap,bT)ci.setPosition(self,ao,ap,bT)ee.basalt_reposition(self:getAnchorPosition())return self end,getBasaltWindow=function()return ee end,getBasaltProcess=function()return ef end,setSize=function(self,a5,m)ci.setSize(self,a5,m)ee.basalt_resize(self.width,self.height)return self end,getStatus=function(self)if ef~=nil then return ef:getStatus()end;return"inactive"end,execute=function(self,aS,...)ef=aQ:new(aS,ee,...)ee.setBackgroundColor(colors.black)ee.setTextColor(colors.white)ee.clear()ee.setCursorPos(1,1)ef:resume()eg=false;return self end,stop=function(self)if ef~=nil then if not ef:isDead()then ef:resume("terminate")if ef:isDead()then if self.parent~=nil then self.parent:setCursor(false)end end end end;return self end,pause=function(self,ei)eg=ei or not eg;if ef~=nil then if not ef:isDead()then if not eg then self:injectEvents(eh)eh={}end end end;return self end,isPaused=function(self)return eg end,injectEvent=function(self,aK,c4,c5,c6,c7,ej)if ef~=nil then if not ef:isDead()then if eg==false or ej then ef:resume(aK,c4,c5,c6,c7)else table.insert(eh,{event=aK,args={c4,c5,c6,c7}})end end end;return self end,getQueuedEvents=function(self)return eh end,updateQueuedEvents=function(self,aI)eh=aI or eh;return self end,injectEvents=function(self,aI)if ef~=nil then if not ef:isDead()then for aN,aO in pairs(aI)do ef:resume(aO.event,table.unpack(aO.args))end end end;return self end,mouseClickHandler=function(self,aK,c1,ao,ap)if ci.mouseClickHandler(self,aK,c1,ao,ap)then if ef==nil then return false end;if not ef:isDead()then if not eg then local ek,el=self:getAbsolutePosition(self:getAnchorPosition(nil,nil,true))ef:resume(aK,c1,ao-ek+1,ap-el+1)end end;return true end end,keyHandler=function(self,aK,bo)ci.keyHandler(self,aK,bo)if self:isFocused()then if ef==nil then return false end;if not ef:isDead()then if not eg then if self.draw then ef:resume(aK,bo)end end end end end,getFocusHandler=function(self)ci.getFocusHandler(self)if ef~=nil then if not ef:isDead()then if not eg then if self.parent~=nil then local em,en=ee.getCursorPos()local cm,cn=self:getAnchorPosition()if self.parent~=nil then if cm+em-1>=1 and cm+em-1<=cm+self.width-1 and en+cn-1>=1 and en+cn-1<=cn+self.height-1 then self.parent:setCursor(ee.getCursorBlink(),cm+em-1,en+cn-1,ee.getTextColor())end end end end end end end,loseFocusHandler=function(self)ci.loseFocusHandler(self)if ef~=nil then if not ef:isDead()then if self.parent~=nil then self.parent:setCursor(false)end end end end,eventHandler=function(self,aK,c4,c5,c6,c7)if ef==nil then return end;if not ef:isDead()then if not eg then if aK~="mouse_click"and aK~="mouse_up"and aK~="mouse_scroll"and aK~="mouse_drag"and aK~="key_up"and aK~="key"and aK~="char"and aK~="terminate"then ef:resume(aK,c4,c5,c6,c7)end;if self:isFocused()then local cm,cn=self:getAnchorPosition()local em,en=ee.getCursorPos()if self.parent~=nil then if cm+em-1>=1 and cm+em-1<=cm+self.width-1 and en+cn-1>=1 and en+cn-1<=cn+self.height-1 then self.parent:setCursor(ee.getCursorBlink(),cm+em-1,en+cn-1,ee.getTextColor())end end;if aK=="terminate"and self:isFocused()then self:stop()end end else if aK~="mouse_click"and aK~="mouse_up"and aK~="mouse_scroll"and aK~="mouse_drag"and aK~="key_up"and aK~="key"and aK~="char"and aK~="terminate"then table.insert(eh,{event=aK,args={c4,c5,c6,c7}})end end end end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()ee.basalt_reposition(cm,cn)self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)ee.basalt_update()end end end}return setmetatable(bN,ci)end;local function eo(bE)local ci=bD(bE)local bF="Progressbar"local ep=0;ci:setZIndex(5)ci:setValue(false)ci.width=25;ci.height=1;ci.bgColor=h.CheckboxBG;ci.fgColor=h.CheckboxFG;local eq=colors.black;local er=""local es=colors.white;local et=""local eu=0;local bN={getType=function(self)return bF end,setDirection=function(self,ev)eu=ev;return self end,setProgressBar=function(self,bV,aC,ew)eq=bV or eq;er=aC or er;es=ew or es;return self end,setBackgroundSymbol=function(self,aC)et=aC:sub(1,1)return self end,setProgress=function(self,aO)if aO>=0 and aO<=100 and ep~=aO then ep=aO;self:setValue(ep)if ep==100 then self:progressDoneHandler()end end;return self end,getProgress=function(self)return ep end,onProgressDone=function(self,x)self:registerEvent("progress_done",x)return self end,progressDoneHandler=function(self)self:sendEvent("progress_done")end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(cm,cn,self.width,self.height,self.fgColor)self.parent:drawTextBox(cm,cn,self.width,self.height,et)if eu==1 then self.parent:drawBackgroundBox(cm,cn,self.width,self.height/100*ep,eq)self.parent:drawForegroundBox(cm,cn,self.width,self.height/100*ep,es)self.parent:drawTextBox(cm,cn,self.width,self.height/100*ep,er)elseif eu==2 then self.parent:drawBackgroundBox(cm,cn+math.ceil(self.height-self.height/100*ep),self.width,self.height/100*ep,eq)self.parent:drawForegroundBox(cm,cn+math.ceil(self.height-self.height/100*ep),self.width,self.height/100*ep,es)self.parent:drawTextBox(cm,cn+math.ceil(self.height-self.height/100*ep),self.width,self.height/100*ep,er)elseif eu==3 then self.parent:drawBackgroundBox(cm+math.ceil(self.width-self.width/100*ep),cn,self.width/100*ep,self.height,eq)self.parent:drawForegroundBox(cm+math.ceil(self.width-self.width/100*ep),cn,self.width/100*ep,self.height,es)self.parent:drawTextBox(cm+math.ceil(self.width-self.width/100*ep),cn,self.width/100*ep,self.height,er)else self.parent:drawBackgroundBox(cm,cn,self.width/100*ep,self.height,eq)self.parent:drawForegroundBox(cm,cn,self.width/100*ep,self.height,es)self.parent:drawTextBox(cm,cn,self.width/100*ep,self.height,er)end end end end}return setmetatable(bN,ci)end;local function ex(bE)local ci=bD(bE)local bF="Radio"ci.width=8;ci.bgColor=h.listBG;ci.fgColor=h.listFG;ci:setZIndex(5)local cr={}local cs=h.selectionBG;local ct=h.selectionFG;local ey=ci.bgColor;local ez=ci.fgColor;local cu=true;local aC="\7"local cv="left"local bN={getType=function(self)return bF end,addItem=function(self,aq,ao,ap,aA,aB,...)table.insert(cr,{x=ao or 1,y=ap or 1,text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})if#cr==1 then self:setValue(cr[1])end;return self end,removeItem=function(self,aJ)table.remove(cr,aJ)return self end,getItem=function(self,aJ)return cr[aJ]end,getItemIndex=function(self)local cD=self:getValue()for bo,aO in pairs(cr)do if aO==cD then return bo end end end,clear=function(self)cr={}self:setValue({})return self end,getItemCount=function(self)return#cr end,editItem=function(self,aJ,aq,ao,ap,aA,aB,...)table.remove(cr,aJ)table.insert(cr,aJ,{x=ao or 1,y=ap or 1,text=aq,bgCol=aA or self.bgColor,fgCol=aB or self.fgColor,args={...}})return self end,selectItem=function(self,aJ)self:setValue(cr[aJ]or{})return self end,setSelectedItem=function(self,aA,aB,eA,eB,cE)cs=aA or cs;ct=aB or ct;ey=eA or ey;ez=eB or ez;cu=cE;return self end,mouseClickHandler=function(self,aK,c1,ao,ap)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if aK=="mouse_click"then if c1==1 then if#cr>0 then for aN,aO in pairs(cr)do if cm+aO.x-1<=ao and cm+aO.x-1+aO.text:len()+2>=ao and cn+aO.y-1==ap then self:setValue(aO)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 ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()for aN,aO in pairs(cr)do if aO==self:getValue()then if cv=="left"then self.parent:writeText(aO.x+cm-1,aO.y+cn-1,aC,ey,ez)self.parent:writeText(aO.x+2+cm-1,aO.y+cn-1,aO.text,cs,ct)end else self.parent:drawBackgroundBox(aO.x+cm-1,aO.y+cn-1,1,1,self.bgColor)self.parent:writeText(aO.x+2+cm-1,aO.y+cn-1,aO.text,aO.bgCol,aO.fgCol)end end end end end}return setmetatable(bN,ci)end;local function eC(bE)local ci=bD(bE)local bF="Scrollbar"ci.width=1;ci.height=8;ci.bgColor=colors.lightGray;ci.fgColor=colors.gray;ci:setValue(1)ci:setZIndex(2)local eD="vertical"local aC=" "local eE=colors.black;local eF="\127"local eG=ci.height;local aJ=1;local eH=1;local bN={getType=function(self)return bF end,setSymbol=function(self,eI)aC=eI:sub(1,1)self:setVisualChanged()return self end,setSymbolSize=function(self,u)eH=tonumber(u)or 1;if eD=="vertical"then self:setValue(aJ-1*eG/(self.height-(eH-1))-eG/(self.height-(eH-1)))elseif eD=="horizontal"then self:setValue(aJ-1*eG/(self.width-(eH-1))-eG/(self.width-(eH-1)))end;self:setVisualChanged()return self end,setMaxValue=function(self,c_)eG=c_;return self end,setBackgroundSymbol=function(self,eJ)eF=string.sub(eJ,1,1)self:setVisualChanged()return self end,setSymbolColor=function(self,eK)eE=eK;self:setVisualChanged()return self end,setBarType=function(self,eL)eD=eL:lower()return self end,mouseClickHandler=function(self,aK,c1,ao,ap)if ci.mouseClickHandler(self,aK,c1,ao,ap)then local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if(aK=="mouse_click"or aK=="mouse_drag")and c1==1 then if eD=="horizontal"then for eM=0,self.width do if cm+eM==ao and cn<=ap and cn+self.height>ap then aJ=math.min(eM+1,self.width-(eH-1))self:setValue(eG/self.width*aJ)self:setVisualChanged()end end end;if eD=="vertical"then for eM=0,self.height do if cn+eM==ap and cm<=ao and cm+self.width>ao then aJ=math.min(eM+1,self.height-(eH-1))self:setValue(eG/self.height*aJ)self:setVisualChanged()end end end end;if aK=="mouse_scroll"then aJ=aJ+c1;if aJ<1 then aJ=1 end;aJ=math.min(aJ,(eD=="vertical"and self.height or self.width)-(eH-1))self:setValue(eG/(eD=="vertical"and self.height or self.width)*aJ)end;return true end end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()if eD=="horizontal"then self.parent:writeText(cm,cn,eF:rep(aJ-1),self.bgColor,self.fgColor)self.parent:writeText(cm+aJ-1,cn,aC:rep(eH),eE,eE)self.parent:writeText(cm+aJ+eH-1,cn,eF:rep(self.width-(aJ+eH-1)),self.bgColor,self.fgColor)end;if eD=="vertical"then for af=0,self.height-1 do if aJ==af+1 then for eN=0,math.min(eH-1,self.height)do self.parent:writeText(cm,cn+af+eN,aC,eE,eE)end else if af+1aJ-1+eH then self.parent:writeText(cm,cn+af,eF,self.bgColor,self.fgColor)end end end end end end end}return setmetatable(bN,ci)end;local function eO(bE)local ci=bD(bE)local bF="Slider"ci.width=8;ci.bgColor=colors.lightGray;ci.fgColor=colors.gray;ci:setValue(1)local eD="horizontal"local aC=" "local eE=colors.black;local eF="\140"local eG=ci.width;local aJ=1;local bN={getType=function(self)return bF end,setSymbol=function(self,eI)aC=eI:sub(1,1)self:setVisualChanged()return self end,setBackgroundSymbol=function(self,eJ)eF=string.sub(eJ,1,1)self:setVisualChanged()return self end,setSymbolColor=function(self,eK)eE=eK;self:setVisualChanged()return self end,setBarType=function(self,eL)eD=eL:lower()return self end,mouseClickHandler=function(self,aK,c1,ao,ap)if ci.mouseClickHandler(self,aK,c1,ao,ap)then local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if eD=="horizontal"then for eM=0,self.width-1 do if cm+eM==ao and cn<=ap and cn+self.height>ap then aJ=eM+1;self:setValue(eG/self.width*aJ)self:setVisualChanged()end end end;if eD=="vertical"then for eM=0,self.height-1 do if cn+eM==ap and cm<=ao and cm+self.width>ao then aJ=eM+1;self:setValue(eG/self.height*aJ)self:setVisualChanged()end end end end end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()if eD=="horizontal"then self.parent:writeText(cm,cn,eF:rep(aJ-1),self.bgColor,self.fgColor)self.parent:writeText(cm+aJ-1,cn,aC,eE,eE)self.parent:writeText(cm+aJ,cn,eF:rep(self.width-aJ),self.bgColor,self.fgColor)end;if eD=="vertical"then for af=0,self.height-1 do if af+1==aJ then self.parent:writeText(cm,cn+af,aC,eE,eE)else self.parent:writeText(cm,cn+af,eF,self.bgColor,self.fgColor)end end end end end end}return setmetatable(bN,ci)end;local function eP(bE)local ci=bD(bE)local bF="Switch"ci.width=3;ci.height=1;ci.bgColor=colors.lightGray;ci.fgColor=colors.gray;ci:setValue(false)ci:setZIndex(5)local bN={getType=function(self)return bF end,mouseClickHandler=function(self,aK,c1,ao,ap)if ci.mouseClickHandler(self,aK,c1,ao,ap)then local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if(aK=="mouse_click"or aK=="mouse_drag")and c1==1 then end;return true end end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()end end end}return setmetatable(bN,ci)end;local function eQ(bE)local ci=bD(bE)local bF="Textfield"local eR,cR,cQ,eS=1,1,1,1;local eT={""}local eU={[colors.purple]={"break"}}ci.width=20;ci.height=8;ci.bgColor=h.textfieldBG;ci.fgColor=h.textfieldFG;ci:setZIndex(5)local bN={getType=function(self)return bF end,getLines=function(self)return eT end,getLine=function(self,aJ)return eT[aJ]or""end,editLine=function(self,aJ,aq)eT[aJ]=aq or eT[aJ]return self end,addLine=function(self,aq,aJ)if aJ~=nil then table.insert(eT,aJ,aq)else table.insert(eT,aq)end;return self end,addKeyword=function(self,eV,bV)end,removeLine=function(self,aJ)table.remove(eT,aJ or#eT)if#eT<=0 then table.insert(eT,"")end;return self end,getTextCursor=function(self)return cQ,eS end,getFocusHandler=function(self)ci.getFocusHandler(self)if self.parent~=nil then local cm,cn=self:getAnchorPosition()if self.parent~=nil then self.parent:setCursor(true,cm+cQ-cR,cn+eS-eR,self.fgColor)end end end,loseFocusHandler=function(self)ci.loseFocusHandler(self)if self.parent~=nil then self.parent:setCursor(false)end end,keyHandler=function(self,aK,bo)if ci.keyHandler(self,aK,bo)then local cm,cn=self:getAnchorPosition()if aK=="key"then if bo==keys.backspace then if eT[eS]==""then if eS>1 then table.remove(eT,eS)cQ=eT[eS-1]:len()+1;cR=cQ-self.width+1;if cR<1 then cR=1 end;eS=eS-1 end elseif cQ<=1 then if eS>1 then cQ=eT[eS-1]:len()+1;cR=cQ-self.width+1;if cR<1 then cR=1 end;eT[eS-1]=eT[eS-1]..eT[eS]table.remove(eT,eS)eS=eS-1 end else eT[eS]=eT[eS]:sub(1,cQ-2)..eT[eS]:sub(cQ,eT[eS]:len())if cQ>1 then cQ=cQ-1 end;if cR>1 then if cQeT[eS]:len()then if eT[eS+1]~=nil then eT[eS]=eT[eS]..eT[eS+1]table.remove(eT,eS+1)end else eT[eS]=eT[eS]:sub(1,cQ-1)..eT[eS]:sub(cQ+1,eT[eS]:len())end end;if bo==keys.enter then table.insert(eT,eS+1,eT[eS]:sub(cQ,eT[eS]:len()))eT[eS]=eT[eS]:sub(1,cQ-1)eS=eS+1;cQ=1;cR=1;if eS-eR>=self.height then eR=eR+1 end;self:setValue("")end;if bo==keys.up then if eS>1 then eS=eS-1;if cQ>eT[eS]:len()+1 then cQ=eT[eS]:len()+1 end;if cR>1 then if cQ1 then if eSeT[eS]:len()+1 then cQ=eT[eS]:len()+1 end;if eS>=eR+self.height then eR=eR+1 end end end;if bo==keys.right then cQ=cQ+1;if eS<#eT then if cQ>eT[eS]:len()+1 then cQ=1;eS=eS+1 end elseif cQ>eT[eS]:len()then cQ=eT[eS]:len()+1 end;if cQ<1 then cQ=1 end;if cQ=self.width+cR then cR=cQ-self.width+1 end;if cR<1 then cR=1 end end;if bo==keys.left then cQ=cQ-1;if cQ>=1 then if cQ=self.width+cR then cR=cQ end end;if eS>1 then if cQ<1 then eS=eS-1;cQ=eT[eS]:len()+1;cR=cQ-self.width+1 end end;if cQ<1 then cQ=1 end;if cR<1 then cR=1 end end end;if aK=="char"then eT[eS]=eT[eS]:sub(1,cQ-1)..bo..eT[eS]:sub(cQ,eT[eS]:len())cQ=cQ+1;if cQ>=self.width+cR then cR=cR+1 end;self:setValue("")end;local d3=(cQ<=eT[eS]:len()and cQ-1 or eT[eS]:len())-(cR-1)if d3>self.x+self.width-1 then d3=self.x+self.width-1 end;local eW=eS-eReT[eS]:len()then cQ=eT[eS]:len()+1 end;if cQeT[eS]:len()then cQ=eT[eS]:len()+1 end;if cQ#eT-(self.height-1)then eR=#eT-(self.height-1)end;if eR<1 then eR=1 end;if self.parent~=nil then if cm+cQ-cR>=cm and cm+cQ-cR<=cm+self.width and(cn+eS-eR>=cn and cn+eS-eR<=cn+self.height)then self.parent:setCursor(true,eX+cQ-cR,eY+eS-eR)else self.parent:setCursor(false)end end end;self:setVisualChanged()return true end end,draw=function(self)if ci.draw(self)then if self.parent~=nil then local cm,cn=self:getAnchorPosition()self.parent:drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(cm,cn,self.width,self.height,self.fgColor)for af=1,self.height do local aq=""if eT[af+eR-1]~=nil then aq=eT[af+eR-1]end;aq=aq:sub(cR,self.width+cR-1)local d4=self.width-aq:len()if d4<0 then d4=0 end;aq=aq..string.rep(" ",d4)self.parent:setText(cm,cn+af-1,aq)end end end end}return setmetatable(bN,ci)end;local function eZ(bE)local bN;local bF="Thread"local aM;local e_;local f0=false;bN={name=bE,getType=function(self)return bF end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,start=function(self,x)if x==nil then error("Function provided to thread is nil")end;aM=x;e_=coroutine.create(aM)f0=true;local aW,aX=coroutine.resume(e_)if not aW then if aX~="Terminated"then error("Thread Error Occurred - "..aX)end end;return self end,getStatus=function(self,x)if e_~=nil then return coroutine.status(e_)end;return nil end,stop=function(self,x)f0=false;return self end,eventHandler=function(self,aK,c4,c5,c6)if f0 then if coroutine.status(e_)~="dead"then local aW,aX=coroutine.resume(e_,aK,c4,c5,c6)if not aW then if aX~="Terminated"then error("Thread Error Occurred - "..aX)end end else f0=false end end end}bN.__index=bN;return bN end;local function f1(bE)local bF="Timer"local f2=0;local f3=0;local f4=0;local c9;local bM=aH()local bN={name=bE,getType=function(self)return bF end,getZIndex=function(self)return 1 end,getName=function(self)return self.name end,setTime=function(self,f5,f6)f2=f5 or 0;f3=f6 or 1;return self end,start=function(self)f4=f3;c9=os.startTimer(f2)return self end,cancel=function(self)if c9~=nil then os.cancelTimer(c9)end;return self end,onCall=function(self,aM)bM:registerEvent("timed_event",aM)return self end,eventHandler=function(self,aK,cg)if aK=="timer"and cg==c9 then bM:sendEvent("timed_event",self)if f4>=1 then f4=f4-1;if f4>=1 then c9=os.startTimer(f2)end elseif f4==-1 then c9=os.startTimer(f2)end end end}bN.__index=bN;return bN end;local function f7(bE,f8)local ci=bD(bE)local bF="Frame"local f9={}local fa={}local bN={}local fb;ci:setZIndex(10)local du=false;local dr=1;local ds=1;local fc=colors.white;local fd,cw=0,0;if f8~=nil then ci.parent=f8;ci.width,ci.height=f8.w,f8.h;ci.bgColor=h.FrameBG;ci.fgColor=h.FrameFG else local fe,ff=e.getSize()ci.width,ci.height=fe,ff;ci.bgColor=h.basaltBG;ci.fgColor=h.basaltFG end;local function fg(bE)for aN,aO in pairs(f9)do for aN,bq in pairs(aO)do if bq.name==bE then return aO end end end end;local function fh(fi)local bG=fi:getZIndex()if fg(fi.name)~=nil then return nil end;if f9[bG]==nil then for ao=1,#fa+1 do if fa[ao]~=nil then if bG==fa[ao]then break end;if bG>fa[ao]then table.insert(fa,ao,bG)break end else table.insert(fa,bG)end end;if#fa<=0 then table.insert(fa,bG)end;f9[bG]={}end;fi.parent=bN;table.insert(f9[bG],fi)return fi end;local function fj(fi)for bp,bq in pairs(f9)do for bo,aO in pairs(bq)do if aO==fi then table.remove(f9[bp],bo)return true end end end;return false end;bN={barActive=false,barBackground=colors.gray,barTextcolor=colors.black,barText="New Frame",barTextAlign="left",isMoveable=false,getType=function(self)return bF end,setFocusedObject=function(self,fi)for aN,aJ in pairs(fa)do for aN,aO in pairs(f9[aJ])do if aO==fi then if fb~=nil then fb:loseFocusHandler()end;fb=fi;fb:getFocusHandler()end end end;return self end,setOffset=function(self,bZ,b_)fd=bZ~=nil and math.floor(bZ<0 and math.abs(bZ)or-bZ)or fd;cw=b_~=nil and math.floor(b_<0 and math.abs(b_)or-b_)or cw;return self end,getFrameOffset=function(self)return fd,cw end,removeFocusedObject=function(self)if fb~=nil then fb:loseFocusHandler()end;fb=nil;return self end,getFocusedObject=function(self)return fb end,show=function(self)ci:show()if self.parent==nil then b=self end;return self end,setCursor=function(self,fk,fl,fm,bV)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())du=fk or false;if fl~=nil then dr=cm+fl-1 end;if fm~=nil then ds=cn+fm-1 end;fc=bV or fc;self:setVisualChanged()return self end,setMoveable=function(self,fn)self.isMoveable=fn or not self.isMoveable;self:setVisualChanged()return self end,showBar=function(self,fo)self.barActive=fo or not self.barActive;self:setVisualChanged()return self end,setBar=function(self,aq,aA,aB)self.barText=aq or""self.barBackground=aA or self.barBackground;self.barTextcolor=aB or self.barTextcolor;self:setVisualChanged()return self end,setBarTextAlign=function(self,cv)self.barTextAlign=cv or"left"self:setVisualChanged()return self end,getVisualChanged=function(self)local fp=ci.getVisualChanged(self)for aN,aJ in pairs(fa)do if f9[aJ]~=nil then for aN,aO in pairs(f9[aJ])do if aO.getVisualChanged~=nil and aO:getVisualChanged()then fp=true end end end end;return fp end,loseFocusHandler=function(self)ci.loseFocusHandler(self)end,getFocusHandler=function(self)ci.getFocusHandler(self)if self.parent~=nil then self.parent:removeObject(self)self.parent:addObject(self)end end,keyHandler=function(self,aK,bo)if fb~=nil then if fb.keyHandler~=nil then if fb:keyHandler(aK,bo)then return true end end end;return false end,backgroundKeyHandler=function(self,aK,bo)ci.backgroundKeyHandler(self,aK,bo)for aN,aJ in pairs(fa)do if f9[aJ]~=nil then for aN,aO in pairs(f9[aJ])do if aO.backgroundKeyHandler~=nil then aO:backgroundKeyHandler(aK,bo)end end end end end,eventHandler=function(self,aK,c4,c5,c6,c7)ci.eventHandler(self,aK,c4,c5,c6,c7)for aN,aJ in pairs(fa)do if f9[aJ]~=nil then for aN,aO in pairs(f9[aJ])do if aO.eventHandler~=nil then aO:eventHandler(aK,c4,c5,c6,c7)end end end end;if aK=="terminate"then e.clear()e.setCursorPos(1,1)a.stop()end end,mouseClickHandler=function(self,aK,c1,ao,ap)local bZ,b_=self:getOffset()bZ=bZ<0 and math.abs(bZ)or-bZ;b_=b_<0 and math.abs(b_)or-b_;if self.drag then if aK=="mouse_drag"then local fq=1;local fr=1;if self.parent~=nil then fq,fr=self.parent:getAbsolutePosition(self.parent:getAnchorPosition())end;self:setPosition(ao+self.xToRem-(fq-1)+bZ,ap-(fr-1)+b_)end;if aK=="mouse_up"then self.drag=false end;return true end;if ci.mouseClickHandler(self,aK,c1,ao,ap)then local bW,bX=self:getAbsolutePosition(self:getAnchorPosition())for aN,aJ in pairs(fa)do if f9[aJ]~=nil then for aN,aO in b3(f9[aJ])do if aO.mouseClickHandler~=nil then if aO:mouseClickHandler(aK,c1,ao+bZ,ap+b_)then return true end end end end end;if self.isMoveable then if ao>=bW and ao<=bW+self.width-1 and ap==bX and aK=="mouse_click"then self.drag=true;self.xToRem=bW-ao end end;if fb~=nil then fb:loseFocusHandler()fb=nil end;return true end;return false end,setText=function(self,ao,ap,aq)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if ap>=1 and ap<=self.height then if self.parent~=nil then self.parent:setText(math.max(ao+cm-1,cm)-(self.parent.x-1),cn+ap-1-(self.parent.y-1),f(aq,math.max(1-ao+1,1),self.width-ao+1))else az.setText(math.max(ao+cm-1,cm),cn+ap-1,f(aq,math.max(1-ao+1,1),self.width-ao+1))end end end,setBG=function(self,ao,ap,aA)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if ap>=1 and ap<=self.height then if self.parent~=nil then self.parent:setBG(math.max(ao+cm-1,cm)-(self.parent.x-1),cn+ap-1-(self.parent.y-1),f(aA,math.max(1-ao+1,1),self.width-ao+1))else az.setBG(math.max(ao+cm-1,cm),cn+ap-1,f(aA,math.max(1-ao+1,1),self.width-ao+1))end end end,setFG=function(self,ao,ap,aB)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if ap>=1 and ap<=self.height then if self.parent~=nil then self.parent:setFG(math.max(ao+cm-1,cm)-(self.parent.x-1),cn+ap-1-(self.parent.y-1),f(aB,math.max(1-ao+1,1),self.width-ao+1))else az.setFG(math.max(ao+cm-1,cm),cn+ap-1,f(aB,math.max(1-ao+1,1),self.width-ao+1))end end end,writeText=function(self,ao,ap,aq,aA,aB)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())if ap>=1 and ap<=self.height then if self.parent~=nil then self.parent:writeText(math.max(ao+cm-1,cm)-(self.parent.x-1),cn+ap-1-(self.parent.y-1),f(aq,math.max(1-ao+1,1),self.width-ao+1),aA,aB)else az.writeText(math.max(ao+cm-1,cm),cn+ap-1,f(aq,math.max(1-ao+1,1),self.width-ao+1),aA,aB)end end end,drawBackgroundBox=function(self,ao,ap,a5,m,aA)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())m=ap<1 and(m+ap>self.height and self.height or m+ap-1)or(m+ap>self.height and self.height-ap+1 or m)a5=ao<1 and(a5+ao>self.width and self.width or a5+ao-1)or(a5+ao>self.width and self.width-ao+1 or a5)if self.parent~=nil then self.parent:drawBackgroundBox(math.max(ao+cm-1,cm)-(self.parent.x-1),math.max(ap+cn-1,cn)-(self.parent.y-1),a5,m,aA)else az.drawBackgroundBox(math.max(ao+cm-1,cm),math.max(ap+cn-1,cn),a5,m,aA)end end,drawTextBox=function(self,ao,ap,a5,m,aC)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())m=ap<1 and(m+ap>self.height and self.height or m+ap-1)or(m+ap>self.height and self.height-ap+1 or m)a5=ao<1 and(a5+ao>self.width and self.width or a5+ao-1)or(a5+ao>self.width and self.width-ao+1 or a5)if self.parent~=nil then self.parent:drawTextBox(math.max(ao+cm-1,cm)-(self.parent.x-1),math.max(ap+cn-1,cn)-(self.parent.y-1),a5,m,aC:sub(1,1))else az.drawTextBox(math.max(ao+cm-1,cm),math.max(ap+cn-1,cn),a5,m,aC:sub(1,1))end end,drawForegroundBox=function(self,ao,ap,a5,m,aB)local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())m=ap<1 and(m+ap>self.height and self.height or m+ap-1)or(m+ap>self.height and self.height-ap+1 or m)a5=ao<1 and(a5+ao>self.width and self.width or a5+ao-1)or(a5+ao>self.width and self.width-ao+1 or a5)if self.parent~=nil then self.parent:drawForegroundBox(math.max(ao+cm-1,cm)-(self.parent.x-1),math.max(ap+cn-1,cn)-(self.parent.y-1),a5,m,aB)else az.drawForegroundBox(math.max(ao+cm-1,cm),math.max(ap+cn-1,cn),a5,m,aB)end end,draw=function(self)if self:getVisualChanged()then if ci.draw(self)then local cm,cn=self:getAbsolutePosition(self:getAnchorPosition())local eX,eY=self:getAnchorPosition()if self.parent~=nil then self.parent:drawBackgroundBox(eX,eY,self.width,self.height,self.bgColor)self.parent:drawForegroundBox(eX,eY,self.width,self.height,self.fgColor)self.parent:drawTextBox(eX,eY,self.width,self.height," ")else az.drawBackgroundBox(cm,cn,self.width,self.height,self.bgColor)az.drawForegroundBox(cm,cn,self.width,self.height,self.fgColor)az.drawTextBox(cm,cn,self.width,self.height," ")end;e.setCursorBlink(false)if self.barActive then if self.parent~=nil then self.parent:writeText(eX,eY,aY(self.barText,self.width,self.barTextAlign),self.barBackground,self.barTextcolor)else az.writeText(cm,cn,aY(self.barText,self.width,self.barTextAlign),self.barBackground,self.barTextcolor)end end;for aN,aJ in b3(fa)do if f9[aJ]~=nil then for aN,aO in pairs(f9[aJ])do if aO.draw~=nil then aO:draw()end end end end;if du then e.setTextColor(fc)e.setCursorPos(dr,ds)if self.parent~=nil then e.setCursorBlink(self:isFocused())else e.setCursorBlink(du)end end;self:setVisualChanged(false)end end end,addObject=function(self,fi)return fh(fi)end,removeObject=function(self,fi)return fj(fi)end,getObject=function(self,fi)return fg(fi)end,addButton=function(self,bE)local fi=ch(bE)fi.name=bE;return fh(fi)end,addLabel=function(self,bE)local fi=d5(bE)fi.name=bE;fi.bgColor=self.bgColor;fi.fgColor=self.fgColor;return fh(fi)end,addCheckbox=function(self,bE)local fi=cp(bE)fi.name=bE;return fh(fi)end,addInput=function(self,bE)local fi=cN(bE)fi.name=bE;return fh(fi)end,addProgram=function(self,bE)local fi=dp(bE)fi.name=bE;return fh(fi)end,addTextfield=function(self,bE)local fi=eQ(bE)fi.name=bE;return fh(fi)end,addList=function(self,bE)local fi=df(bE)fi.name=bE;return fh(fi)end,addDropdown=function(self,bE)local fi=cq(bE)fi.name=bE;return fh(fi)end,addRadio=function(self,bE)local fi=ex(bE)fi.name=bE;return fh(fi)end,addTimer=function(self,bE)local fi=f1(bE)fi.name=bE;return fh(fi)end,addAnimation=function(self,bE)local fi=c8(bE)fi.name=bE;return fh(fi)end,addSlider=function(self,bE)local fi=eO(bE)fi.name=bE;return fh(fi)end,addScrollbar=function(self,bE)local fi=eC(bE)fi.name=bE;return fh(fi)end,addMenubar=function(self,bE)local fi=di(bE)fi.name=bE;return fh(fi)end,addThread=function(self,bE)local fi=eZ(bE)fi.name=bE;return fh(fi)end,addPane=function(self,bE)local fi=dn(bE)fi.name=bE;return fh(fi)end,addImage=function(self,bE)local fi=cF(bE)fi.name=bE;return fh(fi)end,addProgressbar=function(self,bE)local fi=eo(bE)fi.name=bE;return fh(fi)end,addFrame=function(self,bE)local fi=f7(bE,self)fi.name=bE;return fh(fi)end}setmetatable(bN,ci)if f8==nil then table.insert(c,bN)end;return bN end;local fs=false;local function ft(aK,c4,c5,c6,c7)if aK=="mouse_click"then b:mouseClickHandler(aK,c4,c5,c6,c7)end;if aK=="mouse_drag"then b:mouseClickHandler(aK,c4,c5,c6,c7)end;if aK=="mouse_up"then b:mouseClickHandler(aK,c4,c5,c6,c7)end;if aK=="mouse_scroll"then b:mouseClickHandler(aK,c4,c5,c6,c7)end;if aK=="key"or aK=="char"then b:keyHandler(aK,c4)b:backgroundKeyHandler(aK,c4)end;for aN,aO in pairs(c)do aO:eventHandler(aK,c4,c5,c6,c7)end;if fs then b:draw()az.update()end end;function a.autoUpdate(f0)e.clear()fs=f0 or true;b:draw()az.update()while fs do local aK,c4,c5,c6,c7=os.pullEventRaw()ft(aK,c4,c5,c6,c7)end end;function a.update(aK,c4,c5,c6,c7)if aK~="nil"then ft(aK,c4,c5,c6,c7)else b:draw()az.update()end end;function a.stop()fs=false end;function a.getFrame(bE)for aN,aO in pairs(c)do if aO.name==bE then return aO end end end;function a.getActiveFrame()return b end;function a.setActiveFrame(bO)if bO:getType()=="Frame"then b=bO;return true end;return false end;function a.createFrame(bE)local bO=f7(bE)return bO end;function a.removeFrame(bE)for bo,aO in pairs(c)do if aO.name==bE then c[bo]=nil;return true end end;return false end;if a.debugger then a.debugFrame=a.createFrame("basaltDebuggingFrame"):showBar():setBackground(colors.lightGray):setBar("Debug",colors.black,colors.gray)a.debugList=a.debugFrame:addList("debugList"):setSize(a.debugFrame.width-2,a.debugFrame.height-3):setPosition(2,3):setScrollable(true):show()a.debugFrame:addButton("back"):setAnchor("right"):setSize(1,1):setText("\22"):onClick(function()a.oldFrame:show()end):setBackground(colors.red):show()a.debugLabel=a.debugFrame:addLabel("debugLabel"):onClick(function()a.oldFrame=b;a.debugFrame:show()end):setBackground(colors.black):setForeground(colors.white):setAnchor("bottom"):setZIndex(20):show()end;if a.debugger then function a.debug(...)local aU={...}if b.name~="basaltDebuggingFrame"then if b~=a.debugLabel.frame then a.debugLabel:setParent(b)end end;local fu=""for bo,aO in pairs(aU)do fu=fu..tostring(aO)..(#aU~=bo and", "or"")end;a.debugLabel:setText("[Debug] "..fu)a.debugList:addItem(fu)if a.debugList:getItemCount()>50 then a.debugList:removeItem(1)end;a.debugList:setValue(a.debugList:getItem(a.debugList:getItemCount()))a.debugLabel:show()end end;return a \ No newline at end of file diff --git a/source/project/Object.lua b/source/project/Object.lua index 5b6a6a7..254564d 100644 --- a/source/project/Object.lua +++ b/source/project/Object.lua @@ -156,7 +156,11 @@ local function Object(name) end; getWidth = function(self) - return self.w + return self.width + end; + + getSize = function(self) + return self.width, self.height end; setBackground = function(self, color) diff --git a/source/project/lib/bigfont.lua b/source/project/lib/bigfont.lua new file mode 100644 index 0000000..ffa9ea1 --- /dev/null +++ b/source/project/lib/bigfont.lua @@ -0,0 +1,137 @@ +------------------------------------------------------------------------------------- +-- Wojbies API 5.0 - Bigfont - functions to write bigger font using drawing sybols -- +------------------------------------------------------------------------------------- +-- Copyright (c) 2015-2022 Wojbie (wojbie@wojbie.net) +-- Redistribution and use in source and binary forms, with or without modification, are permitted (subject to the limitations in the disclaimer below) provided that the following conditions are met: +-- 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. +-- 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. +-- 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. +-- 4. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +-- 5. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. +-- NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE GRANTED BY THIS LICENSE. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. YOU ACKNOWLEDGE THAT THIS SOFTWARE IS NOT DESIGNED, LICENSED OR INTENDED FOR USE IN THE DESIGN, CONSTRUCTION, OPERATION OR MAINTENANCE OF ANY NUCLEAR FACILITY. + +local rawFont = {{"\32\32\32\137\156\148\158\159\148\135\135\144\159\139\32\136\157\32\159\139\32\32\143\32\32\143\32\32\32\32\32\32\32\32\147\148\150\131\148\32\32\32\151\140\148\151\140\147", "\32\32\32\149\132\149\136\156\149\144\32\133\139\159\129\143\159\133\143\159\133\138\32\133\138\32\133\32\32\32\32\32\32\150\150\129\137\156\129\32\32\32\133\131\129\133\131\132", "\32\32\32\130\131\32\130\131\32\32\129\32\32\32\32\130\131\32\130\131\32\32\32\32\143\143\143\32\32\32\32\32\32\130\129\32\130\135\32\32\32\32\131\32\32\131\32\131", "\139\144\32\32\143\148\135\130\144\149\32\149\150\151\149\158\140\129\32\32\32\135\130\144\135\130\144\32\149\32\32\139\32\159\148\32\32\32\32\159\32\144\32\148\32\147\131\132", "\159\135\129\131\143\149\143\138\144\138\32\133\130\149\149\137\155\149\159\143\144\147\130\132\32\149\32\147\130\132\131\159\129\139\151\129\148\32\32\139\131\135\133\32\144\130\151\32", "\32\32\32\32\32\32\130\135\32\130\32\129\32\129\129\131\131\32\130\131\129\140\141\132\32\129\32\32\129\32\32\32\32\32\32\32\131\131\129\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\159\154\133\133\133\144\152\141\132\133\151\129\136\153\32\32\154\32\159\134\129\130\137\144\159\32\144\32\148\32\32\32\32\32\32\32\32\32\32\32\151\129", "\32\32\32\32\133\32\32\32\32\145\145\132\141\140\132\151\129\144\150\146\129\32\32\32\138\144\32\32\159\133\136\131\132\131\151\129\32\144\32\131\131\129\32\144\32\151\129\32", "\32\32\32\32\129\32\32\32\32\130\130\32\32\129\32\129\32\129\130\129\129\32\32\32\32\130\129\130\129\32\32\32\32\32\32\32\32\133\32\32\32\32\32\129\32\129\32\32", "\150\156\148\136\149\32\134\131\148\134\131\148\159\134\149\136\140\129\152\131\32\135\131\149\150\131\148\150\131\148\32\148\32\32\148\32\32\152\129\143\143\144\130\155\32\134\131\148", "\157\129\149\32\149\32\152\131\144\144\131\148\141\140\149\144\32\149\151\131\148\32\150\32\150\131\148\130\156\133\32\144\32\32\144\32\130\155\32\143\143\144\32\152\129\32\134\32", "\130\131\32\131\131\129\131\131\129\130\131\32\32\32\129\130\131\32\130\131\32\32\129\32\130\131\32\130\129\32\32\129\32\32\133\32\32\32\129\32\32\32\130\32\32\32\129\32", "\150\140\150\137\140\148\136\140\132\150\131\132\151\131\148\136\147\129\136\147\129\150\156\145\138\143\149\130\151\32\32\32\149\138\152\129\149\32\32\157\152\149\157\144\149\150\131\148", "\149\143\142\149\32\149\149\32\149\149\32\144\149\32\149\149\32\32\149\32\32\149\32\149\149\32\149\32\149\32\144\32\149\149\130\148\149\32\32\149\32\149\149\130\149\149\32\149", "\130\131\129\129\32\129\131\131\32\130\131\32\131\131\32\131\131\129\129\32\32\130\131\32\129\32\129\130\131\32\130\131\32\129\32\129\131\131\129\129\32\129\129\32\129\130\131\32", "\136\140\132\150\131\148\136\140\132\153\140\129\131\151\129\149\32\149\149\32\149\149\32\149\137\152\129\137\152\129\131\156\133\149\131\32\150\32\32\130\148\32\152\137\144\32\32\32", "\149\32\32\149\159\133\149\32\149\144\32\149\32\149\32\149\32\149\150\151\129\138\155\149\150\130\148\32\149\32\152\129\32\149\32\32\32\150\32\32\149\32\32\32\32\32\32\32", "\129\32\32\130\129\129\129\32\129\130\131\32\32\129\32\130\131\32\32\129\32\129\32\129\129\32\129\32\129\32\131\131\129\130\131\32\32\32\129\130\131\32\32\32\32\140\140\132", "\32\154\32\159\143\32\149\143\32\159\143\32\159\144\149\159\143\32\159\137\145\159\143\144\149\143\32\32\145\32\32\32\145\149\32\144\32\149\32\143\159\32\143\143\32\159\143\32", "\32\32\32\152\140\149\151\32\149\149\32\145\149\130\149\157\140\133\32\149\32\154\143\149\151\32\149\32\149\32\144\32\149\149\153\32\32\149\32\149\133\149\149\32\149\149\32\149", "\32\32\32\130\131\129\131\131\32\130\131\32\130\131\129\130\131\129\32\129\32\140\140\129\129\32\129\32\129\32\137\140\129\130\32\129\32\130\32\129\32\129\129\32\129\130\131\32", "\144\143\32\159\144\144\144\143\32\159\143\144\159\138\32\144\32\144\144\32\144\144\32\144\144\32\144\144\32\144\143\143\144\32\150\129\32\149\32\130\150\32\134\137\134\134\131\148", "\136\143\133\154\141\149\151\32\129\137\140\144\32\149\32\149\32\149\154\159\133\149\148\149\157\153\32\154\143\149\159\134\32\130\148\32\32\149\32\32\151\129\32\32\32\32\134\32", "\133\32\32\32\32\133\129\32\32\131\131\32\32\130\32\130\131\129\32\129\32\130\131\129\129\32\129\140\140\129\131\131\129\32\130\129\32\129\32\130\129\32\32\32\32\32\129\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32\32\32\32\32\149\32\32\149\32\32\32\32", "\32\32\32\32\32\32\32\32\32\32\32\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\32\32\32\32\32\32\32\32\32\32\32", "\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32\32\149\32", "\32\32\32\32\145\32\159\139\32\151\131\132\155\143\132\134\135\145\32\149\32\158\140\129\130\130\32\152\147\155\157\134\32\32\144\144\32\32\32\32\32\32\152\131\155\131\131\129", "\32\32\32\32\149\32\149\32\145\148\131\32\149\32\149\140\157\132\32\148\32\137\155\149\32\32\32\149\154\149\137\142\32\153\153\32\131\131\149\131\131\129\149\135\145\32\32\32", "\32\32\32\32\129\32\130\135\32\131\131\129\134\131\132\32\129\32\32\129\32\131\131\32\32\32\32\130\131\129\32\32\32\32\129\129\32\32\32\32\32\32\130\131\129\32\32\32", "\150\150\32\32\148\32\134\32\32\132\32\32\134\32\32\144\32\144\150\151\149\32\32\32\32\32\32\145\32\32\152\140\144\144\144\32\133\151\129\133\151\129\132\151\129\32\145\32", "\130\129\32\131\151\129\141\32\32\142\32\32\32\32\32\149\32\149\130\149\149\32\143\32\32\32\32\142\132\32\154\143\133\157\153\132\151\150\148\151\158\132\151\150\148\144\130\148", "\32\32\32\140\140\132\32\32\32\32\32\32\32\32\32\151\131\32\32\129\129\32\32\32\32\134\32\32\32\32\32\32\32\129\129\32\129\32\129\129\130\129\129\32\129\130\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\150\151\129\150\131\132\140\143\144\143\141\145\137\140\148\141\141\144\157\142\32\159\140\32\151\134\32\157\141\32", "\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\157\140\149\151\151\32\154\143\132\157\140\32\157\140\32\157\140\32\157\140\32\32\149\32\32\149\32\32\149\32\32\149\32", "\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\32\129\129\131\129\32\134\32\131\131\129\131\131\129\131\131\129\131\131\129\130\131\32\130\131\32\130\131\32\130\131\32", "\151\131\148\152\137\145\155\140\144\152\142\145\153\140\132\153\137\32\154\142\144\155\159\132\150\156\148\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\136\32\151\140\132", "\151\32\149\151\155\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\152\137\144\157\129\149\149\32\149\149\32\149\149\32\149\149\32\149\130\150\32\32\157\129\149\32\149", "\131\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\129\32\130\131\32\133\131\32", "\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\159\142\32\159\159\144\152\140\144\156\143\32\159\141\129\153\140\132\157\141\32\130\145\32\32\147\32\136\153\32\130\146\32", "\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\152\140\149\149\157\134\154\143\132\157\140\133\157\140\133\157\140\133\157\140\133\32\149\32\32\149\32\32\149\32\32\149\32", "\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\131\129\130\130\131\32\134\32\130\131\129\130\131\129\130\131\129\130\131\129\32\129\32\32\129\32\32\129\32\32\129\32", "\159\134\144\137\137\32\156\143\32\159\141\129\153\140\132\153\137\32\157\141\32\32\132\32\159\143\32\147\32\144\144\130\145\136\137\32\146\130\144\144\130\145\130\138\32\146\130\144", "\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\149\32\149\131\147\129\138\134\149\149\32\149\149\32\149\149\32\149\149\32\149\154\143\149\32\157\129\154\143\149", "\130\131\32\129\32\129\130\131\32\130\131\32\130\131\32\130\131\32\130\131\32\32\32\32\130\131\32\130\131\129\130\131\129\130\131\129\130\131\129\140\140\129\130\131\32\140\140\129" }, {[[000110000110110000110010101000000010000000100101]], [[000000110110000000000010101000000010000000100101]], [[000000000000000000000000000000000000000000000000]], [[100010110100000010000110110000010100000100000110]], [[000000110000000010110110000110000000000000110000]], [[000000000000000000000000000000000000000000000000]], [[000000110110000010000000100000100000000000000010]], [[000000000110110100010000000010000000000000000100]], [[000000000000000000000000000000000000000000000000]], [[010000000000100110000000000000000000000110010000]], [[000000000000000000000000000010000000010110000000]], [[000000000000000000000000000000000000000000000000]], [[011110110000000100100010110000000100000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110000110110000000000000000000010100100010000000]], [[000010000000000000110110000000000100010010000000]], [[000000000000000000000000000000000000000000000000]], [[010110010110100110110110010000000100000110110110]], [[000000000000000000000110000000000110000000000000]], [[000000000000000000000000000000000000000000000000]], [[010100010110110000000000000000110000000010000000]], [[110110000000000000110000110110100000000010000000]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[000000000000000000000000000000000000000000000000]], [[000100011111000100011111000100011111000100011111]], [[000000000000100100100100011011011011111111111111]], [[100100100100100100100100100100100100100100100100]], [[000000110100110110000010000011110000000000011000]], [[000000000100000000000010000011000110000000001000]], [[000000000000000000000000000000000000000000000000]], [[010000100100000000000000000100000000010010110000]], [[000000000000000000000000000000110110110110110000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110000000110110110110110110110110]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[000000000000110110000110010000000000000000010010]], [[000010000000000000000000000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110110110110000000000000]], [[000000000000000000000110000000000000000000000000]], [[000000000000000000000000000000000000000000000000]], [[110110110110110110110000110000000000000000010000]], [[000000000000000000000000100000000000000110000110]], [[000000000000000000000000000000000000000000000000]] }} + +--### Genarate fonts using 3x3 chars per a character. (1 character is 6x9 pixels) +local fonts = {} +local firstFont = {} +do + local char = 0 + local height = #rawFont[1] + local length = #rawFont[1][1] + for i = 1, height, 3 do + for j = 1, length, 3 do + local thisChar = string.char(char) + + local temp = {} + temp[1] = rawFont[1][i]:sub(j, j + 2) + temp[2] = rawFont[1][i + 1]:sub(j, j + 2) + temp[3] = rawFont[1][i + 2]:sub(j, j + 2) + + local temp2 = {} + temp2[1] = rawFont[2][i]:sub(j, j + 2) + temp2[2] = rawFont[2][i + 1]:sub(j, j + 2) + temp2[3] = rawFont[2][i + 2]:sub(j, j + 2) + + firstFont[thisChar] = {temp, temp2} + char = char + 1 + end + end + fonts[1] = firstFont +end + +local function generateFontSize(size,yeld) + local inverter = {["0"] = "1", ["1"] = "0"} --:gsub("[01]",inverter) + if size<= #fonts then return true end + for f = #fonts+1, size do + --automagicly make bigger fonts using firstFont and fonts[f-1]. + local nextFont = {} + local lastFont = fonts[f - 1] + for char = 0, 255 do + local thisChar = string.char(char) + --sleep(0) print(f,thisChar) + + local temp = {} + local temp2 = {} + + local templateChar = lastFont[thisChar][1] + local templateBack = lastFont[thisChar][2] + for i = 1, #templateChar do + local line1, line2, line3, back1, back2, back3 = {}, {}, {}, {}, {}, {} + for j = 1, #templateChar[1] do + local currentChar = firstFont[templateChar[i]:sub(j, j)][1] + table.insert(line1, currentChar[1]) + table.insert(line2, currentChar[2]) + table.insert(line3, currentChar[3]) + + local currentBack = firstFont[templateChar[i]:sub(j, j)][2] + if templateBack[i]:sub(j, j) == "1" then + table.insert(back1, (currentBack[1]:gsub("[01]", inverter))) + table.insert(back2, (currentBack[2]:gsub("[01]", inverter))) + table.insert(back3, (currentBack[3]:gsub("[01]", inverter))) + else + table.insert(back1, currentBack[1]) + table.insert(back2, currentBack[2]) + table.insert(back3, currentBack[3]) + end + end + table.insert(temp, table.concat(line1)) + table.insert(temp, table.concat(line2)) + table.insert(temp, table.concat(line3)) + table.insert(temp2, table.concat(back1)) + table.insert(temp2, table.concat(back2)) + table.insert(temp2, table.concat(back3)) + end + + nextFont[thisChar] = {temp, temp2} + if yeld then yeld = "Font"..f.."Yeld"..char os.queueEvent(yeld) os.pullEvent(yeld) end + end + fonts[f] = nextFont + end + return true +end + +generateFontSize(3,false) + +local function makeText(nSize, sString, nFC, nBC, bBlit) + if not type(sString) == "string" then error("Not a String",3) end --this should never happend with expects in place. + print(tHex, nFC) + local cFC = type(nFC) == "string" and nFC:sub(1, 1) or tHex[nFC] or error("Wrong Front Color",3) + local cBC = type(nBC) == "string" and nBC:sub(1, 1) or tHex[nBC] or error("Wrong Back Color",3) + local font = fonts[nSize] or error("Wrong font size selected",3) + if sString == "" then return {{""}, {""}, {""}} end + + local input = {} + for i in sString:gmatch('.') do table.insert(input, i) end + + local tText = {} + local height = #font[input[1]][1] + + + for nLine = 1, height do + local outLine = {} + for i = 1, #input do + outLine[i] = font[input[i]] and font[input[i]][1][nLine] or "" + end + tText[nLine] = table.concat(outLine) + end + + local tFront = {} + local tBack = {} + local tFrontSub = {["0"] = cFC, ["1"] = cBC} + local tBackSub = {["0"] = cBC, ["1"] = cFC} + + for nLine = 1, height do + local front = {} + local back = {} + for i = 1, #input do + local template = font[input[i]] and font[input[i]][2][nLine] or "" + front[i] = template:gsub("[01]", bBlit and {["0"] = nFC:sub(i, i), ["1"] = nBC:sub(i, i)} or tFrontSub) + back[i] = template:gsub("[01]", bBlit and {["0"] = nBC:sub(i, i), ["1"] = nFC:sub(i, i)} or tBackSub) + end + tFront[nLine] = table.concat(front) + tBack[nLine] = table.concat(back) + end + + return {tText, tFront, tBack} +end \ No newline at end of file diff --git a/source/project/lib/utils.lua b/source/project/lib/utils.lua index f9f3c7a..7cad2d9 100644 --- a/source/project/lib/utils.lua +++ b/source/project/lib/utils.lua @@ -1,13 +1,13 @@ -local function getTextHorizontalAlign(text, width, textAlign) +local function getTextHorizontalAlign(text, width, textAlign, replaceChar) text = string.sub(text, 1, width) local offset = width - string.len(text) if (textAlign == "right") then - text = string.rep(" ", offset) .. text + text = string.rep(replaceChar or " ", offset) .. text elseif (textAlign == "center") then - text = string.rep(" ", math.floor(offset / 2)) .. text .. string.rep(" ", math.floor(offset / 2)) - text = text .. (string.len(text) < width and " " or "") + text = string.rep(replaceChar or " ", math.floor(offset / 2)) .. text .. string.rep(replaceChar or " ", math.floor(offset / 2)) + text = text .. (string.len(text) < width and (replaceChar or " ") or "") else - text = text .. string.rep(" ", offset) + text = text .. string.rep(replaceChar or " ", offset) end return text end @@ -34,4 +34,126 @@ local function rpairs(t) return i, t[i] end end, t, #t + 1 +end + +local function shrink(bLittleData, bgColor) + + -- shrinkSystem is copy pasted (and slightly changed) from blittle by Bomb Bloke: http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/ + local relations = { [0] = { 8, 4, 3, 6, 5 }, { 4, 14, 8, 7 }, { 6, 10, 8, 7 }, { 9, 11, 8, 0 }, { 1, 14, 8, 0 }, { 13, 12, 8, 0 }, { 2, 10, 8, 0 }, { 15, 8, 10, 11, 12, 14 }, + { 0, 7, 1, 9, 2, 13 }, { 3, 11, 8, 7 }, { 2, 6, 7, 15 }, { 9, 3, 7, 15 }, { 13, 5, 7, 15 }, { 5, 12, 8, 7 }, { 1, 4, 7, 15 }, { 7, 10, 11, 12, 14 } } + + local colourNum, exponents, colourChar = {}, {}, {} + for i = 0, 15 do + exponents[2 ^ i] = i + end + do + local hex = "0123456789abcdef" + for i = 1, 16 do + colourNum[hex:sub(i, i)] = i - 1 + colourNum[i - 1] = hex:sub(i, i) + colourChar[hex:sub(i, i)] = 2 ^ (i - 1) + colourChar[2 ^ (i - 1)] = hex:sub(i, i) + + local thisRel = relations[i - 1] + for i = 1, #thisRel do + thisRel[i] = 2 ^ thisRel[i] + end + end + end + + local function getBestColourMatch(usage) + local lastCol = relations[exponents[usage[#usage][1]]] + if(lastCol~=nil)then + for j = 1, #lastCol do + local thisRelation = lastCol[j] + for i = 1, #usage - 1 do + if usage[i][1] == thisRelation then + return i + end + end + end + end + return 1 + end + + local function colsToChar(pattern, totals) + if not totals then + local newPattern = {} + totals = {} + for i = 1, 6 do + local thisVal = pattern[i] + local thisTot = totals[thisVal] + totals[thisVal], newPattern[i] = thisTot and (thisTot + 1) or 1, thisVal + end + pattern = newPattern + end + + local usage = {} + for key, value in pairs(totals) do + usage[#usage + 1] = { key, value } + end + + if #usage > 1 then + -- Reduce the chunk to two colours: + while #usage > 2 do + table.sort(usage, function(a, b) + return a[2] > b[2] + end) + local matchToInd, usageLen = getBestColourMatch(usage), #usage + local matchFrom, matchTo = usage[usageLen][1], usage[matchToInd][1] + for i = 1, 6 do + if pattern[i] == matchFrom then + pattern[i] = matchTo + usage[matchToInd][2] = usage[matchToInd][2] + 1 + end + end + usage[usageLen] = nil + end + + -- Convert to character. Adapted from oli414's function: + -- http://www.computercraft.info/forums2/index.php?/topic/25340-cc-176-easy-drawing-characters/ + local data = 128 + for i = 1, #pattern - 1 do + if pattern[i] ~= pattern[6] then + data = data + 2 ^ (i - 1) + end + end + return string.char(data), colourChar[usage[1][1] == pattern[6] and usage[2][1] or usage[1][1]], colourChar[pattern[6]] + else + -- Solid colour character: + return "\128", colourChar[pattern[1]], colourChar[pattern[1]] + end + end + + local results, width, height, bgCol = { {}, {}, {} }, 0, #bLittleData + #bLittleData % 3, bgColor or colors.black + for i = 1, #bLittleData do + if #bLittleData[i] > width then + width = #bLittleData[i] + end + end + + for y = 0, height - 1, 3 do + local cRow, tRow, bRow, counter = {}, {}, {}, 1 + + for x = 0, width - 1, 2 do + -- Grab a 2x3 chunk: + local pattern, totals = {}, {} + + for yy = 1, 3 do + for xx = 1, 2 do + pattern[#pattern + 1] = (bLittleData[y + yy] and bLittleData[y + yy][x + xx]) and (bLittleData[y + yy][x + xx] == 0 and bgCol or bLittleData[y + yy][x + xx]) or bgCol + totals[pattern[#pattern]] = totals[pattern[#pattern]] and (totals[pattern[#pattern]] + 1) or 1 + end + end + + cRow[counter], tRow[counter], bRow[counter] = colsToChar(pattern, totals) + counter = counter + 1 + end + + results[1][#results[1] + 1], results[2][#results[2] + 1], results[3][#results[3] + 1] = table.concat(cRow), table.concat(tRow), table.concat(bRow) + end + + results.width, results.height = #results[1][1], #results[1] + + return results end \ No newline at end of file diff --git a/source/project/objects/Label.lua b/source/project/objects/Label.lua index 4638064..b0d32ec 100644 --- a/source/project/objects/Label.lua +++ b/source/project/objects/Label.lua @@ -4,12 +4,15 @@ local function Label(name) local objectType = "Label" base:setZIndex(3) + base.fgColor = colors.white + base.bgcolor = colors.black local autoWidth = true base:setValue("") local textHorizontalAlign = "left" local textVerticalAlign = "top" + local fontsize = 0 local object = { getType = function(self) @@ -31,6 +34,17 @@ local function Label(name) return self end; + setFontSize = function(self, size) + if(size>0)and(size<=4)then + fontsize = size-1 or 0 + end + return self + end; + + getFontSize = function(self) + return fontsize+1 + end; + setSize = function(self, width, height) base.setSize(self, width, height) autoWidth = false @@ -46,9 +60,27 @@ local function Label(name) self.parent:drawBackgroundBox(obx, oby, self.width, self.height, self.bgColor) self.parent:drawForegroundBox(obx, oby, self.width, self.height, self.fgColor) self.parent:drawTextBox(obx, oby, self.width, self.height, " ") - for n = 1, self.height do - if (n == verticalAlign) then - self.parent:writeText(obx, oby + (n - 1), getTextHorizontalAlign(self:getValue(), self.width, textHorizontalAlign), self.bgColor, self.fgColor) + if(fontsize==0)then + for n = 1, self.height do + if (n == verticalAlign) then + self.parent:writeText(obx, oby + (n - 1), getTextHorizontalAlign(self:getValue(), self.width, textHorizontalAlign), self.bgColor, self.fgColor) + end + end + else + local tData = makeText(fontsize, self:getValue(), self.fgColor, self.bgColor) + for n = 1, self.height do + if (n == verticalAlign) then + local oX, oY = self.parent:getSize() + local cX, cY = #tData[1][1], #tData[1] + obx = obx or math.floor((oX - cX) / 2) + 1 + oby = oby or math.floor((oY - cY) / 2) + 1 + + for i = 1, cY do + self.parent:setFG(obx, oby + i + n - 2, getTextHorizontalAlign(tData[2][i], self.width, textHorizontalAlign)) + self.parent:setBG(obx, oby + i + n - 2, getTextHorizontalAlign(tData[3][i], self.width, textHorizontalAlign, tHex[self.bgColor])) + self.parent:setText(obx, oby + i + n - 2, getTextHorizontalAlign(tData[1][i], self.width, textHorizontalAlign)) + end + end end end end diff --git a/source/project/objects/example.lua b/source/project/objects/example.lua index 6490553..288b904 100644 --- a/source/project/objects/example.lua +++ b/source/project/objects/example.lua @@ -4,7 +4,7 @@ local function Example(name) -- you can call this function how you want, doesn't -- here you could set some default values, but its not necessary, it doesn't matter if you call the functions or change the values directly, maybe i should change that --i guess its better if you call functions base:setBackground, base:setSize and so on. - base.width = 3 + base.width = 12 base.height = 1 base.bgColor = colors.lightGray base.fgColor = colors.gray @@ -31,8 +31,8 @@ local function Example(name) -- you can call this function how you want, doesn't if (base.draw(self)) then if (self.parent ~= nil) then local obx, oby = self:getAnchorPosition() - --self.parent:setBackground(obx, oby, self.width, self.height, self.bgColor) -- changes the background color of that object - --self.parent:setForeground(obx, oby, self.width, self.height, self.fgColor) -- changes the foreground (textcolor) color of that object + --self.parent:drawBackgroundbox(obx, oby, self.width, self.height, self.bgColor) -- changes the background color of that object + --self.parent:drawForegroundbox(obx, oby, self.width, self.height, self.fgColor) -- changes the foreground (textcolor) color of that object --self.parent:writeText(obx, oby, "Some Text", self.bgColor, self.fgColor) -- writes something on the screen, also able to change its bgcolor and fgcolor --the draw functions always gets called after something got visually changed. I am always redrawing the entire screen, but only if something has changed.