Advertisement
Jummit

Computercraft Atom Text Editor

Aug 5th, 2018
331
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 20.68 KB | None | 0 0
  1. local ui = {}
  2. ui.elements = {
  3.   line = {
  4.     draw = function(self, x, y)
  5.       local x2, y2 = x, y
  6.       if self.y2 then y2 = self.y2-(self.y-y) end
  7.       if self.x2 then x2 = self.x2-(self.x-x) end
  8.       paintutils.drawLine(x, y, x2, y2)
  9.     end
  10.   },
  11.   button = {
  12.     clickedColor = colors.white, label = "", _onPressed = function(self) end, _onReleased = function(self) end,
  13.     update = function(self)
  14.     end,
  15.     draw = function(self, x, y)
  16.       local color = self.color
  17.       if self.isPressed then color = self.clickedColor end
  18.       term.setBackgroundColor(color)
  19.       term.write(self.label)
  20.     end,
  21.     onClick = function(self)
  22.       self.isPressed = true
  23.     end,
  24.     onMouse_up = function(self)
  25.       self.isPressed = false
  26.     end
  27.   },
  28.   label = {
  29.     x = 1, y = 1, w = 1, h = 1, color = colors.gray, textColor = colors.white, text = "",
  30.     draw = function(self, x, y)
  31.       term.write(self.text)
  32.     end
  33.   },
  34.   checkbox = {
  35.     ticked = false,
  36.     draw = function(self, x, y)
  37.       if ticked then
  38.         term.write("x")
  39.       else
  40.         term.write(" ")
  41.       end
  42.     end,
  43.     onClick = function(self)
  44.       self.ticked = not self.ticked
  45.     end
  46.   },
  47.   panel = {
  48.   },
  49.   box = {},
  50.   textinput = {
  51.     text = "", onFinished = function(self, text) end,
  52.     draw = function(self, x, y)
  53.       paintutils.drawLine(x, y, x+self.w-1, y+self.h-1)
  54.       term.setCursorPos(x, y)
  55.       term.write(self.text)
  56.     end,
  57.     onChar = function(self, char)
  58.       self.text = self.text..char
  59.     end,
  60.     onKey = function(self, key)
  61.       if key == keys.backspace then
  62.         self.text = string.sub(self.text, 1, #self.text-1)
  63.       elseif key == keys.enter then
  64.         self:onFinished(self.text)
  65.       end
  66.     end
  67.   },
  68.   textedit = {
  69.     textColor = colors.white, cursorColor = colors.lightGray, keywordColor = colors.magenta, tablecallColor = colors.red, stringColor = colors.lime, numberColor = colors.orange, commentColor = colors.gray, argumentColor = colors.cyan,
  70.  
  71.     text = {
  72.       [1] = "",
  73.       [2] = ""
  74.     },
  75.     movement = {
  76.       up    = { 0, -1},
  77.       down  = { 0,  1},
  78.       left  = {-1,  0},
  79.       right = { 1,  0},
  80.     },
  81.     keywords = {
  82.       "and",
  83.       "break",
  84.       "do",
  85.       "else",
  86.       "elseif",
  87.       "end",
  88.       "false",
  89.       "for",
  90.       "function",
  91.       "if",
  92.       "in",
  93.       "local",
  94.       "nil",
  95.       "not",
  96.       "or",
  97.       "repeat",
  98.       "return",
  99.       "then",
  100.       "true",
  101.       "until",
  102.       "while"
  103.     },
  104.     cursorX = 1, cursorY = 1,
  105.     scrollY = 0,
  106.  
  107.     moveTo = function(self, x, y)
  108.       if not self.text[y] then self.text[y] = "" end
  109.       local x, y = x, y
  110.       if x < 1 and self.cursorY > 1 then return self:moveTo(#self.text[y-1]+1, y-1)
  111.       elseif x < 1 then
  112.         x = 1
  113.       end
  114.       if y < 1 then y = 1 end
  115.       if x > #self.text[y]+1 then x = #self.text[y]+1 end
  116.       if y > #self.text then y = #self.text end
  117.       if y > self.h-self.scrollY then
  118.         self.scrollY = self.scrollY - 1
  119.       elseif y <= -self.scrollY then
  120.         self.scrollY = self.scrollY + 1
  121.       end
  122.       self.cursorX, self.cursorY = x, y
  123.     end,
  124.     move = function(self, x, y)
  125.       self:moveTo(self.cursorX+x, self.cursorY+y)
  126.     end,
  127.     onChar = function(self, char)
  128.       local oldLine = self.text[self.cursorY]
  129.  
  130.       self.text[self.cursorY] = string.sub(oldLine, 1, self.cursorX-1)..char..string.sub(oldLine, self.cursorX)
  131.       self:move(1, 0)
  132.       self:makeTextChangedCallback()
  133.     end,
  134.     onKey = function(self, key)
  135.       local keyname = keys.getName(key)
  136.       if keyname == "backspace" then
  137.         if self.cursorX == 1 and self.cursorY>1 then
  138.           local textLength = #self.text[self.cursorY-1]
  139.           self.text[self.cursorY-1] = self.text[self.cursorY-1]..self.text[self.cursorY]
  140.           table.remove(self.text, self.cursorY)
  141.           self:moveTo(textLength+1, self.cursorY-1)
  142.         elseif self.cursorX > 1 and self.cursorY > 0 then
  143.           self.text[self.cursorY] = string.sub(self.text[self.cursorY], 1, self.cursorX-2)..string.sub(self.text[self.cursorY], self.cursorX)
  144.           self:move(-1, 0)
  145.         end
  146.       elseif self.movement[keyname] then
  147.         local toMove = self.movement[keyname]
  148.         if toMove[1] == 1 and self.cursorX == #self.text[self.cursorY]+1 then
  149.           self:moveTo(1, self.cursorY+1)
  150.         else
  151.           self:move(toMove[1], toMove[2])
  152.         end
  153.       elseif keyname == "enter" then
  154.         local oldLine = self.text[self.cursorY]
  155.         self.text[self.cursorY] = string.sub(oldLine, 1, self.cursorX-1)
  156.         table.insert(self.text, self.cursorY+1, string.sub(oldLine, self.cursorX))
  157.         if self.text[self.cursorY+1] == "" then
  158.           self.text[self.cursorY+1] = string.match(self.text[self.cursorY], "%s*")
  159.           self:moveTo(#self.text[self.cursorY+1]+1, self.cursorY+1)
  160.         else
  161.           self:moveTo(1, self.cursorY+1)
  162.         end
  163.       elseif keyname == "tab" then
  164.         os.queueEvent("char", " ")
  165.       elseif keyname == "end" then
  166.         self.cursorX = #self.text[self.cursorY]+1
  167.       elseif keyname == "home" then
  168.         self.cursorX = 1
  169.       elseif keyname == "delete" then
  170.         self.text[self.cursorY] = string.sub(self.text[self.cursorY], 1, self.cursorX-1)..string.sub(self.text[self.cursorY], self.cursorX+1)
  171.       end
  172.       self:makeTextChangedCallback()
  173.     end,
  174.     onClick = function(self, button, x, y)
  175.       self:moveTo(x, y-self.scrollY)
  176.     end,
  177.     onMouse_scroll = function(self, direction)
  178.       self.scrollY = self.scrollY - direction
  179.     end,
  180.     searchForStrinsToHightlight = function(self, highlightedWords, line, pattern, color, func)
  181.       local lastNumPos = 1
  182.       for num in string.gmatch(line, pattern) do
  183.         local pos = string.find(line, num, lastNumPos, true)
  184.         lastNumPos = pos+1
  185.         if not func or func(pos) then
  186.           table.insert(highlightedWords, {word = num, pos = pos, color = color})
  187.         end
  188.       end
  189.     end,
  190.     drawHightlightedStrings = function(self, x, y, liney, highlightedWords)
  191.       for i, word in ipairs(highlightedWords) do
  192.         if word.pos+#word.word>self.w then
  193.           word.word = string.sub(word.word, 1, self.w-word.pos+1)
  194.         end
  195.         if word.pos<self.w then
  196.           term.setTextColor(word.color)
  197.           term.setCursorPos(x+word.pos-1, y+liney-1)
  198.           term.write(word.word)
  199.         end
  200.       end
  201.     end,
  202.     draw = function(self, x, y)
  203.       if -self.scrollY > #self.text-self.h then self.scrollY = -#self.text+self.h end
  204.       if self.scrollY > 0 then self.scrollY = 0 end
  205.       local y = y + self.scrollY
  206.       local cursorX, cursorY = self.cursorX+x-1, self.cursorY+y-1
  207.       local isMultiLineComment = false
  208.       local isMultiLineString = false
  209.  
  210.       for liney, line in ipairs(self.text) do
  211.         if liney <= self.h-y+2 and liney > -self.scrollY then
  212.           local lineToWrite = string.sub(line, 1, self.w)
  213.  
  214.           term.setBackgroundColor(self.color)
  215.           if isMultiLineComment then
  216.             term.setTextColor(self.commentColor)
  217.           elseif isMultiLineString then
  218.             term.setTextColor(self.stringColor)
  219.           else
  220.             term.setTextColor(self.textColor)
  221.           end
  222.           term.setCursorPos(x, y+liney-1)
  223.           term.write(lineToWrite)
  224.  
  225.           local comments = {}
  226.           self:searchForStrinsToHightlight(comments, line, "%-%-.*", self.commentColor)
  227.           if not isMultiLineComment and not isMultiLineString then
  228.             local highlightedWords = {}
  229.             for i, keyword in ipairs(self.keywords) do
  230.               self:searchForStrinsToHightlight(highlightedWords, line, keyword, self.keywordColor, function(pos)
  231.                 local beforeChar, afterChar = string.sub(line, pos-1, pos-1), string.sub(line, pos+#keyword, pos+#keyword)
  232.                 return beforeChar == "" or beforeChar == " " and afterChar == "" or afterChar == " "
  233.               end)
  234.             end
  235.             self:searchForStrinsToHightlight(highlightedWords, line, "[%(].*[%)]", self.argumentColor)
  236.             self:searchForStrinsToHightlight(highlightedWords, line, "%[%[\".*", self.stringColor)
  237.             self:searchForStrinsToHightlight(highlightedWords, line, "[%w_%[%]]+[%.%:]%a*[%w_%[%]]", self.tablecallColor)
  238.             self:searchForStrinsToHightlight(highlightedWords, line, "[\"].*[\"]", self.stringColor)
  239.             self:searchForStrinsToHightlight(highlightedWords, line, "['].*[']", self.stringColor)
  240.             self:searchForStrinsToHightlight(highlightedWords, line, "%d", self.numberColor)
  241.  
  242.             self:drawHightlightedStrings(x, y, liney, highlightedWords)
  243.           end
  244.           self:drawHightlightedStrings(x, y, liney, comments)
  245.           if liney == self.cursorY then
  246.             paintutils.drawPixel(cursorX, cursorY, self.cursorColor)
  247.             -- the cursor is on this line, so we draw the char the cursor is again, but it in a different color
  248.             term.setBackgroundColor(self.cursorColor)
  249.             term.setTextColor(self.textColor)
  250.             term.setCursorPos(cursorX, cursorY)
  251.             term.write(string.sub(line, self.cursorX, self.cursorX))
  252.           end
  253.  
  254.           if string.find(line, "--[[", 1, true) then
  255.             isMultiLineComment = true
  256.           end
  257.           if not isMultiLineComment and string.find(line, "[[\"", 1, true) then
  258.             isMultiLineString = true
  259.           end
  260.           if string.find(line, "\"]]", 1, true) then
  261.             isMultiLineString = false
  262.           end
  263.           if string.find(line, "]]") then
  264.             isMultiLineComment = false
  265.           end
  266.         end
  267.       end
  268.     end,
  269.     makeTextChangedCallback = function(self)
  270.       if self.onTextChanged then
  271.         self:onTextChanged(self.text)
  272.       end
  273.     end,
  274.     loadFile = function(self, file)
  275.       self.text = {}
  276.       for line, lineNum in io.lines(file) do
  277.         table.insert(self.text, line)
  278.       end
  279.       self.text[1] = self.text[1] or ""
  280.       self.text[2] = self.text[2] or ""
  281.       self.cursorX, self.cursorY = 1, 1
  282.       self:makeTextChangedCallback()
  283.     end,
  284.   },
  285.   paint = {
  286.     canvas = {},
  287.     selectedColors = {colors.black, colors.white},
  288.     onClick = function(self, button, x, y)
  289.       if not self.canvas[x] then self.canvas[x] = {} end
  290.       self.canvas[x][y] = self.selectedColors[button]
  291.     end,
  292.     onMouse_drag = function(self, button, x, y)
  293.       self:onClick(button, x, y)
  294.     end,
  295.     draw = function(self, x, y)
  296.       for pixelX, line in pairs(self.canvas) do
  297.         for pixelY, pixel in pairs(line) do
  298.           paintutils.drawPixel(pixelX+x-1, pixelY+y-1, pixel)
  299.         end
  300.       end
  301.     end,
  302.     loadFile = function(self, file)
  303.       local tColourLookup = {}
  304.       for n=1,16 do
  305.           tColourLookup[ string.byte( "0123456789abcdef",n,n ) ] = 2^(n-1)
  306.       end
  307.       local function getColourOf( char )
  308.           -- Values not in the hex table are transparent (canvas coloured)
  309.           return tColourLookup[char]
  310.       end
  311.  
  312.       local file = fs.open("/project/Game/Level/Dirt.nfp", "r")
  313.       self.canvas = {{}}
  314.       while true do
  315.         table.insert(self.canvas, {})
  316.         local line = file.read()
  317.         if not line then
  318.           break
  319.         else
  320.           for i = 1, #line do
  321.             local color = getColourOf(string.sub(line, i, i))
  322.             table.insert(self.canvas[#self.canvas], color)
  323.           end
  324.         end
  325.       end
  326.       file.close()
  327.     end
  328.   },
  329.   filesystem = {
  330.     path = "", folderColor = colors.gray, fileColor = colors.lightGray, selectedColor = colors.white,
  331.     fileY = 0, selectedFile = nil, draggingFile = nil,
  332.     goThroughFiles = function(self, dir, func, x, y)
  333.       if fs.getName(dir) ~= "rom" and string.sub(fs.getName(dir), 1, 1) ~= "." then
  334.         local files = fs.list(dir)
  335.         for i, fileName in ipairs(files) do
  336.           self.fileY = self.fileY + 1
  337.           local filePath = fs.combine(dir, fileName)
  338.           func(filePath, x, y+self.fileY)
  339.           if fs.isDir(filePath) then
  340.             self:goThroughFiles(filePath, func, x+1, y)
  341.           end
  342.         end
  343.       end
  344.     end,
  345.     drawDir = function(self, x, y)
  346.       self:goThroughFiles(self.path, function(filePath, x, y)
  347.         if fs.isDir(filePath) then
  348.           term.setTextColor(self.folderColor)
  349.         else
  350.           term.setTextColor(self.fileColor)
  351.         end
  352.         if filePath == self.selectedFile then
  353.           term.setBackgroundColor(self.selectedColor)
  354.         else
  355.           term.setBackgroundColor(self.color)
  356.         end
  357.         term.setCursorPos(x, y)
  358.         term.write(fs.getName(filePath))
  359.       end, x, y)
  360.     end,
  361.     draw = function(self, x, y)
  362.       self.fileY = 0
  363.       self:drawDir(x, y)
  364.     end,
  365.     onClick = function(self, button, mx, my)
  366.       self.fileY = 0
  367.       self:goThroughFiles(self.path, function(filePath, x, y)
  368.         if self.fileY == my-1 then
  369.           self.selectedFile = filePath
  370.  
  371.           if fs.isDir(filePath) and self.onFolderClicked then
  372.             self:onFolderClicked(filePath)
  373.           elseif not fs.isDir(filePath) and self.onFileClicked then
  374.             self:onFileClicked(filePath)
  375.           end
  376.         end
  377.       end, 0, 0)
  378.     end,
  379.     onMouse_drag = function(self, x, y)
  380.       self.draggingFile = self.selectedFile
  381.     end,
  382.     onMouse_up = function(self, button, x, y)
  383.       local fileToDragOn = self:getFileByNumber(y-1)
  384.       if fileToDragOn and self.draggingFile then
  385.         if not fs.isDir(fileToDragOn) then
  386.           fileToDragOn = fs.getDir(fileToDragOn)
  387.         end
  388.         local destination = fs.combine(fileToDragOn, fs.getName(self.draggingFile))
  389.         if not fs.exists(destination) then
  390.           local succ, mess = pcall(function() fs.move(self.draggingFile, destination) end)
  391.         end
  392.       end
  393.       self.draggingFile = nil
  394.     end,
  395.     getNumberOfFile = function(self, file)
  396.       self.fileY = 0
  397.       local num
  398.       self:goThroughFiles(self.path, function(filePath, x, y)
  399.         if file == filePath then
  400.           num = y
  401.         end
  402.       end, 0, 0)
  403.       return num
  404.     end,
  405.     getFileByNumber = function(self, num)
  406.       self.fileY = 0
  407.       local file
  408.       self:goThroughFiles(self.path, function(filePath, x, y)
  409.         if num == y then
  410.           file = filePath
  411.         end
  412.       end, 0, 0)
  413.       return file
  414.     end,
  415.     getPositionOfFile = function(self, file)
  416.       self.fileY = 0
  417.       local fx, fy
  418.       self:goThroughFiles(self.path, function(filePath, x, y)
  419.         if filePath == file or (type(file) == "number" and y == file) then
  420.           fx, fy = x, y
  421.         end
  422.       end, 0, 0)
  423.       return fx, fy
  424.     end,
  425.     onKey = function(self, key)
  426.       local currentSelectedNum = self:getNumberOfFile(self.selectedFile)
  427.       if currentSelectedNum then
  428.         if key == keys.up then
  429.           self.selectedFile = self:getFileByNumber(currentSelectedNum-1)
  430.         elseif key == keys.down then
  431.           self.selectedFile = self:getFileByNumber(currentSelectedNum+1)
  432.         elseif key == keys.a or key == keys.f then
  433.           local dir
  434.           if fs.isDir(self.selectedFile) then
  435.             dir = self.selectedFile
  436.           else
  437.             dir = fs.getDir(self.selectedFile)
  438.           end
  439.           if key == keys.a then
  440.             local file = fs.open(fs.combine(dir, "New File"), "w")
  441.             file:close()
  442.             self.selectedFile = fs.combine(dir, "New File")
  443.           elseif key == keys.f then
  444.             fs.makeDir(fs.combine(dir, "New Folder"))
  445.             self.selectedFile = fs.combine(dir, "New Folder")
  446.           end
  447.         elseif key == keys.delete then
  448.           fs.delete(self.selectedFile)
  449.         elseif key == keys.r or key == keys.f2 then
  450.           self.selected = false
  451.           self.children.renameInput = ui.new.textinput{
  452.             selected = true, color = self.folderColor, textColor = self.textColor, w = self.w, text = fs.getName(self.selectedFile),
  453.             onFinished = function(textinput, text)
  454.               local destination = fs.combine(fs.getDir(self.selectedFile), text)
  455.               if text ~= "" and not fs.exists(destination) then
  456.                 fs.move(self.selectedFile, destination)
  457.               end
  458.               self.selectedFile = destination
  459.               self.children.renameInput = nil
  460.               self.selected = true
  461.             end
  462.           }
  463.           self.children.renameInput.x, self.children.renameInput.y = self:getPositionOfFile(currentSelectedNum)
  464.           self.children.renameInput.x, self.children.renameInput.y = self.children.renameInput.x+1, self.children.renameInput.y+1
  465.           self.children.renameInput.w = #self.children.renameInput.text
  466.         end
  467.       end
  468.     end
  469.   },
  470.   rectangle = {
  471.     draw = function(self, x, y)
  472.       paintutils.drawBox(x, y, x+self.w-1, y+self.h-1)
  473.     end
  474.   }
  475. }
  476. ui.new = setmetatable({}, {__index = function(self, elementType)
  477.   return function(args)
  478.     return setmetatable(args, {__index = setmetatable(ui.elements[elementType],{__index = {x = 1, y = 1, w = 1, h = 1, color = colors.gray, textColor = colors.white, children = {}}})})
  479.   end
  480. end})
  481. ui.draw = function(elements, parent, x, y)
  482.   local parent = parent or {x = 1, y = 1}
  483.   local x, y = x or parent.x, y or parent.y
  484.   if not parent.w then
  485.     parent.w, parent.h = term.getSize()
  486.   end
  487.   for elementName, element in pairs(elements) do
  488.     paintutils.drawFilledBox(x+element.x-1, y+element.y-1, x+element.x-1+element.w-1, y+element.y-1+element.h-1, element.color)
  489.     term.setCursorPos(x, y)
  490.     term.setBackgroundColor(element.color)
  491.     term.setTextColor(element.textColor)
  492.     if element.draw then
  493.       element:draw(x+element.x-1, y+element.y-1)
  494.     end
  495.     if element.children then
  496.       ui.draw(element.children, element, x+element.x-1, y+element.y-1)
  497.     end
  498.   end
  499. end
  500. ui.update = function(elements, events, parent, x, y)
  501.   local event, var1, var2, var3 = events[1], events[2], events[3], events[4]
  502.   local parent = parent or {x = 1, y = 1}
  503.   local x, y = x or parent.x, y or parent.y
  504.   if not parent.w then
  505.     parent.w, parent.h = term.getSize()
  506.   end
  507.   for elementName, element in pairs(elements) do
  508.     term.setCursorPos(element.x, element.y)
  509.     local x, y = parent.x+element.x-1, parent.y+element.y-1
  510.     if event == "mouse_click" and var2 >= x and var3 >= y and var2 < x+element.w and var3 < y+element.h then
  511.       if element.onClick then
  512.         element:onClick(var1, var2-x+1, var3-y+1)
  513.       end
  514.       element.selected = true
  515.     elseif event == "mouse_click" then
  516.       element.selected = false
  517.     end
  518.  
  519.     local eventMethod = element["on"..string.upper(string.sub(event, 1, 1))..string.sub(event, 2)]
  520.     if element.selected and eventMethod then
  521.       if string.sub(event, 1, #"mouse") == "mouse" then
  522.         eventMethod(element, var1, var2-x+1, var3-y+1)
  523.       else
  524.         eventMethod(element, var1, var2, var3)
  525.       end
  526.     end
  527.     if element.update then
  528.       element:update(x+element.x-1, y+element.y-1, event, var1, var2, var3)
  529.     end
  530.     if element.children then
  531.       ui.update(element.children, events, element, x+element.x-1, y+element.y-1)
  532.     end
  533.   end
  534. end
  535.  
  536. setmetatable(ui.elements, {__newindex = function(self, elementName, elementTable)
  537.   rawset(self, elementName, elementTable)
  538.   _G[elementName] = function(args)
  539.     return ui.new[elementName](args)
  540.   end
  541. end})
  542.  
  543. for elementName, elementTable in pairs(ui.elements) do
  544.   _G[elementName] = function(args)
  545.     return ui.new[elementName](args)
  546.   end
  547. end
  548.  
  549. local w, h = term.getSize()
  550. local sidebarWidth = math.floor(w/3.5)
  551. local elements = {}
  552.  
  553. elements.editor = textedit{
  554.   x = sidebarWidth+3, y = 2, w = w-sidebarWidth-3, h = h-2, color = colors.black, scrolls = {},
  555.   onTextChanged = function(self, text)
  556.     if self.openFile then
  557.       local file = fs.open(self.openFile, "w")
  558.       for lineNum, line in ipairs(text) do
  559.         file.write(line.."\n")
  560.       end
  561.       file.close()
  562.     end
  563.   end
  564. }
  565. elements.filesystem = filesystem{
  566.   x = 2, y = 2, h = h-2, w = sidebarWidth, folderColor = colors.lightGray, fileColor = colors.white, color = colors.black, path = "", selectedColor = colors.gray,
  567.   onFileClicked = function(self, file)
  568.     if elements.editor.openFile then
  569.       elements.editor.scrolls[elements.editor.openFile] = elements.editor.scrollY
  570.     end
  571.     elements.editor.openFile = file
  572.     elements.editor:loadFile(file)
  573.     if not elements.editor.scrolls[file] then elements.editor.scrolls[file] = 0 end
  574.     elements.editor.scollY = elements.editor.scrolls[file]
  575.     elements.editor.selected = true
  576.     elements.filesystem.selected = false
  577.   end
  578. }
  579.  
  580. while true do
  581.   term.setBackgroundColor(colors.gray)
  582.   term.clear()
  583.   ui.draw(elements)
  584.   ui.update(elements, {os.pullEvent()})
  585. end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement