MattiasBuelens

CCGUI textinput

Jul 13th, 2012
131
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 8.44 KB | None | 0 0
  1. --[[
  2.  
  3.     ComputerCraft GUI
  4.     Text input controls
  5.  
  6. --]]
  7.  
  8. ccgui = ccgui or {}
  9.  
  10. local TextArea = common.newClass({
  11.     -- Colors
  12.     background          = colours.white,
  13.  
  14.     -- Scrolling
  15.     horizontal          = true,
  16.     vertical            = true,
  17.     mouseScroll         = true,
  18.  
  19.     -- Text lines
  20.     lines               = nil,
  21.     longestLineLength   = 0,
  22.  
  23.     -- Cursor position
  24.     cursorLine          = 1, -- [1, #(lines)]
  25.     cursorChar          = 1  -- [1, #(lines[cursorLine])]
  26. }, ccgui.ScrollElement, ccgui.FocusElement)
  27. ccgui.TextArea = TextArea
  28.  
  29. function TextArea:init()
  30.     ccgui.FocusElement.init(self)
  31.     ccgui.ScrollElement.init(self)
  32.  
  33.     self.lines = {""}
  34.  
  35.     self:on("mouse_click", self.textClick, self)
  36.     self:on("key", self.textKey, self)
  37.     self:on("char", self.textChar, self)
  38.  
  39.     self:on("paint", self.drawText, self)
  40.     self:on("afterframe", self.drawCursor, self)
  41. end
  42.  
  43. local function splitLines(str)
  44.     local t = {}
  45.     local function helper(line)
  46.         table.insert(t, line)
  47.         return ""
  48.     end
  49.     helper((string.gsub(str, "(.-)\n", helper)))
  50.     return t
  51. end
  52.  
  53. function TextArea:text()
  54.     local text = ""
  55.     -- Join lines
  56.     for i,line in ipairs(self.lines) do
  57.         if i > 1 then
  58.             text = text.."\n"
  59.         end
  60.         text = text..line
  61.     end
  62.     return text
  63. end
  64.  
  65. function TextArea:setText(text)
  66.     -- Split lines
  67.     self.lines = splitLines(text or "")
  68.     -- Reset cursor
  69.     if self.bbox then
  70.         self:setCursor(1, 1)
  71.     end
  72.     -- Mark for repaint
  73.     --self:markRepaint()
  74. end
  75.  
  76. function TextArea:multiline()
  77.     return true
  78. end
  79.  
  80. function TextArea:readonly()
  81.     return false
  82. end
  83.  
  84. --[[
  85.  
  86.     Scroll
  87.  
  88. ]]--
  89.  
  90. function TextArea:scrollVisible()
  91.     return self:inner(self.bbox):size()
  92. end
  93.  
  94. function TextArea:scrollTotal()
  95.     local bbox = self:inner(self.bbox)
  96.     return vector.new(
  97.         math.max(bbox.w, self.longestLineLength + 1), -- for cursor at end of line
  98.         math.max(bbox.h, #(self.lines))
  99.     )
  100. end
  101.  
  102. function TextArea:updateScroll()
  103.     -- Update longest line length
  104.     local maxLen = 0
  105.     for i,line in ipairs(self.lines) do
  106.         maxLen = math.max(maxLen, #line)
  107.     end
  108.     self.longestLineLength = maxLen
  109. end
  110.  
  111. --[[
  112.  
  113.     Focus
  114.  
  115. ]]--
  116.  
  117. function TextArea:canFocus()
  118.     return ccgui.FocusElement.canFocus(self)
  119. end
  120.  
  121. function TextArea:textBlur()
  122.     if not self.hasFocus then
  123.         self:getOutput().setCursorBlink(false)
  124.     end
  125. end
  126.  
  127. function TextArea:textClick(button, x, y)
  128.     if button == 1 then
  129.         -- Left mouse button, set cursor
  130.         if self:canFocus() and self:contains(x, y) then
  131.             local cursor = self:fromScreen(x, y)
  132.             self:setCursor(cursor.x, cursor.y)
  133.         end
  134.     end
  135. end
  136.  
  137. --[[
  138.  
  139.     Cursor
  140.  
  141. ]]--
  142.  
  143. function TextArea:moveCursor(dx, dy)
  144.     return self:setCursor(self.cursorChar + dx, self.cursorLine + dy)
  145. end
  146.  
  147. function TextArea:setCursor(x, y)
  148.     y = math.max(1, math.min(y, #self.lines))
  149.  
  150.     -- Keep cursor in text bounds
  151.     if x <= 0 then
  152.         if y > 1 then
  153.             -- Move to end of previous line
  154.             y = y - 1
  155.             x = #(self.lines[y]) + 1
  156.         else
  157.             -- Clip to start of first line
  158.             x = 1
  159.         end
  160.     elseif x > #(self.lines[y]) + 1 then
  161.         if y < #self.lines then
  162.             -- Move to start of next line
  163.             y = y + 1
  164.             x = 1
  165.         else
  166.             -- Clip to end of last line
  167.             x = #(self.lines[y]) + 1
  168.         end
  169.     end
  170.  
  171.     self.cursorChar = x
  172.     self.cursorLine = y
  173.  
  174.     -- Adjust scroll position
  175.     local scrollX, scrollY = self.scrollPosition.x, self.scrollPosition.y
  176.     local scrollVis = self:scrollVisible()
  177.  
  178.     if x <= scrollX then
  179.         scrollX = x - 1
  180.     elseif x >= scrollX + scrollVis.x then
  181.         scrollX = x - scrollVis.x
  182.     end
  183.  
  184.     if y <= scrollY then
  185.         scrollY = y - 1
  186.     elseif y >= scrollY + scrollVis.y then
  187.         scrollY = y - scrollVis.y
  188.     end
  189.  
  190.     self.scrollPosition = vector.new(scrollX, scrollY)
  191. end
  192.  
  193. --[[
  194.  
  195.     Manipulations
  196.  
  197. ]]--
  198.  
  199. function TextArea:insert(char)
  200.     -- Insert character
  201.     local line = self.lines[self.cursorLine]
  202.     line = string.sub(line, 1, self.cursorChar-1) .. char .. string.sub(line, self.cursorChar, -1)
  203.     self.lines[self.cursorLine] = line
  204.     -- Move cursor
  205.     self:moveCursor(1, 0)
  206.     self:updateScroll()
  207. end
  208.  
  209. function TextArea:delete(before)
  210.     if before == nil then
  211.         before = true
  212.     else
  213.         before = not not before
  214.     end
  215.  
  216.     if before then
  217.         if self.cursorChar == 1 and self.cursorLine > 1 then
  218.             -- Concat with previous line
  219.             self.lines[self.cursorLine-1] = self.lines[self.cursorLine-1] .. self.lines[self.cursorLine]
  220.             table.remove(self.lines, self.cursorLine)
  221.             local x = #(self.lines[self.cursorLine-1]) + 1
  222.             self:setCursor(x, self.cursorLine-1)
  223.         elseif self.cursorChar > 1 then
  224.             -- Remove previous in current line
  225.             local line = self.lines[self.cursorLine]
  226.             line = string.sub(line, 1, self.cursorChar-2) .. string.sub(line, self.cursorChar, -1)
  227.             self.lines[self.cursorLine] = line
  228.             self:moveCursor(-1, 0)
  229.         end
  230.     else
  231.         local line = self.lines[self.cursorLine]
  232.         if self.cursorChar == #line + 1 and self.cursorLine < #(self.lines) then
  233.             -- Concat with next line
  234.             self.lines[self.cursorLine] = self.lines[self.cursorLine] .. self.lines[self.cursorLine + 1]
  235.             table.remove(self.lines, self.cursorLine + 1)
  236.         elseif self.cursorChar <= #line then
  237.             -- Remove next in current line
  238.             line = string.sub(line, 1, self.cursorChar-1) .. string.sub(line, self.cursorChar + 1, -1)
  239.             self.lines[self.cursorLine] = line
  240.         end
  241.     end
  242.     self:updateScroll()
  243. end
  244.  
  245. function TextArea:newline()
  246.     if not self:multiline() then return end
  247.  
  248.     -- Split current line
  249.     local line = self.lines[self.cursorLine]
  250.     self.lines[self.cursorLine] = string.sub(line, 1,self.cursorChar-1)
  251.     table.insert(self.lines, self.cursorLine + 1, string.sub(line, self.cursorChar, -1))
  252.  
  253.     -- Move to new next line
  254.     self:setCursor(1, self.cursorLine + 1)
  255.     self:updateScroll()
  256. end
  257.  
  258. --[[
  259.  
  260.     Drawing
  261.  
  262. ]]--
  263.  
  264. function TextArea:fromScreen(x, y)
  265.     local screenPos = type(x) == "table" and x or vector.new(x, y)
  266.     return screenPos + vector.new(1, 1) - self:inner(self.bbox):tl() + self.scrollPosition
  267. end
  268.  
  269. function TextArea:toScreen(x, y)
  270.     local textPos = type(x) == "table" and x or vector.new(x, y)
  271.     return textPos - vector.new(1, 1) + self:inner(self.bbox):tl() - self.scrollPosition
  272. end
  273.  
  274. function TextArea:calcSize(size)
  275.     -- Get inner bounding box
  276.     local bbox = self:inner(size)
  277.     local w, h = bbox.w, bbox.h
  278.  
  279.     -- Limit inner height when single line
  280.     if not self:multiline() then h = 1 end
  281.  
  282.     -- Get inner bounding box with new size
  283.     bbox = ccgui.newRectangle(bbox.x, bbox.y, w, h)
  284.     -- Use outer size box
  285.     self.size = self:outer(bbox)
  286. end
  287.  
  288. function TextArea:drawCursor()
  289.     if self.hasFocus then
  290.         local out = self:getOutput()
  291.         -- Get screen position
  292.         local pos = self:toScreen(self.cursorChar, self.cursorLine)
  293.         if self:contains(pos) then
  294.             -- Show blinking cursor
  295.             out.setTextColor(self.foreground)
  296.             out.setBackgroundColor(self.background)
  297.             out.setCursorPos(pos.x, pos.y)
  298.             out.setCursorBlink(true)
  299.         else
  300.             -- Hide blinking cursor
  301.             out.setCursorBlink(false)
  302.         end
  303.     end
  304. end
  305.  
  306. function TextArea:drawText()
  307.     local bbox = self:inner(self.bbox)
  308.     -- Get screen position
  309.     local pos = self:toScreen(1, 1)
  310.     local x, y = pos.x, pos.y
  311.     -- Draw lines
  312.     for i,line in ipairs(self.lines) do
  313.         self:draw(x, y, line, self.foreground, self.background, bbox)
  314.         y = y + 1
  315.     end
  316. end
  317.  
  318. function TextArea:textKey(key)
  319.     -- Translate key to action
  320.     if key == keys.up then
  321.         self:moveCursor(0, -1)
  322.     elseif key == keys.down then
  323.         self:moveCursor(0, 1)
  324.     elseif key == keys.left then
  325.         self:moveCursor(-1, 0)
  326.     elseif key == keys.right then
  327.         self:moveCursor(1, 0)
  328.     elseif key == keys.home then
  329.         self:setCursor(1, self.cursorLine)
  330.     elseif key == keys["end"] then
  331.         self:setCursor(#(self.lines[self.cursorLine]) + 1, self.cursorLine)
  332.     elseif not self:readonly() then
  333.         if key == keys.backspace then
  334.             self:delete(true)
  335.         elseif key == keys.delete then
  336.             self:delete(false)
  337.         elseif key == keys.enter or key == keys.numPadEnter then
  338.             self:newline()
  339.         end
  340.     end
  341.     -- Mark for repaint
  342.     --self:markRepaint()
  343. end
  344.  
  345. function TextArea:textChar(char)
  346.     if not self:readonly() then
  347.         -- Insert character
  348.         self:insert(char)
  349.         -- Mark for repaint
  350.         --self:markRepaint()
  351.     end
  352. end
  353.  
  354. --[[
  355.  
  356.     Single line text input
  357.  
  358. ]]--
  359.  
  360. local TextInput = common.newClass(ccgui.TextArea)
  361. ccgui.TextInput = TextInput
  362.  
  363. function TextInput:init()
  364.     self.horizontal = true
  365.     self.vertical = false
  366.     self.mouseScroll = false
  367.     self.showScrollBars = false
  368.  
  369.     ccgui.TextArea.init(self)
  370. end
  371.  
  372. function TextInput:multiline()
  373.     return false
  374. end
  375.  
  376. --[[
  377.  
  378.     Multiline text viewer
  379.  
  380. ]]--
  381. local TextViewer = common.newClass(ccgui.TextArea)
  382. ccgui.TextViewer = TextViewer
  383.  
  384. function TextViewer:readonly()
  385.     return true
  386. end
Advertisement
Add Comment
Please, Sign In to add comment