Advertisement
Oeed

Bedrock

Jul 18th, 2014
5,147
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 85.85 KB | None | 0 0
  1. --Bedrock Build: 483
  2. --This code is squished down in to one, rather hard to read file.
  3. --As such it is not much good for anything other than being loaded as an API.
  4. --If you want to look at the code to learn from it, copy parts or just take a look,
  5. --you should go to the GitHub repo. http://github.com/oeed/Bedrock/
  6.  
  7. --
  8. --      Bedrock is the core program framework used by all OneOS and OneCode programs.
  9. --                          Inspired by Apple's Cocoa framework.
  10. --                                     (c) oeed 2014
  11. --
  12. --        For documentation see the Bedrock wiki, github.com/oeed/Bedrock/wiki/
  13. --
  14.  
  15. local apis = {
  16. ["Drawing"] = [[
  17. local round = function(num, idp)
  18.     local mult = 10^(idp or 0)
  19.     return math.floor(num * mult + 0.5) / mult
  20. end
  21.  
  22. local _w, _h = term.getSize()
  23. local copyBuffer = nil
  24.  
  25. Screen = {
  26.     Width = _w,
  27.     Height = _h
  28. }
  29.  
  30. Constraints = {
  31.    
  32. }
  33.  
  34. CurrentConstraint = {1,1,_w,_h}
  35. IgnoreConstraint = false
  36.  
  37. function AddConstraint(x, y, width, height)
  38.     local x2 = x + width - 1
  39.     local y2 = y + height - 1
  40.     table.insert(Drawing.Constraints, {x, y, x2, y2})
  41.     Drawing.GetConstraint()
  42. end
  43.  
  44. function RemoveConstraint()
  45.     --table.remove(Drawing.Constraints, #Drawing.Constraints)
  46.     Drawing.Constraints[#Drawing.Constraints] = nil
  47.     Drawing.GetConstraint()
  48. end
  49.  
  50. function GetConstraint()
  51.     local x = 1
  52.     local y = 1
  53.     local x2 = Drawing.Screen.Width
  54.     local y2 = Drawing.Screen.Height
  55.     for i, c in ipairs(Drawing.Constraints) do
  56.         if x < c[1] then
  57.             x = c[1]
  58.         end
  59.         if y < c[2] then
  60.             y = c[2]
  61.         end
  62.         if x2 > c[3] then
  63.             x2 = c[3]
  64.         end
  65.         if y2 > c[4] then
  66.             y2 = c[4]
  67.         end
  68.     end
  69.     Drawing.CurrentConstraint = {x, y, x2, y2}
  70. end
  71.  
  72. function WithinContraint(x, y)
  73.     return Drawing.IgnoreConstraint or
  74.           (x >= Drawing.CurrentConstraint[1] and
  75.            y >= Drawing.CurrentConstraint[2] and
  76.            x <= Drawing.CurrentConstraint[3] and
  77.            y <= Drawing.CurrentConstraint[4])
  78. end
  79.  
  80. colours.transparent = 0
  81. colors.transparent = 0
  82.  
  83. Filters = {
  84.     None = {
  85.         [colours.white] = colours.white,
  86.         [colours.orange] = colours.orange,
  87.         [colours.magenta] = colours.magenta,
  88.         [colours.lightBlue] = colours.lightBlue,
  89.         [colours.yellow] = colours.yellow,
  90.         [colours.lime] = colours.lime,
  91.         [colours.pink] = colours.pink,
  92.         [colours.grey] = colours.grey,
  93.         [colours.lightGrey] = colours.lightGrey,
  94.         [colours.cyan] = colours.cyan,
  95.         [colours.purple] = colours.purple,
  96.         [colours.blue] = colours.blue,
  97.         [colours.brown] = colours.brown,
  98.         [colours.green] = colours.green,
  99.         [colours.red] = colours.red,
  100.         [colours.black] = colours.black,
  101.         [colours.transparent] = colours.transparent,
  102.     },
  103.  
  104.     Greyscale = {
  105.         [colours.white] = colours.white,
  106.         [colours.orange] = colours.lightGrey,
  107.         [colours.magenta] = colours.lightGrey,
  108.         [colours.lightBlue] = colours.lightGrey,
  109.         [colours.yellow] = colours.lightGrey,
  110.         [colours.lime] = colours.lightGrey,
  111.         [colours.pink] = colours.lightGrey,
  112.         [colours.grey] = colours.grey,
  113.         [colours.lightGrey] = colours.lightGrey,
  114.         [colours.cyan] = colours.grey,
  115.         [colours.purple] = colours.grey,
  116.         [colours.blue] = colours.grey,
  117.         [colours.brown] = colours.grey,
  118.         [colours.green] = colours.grey,
  119.         [colours.red] = colours.grey,
  120.         [colours.black] = colours.black,
  121.         [colours.transparent] = colours.transparent,
  122.     },
  123.  
  124.     BlackWhite = {
  125.         [colours.white] = colours.white,
  126.         [colours.orange] = colours.white,
  127.         [colours.magenta] = colours.white,
  128.         [colours.lightBlue] = colours.white,
  129.         [colours.yellow] = colours.white,
  130.         [colours.lime] = colours.white,
  131.         [colours.pink] = colours.white,
  132.         [colours.grey] = colours.black,
  133.         [colours.lightGrey] = colours.white,
  134.         [colours.cyan] = colours.black,
  135.         [colours.purple] = colours.black,
  136.         [colours.blue] = colours.black,
  137.         [colours.brown] = colours.black,
  138.         [colours.green] = colours.black,
  139.         [colours.red] = colours.black,
  140.         [colours.black] = colours.black,
  141.         [colours.transparent] = colours.transparent,
  142.     },
  143.  
  144.     Darker = {
  145.         [colours.white] = colours.lightGrey,
  146.         [colours.orange] = colours.red,
  147.         [colours.magenta] = colours.purple,
  148.         [colours.lightBlue] = colours.cyan,
  149.         [colours.yellow] = colours.orange,
  150.         [colours.lime] = colours.green,
  151.         [colours.pink] = colours.magenta,
  152.         [colours.grey] = colours.black,
  153.         [colours.lightGrey] = colours.grey,
  154.         [colours.cyan] = colours.blue,
  155.         [colours.purple] = colours.grey,
  156.         [colours.blue] = colours.grey,
  157.         [colours.brown] = colours.grey,
  158.         [colours.green] = colours.grey,
  159.         [colours.red] = colours.brown,
  160.         [colours.black] = colours.black,
  161.         [colours.transparent] = colours.transparent,
  162.     },
  163.  
  164.     Lighter = {
  165.         [colours.white] = colours.lightGrey,
  166.         [colours.orange] = colours.yellow,
  167.         [colours.magenta] = colours.pink,
  168.         [colours.lightBlue] = colours.cyan,
  169.         [colours.yellow] = colours.orange,
  170.         [colours.lime] = colours.green,
  171.         [colours.pink] = colours.magenta,
  172.         [colours.grey] = colours.lightGrey,
  173.         [colours.lightGrey] = colours.grey,
  174.         [colours.cyan] = colours.lightBlue,
  175.         [colours.purple] = colours.magenta,
  176.         [colours.blue] = colours.lightBlue,
  177.         [colours.brown] = colours.red,
  178.         [colours.green] = colours.lime,
  179.         [colours.red] = colours.orange,
  180.         [colours.black] = colours.grey,
  181.         [colours.transparent] = colours.transparent,
  182.     },
  183.  
  184.     Highlight = {
  185.         [colours.white] = colours.lightGrey,
  186.         [colours.orange] = colours.yellow,
  187.         [colours.magenta] = colours.pink,
  188.         [colours.lightBlue] = colours.cyan,
  189.         [colours.yellow] = colours.orange,
  190.         [colours.lime] = colours.green,
  191.         [colours.pink] = colours.magenta,
  192.         [colours.grey] = colours.lightGrey,
  193.         [colours.lightGrey] = colours.grey,
  194.         [colours.cyan] = colours.lightBlue,
  195.         [colours.purple] = colours.magenta,
  196.         [colours.blue] = colours.lightBlue,
  197.         [colours.brown] = colours.red,
  198.         [colours.green] = colours.lime,
  199.         [colours.red] = colours.orange,
  200.         [colours.black] = colours.grey,
  201.         [colours.transparent] = colours.transparent,
  202.     },
  203.  
  204.     Invert = {
  205.         [colours.white] = colours.black,
  206.         [colours.orange] = colours.blue,
  207.         [colours.magenta] = colours.green,
  208.         [colours.lightBlue] = colours.brown,
  209.         [colours.yellow] = colours.blue,
  210.         [colours.lime] = colours.purple,
  211.         [colours.pink] = colours.green,
  212.         [colours.grey] = colours.lightGrey,
  213.         [colours.lightGrey] = colours.grey,
  214.         [colours.cyan] = colours.red,
  215.         [colours.purple] = colours.green,
  216.         [colours.blue] = colours.yellow,
  217.         [colours.brown] = colours.lightBlue,
  218.         [colours.green] = colours.purple,
  219.         [colours.red] = colours.cyan,
  220.         [colours.black] = colours.white,
  221.         [colours.transparent] = colours.transparent,
  222.     },
  223. }
  224.  
  225. function FilterColour(colour, filter)
  226.     if filter[colour] then
  227.         return filter[colour]
  228.     else
  229.         return colour
  230.     end
  231. end
  232.  
  233. DrawCharacters = function (x, y, characters, textColour, bgColour)
  234.     Drawing.WriteStringToBuffer(x, y, tostring(characters), textColour, bgColour)
  235. end
  236.  
  237. DrawBlankArea = function (x, y, w, h, colour)
  238.     if colour ~= colours.transparent then
  239.         Drawing.DrawArea (x, y, w, h, " ", 1, colour)
  240.     end
  241. end
  242.  
  243. DrawArea = function (x, y, w, h, character, textColour, bgColour)
  244.     --width must be greater than 1, otherwise we get problems
  245.     if w < 0 then
  246.         w = w * -1
  247.     elseif w == 0 then
  248.         w = 1
  249.     end
  250.  
  251.     for ix = 1, w do
  252.         local currX = x + ix - 1
  253.         for iy = 1, h do
  254.             local currY = y + iy - 1
  255.             Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour)
  256.         end
  257.     end
  258. end
  259.  
  260. DrawImage = function(_x,_y,tImage, w, h)
  261.     if tImage then
  262.         for y = 1, h do
  263.             if not tImage[y] then
  264.                 break
  265.             end
  266.             for x = 1, w do
  267.                 if not tImage[y][x] then
  268.                     break
  269.                 end
  270.                 local bgColour = tImage[y][x]
  271.                 local textColour = tImage.textcol[y][x] or colours.white
  272.                 local char = tImage.text[y][x]
  273.                 Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour)
  274.             end
  275.         end
  276.     elseif w and h then
  277.         Drawing.DrawBlankArea(_x, _y, w, h, colours.lightGrey)
  278.     end
  279. end
  280.  
  281. --using .nft
  282. LoadImage = function(path, global)
  283.     local image = {
  284.         text = {},
  285.         textcol = {}
  286.     }
  287.     if fs.exists(path) then
  288.         local _io = io
  289.         if OneOS and global then
  290.             _io = OneOS.IO
  291.         end
  292.         local file = _io.open(path, "r")
  293.         if not file then
  294.             error('Error Occured. _io:'..tostring(_io)..' OneOS: '..tostring(OneOS)..' OneOS.IO'..tostring(OneOS.IO)..' io: '..tostring(io))
  295.         end
  296.         local sLine = file:read()
  297.         local num = 1
  298.         while sLine do  
  299.             table.insert(image, num, {})
  300.             table.insert(image.text, num, {})
  301.             table.insert(image.textcol, num, {})
  302.                                        
  303.             --As we're no longer 1-1, we keep track of what index to write to
  304.             local writeIndex = 1
  305.             --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour
  306.             local bgNext, fgNext = false, false
  307.             --The current background and foreground colours
  308.             local currBG, currFG = nil,nil
  309.             for i=1,#sLine do
  310.                     local nextChar = string.sub(sLine, i, i)
  311.                     if nextChar:byte() == 30 then
  312.                             bgNext = true
  313.                     elseif nextChar:byte() == 31 then
  314.                             fgNext = true
  315.                     elseif bgNext then
  316.                             currBG = Drawing.GetColour(nextChar)
  317.                             if currBG == nil then
  318.                                 currBG = colours.transparent
  319.                             end
  320.                             bgNext = false
  321.                     elseif fgNext then
  322.                             currFG = Drawing.GetColour(nextChar)
  323.                             if currFG == nil or currFG == colours.transparent then
  324.                                 currFG = colours.white
  325.                             end
  326.                             fgNext = false
  327.                     else
  328.                             if nextChar ~= " " and currFG == nil then
  329.                                     currFG = colours.white
  330.                             end
  331.                             image[num][writeIndex] = currBG
  332.                             image.textcol[num][writeIndex] = currFG
  333.                             image.text[num][writeIndex] = nextChar
  334.                             writeIndex = writeIndex + 1
  335.                     end
  336.             end
  337.             num = num+1
  338.             sLine = file:read()
  339.         end
  340.         file:close()
  341.     else
  342.         return nil
  343.     end
  344.     return image
  345. end
  346.  
  347. DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour)
  348.     w = w or Drawing.Screen.Width
  349.     h = h or Drawing.Screen.Height
  350.     x = x or 0
  351.     y = y or 0
  352.     x = math.floor((w - #characters) / 2) + x
  353.     y = math.floor(h / 2) + y
  354.  
  355.     Drawing.DrawCharacters(x, y, characters, textColour, bgColour)
  356. end
  357.  
  358. GetColour = function(hex)
  359.     if hex == ' ' then
  360.         return colours.transparent
  361.     end
  362.     local value = tonumber(hex, 16)
  363.     if not value then return nil end
  364.     value = math.pow(2,value)
  365.     return value
  366. end
  367.  
  368. Clear = function (_colour)
  369.     _colour = _colour or colours.black
  370.     Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour)
  371. end
  372.  
  373. Buffer = {}
  374. BackBuffer = {}
  375.  
  376. TryRestore = false
  377.  
  378.  
  379. --TODO: make this quicker
  380. -- maybe sort the pixels in order of colour so it doesn't have to set the colour each time
  381. DrawBuffer = function()
  382.     if TryRestore and Restore then
  383.         Restore()
  384.     end
  385.  
  386.     for y,row in pairs(Drawing.Buffer) do
  387.         for x,pixel in pairs(row) do
  388.             local shouldDraw = true
  389.             local hasBackBuffer = true
  390.             if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then
  391.                 hasBackBuffer = false
  392.             end
  393.             if hasBackBuffer and Drawing.BackBuffer[y][x][1] == Drawing.Buffer[y][x][1] and Drawing.BackBuffer[y][x][2] == Drawing.Buffer[y][x][2] and Drawing.BackBuffer[y][x][3] == Drawing.Buffer[y][x][3] then
  394.                 shouldDraw = false
  395.             end
  396.             if shouldDraw then
  397.                 term.setBackgroundColour(pixel[3])
  398.                 term.setTextColour(pixel[2])
  399.                 term.setCursorPos(x, y)
  400.                 term.write(pixel[1])
  401.             end
  402.         end
  403.     end
  404.     Drawing.BackBuffer = Drawing.Buffer
  405.     Drawing.Buffer = {}
  406. end
  407.  
  408. ClearBuffer = function()
  409.     Drawing.Buffer = {}
  410. end
  411.  
  412. WriteStringToBuffer = function (x, y, characters, textColour,bgColour)
  413.     for i = 1, #characters do
  414.         local character = characters:sub(i,i)
  415.         Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour)
  416.     end
  417. end
  418.  
  419. WriteToBuffer = function(x, y, character, textColour,bgColour, cached)
  420.     if not cached and not Drawing.WithinContraint(x, y) then
  421.         return
  422.     end
  423.     x = round(x)
  424.     y = round(y)
  425.  
  426.     if textColour == colours.transparent then
  427.         character = ' '
  428.     end
  429.  
  430.     if bgColour == colours.transparent then
  431.         Drawing.Buffer[y] = Drawing.Buffer[y] or {}
  432.         Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {"", colours.white, colours.black}
  433.         Drawing.Buffer[y][x][1] = character
  434.         Drawing.Buffer[y][x][2] = textColour
  435.     else
  436.         Drawing.Buffer[y] = Drawing.Buffer[y] or {}
  437.         Drawing.Buffer[y][x] = {character, textColour, bgColour}
  438.     end
  439.  
  440.     if copyBuffer then
  441.         copyBuffer[y] = copyBuffer[y] or {}
  442.         copyBuffer[y][x] = {character, textColour, bgColour}       
  443.     end
  444. end
  445.  
  446. DrawCachedBuffer = function(buffer)
  447.     for y, row in pairs(buffer) do
  448.         for x, pixel in pairs(row) do
  449.             WriteToBuffer(x, y, pixel[1], pixel[2], pixel[3], true)
  450.         end
  451.     end
  452. end
  453.  
  454. StartCopyBuffer = function()
  455.     copyBuffer = {}
  456. end
  457.  
  458. EndCopyBuffer = function()
  459.     local tmpCopy = copyBuffer
  460.     copyBuffer = nil
  461.     return tmpCopy
  462. end
  463. ]],
  464. ["Helpers"] = [[
  465. LongestString = function(input, key, isKey)
  466.     local length = 0
  467.     if isKey then
  468.         for k, v in pairs(input) do
  469.             local titleLength = string.len(k)
  470.             if titleLength > length then
  471.                 length = titleLength
  472.             end
  473.         end
  474.     else
  475.         for i = 1, #input do
  476.             local value = input[i]
  477.             if key then
  478.                 if value[key] then
  479.                     value = value[key]
  480.                 else
  481.                     value = ''
  482.                 end
  483.             end
  484.             local titleLength = string.len(value)
  485.             if titleLength > length then
  486.                 length = titleLength
  487.             end
  488.         end
  489.     end
  490.     return length
  491. end
  492.  
  493. Split = function(str,sep)
  494.     sep=sep or'/'
  495.     return str:match("(.*"..sep..")")
  496. end
  497.  
  498. Extension = function(path, addDot)
  499.     if not path then
  500.         return nil
  501.     elseif not string.find(fs.getName(path), '%.') then
  502.         return ''
  503.     else
  504.         local _path = path
  505.         if path:sub(#path) == '/' then
  506.             _path = path:sub(1,#path-1)
  507.         end
  508.         local extension = _path:gmatch('%.[0-9a-z]+$')()
  509.         if extension then
  510.             extension = extension:sub(2)
  511.         else
  512.             --extension = nil
  513.             return ''
  514.         end
  515.         if addDot then
  516.             extension = '.'..extension
  517.         end
  518.         return extension:lower()
  519.     end
  520. end
  521.  
  522. RemoveExtension = function(path)
  523. --local name = string.match(fs.getName(path), '(%a+)%.?.-')
  524.     if not path:find('%.') then
  525.         return path
  526.     end
  527.     local extension = Helpers.Extension(path)
  528.     if extension == path then
  529.         return fs.getName(path)
  530.     end
  531.     return string.gsub(path, extension, ''):sub(1, -2)
  532. end
  533.  
  534. RemoveFileName = function(path)
  535.     if string.sub(path, -1) == '/' then
  536.         path = string.sub(path, 1, -2)
  537.     end
  538.     local v = string.match(path, "(.-)([^\\/]-%.?([^%.\\/]*))$")
  539.     if type(v) == 'string' then
  540.         return v
  541.     end
  542.     return v[1]
  543. end
  544.  
  545. TruncateString = function(sString, maxLength)
  546.     if #sString > maxLength then
  547.         sString = sString:sub(1,maxLength-3)
  548.         if sString:sub(-1) == ' ' then
  549.             sString = sString:sub(1,maxLength-4)
  550.         end
  551.         sString = sString  .. '...'
  552.     end
  553.     return sString
  554. end
  555.  
  556. TruncateStringStart = function(sString, maxLength)
  557.     local len = #sString
  558.     if #sString > maxLength then
  559.         sString = sString:sub(len - maxLength, len - 3)
  560.         if sString:sub(-1) == ' ' then
  561.             sString = sString:sub(len - maxLength, len - 4)
  562.         end
  563.         sString = '...' .. sString
  564.     end
  565.     return sString
  566. end
  567.  
  568. WrapText = function(text, maxWidth)
  569.     local lines = {''}
  570.     for word, space in text:gmatch('(%S+)(%s*)') do
  571.             local temp = lines[#lines] .. word .. space:gsub('\n','')
  572.             if #temp > maxWidth then
  573.                     table.insert(lines, '')
  574.             end
  575.             if space:find('\n') then
  576.                     lines[#lines] = lines[#lines] .. word
  577.                    
  578.                     space = space:gsub('\n', function()
  579.                             table.insert(lines, '')
  580.                             return ''
  581.                     end)
  582.             else
  583.                     lines[#lines] = lines[#lines] .. word .. space
  584.             end
  585.     end
  586.     if #lines[1] == 0 then
  587.         table.remove(lines,1)
  588.     end
  589.     return lines
  590. end
  591.  
  592. TidyPath = function(path)
  593.     path = '/'..path
  594.     if fs.exists(path) and fs.isDir(path) then
  595.         path = path .. '/'
  596.     end
  597.  
  598.     path, n = path:gsub("//", "/")
  599.     while n > 0 do
  600.         path, n = path:gsub("//", "/")
  601.     end
  602.     return path
  603. end
  604.  
  605. Capitalise = function(str)
  606.     return str:sub(1, 1):upper() .. str:sub(2, -1)
  607. end
  608.  
  609. Round = function(num, idp)
  610.     local mult = 10^(idp or 0)
  611.     return math.floor(num * mult + 0.5) / mult
  612. end
  613. ]],
  614. ["Object"] = [[
  615. X = 1
  616. Y = 1
  617. Width = 1
  618. Height = 1
  619. Parent = nil
  620. OnClick = nil
  621. Visible = true
  622. IgnoreClick = false
  623. Name = nil
  624. ClipDrawing = true
  625. UpdateDrawBlacklist = {}
  626. Fixed = false
  627. Ready = false
  628.  
  629. DrawCache = {}
  630.  
  631. NeedsDraw = function(self)
  632.     if not self.Visible then
  633.         return false
  634.     end
  635.    
  636.     if not self.DrawCache.Buffer or self.DrawCache.AlwaysDraw or self.DrawCache.NeedsDraw then
  637.         return true
  638.     end
  639.  
  640.     if self.OnNeedsUpdate then
  641.         if self.OnNeedsUpdate() then
  642.             return true
  643.         end
  644.     end
  645.  
  646.     if self.Children then
  647.         for i, v in ipairs(self.Children) do
  648.             if v:NeedsDraw() then
  649.                 return true
  650.             end
  651.         end
  652.     end
  653. end
  654.  
  655. GetPosition = function(self)
  656.     return self.Bedrock:GetAbsolutePosition(self)
  657. end
  658.  
  659. GetOffsetPosition = function(self)
  660.     if not self.Parent then
  661.         return {X = 1, Y = 1}
  662.     end
  663.  
  664.     local offset = {X = 0, Y = 0}
  665.     if not self.Fixed and self.Parent.ChildOffset then
  666.         offset = self.Parent.ChildOffset
  667.     end
  668.  
  669.     return {X = self.X + offset.X, Y = self.Y + offset.Y}
  670. end
  671.  
  672. Draw = function(self)
  673.     if not self.Visible then
  674.         return
  675.     end
  676.  
  677.     self.DrawCache.NeedsDraw = false
  678.     local pos = self:GetPosition()
  679.     Drawing.StartCopyBuffer()
  680.  
  681.     if self.ClipDrawing then
  682.         Drawing.AddConstraint(pos.X, pos.Y, self.Width, self.Height)
  683.     end
  684.  
  685.     if self.OnDraw then
  686.         self:OnDraw(pos.X, pos.Y)
  687.     end
  688.  
  689.     self.DrawCache.Buffer = Drawing.EndCopyBuffer()
  690.    
  691.     if self.Children then
  692.         for i, child in ipairs(self.Children) do
  693.             local pos = child:GetOffsetPosition()
  694.             if pos.Y + self.Height > 1 and pos.Y <= self.Height and pos.X + self.Width > 1 and pos.X <= self.Width then
  695.                 child:Draw()
  696.             end
  697.         end
  698.     end
  699.  
  700.  
  701.     if self.OnPostChildrenDraw then
  702.         self:OnPostChildrenDraw(pos.X, pos.Y)
  703.     end
  704.  
  705.     if self.ClipDrawing then
  706.         Drawing.RemoveConstraint()
  707.     end
  708. end
  709.  
  710. ForceDraw = function(self, ignoreChildren, ignoreParent, ignoreBedrock)
  711.     if not ignoreBedrock and self.Bedrock then
  712.         self.Bedrock:ForceDraw()
  713.     end
  714.     self.DrawCache.NeedsDraw = true
  715.     if not ignoreParent and self.Parent then
  716.         self.Parent:ForceDraw(true, nil, true)
  717.     end
  718.     if not ignoreChildren and self.Children then
  719.         for i, child in ipairs(self.Children) do
  720.             child:ForceDraw(nil, true, true)
  721.         end
  722.     end
  723. end
  724.  
  725. OnRemove = function(self)
  726.     if self == self.Bedrock:GetActiveObject() then
  727.         self.Bedrock:SetActiveObject()
  728.     end
  729. end
  730.  
  731. local function ParseColour(value)
  732.     if type(value) == 'string' then
  733.         if colours[value] and type(colours[value]) == 'number' then
  734.             return colours[value]
  735.         elseif colors[value] and type(colors[value]) == 'number' then
  736.             return colors[value]
  737.         end
  738.     elseif type(value) == 'number' and (value == colours.transparent or (value >= colours.white and value <= colours.black)) then
  739.         return value
  740.     end
  741.     error('Invalid colour: "'..tostring(value)..'"')
  742. end
  743.  
  744. Initialise = function(self, values)
  745.     local _new = values    -- the new instance
  746.     _new.DrawCache = {
  747.         NeedsDraw = true,
  748.         AlwaysDraw = false,
  749.         Buffer = nil
  750.     }
  751.     setmetatable(_new, {__index = self} )
  752.  
  753.     local new = {} -- the proxy
  754.     setmetatable(new, {
  755.         __index = function(t, k)
  756.             if k:find('Color') then
  757.                 k = k:gsub('Color', 'Colour')
  758.             end
  759.  
  760.             if k:find('Colour') and type(_new[k]) ~= 'table' and type(_new[k]) ~= 'function' then
  761.                 if _new[k] then
  762.                     return ParseColour(_new[k])
  763.                 end
  764.             elseif _new[k] ~= nil then
  765.                 return _new[k]
  766.             end
  767.         end,
  768.  
  769.         __newindex = function (t,k,v)
  770.             if k:find('Color') then
  771.                 k = k:gsub('Color', 'Colour')
  772.             end
  773.  
  774.             if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then
  775.                 v = new.Bedrock:ParseStringSize(new.Parent, k, v)
  776.             end
  777.  
  778.             if v ~= _new[k] then
  779.                 _new[k] = v
  780.                 if t.OnUpdate then
  781.                     t:OnUpdate(k)
  782.                 end
  783.  
  784.                 if t.UpdateDrawBlacklist[k] == nil then
  785.                     t:ForceDraw()
  786.                 end
  787.             end
  788.         end
  789.     })
  790.     if new.OnInitialise then
  791.         new:OnInitialise()
  792.     end
  793.  
  794.     return new
  795. end
  796.  
  797. AnimateValue = function(self, valueName, from, to, duration, done, tbl)
  798.     tbl = tbl or self
  799.     if type(tbl[valueName]) ~= 'number' then
  800.         error('Animated value ('..valueName..') must be number.')
  801.     elseif not self.Bedrock.AnimationEnabled then
  802.         tbl[valueName] = to
  803.         if done then
  804.             done()
  805.         end
  806.         return
  807.     end
  808.     from = from or tbl[valueName]
  809.     duration = duration or 0.2
  810.     local delta = to - from
  811.  
  812.     local startTime = os.clock()
  813.     local previousFrame = startTime
  814.     local frame
  815.     frame = function()
  816.         local time = os.clock()
  817.         local totalTime = time - startTime
  818.         local isLast = totalTime >= duration
  819.  
  820.         if isLast then
  821.             tbl[valueName] = to
  822.             self:ForceDraw()
  823.             if done then
  824.                 done()
  825.             end
  826.         else
  827.             tbl[valueName] = self.Bedrock.Helpers.Round(from + delta * (totalTime / duration))
  828.             self:ForceDraw()
  829.             self.Bedrock:StartTimer(function()
  830.                 frame()
  831.             end, 0.05)
  832.         end
  833.     end
  834.     frame()
  835. end
  836.  
  837. Click = function(self, event, side, x, y)
  838.     if self.Visible and not self.IgnoreClick then
  839.         if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then
  840.             return true
  841.         elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then
  842.             return true
  843.         elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then
  844.             return true
  845.         else
  846.             return false
  847.         end
  848.     else
  849.         return false
  850.     end
  851.  
  852. end
  853.  
  854. ToggleMenu = function(self, name, x, y)
  855.     return self.Bedrock:ToggleMenu(name, self, x, y)
  856. end
  857.  
  858. function OnUpdate(self, value)
  859.     if value == 'Z' then
  860.         self.Bedrock:ReorderObjects()
  861.     end
  862. end
  863. ]],
  864. }
  865. local objects = {
  866. ["Button"] = [[
  867. BackgroundColour = colours.lightGrey
  868. ActiveBackgroundColour = colours.blue
  869. ActiveTextColour = colours.white
  870. TextColour = colours.black
  871. DisabledTextColour = colours.lightGrey
  872. Text = ""
  873. Toggle = nil
  874. Momentary = true
  875. AutoWidth = true
  876. Align = 'Center'
  877. Enabled = true
  878.  
  879. OnUpdate = function(self, value)
  880.     if value == 'Text' and self.AutoWidth then
  881.         self.Width = #self.Text + 2
  882.     end
  883. end
  884.  
  885. OnDraw = function(self, x, y)
  886.     local bg = self.BackgroundColour
  887.  
  888.     if self.Toggle then
  889.         bg = self.ActiveBackgroundColour
  890.     end
  891.  
  892.     local txt = self.TextColour
  893.     if self.Toggle then
  894.         txt = self.ActiveTextColour
  895.     end
  896.     if not self.Enabled then
  897.         txt = self.DisabledTextColour
  898.     end
  899.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, bg)
  900.  
  901.     local _x = 1
  902.     if self.Align == 'Right' then
  903.         _x = self.Width - #self.Text - 1
  904.     elseif self.Align == 'Center' then
  905.         _x = math.floor((self.Width - #self.Text) / 2)
  906.     end
  907.  
  908.     Drawing.DrawCharacters(x + _x, y-1+math.ceil(self.Height/2), self.Text, txt, bg)
  909. end
  910.  
  911. OnLoad = function(self)
  912.     if self.Toggle ~= nil then
  913.         self.Momentary = false
  914.     end
  915. end
  916.  
  917. Click = function(self, event, side, x, y)
  918.     if self.Visible and not self.IgnoreClick and self.Enabled and event ~= 'mouse_scroll' then
  919.         if self.OnClick then
  920.             if self.Momentary then
  921.                 self.Toggle = true
  922.                 self.Bedrock:StartTimer(function()self.Toggle = false end,0.25)
  923.             elseif self.Toggle ~= nil then
  924.                 self.Toggle = not self.Toggle
  925.             end
  926.  
  927.             self:OnClick(event, side, x, y, self.Toggle)
  928.         else
  929.             self.Toggle = not self.Toggle
  930.         end
  931.         return true
  932.     else
  933.         return false
  934.     end
  935. end
  936. ]],
  937. ["CollectionView"] = [[
  938. Inherit = 'ScrollView'
  939. UpdateDrawBlacklist = {['NeedsItemUpdate']=true}
  940.  
  941. TextColour = colours.black
  942. BackgroundColour = colours.white
  943. Items = false
  944. NeedsItemUpdate = false
  945. SpacingX = 2
  946. SpacingY = 1
  947.  
  948. OnDraw = function(self, x, y)
  949.     if self.NeedsItemUpdate then
  950.         self:UpdateItems()
  951.         self.NeedsItemUpdate = false
  952.     end
  953.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  954. end
  955.  
  956. local function MaxIcons(self, obj)
  957.     local x, y = 2, 1
  958.     if not obj.Height or not obj.Width then
  959.         error('You must provide each object\'s height when adding to a CollectionView.')
  960.     end
  961.     local slotHeight = obj.Height + self.SpacingY
  962.     local slotWidth = obj.Width + self.SpacingX
  963.     local maxX = math.floor((self.Width - 2) / slotWidth)
  964.     return x, y, maxX, slotWidth, slotHeight
  965. end
  966.  
  967. local function IconLocation(self, obj, i)
  968.     local x, y, maxX, slotWidth, slotHeight = MaxIcons(self, obj)
  969.     local rowPos = ((i - 1) % maxX)
  970.     local colPos = math.ceil(i / maxX) - 1
  971.     x = x + (slotWidth * rowPos)
  972.     y = y + colPos * slotHeight
  973.     return x, y
  974. end
  975.  
  976. local function AddItem(self, v, i)
  977.     local toggle = false
  978.     if not self.CanSelect then
  979.         toggle = nil
  980.     end
  981.     local x, y = IconLocation(self, v, i)
  982.     local item = {
  983.         ["X"]=x,
  984.         ["Y"]=y,
  985.         ["Name"]="CollectionViewItem",
  986.         ["Type"]="View",
  987.         ["TextColour"]=self.TextColour,
  988.         ["BackgroundColour"]=0,
  989.         OnClick = function(itm)
  990.             if self.CanSelect then
  991.                 for i2, _v in ipairs(self.Children) do
  992.                     _v.Toggle = false
  993.                 end
  994.                 self.Selected = itm
  995.             end
  996.         end
  997.     }
  998.     for k, _v in pairs(v) do
  999.         item[k] = _v
  1000.     end
  1001.     self:AddObject(item)
  1002. end
  1003.  
  1004.  
  1005. UpdateItems = function(self)
  1006.     self:RemoveAllObjects()
  1007.     local groupMode = false
  1008.     for k, v in pairs(self.Items) do
  1009.         if type(k) == 'string' then
  1010.             groupMode = true
  1011.             break
  1012.         end
  1013.     end
  1014.  
  1015.     for i, v in ipairs(self.Items) do
  1016.         AddItem(self, v, i)
  1017.     end
  1018.     self:UpdateScroll()
  1019. end
  1020.  
  1021. OnUpdate = function(self, value)
  1022.     if value == 'Items' then
  1023.         self.NeedsItemUpdate = true
  1024.     end
  1025. end
  1026. ]],
  1027. ["ImageView"] = [[
  1028. Image = false
  1029.  
  1030. OnDraw = function(self, x, y)
  1031.     Drawing.DrawImage(x, y, self.Image, self.Width, self.Height)
  1032. end
  1033.  
  1034. OnLoad = function(self)
  1035.     if self.Path and fs.exists(self.Path) then
  1036.         self.Image = Drawing.LoadImage(self.Path)
  1037.     end
  1038. end
  1039.  
  1040. OnUpdate = function(self, value)
  1041.     if value == 'Path' then
  1042.         if self.Path and fs.exists(self.Path) then
  1043.             self.Image = Drawing.LoadImage(self.Path)
  1044.         end
  1045.     end
  1046. end
  1047. ]],
  1048. ["Label"] = [[
  1049. TextColour = colours.black
  1050. BackgroundColour = colours.transparent
  1051. Text = ""
  1052. AutoWidth = false
  1053. Align = 'Left'
  1054.  
  1055. local wrapText = function(text, maxWidth)
  1056.     local lines = {''}
  1057.     for word, space in text:gmatch('(%S+)(%s*)') do
  1058.         local temp = lines[#lines] .. word .. space:gsub('\n','')
  1059.         if #temp > maxWidth then
  1060.             table.insert(lines, '')
  1061.         end
  1062.         if space:find('\n') then
  1063.             lines[#lines] = lines[#lines] .. word
  1064.            
  1065.             space = space:gsub('\n', function()
  1066.                     table.insert(lines, '')
  1067.                     return ''
  1068.             end)
  1069.         else
  1070.             lines[#lines] = lines[#lines] .. word .. space
  1071.         end
  1072.     end
  1073.     if #lines[1] == 0 then
  1074.         table.remove(lines,1)
  1075.     end
  1076.     return lines
  1077. end
  1078.  
  1079. OnUpdate = function(self, value)
  1080.     if value == 'Text' then
  1081.         if self.AutoWidth then
  1082.             self.Width = #self.Text
  1083.         else
  1084.             self.Height = #wrapText(self.Text, self.Width)
  1085.         end
  1086.     end
  1087. end
  1088.  
  1089. OnDraw = function(self, x, y)
  1090.     for i, v in ipairs(wrapText(self.Text, self.Width)) do
  1091.         local _x = 0
  1092.         if self.Align == 'Right' then
  1093.             _x = self.Width - #v
  1094.         elseif self.Align == 'Center' then
  1095.             _x = math.floor((self.Width - #v) / 2)
  1096.         end
  1097.         Drawing.DrawCharacters(x + _x, y + i - 1, v, self.TextColour, self.BackgroundColour)
  1098.     end
  1099. end
  1100. ]],
  1101. ["ListView"] = [[
  1102. Inherit = 'ScrollView'
  1103. UpdateDrawBlacklist = {['NeedsItemUpdate']=true}
  1104.  
  1105. TextColour = colours.black
  1106. BackgroundColour = colours.white
  1107. HeadingColour = colours.lightGrey
  1108. SelectionBackgroundColour = colours.blue
  1109. SelectionTextColour = colours.white
  1110. Items = false
  1111. CanSelect = false
  1112. Selected = nil
  1113. NeedsItemUpdate = false
  1114. ItemMargin = 1
  1115. HeadingMargin = 0
  1116. TopMargin = 0
  1117.  
  1118. OnDraw = function(self, x, y)
  1119.     if self.NeedsItemUpdate then
  1120.         self:UpdateItems()
  1121.     end
  1122.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1123. end
  1124.  
  1125. local function AddItem(self, v, x, y, group)
  1126.     local toggle = false
  1127.     if not self.CanSelect then
  1128.         toggle = nil
  1129.     elseif v.Selected then
  1130.         toggle = true
  1131.     end
  1132.     local item = {
  1133.         ["Width"]=self.Width,
  1134.         ["X"]=x,
  1135.         ["Y"]=y,
  1136.         ["Name"]="ListViewItem",
  1137.         ["Type"]="Button",
  1138.         ["TextColour"]=self.TextColour,
  1139.         ["BackgroundColour"]=0,
  1140.         ["ActiveTextColour"]=self.SelectionTextColour,
  1141.         ["ActiveBackgroundColour"]=self.SelectionBackgroundColour,
  1142.         ["Align"]='Left',
  1143.         ["Toggle"]=toggle,
  1144.         ["Group"]=group,
  1145.         OnClick = function(itm)
  1146.             if self.CanSelect then
  1147.                 self:SelectItem(itm)
  1148.             elseif self.OnSelect then
  1149.                 self:OnSelect(itm.Text)
  1150.             end
  1151.         end
  1152.     }
  1153.     if type(v) == 'table' then
  1154.         for k, _v in pairs(v) do
  1155.             item[k] = _v
  1156.         end
  1157.     else
  1158.         item.Text = v
  1159.     end
  1160.    
  1161.     local itm = self:AddObject(item)
  1162.     if v.Selected then
  1163.         self:SelectItem(itm)
  1164.     end
  1165. end
  1166.  
  1167. UpdateItems = function(self)
  1168.     if not self.Items or type(self.Items) ~= 'table' then
  1169.         self.Items = {}
  1170.     end
  1171.     self.Selected = nil
  1172.     self:RemoveAllObjects()
  1173.     local groupMode = false
  1174.     for k, v in pairs(self.Items) do
  1175.         if type(k) == 'string' then
  1176.             groupMode = true
  1177.             break
  1178.         end
  1179.     end
  1180.  
  1181.     if not groupMode then
  1182.         for i, v in ipairs(self.Items) do
  1183.             AddItem(self, v, self.ItemMargin, i)
  1184.         end
  1185.     else
  1186.         local y = self.TopMargin
  1187.         for k, v in pairs(self.Items) do
  1188.             y = y + 1
  1189.             AddItem(self, {Text = k, TextColour = self.HeadingColour, IgnoreClick = true}, self.HeadingMargin, y)
  1190.             for i, _v in ipairs(v) do
  1191.                 y = y + 1
  1192.                 AddItem(self, _v, 1, y, k)
  1193.             end
  1194.             y = y + 1
  1195.         end
  1196.     end
  1197.     self:UpdateScroll()
  1198.     self.NeedsItemUpdate = false
  1199. end
  1200.  
  1201. OnKeyChar = function(self, event, keychar)
  1202.     if keychar == keys.up or keychar == keys.down then
  1203.         local n = self:GetIndex(self.Selected)
  1204.         if keychar == keys.up then
  1205.             n = n - 1
  1206.         else
  1207.             n = n + 1
  1208.         end
  1209.         local new = self:GetNth(n)
  1210.         if new then
  1211.             self:SelectItem(new)
  1212.         end
  1213.     elseif keychar == keys.enter and self.Selected then
  1214.         self.Selected:Click('mouse_click', 1, 1, 1)
  1215.     end
  1216. end
  1217.  
  1218. --returns the index/'n' of the given item
  1219. GetIndex = function(self, obj)
  1220.     local n = 1
  1221.     for i, v in ipairs(self.Children) do
  1222.         if not v.IgnoreClick then
  1223.             if obj == v then
  1224.                 return n
  1225.             end
  1226.             n = n + 1
  1227.         end
  1228.     end
  1229. end
  1230.  
  1231. --gets the 'nth' list item (does not include headings)
  1232. GetNth = function(self, n)
  1233.     local _n = 1
  1234.     for i, v in ipairs(self.Children) do
  1235.         if not v.IgnoreClick then
  1236.             if n == _n then
  1237.                 return v
  1238.             end
  1239.             _n = _n + 1
  1240.         end
  1241.     end
  1242. end
  1243.  
  1244. SelectItem = function(self, item)
  1245.     for i, v in ipairs(self.Children) do
  1246.         v.Toggle = false
  1247.     end
  1248.     self.Selected = item
  1249.     item.Toggle = true
  1250.     if self.OnSelect then
  1251.         self:OnSelect(item.Text)
  1252.     end
  1253. end
  1254.  
  1255. OnUpdate = function(self, value)
  1256.     if value == 'Items' then
  1257.         self.NeedsItemUpdate = true
  1258.     end
  1259. end
  1260. ]],
  1261. ["Menu"] = [[
  1262. Inherit = 'View'
  1263.  
  1264. TextColour = colours.black
  1265. BackgroundColour = colours.white
  1266. HideTop = false
  1267. Prepared = false
  1268.  
  1269. OnDraw = function(self, x, y)
  1270.     Drawing.IgnoreConstraint = true
  1271.     Drawing.DrawBlankArea(x + 1, y + (self.HideTop and 0 or 1), self.Width, self.Height + (self.HideTop and 1 or 0), colours.grey)
  1272.     Drawing.IgnoreConstraint = false
  1273.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1274. end
  1275.  
  1276. OnLoad = function(self)
  1277.     local owner = self.Owner
  1278.     if type(owner) == 'string' then
  1279.         owner = self.Bedrock:GetObject(self.Owner)
  1280.     end
  1281.  
  1282.     if owner then
  1283.         if self.X == 0 and self.Y == 0 then
  1284.             local pos = owner:GetPosition()
  1285.             self.X = pos.X
  1286.             self.Y = pos.Y + owner.Height
  1287.         end
  1288.         self.Owner = owner
  1289.     else
  1290.         self.Owner = nil
  1291.     end
  1292. end
  1293.  
  1294. OnUpdate = function(self, value)
  1295.     if value == 'Children' then
  1296.         self.Height = #self.Children + 1 + (self.HideTop and 0 or 1)
  1297.         if not self.BaseY then
  1298.             self.BaseY = self.Y
  1299.         end
  1300.         if #self.Children > 0 and self.Children[1].Type == 'Button' then
  1301.             self.Width = self.Bedrock.Helpers.LongestString(self.Children, 'Text') + 2
  1302.             for i, v in ipairs(self.Children) do
  1303.                 if v.TextColour then
  1304.                     v.TextColour = self.TextColour
  1305.                 end
  1306.                 if v.BackgroundColour then
  1307.                     v.BackgroundColour = colours.transparent
  1308.                 end
  1309.                 if v.Colour then
  1310.                     v.Colour = colours.lightGrey
  1311.                 end
  1312.                 v.Align = 'Left'
  1313.                 v.X = 1
  1314.                 v.Y = i + (self.HideTop and 0 or 1)
  1315.                 v.Width = self.Width
  1316.                 v.Height = 1
  1317.             end
  1318.         elseif #self.Children > 0 and self.Children[1].Type == 'MenuItem' then
  1319.             local width = 1
  1320.             for i, v in ipairs(self.Children) do
  1321.                 if v.Width > width then
  1322.                     width = v.Width
  1323.                 end
  1324.             end
  1325.             self.Width = width
  1326.             for i, v in ipairs(self.Children) do
  1327.                 if v.TextColour then
  1328.                     v.TextColour = self.TextColour
  1329.                 end
  1330.                 if v.Colour then
  1331.                     v.Colour = colours.lightGrey
  1332.                 end
  1333.                 v.X = 1
  1334.                 v.Y = i + (self.HideTop and 0 or 1)
  1335.                 v.Width = width
  1336.                 v.Height = 1
  1337.             end
  1338.         end
  1339.  
  1340.         self.Y = self.BaseY
  1341.         local pos = self:GetPosition()
  1342.         if pos.Y + self.Height + 1 > Drawing.Screen.Height then
  1343.             self.Y = self.BaseY - ((self.Height +  pos.Y) - Drawing.Screen.Height)
  1344.         end
  1345.        
  1346.         if pos.X + self.Width > Drawing.Screen.Width then
  1347.             self.X = Drawing.Screen.Width - self.Width
  1348.         end
  1349.     end
  1350. end
  1351.  
  1352. Close = function(self, isBedrockCall)
  1353.     self.Bedrock.Menu = nil
  1354.     if not self.Prepared then
  1355.         self.Parent:RemoveObject(self)
  1356.     else
  1357.         self.Visible = false
  1358.     end
  1359.  
  1360.     if self.Owner and self.Owner.Toggle then
  1361.         self.Owner.Toggle = false
  1362.     end
  1363.     self.Parent:ForceDraw()
  1364.     self = nil
  1365. end
  1366.  
  1367. OnChildClick = function(self, child, event, side, x, y)
  1368.     self:Close()
  1369. end
  1370. ]],
  1371. ["MenuItem"] = [[
  1372. BackgroundColour = colours.white
  1373. TextColour = colours.black
  1374. DisabledTextColour = colours.lightGrey
  1375. ShortcutTextColour = colours.grey
  1376. Text = ""
  1377. Enabled = true
  1378. Shortcut = nil
  1379. ShortcutPadding = 2
  1380. ShortcutName = nil
  1381.  
  1382. OnUpdate = function(self, value)
  1383.     if value == 'Text' then
  1384.         if self.Shortcut then
  1385.             self.Width = #self.Text + 2 + self.ShortcutPadding + #self.Shortcut
  1386.         else
  1387.             self.Width = #self.Text + 2
  1388.         end
  1389.     elseif value == 'OnClick' then
  1390.         self:RegisterShortcut()
  1391.     end
  1392. end
  1393.  
  1394. OnDraw = function(self, x, y)
  1395.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1396.  
  1397.     local txt = self.TextColour
  1398.     if not self.Enabled then
  1399.         txt = self.DisabledTextColour
  1400.     end
  1401.     Drawing.DrawCharacters(x + 1, y, self.Text, txt, colours.transparent)
  1402.  
  1403.     if self.Shortcut then
  1404.         local shrt = self.ShortcutTextColour
  1405.         if not self.Enabled then
  1406.             shrt = self.DisabledTextColour
  1407.         end
  1408.         Drawing.DrawCharacters(x + self.Width - #self.Shortcut - 1, y, self.Shortcut, shrt, colours.transparent)
  1409.     end
  1410. end
  1411.  
  1412. ParseShortcut = function(self)
  1413.     local special = {
  1414.         ['^'] = keys.leftShift,
  1415.         ['<'] = keys.delete,
  1416.         ['>'] = keys.delete,
  1417.         ['#'] = keys.leftCtrl,
  1418.         ['~'] = keys.leftAlt,
  1419.     }
  1420.  
  1421.     local keys = {}
  1422.     for i = 1, #self.Shortcut do
  1423.         local c = self.Shortcut:sub(i,i)
  1424.         table.insert(keys, special[c] or c:lower())
  1425.     end
  1426.     return keys
  1427. end
  1428.  
  1429. RegisterShortcut = function(self)
  1430.     if self.Shortcut then
  1431.         self.Shortcut = self.Shortcut:upper()
  1432.         self.ShortcutName = self.Bedrock:RegisterKeyboardShortcut(self:ParseShortcut(), function()
  1433.             if self.OnClick and self.Enabled then
  1434.                 if self.Parent.Owner then
  1435.                     self.Parent:Close()
  1436.                     self.Parent.Owner.Toggle = true
  1437.                     self.Bedrock:StartTimer(function()
  1438.                         self.Parent.Owner.Toggle = false
  1439.                     end, 0.3)
  1440.                 end
  1441.                 return self:OnClick('keyboard_shortcut', 1, 1, 1)
  1442.             else
  1443.                 return false
  1444.             end
  1445.         end)
  1446.     end
  1447. end
  1448.  
  1449. OnRemove = function(self)
  1450.     if self.ShortcutName then
  1451.         self.Bedrock:UnregisterKeyboardShortcut(self.ShortcutName)
  1452.     end
  1453. end
  1454.  
  1455. OnLoad = function(self)
  1456.     if self.OnClick ~= nil then
  1457.         self:RegisterShortcut()
  1458.     end
  1459.     -- self:OnUpdate('Text')
  1460. end
  1461. ]],
  1462. ["NumberBox"] = [[
  1463. Inherit = 'View'
  1464.  
  1465. Value = 1
  1466. Minimum = 1
  1467. Maximum = 99
  1468. BackgroundColour = colours.lightGrey
  1469. TextBoxTimer = nil
  1470. Width = 7
  1471.  
  1472. OnLoad = function(self)
  1473.     self:AddObject({
  1474.         X = self.Width - 1,
  1475.         Y = 1,
  1476.         Width = 1,
  1477.         AutoWidth = false,
  1478.         Text = '-',
  1479.         Type = 'Button',
  1480.         Name = 'AddButton',
  1481.         BackgroundColour = colours.transparent,
  1482.         OnClick = function()
  1483.             self:ShiftValue(-1)
  1484.         end
  1485.     })
  1486.  
  1487.     self:AddObject({
  1488.         X = self.Width,
  1489.         Y = 1,
  1490.         Width = 1,
  1491.         AutoWidth = false,
  1492.         Text = '+',
  1493.         Type = 'Button',
  1494.         Name = 'SubButton',
  1495.         BackgroundColour = colours.transparent,
  1496.         OnClick = function()
  1497.             self:ShiftValue(1)
  1498.         end
  1499.     })
  1500.  
  1501.     self:AddObject({
  1502.         X = 1,
  1503.         Y = 1,
  1504.         Width = self.Width - 2,
  1505.         Text = tostring(self.Value),
  1506.         Align = 'Center',
  1507.         Type = 'TextBox',
  1508.         BackgroundColour = colours.transparent,
  1509.         OnChange = function(_self, event, keychar)
  1510.             if keychar == keys.enter then
  1511.                 self:SetValue(tonumber(_self.Text))
  1512.                 self.TextBoxTimer = nil
  1513.             end
  1514.             if self.TextBoxTimer then
  1515.                 self.Bedrock:StopTimer(self.TextBoxTimer)
  1516.             end
  1517.  
  1518.             self.TextBoxTimer = self.Bedrock:StartTimer(function(_, timer)
  1519.                 if timer and timer == self.TextBoxTimer then
  1520.                     self:SetValue(tonumber(_self.Text))
  1521.                     self.TextBoxTimer = nil
  1522.                 end
  1523.             end, 2)
  1524.         end
  1525.     })
  1526. end
  1527.  
  1528. OnScroll = function(self, event, dir, x, y)
  1529.     self:ShiftValue(-dir)
  1530. end
  1531.  
  1532. ShiftValue = function(self, delta)
  1533.     local val = tonumber(self:GetObject('TextBox').Text) or self.Minimum
  1534.     self:SetValue(val + delta)
  1535. end
  1536.  
  1537. SetValue = function(self, newValue)
  1538.     newValue = newValue or 0
  1539.     if self.Maximum and newValue > self.Maximum then
  1540.         newValue = self.Maximum
  1541.     elseif self.Minimum and newValue < self.Minimum then
  1542.         newValue = self.Minimum
  1543.     end
  1544.     self.Value = newValue
  1545.     if self.OnChange then
  1546.         self:OnChange()
  1547.     end
  1548. end
  1549.  
  1550. OnUpdate = function(self, value)
  1551.     if value == 'Value' then
  1552.         local textbox = self:GetObject('TextBox')
  1553.         if textbox then
  1554.             textbox.Text = tostring(self.Value)
  1555.         end
  1556.     end
  1557. end
  1558. ]],
  1559. ["ProgressBar"] = [[
  1560. BackgroundColour = colours.lightGrey
  1561. BarColour = colours.blue
  1562. TextColour = colours.white
  1563. ShowText = false
  1564. Value = 0
  1565. Maximum = 1
  1566. Indeterminate = false
  1567. AnimationStep = 0
  1568.  
  1569. OnDraw = function(self, x, y)
  1570.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1571.  
  1572.     -- if self.Indeterminate then
  1573.     --  for i = 1, self.Width do
  1574.     --      local s = x + i - 1 + self.AnimationStep
  1575.     --      if s % 4 == 1 or s % 4 == 2 then
  1576.     --          Drawing.DrawBlankArea(s, y, 1, self.Height, self.BarColour)
  1577.     --      end
  1578.     --  end
  1579.     --  self.AnimationStep = self.AnimationStep + 1
  1580.     --  if self.AnimationStep >= 4 then
  1581.     --      self.AnimationStep = 0
  1582.     --  end
  1583.     --  self.Bedrock:StartTimer(function()
  1584.     --      self:Draw()
  1585.     --  end, 0.25)
  1586.     -- else
  1587.         local values = self.Value
  1588.         local barColours = self.BarColour
  1589.         if type(values) == 'number' then
  1590.             values = {values}
  1591.         end
  1592.         if type(barColours) == 'number' then
  1593.             barColours = {barColours}
  1594.         end
  1595.         local total = 0
  1596.         local _x = x
  1597.         for i, v in ipairs(values) do
  1598.             local width = self.Bedrock.Helpers.Round((v / self.Maximum) * self.Width)
  1599.             total = total + v
  1600.             Drawing.DrawBlankArea(_x, y, width, self.Height, barColours[((i-1)%#barColours)+1])
  1601.             _x = _x + width
  1602.         end
  1603.  
  1604.         if self.ShowText then
  1605.             local text = self.Bedrock.Helpers.Round((total / self.Maximum) * 100) .. '%'
  1606.             Drawing.DrawCharactersCenter(x, y, self.Width, self.Height, text, self.TextColour, colours.transparent)
  1607.         end
  1608.     -- end
  1609. end
  1610. ]],
  1611. ["ScrollBar"] = [[
  1612. BackgroundColour = colours.lightGrey
  1613. BarColour = colours.lightBlue
  1614. Scroll = 0
  1615. MaxScroll = 0
  1616. ClickPoint = nil
  1617. Fixed = true
  1618.  
  1619. OnUpdate = function(self, value)
  1620.     if value == 'Text' and self.AutoWidth then
  1621.         self.Width = #self.Text + 2
  1622.     end
  1623. end
  1624.  
  1625. OnDraw = function(self, x, y)
  1626.     local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))
  1627.     if barHeight < 3 then
  1628.       barHeight = 3
  1629.     end
  1630.     local percentage = (self.Scroll/self.MaxScroll)
  1631.  
  1632.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1633.     Drawing.DrawBlankArea(x, y + math.ceil(self.Height*percentage - barHeight*percentage), self.Width, barHeight, self.BarColour)
  1634. end
  1635.  
  1636. OnScroll = function(self, event, direction, x, y)
  1637.     if event == 'mouse_scroll' then
  1638.         direction = self.Bedrock.Helpers.Round(direction * 3)
  1639.     end
  1640.     if self.Scroll < 0 or self.Scroll > self.MaxScroll then
  1641.         return false
  1642.     end
  1643.     local old = self.Scroll
  1644.     self.Scroll = self.Bedrock.Helpers.Round(self.Scroll + direction)
  1645.     if self.Scroll < 0 then
  1646.         self.Scroll = 0
  1647.     elseif self.Scroll > self.MaxScroll then
  1648.         self.Scroll = self.MaxScroll
  1649.     end
  1650.  
  1651.     if self.Scroll ~= old and self.OnChange then
  1652.         self:OnChange()
  1653.     end
  1654. end
  1655.  
  1656. OnClick = function(self, event, side, x, y)
  1657.     if event == 'mouse_click' then
  1658.         self.ClickPoint = y
  1659.     else
  1660.         if self.ClickPoint then
  1661.             local gapHeight = self.Height - (self.Height * (self.Height / (self.Height + self.MaxScroll)))
  1662.             local barHeight = self.Height * (self.Height / (self.Height + self.MaxScroll))
  1663.             --local delta = (self.Height + self.MaxScroll) * ((y - self.ClickPoint) / barHeight)
  1664.             local delta = ((y - self.ClickPoint)/gapHeight)*self.MaxScroll
  1665.             --l(((y - self.ClickPoint)/gapHeight))
  1666.             --l(delta)
  1667.             self.Scroll = self.Bedrock.Helpers.Round(delta)
  1668.             --l(self.Scroll)
  1669.             --l('----')
  1670.             if self.Scroll < 0 then
  1671.                 self.Scroll = 0
  1672.             elseif self.Scroll > self.MaxScroll then
  1673.                 self.Scroll = self.MaxScroll
  1674.             end
  1675.             if self.OnChange then
  1676.                 self:OnChange()
  1677.             end
  1678.         end
  1679.     end
  1680.  
  1681.     local relScroll = self.MaxScroll * ((y-1)/self.Height)
  1682.     if y == self.Height then
  1683.         relScroll = self.MaxScroll
  1684.     end
  1685.     self.Scroll = self.Bedrock.Helpers.Round(relScroll)
  1686.  
  1687.  
  1688. end
  1689.  
  1690. OnDrag = OnClick
  1691. ]],
  1692. ["ScrollView"] = [[
  1693. Inherit = 'View'
  1694. ChildOffset = false
  1695. ContentWidth = 0
  1696. ContentHeight = 0
  1697. ScrollBarBackgroundColour = colours.lightGrey
  1698. ScrollBarColour = colours.lightBlue
  1699.  
  1700. CalculateContentSize = function(self)
  1701.     local function calculateObject(obj)
  1702.         local pos = obj:GetPosition()
  1703.         local x2 = pos.X + obj.Width - 1
  1704.         local y2 = pos.Y + obj.Height - 1
  1705.         if obj.Children then
  1706.             for i, child in ipairs(obj.Children) do
  1707.                 local _x2, _y2 = calculateObject(child)
  1708.                 if _x2 > x2 then
  1709.                     x2 = _x2
  1710.                 end
  1711.                 if _y2 > y2 then
  1712.                     y2 = _y2
  1713.                 end
  1714.             end
  1715.         end
  1716.         return x2, y2
  1717.     end
  1718.  
  1719.     local pos = self:GetPosition()
  1720.     local x2, y2 = calculateObject(self)
  1721.     self.ContentWidth = x2 - pos.X + 1
  1722.     self.ContentHeight = y2 - pos.Y + 1
  1723. end
  1724.  
  1725. UpdateScroll = function(self)
  1726.     self.ChildOffset.Y = 0
  1727.     self:CalculateContentSize()
  1728.     if self.ContentHeight > self.Height then
  1729.         if not self:GetObject('ScrollViewScrollBar') then
  1730.             local _scrollBar = self:AddObject({
  1731.                 ["Name"] = 'ScrollViewScrollBar',
  1732.                 ["Type"] = 'ScrollBar',
  1733.                 ["X"] = self.Width,
  1734.                 ["Y"] = 1,
  1735.                 ["Width"] = 1,
  1736.                 ["Height"] = self.Height,
  1737.                 ["BackgroundColour"] = self.ScrollBarBackgroundColour,
  1738.                 ["BarColour"] = self.ScrollBarColour,
  1739.                 ["Z"]=999
  1740.             })
  1741.  
  1742.             _scrollBar.OnChange = function(scrollBar)
  1743.                 self.ChildOffset.Y = -scrollBar.Scroll
  1744.                 for i, child in ipairs(self.Children) do
  1745.                     child:ForceDraw()
  1746.                 end
  1747.             end
  1748.         end
  1749.  
  1750.         if self:GetObject('ScrollViewScrollBar') then
  1751.             self:GetObject('ScrollViewScrollBar').MaxScroll = self.ContentHeight - self.Height
  1752.         end
  1753.     else
  1754.         self:RemoveObject('ScrollViewScrollBar')
  1755.     end
  1756. end
  1757.  
  1758. OnScroll = function(self, event, direction, x, y)
  1759.     if self:GetObject('ScrollViewScrollBar') then
  1760.         self:GetObject('ScrollViewScrollBar'):OnScroll(event, direction, x, y)
  1761.     end
  1762. end
  1763.  
  1764. OnLoad = function(self)
  1765.     if not self.ChildOffset or not self.ChildOffset.X or not self.ChildOffset.Y then
  1766.         self.ChildOffset = {X = 0, Y = 0}
  1767.     end
  1768.     self:UpdateScroll()
  1769. end
  1770. ]],
  1771. ["SecureTextBox"] = [[
  1772. Inherit = 'TextBox'
  1773. MaskCharacter = '*'
  1774.  
  1775. OnDraw = function(self, x, y)
  1776.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1777.     if self.CursorPos > #self.Text then
  1778.         self.CursorPos = #self.Text
  1779.     elseif self.CursorPos < 0 then
  1780.         self.CursorPos = 0
  1781.     end
  1782.     local text = ''
  1783.  
  1784.     for i = 1, #self.Text do
  1785.         text = text .. self.MaskCharacter
  1786.     end
  1787.  
  1788.     if self.Bedrock:GetActiveObject() == self then
  1789.         if #text > (self.Width - 2) then
  1790.             text = text:sub(#text-(self.Width - 3))
  1791.             self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}
  1792.         else
  1793.             self.Bedrock.CursorPos = {x + 1 + self.CursorPos, y}
  1794.         end
  1795.         self.Bedrock.CursorColour = self.TextColour
  1796.     end
  1797.  
  1798.     if #tostring(text) == 0 then
  1799.         Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)
  1800.     else
  1801.         if not self.Selected then
  1802.             Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)
  1803.         else
  1804.             for i = 1, #text do
  1805.                 local char = text:sub(i, i)
  1806.                 local textColour = self.TextColour
  1807.                 local backgroundColour = self.BackgroundColour
  1808.                 if i > self.DragStart and i - 1 <= self.CursorPos then
  1809.                     textColour = self.SelectedTextColour
  1810.                     backgroundColour = self.SelectedBackgroundColour
  1811.                 end
  1812.                 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)
  1813.             end
  1814.         end
  1815.     end
  1816. end
  1817. ]],
  1818. ["Separator"] = [[
  1819. Colour = colours.grey
  1820.  
  1821. OnDraw = function(self, x, y)
  1822.     local char = "|"
  1823.     if self.Width > self.Height then
  1824.         char = '-'
  1825.     end
  1826.     Drawing.DrawArea(x, y, self.Width, self.Height, char, self.Colour, colours.transparent)
  1827. end
  1828. ]],
  1829. ["TextBox"] = [[
  1830. BackgroundColour = colours.lightGrey
  1831. SelectedBackgroundColour = colours.blue
  1832. SelectedTextColour = colours.white
  1833. TextColour = colours.black
  1834. PlaceholderTextColour = colours.grey
  1835. Placeholder = ''
  1836. AutoWidth = false
  1837. Text = ""
  1838. CursorPos = nil
  1839. Numerical = false
  1840. DragStart = nil
  1841. Selected = false
  1842. SelectOnClick = false
  1843. ActualDragStart = nil
  1844.  
  1845. OnDraw = function(self, x, y)
  1846.     Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  1847.     if self.CursorPos > #self.Text then
  1848.         self.CursorPos = #self.Text
  1849.     elseif self.CursorPos < 0 then
  1850.         self.CursorPos = 0
  1851.     end
  1852.     local text = self.Text
  1853.     local offset = self:TextOffset()
  1854.     if #text > (self.Width - 2) then
  1855.         text = text:sub(offset+1, offset + self.Width - 2)
  1856.         -- self.Bedrock.CursorPos = {x + 1 + self.Width-2, y}
  1857.     -- else
  1858.     end
  1859.     if self.Bedrock:GetActiveObject() == self then
  1860.         self.Bedrock.CursorPos = {x + 1 + self.CursorPos - offset, y}
  1861.         self.Bedrock.CursorColour = self.TextColour
  1862.     else
  1863.         self.Selected = false
  1864.     end
  1865.  
  1866.     if #tostring(text) == 0 then
  1867.         Drawing.DrawCharacters(x + 1, y, self.Placeholder, self.PlaceholderTextColour, self.BackgroundColour)
  1868.     else
  1869.         if not self.Selected then
  1870.             Drawing.DrawCharacters(x + 1, y, text, self.TextColour, self.BackgroundColour)
  1871.         else
  1872.             local startPos = self.DragStart - offset
  1873.             local endPos = self.CursorPos - offset
  1874.             if startPos > endPos then
  1875.                 startPos = self.CursorPos - offset
  1876.                 endPos = self.DragStart - offset
  1877.             end
  1878.             for i = 1, #text do
  1879.                 local char = text:sub(i, i)
  1880.                 local textColour = self.TextColour
  1881.                 local backgroundColour = self.BackgroundColour
  1882.  
  1883.                 if i > startPos and i - 1 <= endPos then
  1884.                     textColour = self.SelectedTextColour
  1885.                     backgroundColour = self.SelectedBackgroundColour
  1886.                 end
  1887.                 Drawing.DrawCharacters(x + i, y, char, textColour, backgroundColour)
  1888.             end
  1889.         end
  1890.     end
  1891. end
  1892.  
  1893. TextOffset = function(self)
  1894.     if #self.Text < (self.Width - 2) then
  1895.         return 0
  1896.     elseif self.Bedrock:GetActiveObject() ~= self then
  1897.         return 0
  1898.     else
  1899.         local textWidth = (self.Width - 2)
  1900.         local offset = self.CursorPos - textWidth
  1901.         if offset < 0 then
  1902.             offset = 0
  1903.         end
  1904.         return offset
  1905.     end
  1906. end
  1907.  
  1908. OnLoad = function(self)
  1909.     if not self.CursorPos then
  1910.         self.CursorPos = #self.Text
  1911.     end
  1912. end
  1913.  
  1914. OnClick = function(self, event, side, x, y)
  1915.     if self.Bedrock:GetActiveObject() ~= self and self.SelectOnClick then
  1916.         self.CursorPos = #self.Text - 1
  1917.         self.DragStart = 0
  1918.         self.ActualDragStart = x - 2 + self:TextOffset()
  1919.         self.Selected = true
  1920.     else
  1921.         self.CursorPos = x - 2 + self:TextOffset()
  1922.         self.DragStart = self.CursorPos
  1923.         self.Selected = false
  1924.     end
  1925.     self.Bedrock:SetActiveObject(self)
  1926. end
  1927.  
  1928. OnDrag = function(self, event, side, x, y)
  1929.     self.CursorPos = x - 2 + self:TextOffset()
  1930.     if self.ActualDragStart then
  1931.         self.DragStart = self.ActualDragStart
  1932.         self.ActualDragStart = nil
  1933.     end
  1934.     if self.DragStart then
  1935.         self.Selected = true
  1936.     end
  1937. end
  1938.  
  1939. OnKeyChar = function(self, event, keychar)
  1940.     local deleteSelected = function()
  1941.         if self.Selected then
  1942.             local startPos = self.DragStart
  1943.             local endPos = self.CursorPos
  1944.             if startPos > endPos then
  1945.                 startPos = self.CursorPos
  1946.                 endPos = self.DragStart
  1947.             end
  1948.             self.Text = self.Text:sub(1, startPos) .. self.Text:sub(endPos + 2)
  1949.             self.CursorPos = startPos
  1950.             self.DragStart = nil
  1951.             self.Selected = false
  1952.             return true
  1953.         end
  1954.     end
  1955.  
  1956.     if event == 'char' then
  1957.         deleteSelected()
  1958.         if self.Numerical then
  1959.             keychar = tostring(tonumber(keychar))
  1960.         end
  1961.         if keychar == 'nil' then
  1962.             return
  1963.         end
  1964.         self.Text = string.sub(self.Text, 1, self.CursorPos ) .. keychar .. string.sub( self.Text, self.CursorPos + 1 )
  1965.         if self.Numerical then
  1966.             self.Text = tostring(tonumber(self.Text))
  1967.             if self.Text == 'nil' then
  1968.                 self.Text = '1'
  1969.             end
  1970.         end
  1971.        
  1972.         self.CursorPos = self.CursorPos + 1
  1973.         if self.OnChange then
  1974.             self:OnChange(event, keychar)
  1975.         end
  1976.         return false
  1977.     elseif event == 'key' then
  1978.         if keychar == keys.enter then
  1979.             if self.OnChange then
  1980.                 self:OnChange(event, keychar)
  1981.             end
  1982.         elseif keychar == keys.left then
  1983.             -- Left
  1984.             if self.CursorPos > 0 then
  1985.                 if self.Selected then
  1986.                     self.CursorPos = self.DragStart
  1987.                     self.DragStart = nil
  1988.                     self.Selected = false
  1989.                 else
  1990.                     self.CursorPos = self.CursorPos - 1
  1991.                 end
  1992.                 if self.OnChange then
  1993.                     self:OnChange(event, keychar)
  1994.                 end
  1995.             end
  1996.            
  1997.         elseif keychar == keys.right then
  1998.             -- Right               
  1999.             if self.CursorPos < string.len(self.Text) then
  2000.                 if self.Selected then
  2001.                     self.CursorPos = self.CursorPos
  2002.                     self.DragStart = nil
  2003.                     self.Selected = false
  2004.                 else
  2005.                     self.CursorPos = self.CursorPos + 1
  2006.                 end
  2007.                 if self.OnChange then
  2008.                     self:OnChange(event, keychar)
  2009.                 end
  2010.             end
  2011.        
  2012.         elseif keychar == keys.backspace then
  2013.             -- Backspace
  2014.             if not deleteSelected() and self.CursorPos > 0 then
  2015.                 self.Text = string.sub( self.Text, 1, self.CursorPos - 1 ) .. string.sub( self.Text, self.CursorPos + 1 )
  2016.                 self.CursorPos = self.CursorPos - 1                
  2017.                 if self.Numerical then
  2018.                     self.Text = tostring(tonumber(self.Text))
  2019.                     if self.Text == 'nil' then
  2020.                         self.Text = '1'
  2021.                     end
  2022.                 end
  2023.                 if self.OnChange then
  2024.                     self:OnChange(event, keychar)
  2025.                 end
  2026.             end
  2027.         elseif keychar == keys.home then
  2028.             -- Home
  2029.             self.CursorPos = 0
  2030.             if self.OnChange then
  2031.                 self:OnChange(event, keychar)
  2032.             end
  2033.         elseif keychar == keys.delete then
  2034.             if not deleteSelected() and self.CursorPos < string.len(self.Text) then
  2035.                 self.Text = string.sub( self.Text, 1, self.CursorPos ) .. string.sub( self.Text, self.CursorPos + 2 )      
  2036.                 if self.Numerical then
  2037.                     self.Text = tostring(tonumber(self.Text))
  2038.                     if self.Text == 'nil' then
  2039.                         self.Text = '1'
  2040.                     end
  2041.                 end
  2042.                 if self.OnChange then
  2043.                     self:OnChange(keychar)
  2044.                 end
  2045.             end
  2046.         elseif keychar == keys["end"] then
  2047.             -- End
  2048.             self.CursorPos = string.len(self.Text)
  2049.         else
  2050.             if self.OnChange then
  2051.                 self:OnChange(event, keychar)
  2052.             end
  2053.             return false
  2054.         end
  2055.     end
  2056. end
  2057. ]],
  2058. ["View"] = [[
  2059. BackgroundColour = colours.transparent
  2060. Children = {}
  2061.  
  2062. OnDraw = function(self, x, y)
  2063.     if self.BackgroundColour then
  2064.         Drawing.DrawBlankArea(x, y, self.Width, self.Height, self.BackgroundColour)
  2065.     end
  2066. end
  2067.  
  2068. OnInitialise = function(self)
  2069.     self.Children = {}
  2070. end
  2071.  
  2072. InitialiseFile = function(self, bedrock, file, name)
  2073.     local _new = {}
  2074.     _new.X = 1
  2075.     _new.Y = 1
  2076.     _new.Width = Drawing.Screen.Width
  2077.     _new.Height = Drawing.Screen.Height
  2078.     _new.BackgroundColour = file.BackgroundColour
  2079.     _new.Name = name
  2080.     _new.Children = {}
  2081.     _new.Bedrock = bedrock
  2082.     local new = self:Initialise(_new)
  2083.     for i, obj in ipairs(file.Children) do
  2084.         local view = bedrock:ObjectFromFile(obj, new)
  2085.         if not view.Z then
  2086.             view.Z = i
  2087.         end
  2088.         view.Parent = new
  2089.         table.insert(new.Children, view)
  2090.     end
  2091.     return new
  2092. end
  2093.  
  2094. function CheckClick(self, object, x, y)
  2095.     local offset = {X = 0, Y = 0}
  2096.     if not object.Fixed and self.ChildOffset then
  2097.         offset = self.ChildOffset
  2098.     end
  2099.     if object.X + offset.X <= x and object.Y + offset.Y <= y and  object.X + offset.X + object.Width > x and object.Y + offset.Y + object.Height > y then
  2100.         return true
  2101.     end
  2102. end
  2103.  
  2104. function DoClick(self, object, event, side, x, y)
  2105.     if object then
  2106.         if self:CheckClick(object, x, y) then
  2107.             local offset = {X = 0, Y = 0}
  2108.             if not object.Fixed and self.ChildOffset then
  2109.                 offset = self.ChildOffset
  2110.             end
  2111.             return object:Click(event, side, x - object.X - offset.X + 1, y - object.Y + 1 - offset.Y)
  2112.         end
  2113.     end
  2114. end
  2115.  
  2116. Click = function(self, event, side, x, y, z)
  2117.     if self.Visible and not self.IgnoreClick then
  2118.         for i = #self.Children, 1, -1 do --children are ordered from smallest Z to highest, so this is done in reverse
  2119.             local child = self.Children[i]
  2120.             if self:DoClick(child, event, side, x, y) then
  2121.                 if self.OnChildClick then
  2122.                     self:OnChildClick(child, event, side, x, y)
  2123.                 end
  2124.                 return true
  2125.             end
  2126.         end
  2127.         if event == 'mouse_click' and self.OnClick and self:OnClick(event, side, x, y) ~= false then
  2128.             return true
  2129.         elseif event == 'mouse_drag' and self.OnDrag and self:OnDrag(event, side, x, y) ~= false then
  2130.             return true
  2131.         elseif event == 'mouse_scroll' and self.OnScroll and self:OnScroll(event, side, x, y) ~= false then
  2132.             return true
  2133.         else
  2134.             return false
  2135.         end
  2136.     else
  2137.         return false
  2138.     end
  2139. end
  2140.  
  2141. OnRemove = function(self)
  2142.     if self == self.Bedrock:GetActiveObject() then
  2143.         self.Bedrock:SetActiveObject()
  2144.     end
  2145.     for i, child in ipairs(self.Children) do
  2146.         child:OnRemove()
  2147.     end
  2148. end
  2149.  
  2150. local function findObjectNamed(view, name, minI)
  2151.     local minI = minI or 0
  2152.     if view and view.Children then
  2153.         for i, child in ipairs(view.Children) do
  2154.             if child.Name == name or child == name then
  2155.                 return child, i, view
  2156.             elseif child.Children then
  2157.                 local found, index, foundView = findObjectNamed(child, name)
  2158.                 if found and minI <= index then
  2159.                     return found, index, foundView
  2160.                 end
  2161.             end
  2162.         end
  2163.     end
  2164. end
  2165.  
  2166. function ReorderObjects(self)
  2167.     if self.Children then
  2168.         table.sort(self.Children, function(a,b)
  2169.             return a.Z < b.Z
  2170.         end)
  2171.         for i, v in ipairs(self.Children) do
  2172.             if v.ReorderObjects then
  2173.                 v:ReorderObjects()
  2174.             end
  2175.         end
  2176.     end
  2177. end
  2178.  
  2179. function AddObject(self, info, extra, first)
  2180.     if type(info) == 'string' then
  2181.         local h = fs.open(self.Bedrock.ViewPath..info..'.view', 'r')
  2182.         if h then
  2183.             info = textutils.unserialize(h.readAll())
  2184.             h.close()
  2185.         else
  2186.             error('Error in opening object: '..info)
  2187.         end
  2188.     end
  2189.  
  2190.     if extra then
  2191.         for k, v in pairs(extra) do
  2192.             if v then
  2193.                 info[k] = v
  2194.             end
  2195.         end
  2196.     end
  2197.  
  2198.     local view = self.Bedrock:ObjectFromFile(info, self)
  2199.     if not view.Z then
  2200.         if first then
  2201.             view.Z = 1
  2202.         else
  2203.             view.Z = #self.Children + 1
  2204.         end
  2205.     end
  2206.    
  2207.     if first then
  2208.         table.insert(self.Children, 1, view)
  2209.     else
  2210.         table.insert(self.Children, view)
  2211.     end
  2212.     if self.Bedrock.View then
  2213.         self.Bedrock:ReorderObjects()
  2214.     end
  2215.     self:ForceDraw()
  2216.     return view
  2217. end
  2218.  
  2219. function GetObject(self, name)
  2220.     return findObjectNamed(self, name)
  2221. end
  2222.  
  2223. local function findObjects(view, name)
  2224.     local objects = {}
  2225.     if view and view.Children then
  2226.         for i, child in ipairs(view.Children) do
  2227.             if child.Name == name or child == name then
  2228.                 table.insert(objects, child)
  2229.             elseif child.Children then
  2230.                 local objs = findObjects(child, name)
  2231.                 if objs then
  2232.                     for i2, v in ipairs(objs) do
  2233.                         table.insert(objects, v)
  2234.                     end
  2235.                 end
  2236.             end
  2237.         end
  2238.     end
  2239.     return objects
  2240. end
  2241.  
  2242. function GetObjects(self, name)
  2243.     return findObjects(self, name)
  2244. end
  2245.  
  2246. function RemoveObject(self, name)
  2247.     local obj, index, view = findObjectNamed(self, name, minI)
  2248.     if index then
  2249.         view.Children[index]:OnRemove()
  2250.         table.remove(view.Children, index)
  2251.         if view.OnUpdate then
  2252.             view:OnUpdate('Children')
  2253.         end
  2254.         return true
  2255.     end
  2256.     return false
  2257. end
  2258.  
  2259. function RemoveObjects(self, name)
  2260.     local i = 1
  2261.     while self:RemoveObject(name) and i < 100 do
  2262.         i = i + 1
  2263.     end
  2264.    
  2265. end
  2266.  
  2267. function RemoveAllObjects(self)
  2268.     for i, child in ipairs(self.Children) do
  2269.         child:OnRemove()
  2270.         self.Children[i] = nil
  2271.     end
  2272.     self:ForceDraw()
  2273. end
  2274. ]],
  2275. ["Window"] = [[
  2276. Inherit = 'View'
  2277.  
  2278. ToolBarColour = colours.lightGrey
  2279. ToolBarTextColour = colours.black
  2280. ShadowColour = colours.grey
  2281. Title = ''
  2282. Flashing = false
  2283. CanClose = true
  2284. OnCloseButton = nil
  2285. OldActiveObject = nil
  2286.  
  2287. LoadView = function(self)
  2288.     local view = self:GetObject('View')
  2289.     if view.ToolBarColour then
  2290.         window.ToolBarColour = view.ToolBarColour
  2291.     end
  2292.     if view.ToolBarTextColour then
  2293.         window.ToolBarTextColour = view.ToolBarTextColour
  2294.     end
  2295.     view.X = 1
  2296.     view.Y = 2
  2297.  
  2298.     view:ForceDraw()
  2299.     self:OnUpdate('View')
  2300.     if self.OnViewLoad then
  2301.         self.OnViewLoad(view)
  2302.     end
  2303.     self.OldActiveObject = self.Bedrock:GetActiveObject()
  2304.     self.Bedrock:SetActiveObject(view)
  2305. end
  2306.  
  2307. SetView = function(self, view)
  2308.     self:RemoveObject('View')
  2309.     table.insert(self.Children, view)
  2310.     view.Parent = self
  2311.     self:LoadView()
  2312. end
  2313.  
  2314. Flash = function(self)
  2315.     self.Flashing = true
  2316.     self:ForceDraw()
  2317.     self.Bedrock:StartTimer(function()self.Flashing = false end, 0.4)
  2318. end
  2319.  
  2320. OnDraw = function(self, x, y)
  2321.     local toolBarColour = (self.Flashing and colours.grey or self.ToolBarColour)
  2322.     local toolBarTextColour = (self.Flashing and colours.black or self.ToolBarTextColour)
  2323.     if toolBarColour then
  2324.         Drawing.DrawBlankArea(x, y, self.Width, 1, toolBarColour)
  2325.     end
  2326.     if toolBarTextColour then
  2327.         local title = self.Bedrock.Helpers.TruncateString(self.Title, self.Width - 2)
  2328.         Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, title, toolBarTextColour, toolBarColour)
  2329.     end
  2330.     Drawing.IgnoreConstraint = true
  2331.     Drawing.DrawBlankArea(x + 1, y + 1, self.Width, self.Height, self.ShadowColour)
  2332.     Drawing.IgnoreConstraint = false
  2333. end
  2334.  
  2335. Close = function(self)
  2336.     self.Bedrock:SetActiveObject(self.OldActiveObject)
  2337.     self.Bedrock.Window = nil
  2338.     self.Bedrock:RemoveObject(self)
  2339.     if self.OnClose then
  2340.         self:OnClose()
  2341.     end
  2342.     self = nil
  2343. end
  2344.  
  2345. OnUpdate = function(self, value)
  2346.     if value == 'View' and self:GetObject('View') then
  2347.         self.Width = self:GetObject('View').Width
  2348.         self.Height = self:GetObject('View').Height + 1
  2349.         self.X = math.ceil((Drawing.Screen.Width - self.Width) / 2)
  2350.         self.Y = math.ceil((Drawing.Screen.Height - self.Height) / 2)
  2351.     elseif value == 'CanClose' then
  2352.         self:RemoveObject('CloseButton')
  2353.         if self.CanClose then
  2354.             local button = self:AddObject({X = 1, Y = 1, Width = 1, Height = 1, Type = 'Button', BackgroundColour = colours.red, TextColour = colours.white, Text = 'x', Name = 'CloseButton'})
  2355.             button.OnClick = function(btn)
  2356.                 if self.OnCloseButton then
  2357.                     self:OnCloseButton()
  2358.                 end
  2359.                 self:Close()
  2360.             end
  2361.         end
  2362.     end
  2363. end
  2364. ]],
  2365. }
  2366. BasePath = ''
  2367. ProgramPath = nil
  2368.  
  2369. -- Program functions...
  2370.  
  2371. local function main(...)
  2372.     -- Code here...
  2373. end
  2374.  
  2375. -- Run
  2376. local args = {...}
  2377. local _, err = pcall(function() main(unpack(args)) end)
  2378. if err then
  2379.     -- Make a nice error handling screen here...
  2380.     term.setBackgroundColor(colors.black)
  2381.     term.setTextColor(colors.white)
  2382.     term.clear()
  2383.     term.setCursorPos(1, 3)
  2384.     print(" An Error Has Occured! D:\n\n")
  2385.     print(" " .. tostring(err) .. "\n\n")
  2386.     print(" Press any key to exit...")
  2387.     os.pullEvent("key")
  2388. end
  2389.  
  2390. function LoadAPIs(self)
  2391.     local function loadAPI(name, content)
  2392.         local env = setmetatable({}, { __index = getfenv() })
  2393.         local func, err = loadstring(content, name..' (Bedrock API)')
  2394.         if not func then
  2395.             return false, printError(err)
  2396.         end
  2397.         setfenv(func, env)
  2398.         func()
  2399.         local api = {}
  2400.         for k,v in pairs(env) do
  2401.             api[k] = v
  2402.         end
  2403.         _G[name] = api
  2404.         return true
  2405.     end
  2406.  
  2407.     local env = getfenv()
  2408.     local function loadObject(name, content)
  2409.         loadAPI(name, content)
  2410.         if env[name].Inherit then
  2411.             if not getfenv()[env[name].Inherit] then   
  2412.                 if objects[env[name].Inherit] then
  2413.                     loadObject(env[name].Inherit, objects[env[name].Inherit])
  2414.                 elseif fs.exists(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua') then
  2415.                     local h = fs.open(self.ProgramPath..'/Objects/'..env[name].Inherit..'.lua', 'r')
  2416.                     loadObject(env[name].Inherit, h.readAll())
  2417.                     h.close()
  2418.                     loadObject(name, content)
  2419.                     return
  2420.                 end
  2421.             end
  2422.             env[name].__index = getfenv()[env[name].Inherit]
  2423.         else
  2424.             env[name].__index = Object
  2425.         end
  2426.         setmetatable(env[name], env[name])
  2427.     end
  2428.  
  2429.     for k, v in pairs(apis) do
  2430.         loadAPI(k, v)
  2431.         if k == 'Helpers' then
  2432.             self.Helpers = Helpers
  2433.         end
  2434.     end
  2435.  
  2436.     for k, v in pairs(objects) do
  2437.         loadObject(k, v)
  2438.     end
  2439.    
  2440.     local privateObjPath = self.ProgramPath..'/Objects/'
  2441.     if fs.exists(privateObjPath) and fs.isDir(privateObjPath) then
  2442.         for i, v in ipairs(fs.list(privateObjPath)) do
  2443.             if v ~= '.DS_Store' then
  2444.                 local name = string.match(v, '(%a+)%.?.-')
  2445.                 local h = fs.open(privateObjPath..v, 'r')
  2446.                 loadObject(name, h.readAll())
  2447.                 h.close()
  2448.             end
  2449.         end
  2450.     end
  2451.    
  2452.     local privateAPIPath = self.ProgramPath..'/APIs/'
  2453.     if fs.exists(privateAPIPath) and fs.isDir(privateAPIPath) then
  2454.         for i, v in ipairs(fs.list(privateAPIPath)) do
  2455.             if v ~= '.DS_Store' then
  2456.                 local name = string.match(v, '(%a+)%.?.-')
  2457.                 local h = fs.open(privateAPIPath..v, 'r')
  2458.                 loadAPI(name, h.readAll())
  2459.                 h.close()
  2460.             end
  2461.         end
  2462.     end
  2463. end
  2464.  
  2465. AllowTerminate = true
  2466.  
  2467. View = nil
  2468. Menu = nil
  2469.  
  2470. ActiveObject = nil
  2471.  
  2472. DrawTimer = nil
  2473. DrawTimerExpiry = 0
  2474.  
  2475. IsDrawing = false
  2476.  
  2477. Running = true
  2478.  
  2479. DefaultView = 'main'
  2480.  
  2481. AnimationEnabled = true
  2482.  
  2483. EventHandlers = {
  2484.    
  2485. }
  2486.  
  2487. ObjectClickHandlers = {
  2488.    
  2489. }
  2490.  
  2491. ObjectUpdateHandlers = {
  2492.    
  2493. }
  2494.  
  2495. Timers = {
  2496.    
  2497. }
  2498.  
  2499. ModifierKeys = {}
  2500. KeyboardShortcuts = {}
  2501.  
  2502. keys.leftCommand = 219
  2503. keys.rightCommand = 220
  2504.  
  2505. function Initialise(self, programPath)
  2506.     self.ProgramPath = programPath or self.ProgramPath
  2507.     if not programPath then
  2508.         if self.ProgramPath then
  2509.             local prgPath = self.ProgramPath
  2510.             local prgName = fs.getName(prgPath)
  2511.             if prgPath:find('/') then
  2512.                 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)
  2513.                 self.ProgramPath = prgPath:sub(1, #prgPath-#prgName-1)
  2514.             else
  2515.                 self.ProgramPath = ''
  2516.             end
  2517.         else
  2518.             self.ProgramPath = ''
  2519.         end
  2520.     end
  2521.     self:LoadAPIs()
  2522.     self.ViewPath = self.ProgramPath .. '/Views/'
  2523.     --first, check that the barebones APIs are available
  2524.     local requiredApis = {
  2525.         'Drawing',
  2526.         'View'
  2527.     }
  2528.     local env = getfenv()
  2529.     for i,v in ipairs(requiredApis) do
  2530.         if not env[v] then
  2531.             error('The API: '..v..' is not loaded. Please make sure you load it to use Bedrock.')
  2532.         end
  2533.     end
  2534.  
  2535.     local copy = { }
  2536.     for k, v in pairs(self) do
  2537.         if k ~= 'Initialise' then
  2538.             copy[k] = v
  2539.         end
  2540.     end
  2541.     return setmetatable(copy, getmetatable(self))
  2542. end
  2543.  
  2544. function HandleClick(self, event, side, x, y)
  2545.     if self.Menu then
  2546.         if not self.View:DoClick(self.Menu, event, side, x, y) then
  2547.             self.Menu:Close()
  2548.         end
  2549.     elseif self.Window then
  2550.         if not self.View:CheckClick(self.Window, x, y) then
  2551.             self.Window:Flash()
  2552.         else
  2553.             self.View:DoClick(self.Window, event, side, x, y)
  2554.         end
  2555.     elseif self.View then
  2556.         if self.View:Click(event, side, x, y) ~= false then
  2557.         end    
  2558.     end
  2559. end
  2560.  
  2561. function UnregisterKeyboardShortcut(self, name)
  2562.     if name then
  2563.         self.KeyboardShortcuts[name] = nil
  2564.     end
  2565. end
  2566.  
  2567. function RegisterKeyboardShortcut(self, keys, func, name)
  2568.     name = name or tostring(math.random(1, 10000))
  2569.     if type(keys[1]) == 'table' then
  2570.         for i, v in ipairs(keys) do
  2571.             self.KeyboardShortcuts[name] = {Keys = v, Function = func}
  2572.         end
  2573.     else
  2574.         self.KeyboardShortcuts[name] = {Keys = keys, Function = func}
  2575.     end
  2576.     return name
  2577. end
  2578.  
  2579. function TryKeyboardShortcuts(self, keychar)
  2580.     if keychar == keys.backspace then
  2581.         keychar = keys.delete
  2582.     end
  2583.  
  2584.     local len = 1 -- + keychar
  2585.     for k, v in pairs(self.ModifierKeys) do
  2586.         len = len + 1
  2587.     end
  2588.  
  2589.     for _, shortcut in pairs(self.KeyboardShortcuts) do
  2590.         local match = true
  2591.         for i2, key in ipairs(shortcut.Keys) do
  2592.             if self.ModifierKeys[key] == nil and key ~= keychar then
  2593.                 match = false
  2594.             end
  2595.         end
  2596.  
  2597.         if match and #shortcut.Keys == len then
  2598.             return shortcut.Function() ~= false
  2599.         end
  2600.     end
  2601. end
  2602.  
  2603. function HandleKeyChar(self, event, keychar)
  2604.     if keychar == keys.leftCtrl or keychar == keys.leftShift or keychar == keys.leftAlt or keychar == keys.leftCommand or keychar == keys.rightCommand or keychar == keys.rightCtrl or keychar == keys.rightShift or keychar == keys.rightAlt then
  2605.         if keychar == keys.leftCommand or keychar == keys.rightCommand or keychar == keys.rightCtrl then
  2606.             keychar = keys.leftCtrl
  2607.         elseif keychar == keys.rightAlt then
  2608.             keychar = keys.leftAlt
  2609.         elseif keychar == keys.rightShift then
  2610.             keychar = keys.leftShift
  2611.         end
  2612.         self.ModifierKeys[keychar] = self:StartTimer(function(_, timer)
  2613.             if timer == self.ModifierKeys[keychar] then
  2614.                 self.ModifierKeys[keychar] = nil
  2615.             end
  2616.         end, 1)
  2617.     elseif self:TryKeyboardShortcuts(keychar) then
  2618.         return
  2619.     end
  2620.  
  2621.     if self:GetActiveObject() then
  2622.         local activeObject = self:GetActiveObject()
  2623.         if activeObject.OnKeyChar then
  2624.             if activeObject:OnKeyChar(event, keychar) ~= false then
  2625.                 --self:Draw()
  2626.             end
  2627.         end
  2628.     end
  2629. end
  2630.  
  2631. PreparedMenus = {}
  2632.  
  2633. function PrepareMenu(self, name)
  2634.     local menu = self:AddObject(name, {Type = 'Menu', X = 1, Y = 1, Prepared = true})
  2635.     menu.Visible = false
  2636.     self.PreparedMenus[name] = menu
  2637.     return menu
  2638. end
  2639.  
  2640. function ToggleMenu(self, name, owner, x, y)
  2641.     if self.Menu then
  2642.         self.Menu:Close()
  2643.         return false
  2644.     else
  2645.         self:SetMenu(name, owner, x, y)
  2646.         return true
  2647.     end
  2648. end
  2649.  
  2650. function SetMenu(self, menu, owner, x, y)
  2651.     x = x or 1
  2652.     y = y or 1
  2653.     if self.Menu then
  2654.         self.Menu:Close()
  2655.     end
  2656.     if menu then
  2657.         local pos = owner:GetPosition()
  2658.         if self.PreparedMenus[menu] then
  2659.             self.Menu = self.PreparedMenus[menu]
  2660.             self.Menu.Visible = true
  2661.             self.Menu.Owner = owner
  2662.             self.Menu.X = pos.X + x - 1
  2663.             self.Menu.Y = pos.Y + y
  2664.             self.Menu.Z = self.View.Children[#self.View.Children].Z + 1
  2665.             self:ReorderObjects()
  2666.         else
  2667.             self.Menu = self:AddObject(menu, {Type = 'Menu', Owner = owner, X = pos.X + x - 1, Y = pos.Y + y, Z = self.View.Children[#self.View.Children].Z + 1})
  2668.         end
  2669.     end
  2670. end
  2671.  
  2672. function ObjectClick(self, name, func)
  2673.     self.ObjectClickHandlers[name] = func
  2674. end
  2675.  
  2676. function ClickObject(self, object, event, side, x, y)
  2677.     if self.ObjectClickHandlers[object.Name] then
  2678.         return self.ObjectClickHandlers[object.Name](object, event, side, x, y)
  2679.     end
  2680.     return false
  2681. end
  2682.  
  2683. function ObjectUpdate(self, name, func)
  2684.     self.ObjectUpdateHandlers[name] = func
  2685. end
  2686.  
  2687. function UpdateObject(self, object, ...)
  2688.     if self.ObjectUpdateHandlers[object.Name] then
  2689.         self.ObjectUpdateHandlers[object.Name](object, ...)
  2690.         --self:Draw()
  2691.     end
  2692. end
  2693.  
  2694. function GetAbsolutePosition(self, obj)
  2695.     if not obj.Parent then
  2696.         return {X = obj.X, Y = obj.Y}
  2697.     else
  2698.         local pos = self:GetAbsolutePosition(obj.Parent)
  2699.         local x = pos.X + obj.X - 1
  2700.         local y = pos.Y + obj.Y - 1
  2701.         if not obj.Fixed and obj.Parent.ChildOffset then
  2702.             x = x + obj.Parent.ChildOffset.X
  2703.             y = y + obj.Parent.ChildOffset.Y
  2704.         end
  2705.         return {X = x, Y = y}
  2706.     end
  2707. end
  2708.  
  2709. function LoadView(self, name, draw)
  2710.     if self.View and self.OnViewClose then
  2711.         self.OnViewClose(self.View.Name)
  2712.     end
  2713.     if self.View then
  2714.         self.View:OnRemove()
  2715.     end
  2716.     local success = false
  2717.  
  2718.     if Drawing.Screen.Width <= 26 and fs.exists(self.ViewPath..name..'-pocket.view') then
  2719.         name = name..'-pocket'
  2720.     elseif Drawing.Screen.Width <= 39 and fs.exists(self.ViewPath..name..'-turtle.view') then
  2721.         name = name..'-turtle'
  2722.     end
  2723.  
  2724.     if not fs.exists(self.ViewPath..name..'.view') then
  2725.         error('The view: '..name..'.view does not exist.')
  2726.     end
  2727.  
  2728.     local h = fs.open(self.ViewPath..name..'.view', 'r')
  2729.     if h then
  2730.         local view = textutils.unserialize(h.readAll())
  2731.         h.close()
  2732.         if view then
  2733.             self.View = View:InitialiseFile(self, view, name)
  2734.             self:ReorderObjects()
  2735.  
  2736.             if OneOS and view.ToolBarColour then
  2737.                 OneOS.ToolBarColour = view.ToolBarColour
  2738.             end
  2739.             if OneOS and view.ToolBarTextColour then
  2740.                 OneOS.ToolBarTextColour = view.ToolBarTextColour
  2741.             end
  2742.             if not self:GetActiveObject() then
  2743.                 self:SetActiveObject()
  2744.             end
  2745.             success = true
  2746.         end
  2747.     end
  2748.  
  2749.     if success and self.OnViewLoad then
  2750.         self.OnViewLoad(name)
  2751.     end
  2752.  
  2753.     if draw ~= false then
  2754.         self:Draw()
  2755.     end
  2756.  
  2757.     if not success then
  2758.         error('Failed to load view: '..name..'. It probably isn\'t formatted correctly. Did you forget a } or ,?')
  2759.     end
  2760.  
  2761.     return success
  2762. end
  2763.  
  2764. function InheritFile(self, file, name)
  2765.     local h = fs.open(self.ViewPath..name..'.view', 'r')
  2766.     if h then
  2767.         local super = textutils.unserialize(h.readAll())
  2768.         if super then
  2769.             if type(super) ~= 'table' then
  2770.                 error('View: "'..name..'.view" is not formatted correctly.')
  2771.             end
  2772.  
  2773.             for k, v in pairs(super) do
  2774.                 if not file[k] then
  2775.                     file[k] = v
  2776.                 end
  2777.             end
  2778.             return file
  2779.         end
  2780.     end
  2781.     return file
  2782. end
  2783.  
  2784. function ParseStringSize(self, parent, k, v)
  2785.     local parentSize = parent.Width
  2786.     if k == 'Height' or k == 'Y' then
  2787.         parentSize = parent.Height
  2788.     end
  2789.     local parts = {v}
  2790.     if type(v) == 'string' and string.find(v, ',') then
  2791.         parts = {}
  2792.         for word in string.gmatch(v, '([^,]+)') do
  2793.             table.insert(parts, word)
  2794.         end
  2795.     end
  2796.  
  2797.     v = 0
  2798.     for i2, part in ipairs(parts) do
  2799.         if type(part) == 'string' and part:sub(#part) == '%' then
  2800.             v = v + math.ceil(parentSize * (tonumber(part:sub(1, #part-1)) / 100))
  2801.         else
  2802.             v = v + tonumber(part)
  2803.         end
  2804.     end
  2805.     return v
  2806. end
  2807.  
  2808. function ObjectFromFile(self, file, view)
  2809.     local env = getfenv()
  2810.     if env[file.Type] then
  2811.         if not env[file.Type].Initialise then
  2812.             error('Malformed Object: '..file.Type)
  2813.         end
  2814.         local object = {}
  2815.  
  2816.         if file.InheritView then
  2817.             file = self:InheritFile(file, file.InheritView)
  2818.         end
  2819.        
  2820.         object.AutoWidth = true
  2821.         for k, v in pairs(file) do
  2822.             if k == 'Width' or k == 'X' or k == 'Height' or k == 'Y' then
  2823.                 v = self:ParseStringSize(view, k, v)
  2824.             end
  2825.  
  2826.             if k == 'Width' then
  2827.                 object.AutoWidth = false
  2828.             end
  2829.             if k ~= 'Children' then
  2830.                 object[k] = v
  2831.             else
  2832.                 object[k] = {}
  2833.             end
  2834.         end
  2835.  
  2836.         object.Parent = view
  2837.         object.Bedrock = self
  2838.         if not object.Name then
  2839.             object.Name = file.Type
  2840.         end
  2841.  
  2842.         object = env[file.Type]:Initialise(object)
  2843.  
  2844.         if file.Children then
  2845.             for i, obj in ipairs(file.Children) do
  2846.                 local _view = self:ObjectFromFile(obj, object)
  2847.                 if not _view.Z then
  2848.                     _view.Z = i
  2849.                 end
  2850.                 _view.Parent = object
  2851.                 table.insert(object.Children, _view)
  2852.             end
  2853.         end
  2854.  
  2855.         if not object.OnClick then
  2856.             object.OnClick = function(...) return self:ClickObject(...) end
  2857.         end
  2858.         --object.OnUpdate = function(...) self:UpdateObject(...) end
  2859.  
  2860.         if object.OnUpdate then
  2861.             for k, v in pairs(env[file.Type]) do
  2862.                 object:OnUpdate(k)
  2863.             end
  2864.  
  2865.             for k, v in pairs(object.__index) do
  2866.                 object:OnUpdate(k)
  2867.             end
  2868.         end
  2869.  
  2870.         if object.Active then
  2871.             object.Bedrock:SetActiveObject(object)
  2872.         end
  2873.         if object.OnLoad then
  2874.             object:OnLoad()
  2875.         end
  2876.         object.Ready = true
  2877.         return object
  2878.     elseif not file.Type then
  2879.         error('No object type specified. (e.g. Type = "Button")')
  2880.     else
  2881.         error('No Object: '..file.Type..'. The API probably isn\'t loaded')
  2882.     end
  2883. end
  2884.  
  2885. function ReorderObjects(self)
  2886.     if self.View and self.View.Children then
  2887.         self.View:ReorderObjects()
  2888.     end
  2889. end
  2890.  
  2891. function AddObject(self, info, extra, first)
  2892.     return self.View:AddObject(info, extra, first)
  2893. end
  2894.  
  2895. function GetObject(self, name)
  2896.     return self.View:GetObject(name)
  2897. end
  2898.  
  2899. function GetObjects(self, name)
  2900.     return self.View:GetObjects(name)
  2901. end
  2902.  
  2903. function RemoveObject(self, name)
  2904.     return self.View:RemoveObject(name)
  2905. end
  2906.  
  2907. function RemoveObjects(self, name)
  2908.     return self.View:RemoveObjects(name)
  2909. end
  2910.  
  2911. DrawEvent = nil
  2912.  
  2913. function HandleDraw(self, event, id)
  2914.     if id == self.DrawEvent then
  2915.         self.DrawEvent = nil
  2916.         self:Draw()
  2917.     end
  2918. end
  2919.  
  2920. function ForceDraw(self)
  2921.     if not self.DrawEvent then--or self.DrawTimerExpiry <= os.clock() then
  2922.         self.DrawEvent = math.random()
  2923.         os.queueEvent('bedrock_draw', self.DrawEvent)
  2924.         -- self:StartTimer(function()
  2925.         --  self.DrawTimer = nil
  2926.         --  self:Draw()
  2927.         -- end, 0.05)
  2928.         -- self.DrawTimerExpiry = os.clock() + 0.1
  2929.     end
  2930. end
  2931.  
  2932. function DisplayWindow(self, _view, title, canClose)
  2933.     if canClose == nil then
  2934.         canClose = true
  2935.     end
  2936.     if type(_view) == 'string' then
  2937.         local h = fs.open(self.ViewPath.._view..'.view', 'r')
  2938.         if h then
  2939.             _view = textutils.unserialize(h.readAll())
  2940.             h.close()
  2941.         end
  2942.     end
  2943.  
  2944.     self.Window = self:AddObject({Type = 'Window', Z = 999, Title = title, CanClose = canClose})
  2945.     _view.Type = 'View'
  2946.     _view.Name = 'View'
  2947.     _view.BackgroundColour = _view.BackgroundColour or colours.white
  2948.     self.Window:SetView(self:ObjectFromFile(_view, self.Window))
  2949. end
  2950.  
  2951. function DisplayAlertWindow(self, title, text, buttons, callback)
  2952.     local func = function(btn)
  2953.         self.Window:Close()
  2954.         if callback then
  2955.             callback(btn.Text)
  2956.         end
  2957.     end
  2958.     local children = {}
  2959.     local usedX = -1
  2960.     if buttons then
  2961.         for i, text in ipairs(buttons) do
  2962.             usedX = usedX + 3 + #text
  2963.             table.insert(children, {
  2964.                 ["Y"]="100%,-1",
  2965.                 ["X"]="100%,-"..usedX,
  2966.                 ["Name"]=text.."Button",
  2967.                 ["Type"]="Button",
  2968.                 ["Text"]=text,
  2969.                 OnClick = func
  2970.             })
  2971.         end
  2972.     end
  2973.  
  2974.     local width = usedX + 2
  2975.     if width < 28 then
  2976.         width = 28
  2977.     end
  2978.  
  2979.     local canClose = true
  2980.     if buttons and #buttons~=0 then
  2981.         canClose = false
  2982.     end
  2983.  
  2984.     local height = 0
  2985.     if text then
  2986.         height = #Helpers.WrapText(text, width - 2)
  2987.         table.insert(children, {
  2988.             ["Y"]=2,
  2989.             ["X"]=2,
  2990.             ["Width"]="100%,-2",
  2991.             ["Height"]=height,
  2992.             ["Name"]="Label",
  2993.             ["Type"]="Label",
  2994.             ["Text"]=text
  2995.         })
  2996.     end
  2997.     local view = {
  2998.         Children = children,
  2999.         Width=width,
  3000.         Height=3+height+(canClose and 0 or 1),
  3001.         OnKeyChar = function(_view, keychar)
  3002.             func({Text=buttons[1]})
  3003.         end
  3004.     }
  3005.     self:DisplayWindow(view, title, canClose)
  3006. end
  3007.  
  3008. function DisplayTextBoxWindow(self, title, text, callback, textboxText, cursorAtEnd)
  3009.     textboxText = textboxText or ''
  3010.     local children = {
  3011.         {
  3012.             ["Y"]="100%,-1",
  3013.             ["X"]="100%,-4",
  3014.             ["Name"]="OkButton",
  3015.             ["Type"]="Button",
  3016.             ["Text"]="Ok",
  3017.             OnClick = function()
  3018.                 local text = self.Window:GetObject('TextBox').Text
  3019.                 self.Window:Close()
  3020.                 callback(true, text)
  3021.             end
  3022.         },
  3023.         {
  3024.             ["Y"]="100%,-1",
  3025.             ["X"]="100%,-13",
  3026.             ["Name"]="CancelButton",
  3027.             ["Type"]="Button",
  3028.             ["Text"]="Cancel",
  3029.             OnClick = function()
  3030.                 self.Window:Close()
  3031.                 callback(false)
  3032.             end
  3033.         }
  3034.     }
  3035.  
  3036.     local height = -1
  3037.     if text and #text ~= 0 then
  3038.         height = #Helpers.WrapText(text, 26)
  3039.         table.insert(children, {
  3040.             ["Y"]=2,
  3041.             ["X"]=2,
  3042.             ["Width"]="100%,-2",
  3043.             ["Height"]=height,
  3044.             ["Name"]="Label",
  3045.             ["Type"]="Label",
  3046.             ["Text"]=text
  3047.         })
  3048.     end
  3049.     table.insert(children,
  3050.         {
  3051.             ["Y"]=3+height,
  3052.             ["X"]=2,
  3053.             ["Width"]="100%,-2",
  3054.             ["Name"]="TextBox",
  3055.             ["Type"]="TextBox",
  3056.             ["Text"]=textboxText,
  3057.             ["CursorPos"]=(cursorAtEnd or 0)
  3058.         })
  3059.     local view = {
  3060.         Children = children,
  3061.         Width=28,
  3062.         Height=5+height+(canClose and 0 or 1),
  3063.     }
  3064.     self:DisplayWindow(view, title)
  3065.     self.Window:GetObject('TextBox').OnChange = function(txtbox, event, keychar)
  3066.         if keychar == keys.enter then
  3067.             self.Window:Close()
  3068.             callback(true, txtbox.Text)
  3069.         end
  3070.     end
  3071.     self:SetActiveObject(self.Window:GetObject('TextBox'))
  3072.     self.Window.OnCloseButton = function()callback(false)end
  3073. end
  3074.  
  3075. function DisplayOpenFileWindow(self, title, callback)
  3076.     title = title or 'Open File'
  3077.     local func = function(btn)
  3078.         self.Window:Close()
  3079.         if callback then
  3080.             callback(btn.Text)
  3081.         end
  3082.     end
  3083.  
  3084.     local sidebarItems = {}
  3085.  
  3086.     --this is a really, really super bad way of doing it
  3087.     local separator = '                               !'
  3088.  
  3089.     local function addFolder(path, level)
  3090.         for i, v in ipairs(_fs.list(path)) do
  3091.             local fPath = path .. '/' .. v
  3092.             if fPath ~= '/rom' and _fs.isDir(fPath) then
  3093.                 table.insert(sidebarItems, level .. v..separator..fPath)
  3094.                 addFolder(fPath, level .. '  ')
  3095.             end
  3096.         end
  3097.     end
  3098.    
  3099.     if OneOS then
  3100.         _fs = OneOS.FS
  3101.     end
  3102.  
  3103.     addFolder('','')
  3104.  
  3105.     local currentFolder = ''
  3106.     local selectedPath = nil
  3107.  
  3108.     local goToFolder = nil
  3109.  
  3110.     local children = {
  3111.         {
  3112.             ["Y"]="100%,-2",
  3113.             ["X"]=1,
  3114.             ["Height"]=3,
  3115.             ["Width"]="100%",
  3116.             ["BackgroundColour"]=colours.lightGrey,
  3117.             ["Name"]="SidebarListView",
  3118.             ["Type"]="View"
  3119.         },
  3120.         {
  3121.             ["Y"]="100%,-1",
  3122.             ["X"]="100%,-4",
  3123.             ["Name"]="OkButton",
  3124.             ["Type"]="Button",
  3125.             ["Text"]="Ok",
  3126.             ["BackgroundColour"]=colours.white,
  3127.             ["Enabled"]=false,
  3128.             OnClick = function()
  3129.                 if selectedPath then
  3130.                     self.Window:Close()
  3131.                     callback(true, Helpers.TidyPath(selectedPath))
  3132.                 end
  3133.             end
  3134.         },
  3135.         {
  3136.             ["Y"]="100%,-1",
  3137.             ["X"]="100%,-13",
  3138.             ["Name"]="CancelButton",
  3139.             ["Type"]="Button",
  3140.             ["Text"]="Cancel",
  3141.             ["BackgroundColour"]=colours.white,
  3142.             OnClick = function()
  3143.                 self.Window:Close()
  3144.                 callback(false)
  3145.             end
  3146.         },
  3147.         {
  3148.             ["Y"]=1,
  3149.             ["X"]=1,
  3150.             ["Height"]="100%,-3",
  3151.             ["Width"]="40%,-1",
  3152.             ["Name"]="SidebarListView",
  3153.             ["Type"]="ListView",
  3154.             ["CanSelect"]=true,
  3155.             ["Items"]={
  3156.                 ["Computer"] = sidebarItems
  3157.             },
  3158.             OnSelect = function(listView, text)
  3159.                 local _,s = text:find(separator)
  3160.                 if s then
  3161.                     local path = text:sub(s + 1)
  3162.                     goToFolder(path)
  3163.                 end
  3164.             end,
  3165.             OnClick = function(listView, event, side, x, y)
  3166.                 if y == 1 then
  3167.                     goToFolder('/')
  3168.                 end
  3169.             end
  3170.         },
  3171.         {
  3172.             ["Y"]=1,
  3173.             ["X"]="40%",
  3174.             ["Height"]="100%,-3",
  3175.             ["Width"]=1,
  3176.             ["Type"]="Separator"
  3177.         },
  3178.         {
  3179.             ["Y"]=1,
  3180.             ["X"]="40%,2",
  3181.             ["Width"]="65%,-3",
  3182.             ["Height"]=1,
  3183.             ["Type"]="Label",
  3184.             ["Name"]="PathLabel",
  3185.             ["TextColour"]=colours.lightGrey,
  3186.             ["Text"]='/'
  3187.         },
  3188.         {
  3189.             ["Y"]=2,
  3190.             ["X"]="40%,1",
  3191.             ["Height"]="100%,-4",
  3192.             ["Width"]="65%,-1",
  3193.             ["Name"]="FilesListView",
  3194.             ["Type"]="ListView",
  3195.             ["CanSelect"]=true,
  3196.             ["Items"]={},
  3197.             OnSelect = function(listView, text)
  3198.                 selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text)
  3199.                 self.Window:GetObject('OkButton').Enabled = true
  3200.             end,
  3201.             OnClick = function(listView, event, side, x, y)
  3202.                 if y == 1 then
  3203.                     goToFolder('/')
  3204.                 end
  3205.             end
  3206.         },
  3207.     }
  3208.     local view = {
  3209.         Children = children,
  3210.         Width=40,
  3211.         Height= Drawing.Screen.Height - 4,
  3212.         OnCloseButton=function()
  3213.             callback(false)
  3214.         end
  3215.     }
  3216.     self:DisplayWindow(view, title)
  3217.  
  3218.     goToFolder = function(path)
  3219.         path = Helpers.TidyPath(path)
  3220.         self.Window:GetObject('PathLabel').Text = path
  3221.         currentFolder = path
  3222.  
  3223.         local filesListItems = {}
  3224.         for i, v in ipairs(_fs.list(path)) do
  3225.             if not _fs.isDir(currentFolder .. v) then
  3226.                 table.insert(filesListItems, v)
  3227.             end
  3228.         end
  3229.         self.Window:GetObject('OkButton').Enabled = false
  3230.         selectedPath = nil
  3231.         self.Window:GetObject('FilesListView').Items = filesListItems
  3232.  
  3233.     end
  3234.  
  3235.     if startPath then
  3236.         goToFolder(startPath)
  3237.     elseif OneOS then
  3238.         goToFolder('/Desktop/Documents/')
  3239.     else
  3240.         goToFolder('')
  3241.     end
  3242.  
  3243.     self.Window.OnCloseButton = function()callback(false)end
  3244. end
  3245.  
  3246. function DisplaySaveFileWindow(self, title, callback, extension, startPath)
  3247.     local _fs = fs
  3248.     if extension and extension:sub(1,1) ~= '.' then
  3249.         extension = '.' .. extension
  3250.     end
  3251.     extension = extension or ''
  3252.  
  3253.     title = title or 'Save File'
  3254.     local func = function(btn)
  3255.         self.Window:Close()
  3256.         if callback then
  3257.             callback(btn.Text)
  3258.         end
  3259.     end
  3260.  
  3261.     local sidebarItems = {}
  3262.  
  3263.     --this is a really, really super bad way of doing it
  3264.     local separator = '                                                       !'
  3265.  
  3266.     local function addFolder(path, level)
  3267.         for i, v in ipairs(_fs.list(path)) do
  3268.             local fPath = path .. '/' .. v
  3269.             if fPath ~= '/rom' and _fs.isDir(fPath) then
  3270.                 table.insert(sidebarItems, level .. v..separator..fPath)
  3271.                 addFolder(fPath, level .. '  ')
  3272.             end
  3273.         end
  3274.     end
  3275.    
  3276.     if OneOS then
  3277.         _fs = OneOS.FS
  3278.     end
  3279.     addFolder('','')
  3280.  
  3281.     local currentFolder = ''
  3282.     local selectedPath = nil
  3283.  
  3284.     local goToFolder = nil
  3285.  
  3286.     local function updatePath()
  3287.         local text = self:GetObject('FileNameTextBox').Text
  3288.         if #text == 0 then
  3289.             self.Window:GetObject('OkButton').Enabled = false
  3290.             selectedPath = Helpers.TidyPath(currentFolder)
  3291.         else
  3292.             self.Window:GetObject('OkButton').Enabled = true
  3293.             selectedPath = Helpers.TidyPath(currentFolder .. '/' .. text .. extension)
  3294.         end
  3295.         self:GetObject('PathLabel').Text = selectedPath
  3296.     end
  3297.  
  3298.     local children = {
  3299.         {
  3300.             ["Y"]="100%,-2",
  3301.             ["X"]=1,
  3302.             ["Height"]=3,
  3303.             ["Width"]="100%",
  3304.             ["BackgroundColour"]=colours.lightGrey,
  3305.             ["Type"]="View"
  3306.         },
  3307.         {
  3308.             ["Y"]="100%,-1",
  3309.             ["X"]="100%,-4",
  3310.             ["Name"]="OkButton",
  3311.             ["Type"]="Button",
  3312.             ["Text"]="Ok",
  3313.             ["BackgroundColour"]=colours.white,
  3314.             ["Enabled"]=false,
  3315.             OnClick = function()
  3316.                 if selectedPath then
  3317.                     local text = self:GetObject('FileNameTextBox').Text
  3318.                     self.Window:Close()
  3319.                     callback(true, selectedPath, text)
  3320.                 end
  3321.             end
  3322.         },
  3323.         {
  3324.             ["Y"]="100%,-1",
  3325.             ["X"]="100%,-13",
  3326.             ["Name"]="CancelButton",
  3327.             ["Type"]="Button",
  3328.             ["Text"]="Cancel",
  3329.             ["BackgroundColour"]=colours.white,
  3330.             OnClick = function()
  3331.                 self.Window:Close()
  3332.                 callback(false)
  3333.             end
  3334.         },
  3335.         {
  3336.             ["Y"]="100%,-2",
  3337.             ["X"]=3,
  3338.             ["Width"]="100%,-4",
  3339.             ["Name"]="PathLabel",
  3340.             ["Type"]="Label",
  3341.             ["Text"]="/",
  3342.             ["TextColour"]=colours.grey
  3343.         },
  3344.         {
  3345.             ["Y"]="100%,-1",
  3346.             ["X"]=3,
  3347.             ["Width"]="100%,-17",
  3348.             ["Name"]="FileNameTextBox",
  3349.             ["Type"]="TextBox",
  3350.             ["Placeholder"]="File Name",
  3351.             ["Active"]=true,
  3352.             ["BackgroundColour"]=colours.white,
  3353.             OnChange = function(_self, event, keychar)
  3354.                 if keychar == keys.enter then
  3355.                     self:GetObject('OkButton'):OnClick()
  3356.                 else
  3357.                     updatePath()
  3358.                 end
  3359.             end
  3360.         },
  3361.         {
  3362.             ["Y"]=1,
  3363.             ["X"]=2,
  3364.             ["Height"]="100%,-3",
  3365.             ["Width"]="100%,-1",
  3366.             ["Name"]="SidebarListView",
  3367.             ["Type"]="ListView",
  3368.             ["CanSelect"]=true,
  3369.             ["Items"]={
  3370.                 ["Computer"] = sidebarItems
  3371.             },
  3372.             OnSelect = function(listView, text)
  3373.                 local _,s = text:find(separator)
  3374.                 if s then
  3375.                     local path = text:sub(s + 1)
  3376.                     goToFolder(path)
  3377.                 end
  3378.             end,
  3379.             OnClick = function(listView, event, side, x, y)
  3380.                 if y == 1 then
  3381.                     goToFolder('/')
  3382.                 end
  3383.             end
  3384.         },
  3385.     }
  3386.     local view = {
  3387.         Children = children,
  3388.         Width=35,
  3389.         Height= Drawing.Screen.Height - 4,
  3390.         OnCloseButton=function()
  3391.             callback(false)
  3392.         end
  3393.     }
  3394.     self:DisplayWindow(view, title)
  3395.  
  3396.     self:SetActiveObject(self.Window:GetObject('FileNameTextBox'))
  3397.  
  3398.     goToFolder = function(path)
  3399.         path = Helpers.TidyPath(path)
  3400.         currentFolder = path
  3401.         selectedPath = nil
  3402.         updatePath()
  3403.     end
  3404.  
  3405.     if startPath then
  3406.         goToFolder(startPath)
  3407.     elseif OneOS then
  3408.         goToFolder('/Desktop/Documents/')
  3409.     else
  3410.         goToFolder('')
  3411.     end
  3412.  
  3413.     self.Window.OnCloseButton = function()callback(false)end
  3414. end
  3415.  
  3416. function RegisterEvent(self, event, func)
  3417.     if not self.EventHandlers[event] then
  3418.         self.EventHandlers[event] = {}
  3419.     end
  3420.     table.insert(self.EventHandlers[event], func)
  3421. end
  3422.  
  3423. function StartRepeatingTimer(self, func, interval)
  3424.     local int = interval
  3425.     if type(int) == 'function' then
  3426.         int = int()
  3427.     end
  3428.     if not int or int <= 0 then
  3429.         return
  3430.     end
  3431.     local timer = os.startTimer(int)
  3432.  
  3433.     self.Timers[timer] = {func, true, interval}
  3434.     return timer
  3435. end
  3436.  
  3437. function StartTimer(self, func, delay)
  3438.     local timer = os.startTimer(delay)
  3439.     self.Timers[timer] = {func, false}
  3440.     return timer
  3441. end
  3442.  
  3443. function StopTimer(self, timer)
  3444.     if self.Timers[timer] then
  3445.         self.Timers[timer] = nil
  3446.     end
  3447. end
  3448.  
  3449. function HandleTimer(self, event, timer)
  3450.     if self.Timers[timer] then
  3451.         local oldTimer = self.Timers[timer]
  3452.         self.Timers[timer] = nil
  3453.         local new = nil
  3454.         if oldTimer[2] then
  3455.             new = self:StartRepeatingTimer(oldTimer[1], oldTimer[3])
  3456.         end
  3457.         if oldTimer and oldTimer[1] then
  3458.             oldTimer[1](new, timer)
  3459.         end
  3460.     elseif self.OnTimer then
  3461.         self.OnTimer(self, event, timer)
  3462.     end
  3463. end
  3464.  
  3465. function SetActiveObject(self, object)
  3466.     if object then
  3467.         if object ~= self.ActiveObject then
  3468.             self.ActiveObject = object
  3469.             object:ForceDraw()
  3470.         end
  3471.     elseif self.ActiveObject ~= nil then
  3472.         self.ActiveObject = nil
  3473.         self.CursorPos = nil
  3474.         self.View:ForceDraw()
  3475.     end
  3476. end
  3477.  
  3478. function GetActiveObject(self)
  3479.     return self.ActiveObject
  3480. end
  3481.  
  3482. OnTimer = nil
  3483. OnClick = nil
  3484. OnKeyChar = nil
  3485. OnDrag = nil
  3486. OnScroll = nil
  3487. OnViewLoad = nil
  3488. OnViewClose = nil
  3489. OnDraw = nil
  3490. OnQuit = nil
  3491.  
  3492. local eventFuncs = {
  3493.     OnClick = {'mouse_click', 'monitor_touch'},
  3494.     OnKeyChar = {'key', 'char'},
  3495.     OnDrag = {'mouse_drag'},
  3496.     OnScroll = {'mouse_scroll'},
  3497.     HandleClick = {'mouse_click', 'mouse_drag', 'mouse_scroll', 'monitor_touch'},
  3498.     HandleKeyChar = {'key', 'char'},
  3499.     HandleTimer = {'timer'},
  3500.     HandleDraw = {'bedrock_draw'}
  3501. }
  3502.  
  3503. local drawCalls = 0
  3504. local ignored = 0
  3505. function Draw(self)
  3506.     self.IsDrawing = true
  3507.     if self.OnDraw then
  3508.         self:OnDraw()
  3509.     end
  3510.  
  3511.     if self.View and self.View:NeedsDraw() then
  3512.         self.View:Draw()
  3513.         Drawing.DrawBuffer()
  3514.         if isDebug then
  3515.             drawCalls = drawCalls + 1
  3516.         end
  3517.     elseif not self.View then
  3518.         print('No loaded view. You need to do program:LoadView first.')
  3519.     end
  3520.  
  3521.     if self:GetActiveObject() and self.CursorPos and type(self.CursorPos[1]) == 'number' and type(self.CursorPos[2]) == 'number' then
  3522.         term.setCursorPos(self.CursorPos[1], self.CursorPos[2])
  3523.         term.setTextColour(self.CursorColour)
  3524.         term.setCursorBlink(true)
  3525.     else
  3526.         term.setCursorBlink(false)
  3527.     end
  3528.  
  3529.     self.IsDrawing = false
  3530. end
  3531.  
  3532. function EventHandler(self)
  3533.     local event = { os.pullEventRaw() }
  3534.    
  3535.     if self.EventHandlers[event[1]] then
  3536.         for i, e in ipairs(self.EventHandlers[event[1]]) do
  3537.             e(self, unpack(event))
  3538.         end
  3539.     end
  3540. end
  3541.  
  3542. function Quit(self)
  3543.     self.Running = false
  3544.     if self.OnQuit then
  3545.         self:OnQuit()
  3546.     end
  3547.     if OneOS then
  3548.         OneOS.Close()
  3549.     end
  3550. end
  3551.  
  3552. function Run(self, ready)
  3553.     if not  term.isColour or not term.isColour() then
  3554.         print('This program requires an advanced (golden) comptuer to run, sorry.')
  3555.         error('', 0)
  3556.     end
  3557.  
  3558.     for name, events in pairs(eventFuncs) do
  3559.         if self[name] then
  3560.             for i, event in ipairs(events) do
  3561.                 self:RegisterEvent(event, self[name])
  3562.             end
  3563.         end
  3564.     end
  3565.  
  3566.     if self.AllowTerminate then
  3567.         self:RegisterEvent('terminate', function()error('Terminated', 0) end)
  3568.     end
  3569.  
  3570.     if self.DefaultView and self.DefaultView ~= '' and fs.exists(self.ViewPath..self.DefaultView..'.view') then
  3571.         self:LoadView(self.DefaultView)
  3572.     end
  3573.  
  3574.     if ready then
  3575.         ready()
  3576.     end
  3577.    
  3578.     self:Draw()
  3579.  
  3580.     while self.Running do
  3581.         self:EventHandler()
  3582.     end
  3583. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement