Advertisement
Guest User

bedrock

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