Kingdaro

Canvas

Mar 23rd, 2013
223
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Lua 22.65 KB | None | 0 0
  1. local windows = {}
  2. local running = true
  3. local altmode = false
  4. local warningMsg = ''
  5. --local warningTime = 0
  6.  
  7. local init, update, draw, exit
  8. local newImage, newWindow, newImageWindow, newTitlebar
  9. local bgMenu, imageMenu, colorMenu
  10.  
  11.  
  12. local function rect(x, y, width, height, color, noshadow)
  13.     local spaces = string.rep(' ', width)
  14.     local t = term
  15.    
  16.     local r = function(x, y, color)
  17.         t.setBackgroundColor(color)
  18.         for i=y, y + height - 1 do
  19.             t.setCursorPos(x, i)
  20.             t.write(spaces)
  21.         end
  22.     end
  23.    
  24.     if not noshadow then r(x + 1, y + 1, colors.black) end
  25.     r(x, y, color)
  26. end
  27.  
  28. local function checkArea(x, y, ax, ay, aw, ah)
  29.     return
  30.         x >= ax and
  31.         x < ax + aw and
  32.         y >= ay and
  33.         y < ay + ah
  34. end
  35.  
  36. -- i thought i would need this but i guess not
  37. local function combine(t1, t2)
  38.     for i=1, #t2 do
  39.         table.insert(t1, t2)
  40.     end
  41.     return t1
  42. end
  43.  
  44. local function tohex(color)
  45.     local n = math.log(color) / math.log(2) + 1
  46.     return string.sub('0123456789abcdef', n, n)
  47. end
  48.  
  49. local function tocolor(hex)
  50.     local n = tonumber(hex, 16)
  51.     return n and 2 ^ n or 0
  52. end
  53.  
  54. local function menuOption(text, x, y, action, color, bg)
  55.     return {
  56.         text = text;
  57.         x = x;
  58.         y = y;
  59.         action = action;
  60.         color = color or colors.lightGray;
  61.         bg = bg or colors.gray;
  62.     }
  63. end
  64.  
  65. local function contextMenu(options)
  66.     for i=1, #options do
  67.         local opt = options[i]
  68.        
  69.         opt.x = 1
  70.         opt.y = i
  71.         opt.text = ' '..opt.text..' '
  72.     end
  73.     return options
  74. end
  75.  
  76. local function separator()
  77.     return menuOption('---', 1,1, function() end)
  78. end
  79.  
  80. local function prompt(text, placeholder)
  81.     local t = term
  82.    
  83.     draw()
  84.     rect(1, 1, t.getSize(), 1, colors.gray, true)
  85.    
  86.     t.setCursorPos(2,1)
  87.     t.setTextColor(colors.white)
  88.     t.write(text)
  89.    
  90.     if placeholder then
  91.         for i=1, #placeholder do
  92.             os.queueEvent('char', placeholder:sub(i,i))
  93.         end
  94.     end
  95.    
  96.     return read()
  97. end
  98.  
  99. local function openMenu(x, y, menu, ...)
  100.     y = y + 1
  101.     --[[
  102.         menu table format
  103.        
  104.         {
  105.             text = 'OptionText';
  106.             x = 1;
  107.             y = 1;
  108.             color = someTextColor;
  109.             bg = someBackgroundColor;
  110.             action = function()
  111.                 doStuff()
  112.                 if i decide to change the menu then
  113.                     return a new menu
  114.                 end
  115.             end;
  116.         }
  117.     ]]
  118.    
  119.     local ox, oy
  120.     local width = 1
  121.     local height = 1
  122.     local t = term
  123.     local w,h = term.getSize()
  124.    
  125.     local function offsetOptions()
  126.         -- find the longest menu option and base the width off of that
  127.         for i=1, #menu do
  128.             local opt = menu[i]
  129.             local cur = opt.x + #opt.text - 1
  130.             if cur > width then
  131.                 width = cur
  132.             end
  133.         end
  134.        
  135.         -- same for height
  136.         for i=1, #menu do
  137.             local opt = menu[i]
  138.             if opt.y > height then
  139.                 height = opt.y
  140.             end
  141.         end
  142.        
  143.         -- add the x and y offset to the options
  144.         ox = x + width - 1 > w and w - width + 1 or x
  145.         oy = y + height - 1 > h and h - height + 1 or y
  146.         for i=1, #menu do
  147.             local opt = menu[i]
  148.             opt.x = opt.x + ox - 1
  149.             opt.y = opt.y + oy - 1
  150.         end
  151.     end
  152.     offsetOptions()
  153.    
  154.     draw()
  155.     t.setCursorBlink(false)
  156.     rect(ox, oy - 1, width, height + 1, colors.gray)
  157.     for i=1, #menu do
  158.         local opt = menu[i]
  159.        
  160.         rect(opt.x, opt.y, #opt.text, 1, opt.bg, true)
  161.         t.setCursorPos(opt.x, opt.y)
  162.         t.setTextColor(opt.color)
  163.         t.write(opt.text)
  164.     end
  165.    
  166.     local _, button, mx, my = os.pullEvent('mouse_click')
  167.    
  168.     for i=1, #menu do
  169.         local opt = menu[i]
  170.         if checkArea(mx, my, opt.x, opt.y, #opt.text, 1) then
  171.             local res = opt.action(...)
  172.             if type(res) == 'table' then
  173.                 draw()
  174.                 openMenu(x, y, res, ...)
  175.             end
  176.             break
  177.         end
  178.     end
  179. end
  180.  
  181. local function addWindow(window)
  182.     table.insert(windows, 1, window)
  183. end
  184.  
  185. local function warning(message, time)
  186.     warningMsg = message
  187.     warningTime = os.clock()
  188. end
  189.  
  190.  
  191. function newTitleBar(obj)
  192.     obj = obj or {}
  193.    
  194.     local titlebar = {
  195.         x = 3;
  196.         y = 2;
  197.         width = 12;
  198.         text = 'image';
  199.     }
  200.    
  201.     function titlebar:rect(dx, dy, dw, dh)
  202.         return
  203.             self.x + (dx or 0),
  204.             self.y + (dy or 0),
  205.             self.width + (dw or 0),
  206.             1 + (dh or 0)
  207.     end
  208.    
  209.     function titlebar:draw()
  210.         local t = term
  211.         t.setTextColor(windows[1].title == self and colors.white or colors.lightGray)
  212.        
  213.         -- limit the width if needed
  214.         self.width = self.width > 5 and self.width or 5
  215.        
  216.         -- limit the text if needed
  217.         local text =
  218.             #self.text > self.width - 2 and
  219.             self.text:sub(1, self.width - 4) .. '..' or
  220.             self.text
  221.        
  222.         -- draw the title background
  223.         rect(self.x, self.y, self.width, 1, colors.gray)
  224.        
  225.         -- draw the title text
  226.         t.setCursorPos(self.x + self.width/2 - #text/2, self.y)
  227.     --  t.setCursorPos(self.x + 1, self.y)
  228.         t.write(text)
  229.     end
  230.    
  231.     return setmetatable(obj, {__index = titlebar})
  232. end
  233.  
  234. function newImage(obj)
  235.     obj = obj or {}
  236.    
  237.     function obj:__call(x, y, value)
  238.         if not self[y] then
  239.             self[y] = {}
  240.         end
  241.         self[y][x] = value or self[y][x]
  242.         return self[y][x]
  243.     end
  244.    
  245.     return setmetatable(obj, obj)
  246. end
  247.  
  248. function newWindow(obj)
  249.     obj = obj or {}
  250.    
  251.     local window = {
  252.         name = 'window';
  253.        
  254.         width = 15;
  255.         height = 8;
  256.        
  257.         drag = nil;
  258.         resize = false;
  259.         resizeTimer = nil;
  260.        
  261.         lastClick = nil;
  262.         maximized = false;
  263.        
  264.         remove = false;
  265.     }
  266.    
  267.     window.title = newTitleBar{
  268.         x = obj.x or 2;
  269.         y = obj.y or 3;
  270.         text = obj.name or window.name;
  271.         width = obj.width or window.width;
  272.     }
  273.    
  274.     local lastClick
  275.    
  276.     function window:rect(dx, dy, dw, dh)
  277.         return
  278.             self.title.x + (dx or 0),
  279.             self.title.y + (dy or 0),
  280.             self.width + (dw or 0),
  281.             self.height + (dh or 0)
  282.     end
  283.    
  284.     function window:setName(name)
  285.         self.name = name
  286.         self.title.text = name
  287.     end
  288.    
  289.     function window:setSize(width, height)
  290.         self.width = width > 1 and width or 1
  291.         self.height = height > 1 and height or 1
  292.         self.title.width = self.width
  293.     end
  294.    
  295.     function window:maximize()
  296.         if not self.maximized then
  297.             self.maximized = true
  298.             local w,h = term.getSize()
  299.             -- "n" in this case stands for "normal"
  300.             self.nsize = {self.width, self.height}
  301.             self.npos = {self.title.x, self.title.y}
  302.            
  303.             self.title.x, self.title.y = 1, 1
  304.             self:setSize(w, h - 1)
  305.         end
  306.     end
  307.    
  308.     function window:restore()
  309.         if self.maximized then
  310.             self.maximized = false
  311.             self:setSize(unpack(self.nsize))
  312.             self.title.x, self.title.y = unpack(self.npos)
  313.         end
  314.     end
  315.    
  316.     function window:onClick(button, x, y, index)
  317.         self.drag = nil
  318.         self.resize = false
  319.         if checkArea(x, y, self:rect()) then
  320.             if checkArea(x, y, self.title:rect(0, 0, -1, 0))
  321.             or altmode then
  322.                 if button == 1 then
  323.                     if self.maximized and x == self.title.x and y == self.title.y then
  324.                         openMenu(x, y, bgMenu(), x, y)
  325.                     end
  326.                        
  327.                     if not self.maximized then
  328.                         self.drag = {
  329.                             x = self.title.x - x;
  330.                             y = self.title.y - y;
  331.                         }
  332.                     end
  333.                    
  334.                     if lastClick then
  335.                         if os.clock() - lastClick <= 0.3 then
  336.                             if self.maximized then
  337.                                 self:restore()
  338.                             else
  339.                                 self:maximize()
  340.                             end
  341.                         end
  342.                     end
  343.                     lastClick = os.clock()
  344.                 end
  345.             end
  346.            
  347.             return 'focusme'
  348.         elseif x == self.title.x + self.width and y == self.title.y + self.height + 1 then
  349.             self.resize = true
  350.             self.resizeTimer = os.startTimer(1)
  351.            
  352.             return 'focusme'
  353.         end
  354.     end
  355.    
  356.     function window:onDrag(button, x, y)
  357.         if windows[1] == self then
  358.             if not self.maximized then
  359.                 if self.drag then
  360.                     self.title.x = x + self.drag.x
  361.                     self.title.y = y + self.drag.y
  362.                 elseif self.resize then
  363.                     local width, height = x - self.title.x, y - self.title.y - 1
  364.                     self:setSize(width, height)
  365.                 --  self.resizeTimer = os.startTimer(1)
  366.                 end
  367.             elseif self.drag then
  368.                 if self.drag.y > 0 then
  369.                     self:restore()
  370.                     self.title.x = x + self.drag.x
  371.                     self.title.y = y + self.drag.y
  372.                 end
  373.             end
  374.         end
  375.     end
  376.    
  377.     function window:onKey(key)
  378.     end
  379.    
  380.     function window:onChar(char)
  381.     end
  382.    
  383.     function window:onTimer(t)
  384.         if t == self.resizeTimer then
  385.             self.resizeTimer = nil
  386.         end
  387.     end
  388.    
  389.     function window:draw()
  390.         local t = term
  391.         local w,h = t.getSize()
  392.         local ox, oy = self.title.x, self.title.y + 1
  393.        
  394.         -- draw the titlebar
  395.         self.title:draw()
  396.        
  397.         -- draw the backing
  398.         rect(self.title.x, self.title.y + 1, self.width, self.height, colors.gray)
  399.        
  400.         -- also draw a little dot to show we can access the background menu when maximized
  401.         if self.maximized then
  402.             t.setCursorPos(self.title.x, self.title.y)
  403.             t.setBackgroundColor(colors.gray)
  404.             t.setTextColor(colors.white)
  405.             t.write '.'
  406.         end
  407.        
  408.         -- draw the resize handle (if we're timed)
  409.         if self.resizeTimer then
  410.             t.setCursorPos(self.title.x + self.width, self.title.y + self.height + 1)
  411.             t.setBackgroundColor(colors.black)
  412.             t.setTextColor(colors.gray)
  413.             t.write '%'
  414.         end
  415.     end
  416.    
  417.     function window:close()
  418.         self.remove = true
  419.     end
  420.    
  421.     if obj then
  422.         for i,v in pairs(obj) do
  423.             window[i] = v
  424.         end
  425.     end
  426.    
  427.     return window
  428. end
  429.  
  430. function newImageWindow(obj)
  431.     obj = obj or {}
  432.    
  433.     local super = newWindow()
  434.     local window = {
  435.         mode = 'paint';
  436.         modes = {'paint','char'};
  437.     --  modes = {'paint','char','select'};
  438.         color = colors.lightGray;
  439.         textColor = colors.gray;
  440.         pos = {x=1, y=1};
  441.         savePath = nil;
  442.        
  443.         image = newImage();
  444.     }
  445.    
  446.     setmetatable(window, {__index = super})
  447.    
  448.     function window:paint(mode, x, y)
  449.         -- lucky for me, modes correspond nicely with button numbering
  450.         local ix, iy = x - self.title.x + 1, y - self.title.y
  451.         if checkArea(ix, iy, 1, 1, self.width, self.height) then
  452.             local pixel = self.image(ix, iy)
  453.             if mode == 1 then                   -- color
  454.                 pixel.color
  455.                     = self.color
  456.             elseif mode == 2 then               -- erase
  457.                 pixel.color = 0
  458.                 pixel.char = ' '
  459.             elseif mode == 3 then               -- pick
  460.                 self.color = pixel.color > 0 and pixel.color or self.color
  461.             end
  462.         end
  463.     end
  464.    
  465.     function window:onClick(button, x, y)
  466.         local m = self.maximized
  467.         super.onClick(self, button, x, y)
  468.        
  469.         if checkArea(x, y, self:rect(0, 0, 0, 1)) then
  470.             if checkArea(x, y, self.title:rect(0, 0, -1, 0))
  471.             or altmode then
  472.                 if button == 2 then
  473.                     openMenu(x, y, imageMenu(), self)
  474.                 end
  475.             elseif x == self.title.x + self.title.width - 1 and y == self.title.y then
  476.                 openMenu(x + 1, y, colorMenu(), self)
  477.             else
  478.                 if self.mode == 'paint' then
  479.                     if self.maximized == m then
  480.                         self:paint(button, x, y)
  481.                     end
  482.                 elseif self.mode == 'char' then
  483.                     self.pos.x = x - self.title.x + 1
  484.                     self.pos.y = y - self.title.y
  485.                 end
  486.             end
  487.            
  488.             return 'focusme'
  489.         end
  490.     end
  491.    
  492.     function window:onDrag(button, x, y)
  493.         super.onDrag(self, button, x, y)
  494.        
  495.         if windows[1] == self and self.mode == 'paint' then
  496.             self:paint(button, x, y)
  497.         end
  498.     end
  499.    
  500.     function window:onKey(key)
  501.         if key == keys.leftCtrl then
  502.             table.insert(self.modes, table.remove(self.modes, 1))
  503.             self.mode = self.modes[1]
  504.             return
  505.         end
  506.        
  507.         if self.mode == 'char' then
  508.             local x, y = self.pos.x, self.pos.y
  509.            
  510.             local function forward()
  511.                 self.pos.y = (x == self.width and y < self.height) and y + 1 or y
  512.                 self.pos.x = x < self.width and x + 1 or
  513.                     y < self.height and 1 or x
  514.             end
  515.            
  516.             local function back()
  517.                 self.pos.y = (x == 1 and y > 1) and y - 1 or y
  518.                 self.pos.x = x > 1 and x - 1 or
  519.                     y > 1 and self.width or x
  520.             end
  521.            
  522.             if key == keys.right then
  523.                 forward()
  524.             elseif key == keys.left then
  525.                 back()
  526.             elseif key == keys.down then
  527.                 self.pos.y = y < self.height and y + 1 or y
  528.             elseif key == keys.up then
  529.                 self.pos.y = y > 1 and y - 1 or y
  530.            
  531.             elseif key == keys.backspace then
  532.                 back()
  533.                 self.image(self.pos.x, self.pos.y).char = ' '
  534.                
  535.             elseif key == keys.enter then
  536.                 if self.pos.y < self.height then
  537.                     self.pos.x = 1
  538.                     self.pos.y = y + 1
  539.                 end
  540.                
  541.             elseif key == keys.home then
  542.                 self.pos.x = 1
  543.                
  544.             elseif key == keys['end'] then
  545.                 self.pos.x = self.width
  546.             end
  547.         end
  548.     end
  549.    
  550.     function window:onChar(char)
  551.         if self.mode == 'char' then
  552.             local x, y = self.pos.x, self.pos.y
  553.             local pixel = self.image(x, y)
  554.             if pixel.color == 0 then
  555.                 pixel.color = self.color
  556.             end
  557.             pixel.char = char
  558.             pixel.textColor = self.textColor
  559.            
  560.             if x < self.width then
  561.                 self.pos.x = x + 1
  562.             elseif y < self.height then
  563.                 self.pos.x = 1
  564.                 self.pos.y = y + 1
  565.             end
  566.         end
  567.     end
  568.    
  569.     function window:draw()
  570.         super.draw(self)
  571.        
  572.         local t = term
  573.         local w,h = t.getSize()
  574.         local ox, oy = self.title.x, self.title.y + 1
  575.        
  576.         -- draw the image
  577.         for x=1, self.width do
  578.             for y=1, self.height do
  579.                 t.setCursorPos(x + ox - 1, y + oy - 1)
  580.                 local pixel = self.image(x, y)
  581.                
  582.                 if not pixel then
  583.                     pixel = self.image(x, y, {
  584.                         color = 0;
  585.                         textColor = 0;
  586.                         char = ' ';
  587.                     })
  588.                 end
  589.                
  590.                 if pixel.color > 0 then
  591.                     t.setBackgroundColor(pixel.color)
  592.                     if pixel.textColor > 0 then t.setTextColor(pixel.textColor) end
  593.                     t.write(pixel.char)
  594.                 else
  595.                     t.setBackgroundColor(colors.gray)
  596.                     t.setTextColor(colors.lightGray)
  597.                     t.write '.'
  598.                 end
  599.             end
  600.         end
  601.        
  602.         -- draw our color on the titlebar
  603.         t.setCursorPos(self.title.x + self.title.width - 1, self.title.y)
  604.         if self.mode == 'paint' then
  605.             t.setBackgroundColor(self.color)
  606.             t.write ' '
  607.         elseif self.mode == 'char' then
  608.             t.setBackgroundColor(self.textColor == colors.gray and colors.lightGray or colors.gray)
  609.             t.setTextColor(self.textColor)
  610.             t.write 'A'
  611.         end
  612.        
  613.         -- limit the cursor position if we're on char mode
  614.         if mode == 'char' then
  615.             local x, y = self.pos.x, self.pos.y
  616.             self.pos.x =
  617.                 x > self.width and self.width or
  618.                 x < 1 and 1 or x
  619.             self.pos.y =
  620.                 y > self.height and self.height or
  621.                 y < 1 and 1 or y
  622.         end
  623.        
  624.         -- reposition our cursor
  625.         -- also choose a cursor color that doesn't blend with the background
  626.         local pixel = self.image(self.pos.x, self.pos.y)
  627.         t.setTextColor(
  628.             (self.textColor == pixel.color or self.textColor == colors.gray and pixel.color == 0) and
  629.             (self.textColor == colors.white and colors.black or colors.white) or
  630.             self.textColor
  631.         )
  632.         t.setCursorPos(self.pos.x + self.title.x - 1, self.pos.y + self.title.y)
  633.         t.setCursorBlink(self.mode == 'char')
  634.     end
  635.    
  636.     local function imageData()
  637.         local self = window
  638.         local data = ''
  639.         for y=1, self.height do
  640.             for x=1, self.width do
  641.                 local pixel = self.image(x, y)
  642.                 local color = pixel.color > 0 and tohex(pixel.color) or ' '
  643.                 local textColor = pixel.textColor > 0 and tohex(pixel.textColor) or ' '
  644.                
  645.                 data = data .. color..textColor..pixel.char
  646.             end
  647.             data = data..'\n'
  648.         end
  649.         return data
  650.     end
  651.    
  652.     local function save(data)
  653.         local self = window
  654.         local path = self.savePath
  655.        
  656.         if not path then
  657.             path = prompt('Save as: ', window.name)
  658.         end
  659.        
  660.         local file = fs.open(path, 'w')
  661.         if file then
  662.             self.savePath = path
  663.             self:setName(path)
  664.            
  665.             local folder = path:match('(.*/).+')
  666.             if folder and not fs.exists(folder) then
  667.                 fs.makeDir(folder)
  668.             end
  669.            
  670.             file.write(data)
  671.             file.close()
  672.             return true
  673.         else
  674.             return false, 'File is read only'
  675.         end
  676.     end
  677.    
  678.     function window:saveImage()
  679.         return save('@canvas\n'..imageData())
  680.     end
  681.    
  682.     function window:saveImageObject()
  683.         local script = [[
  684.             --@canvasobj
  685.  
  686.             local data = %s
  687.             local image = {}
  688.             local mt = {}
  689.            
  690.             local function tocolor(hex)
  691.                 local n = tonumber(hex, 16)
  692.                 return n and 2 ^ n or 0
  693.             end
  694.            
  695.             local y = 1
  696.             for line in data:gmatch('[^\n]+') do
  697.                 local x = 1
  698.                 image[y] = {}
  699.                 for color, textColor, char in line:gmatch('(.)(.)(.)') do
  700.                     color = tocolor(color)
  701.                     textColor = tocolor(textColor)
  702.                    
  703.                     image[y][x] = {
  704.                         color = color;
  705.                         textColor = textColor;
  706.                         char = char;
  707.                     }
  708.                    
  709.                     x = x + 1
  710.                 end
  711.                 y = y + 1
  712.             end
  713.            
  714.             function image:draw(ox, oy)
  715.                 local t = term
  716.                 for y=1, #self do
  717.                     for x=1, #self[y] do
  718.                         local pixel = self[y][x]
  719.                        
  720.                         t.setCursorPos(x + ox - 1, y + oy - 1)
  721.                         if pixel.color then
  722.                             t.setBackgroundColor(pixel.color)
  723.                         end
  724.                         if pixel.textColor then
  725.                             t.setTextColor(pixel.textColor)
  726.                         end
  727.                         t.write(pixel.char)
  728.                     end
  729.                 end
  730.             end
  731.            
  732.             return image]]
  733.        
  734.         local content = ''
  735.        
  736.         -- remove that extra whitespace
  737.         for line in script:gmatch('[^\n]+') do
  738.             content = content .. line:gsub('^\t\t\t','') .. '\n'
  739.         end
  740.        
  741.         content = content:format('[['..imageData()..']]')
  742.        
  743.         return save(content)
  744.     end
  745.    
  746.     function window:loadImage(path)
  747.         local file = fs.open(path, 'r')
  748.         if file then
  749.             self.image = newImage()
  750.            
  751.             local line = file.readLine()
  752.             if line:match('@canvasobj') then -- is canvas object
  753.                 local image = loadstring(file.readAll())()
  754.                
  755.                 for y=1, #image do
  756.                     for x=1, #image[y] do
  757.                         local pixel = image[y][x]
  758.                         self.image(x, y, {
  759.                             color = pixel.color;
  760.                             textColor = pixel.textColor;
  761.                             char = pixel.char;
  762.                         })
  763.                     end
  764.                 end
  765.             elseif line:match('@canvas') then -- is raw canvas format image
  766.                 local y = 1
  767.                 for line in file.readLine do
  768.                     local x = 1
  769.                     for color, textColor, char in line:gmatch('(.)(.)(.)') do
  770.                         color = color ~= ' ' and tocolor(color) or 0
  771.                         textColor = textColor ~= ' ' and tocolor(textColor) or 0
  772.                        
  773.                         self.image(x, y, {
  774.                             color = color;
  775.                             textColor = textColor;
  776.                             char = char;
  777.                         })
  778.                        
  779.                         x = x + 1
  780.                     end
  781.                     y = y + 1
  782.                 end
  783.             else -- is vanilla/native image(?)
  784.                 local y = 1
  785.                 for line in file.readLine do
  786.                     local x = 1
  787.                     for color in line:gmatch('.') do
  788.                         self.image(x, y, {
  789.                             color = tocolor(color) or 0;
  790.                             textColor = colors.white;
  791.                             char = ' ';
  792.                         })
  793.                        
  794.                         x = x + 1
  795.                     end
  796.                     y = y + 1
  797.                 end
  798.             end
  799.                
  800.             self.height = #self.image
  801.             self.width = #self.image[1]
  802.             self.title.width = self.width
  803.             self:setName(path)
  804.            
  805.             return true, file.close()
  806.         else
  807.             return false, 'File does not exist'
  808.         end
  809.     end
  810.    
  811.     for x=1, window.width do
  812.         for y=1, window.height do
  813.             window.image(x, y, {
  814.                 color = 0;
  815.                 textColor = 0;
  816.                 char = ' ';
  817.             })
  818.         end
  819.     end
  820.    
  821.     window:setName(obj.name or '')
  822.     window:setSize(obj.width or 15, obj.height or 8)
  823.     window.title.x = obj.x or 4
  824.     window.title.y = obj.y or 3
  825.    
  826.     if obj then
  827.         for i,v in pairs(obj) do
  828.             window[i] = v
  829.         end
  830.     end
  831.    
  832.     return window
  833. end
  834.  
  835. function newFileWindow(obj)
  836.     obj = obj or {}
  837.    
  838.     -- stuff
  839. end
  840.  
  841.  
  842. function bgMenu()
  843.     return contextMenu{
  844.         menuOption('New..', 1, 1, function(mx, my)
  845.             local name = prompt('Image Name: ')
  846.            
  847.             if #name > 0 then
  848.                 local window = newImageWindow{
  849.                     name = shell.resolve(name);
  850.                     x = mx - 6;
  851.                     y = my;
  852.                 }
  853.                 addWindow(window)
  854.             else
  855.                 -- send a warning
  856.             end
  857.         end,
  858.         colors.lime);
  859.        
  860.         menuOption('Open..', 1, 2, function(window)
  861.             local path = prompt('Image Path: ')
  862.            
  863.             if #path > 0 then
  864.                 local window = newImageWindow()
  865.                 local ok, err = window:loadImage(path)
  866.                 if not ok then
  867.                     warning(err)
  868.                 else
  869.                     addWindow(window)
  870.                 end
  871.             end
  872.         end,
  873.         colors.orange);
  874.        
  875.         separator();
  876.        
  877.         menuOption('Exit', 1, 3, function()
  878.             running = false
  879.         end,
  880.         colors.red);
  881.     }
  882. end
  883.  
  884. function imageMenu()
  885.     return contextMenu{
  886.         menuOption('Load..', 1,1, function(window)
  887.             local path = prompt('Image Path: ')
  888.            
  889.             if #path > 0 then
  890.                 local ok, err = window:loadImage(path)
  891.                 if not ok then
  892.                     warning(err)
  893.                 end
  894.             else
  895.                 -- send a warning
  896.             end
  897.         end,
  898.         colors.magenta);
  899.        
  900.         menuOption('Save', 1,1, function(window)
  901.             local ok, err = window:saveImage()
  902.             if not ok then
  903.                 warning(err)
  904.             end
  905.         end,
  906.         colors.lime);
  907.        
  908.         menuOption('Save as Script Object', 1,1, function(window)
  909.             local ok, err = window:saveImageObject()
  910.             if not ok then
  911.                 warning(err)
  912.             end
  913.         end,
  914.         colors.orange);
  915.        
  916.         menuOption('Rename', 1,1, function(window)
  917.             local name = prompt('New Name: ', window.name)
  918.             window:setName(name)
  919.         end,
  920.         colors.orange);
  921.        
  922.         separator();
  923.        
  924.         menuOption('Close', 1,1, function(window)
  925.             window:close()
  926.         end,
  927.         colors.red);
  928.     }
  929. end
  930.  
  931. function colorMenu()
  932.     local grid = {
  933.         ---[[
  934.         {'red',     'lime',         'blue',     'white'},
  935.         {'orange',  'lightBlue',    'cyan',     'lightGray'},
  936.         {'yellow',  'purple',       'green',    'gray'},
  937.         {'pink',    'magenta',      'brown',    'black'}
  938.         --]]
  939.     }
  940.     local options = {}
  941.    
  942.     for y=1, #grid do
  943.         for x=1, #grid[y] do
  944.             local color = colors[grid[y][x]]
  945.             table.insert(options, menuOption(' ', x, y, function(window)
  946.                 if window.mode == 'paint' then
  947.                     window.color = color
  948.                 elseif window.mode == 'char' then
  949.                     window.textColor = color
  950.                 end
  951.             end,
  952.             colors.white, color))
  953.         end
  954.     end
  955.    
  956.     return options
  957. end
  958.  
  959.  
  960. function init()
  961.     --[[
  962.     local function new(n)
  963.         local image = newImageWindow{
  964.             x = #windows*-2 + 11;
  965.             y = #windows*2 + 2;
  966.             name = n;
  967.         }
  968.     end
  969.    
  970.     local words = {'hello', 'and', 'welcome', 'to', 'canvas'}
  971.     for i=5, 1, -1 do
  972.         new(words[i])
  973.     end
  974.     --]]
  975.    
  976.     addWindow(newImageWindow{name='untitled'})
  977. end
  978.  
  979. function update(ev, p1, p2, p3)
  980.     warningMsg = ''
  981.    
  982.     if ev == 'mouse_click' then
  983.         (function()
  984.             local button, x, y = p1, p2, p3
  985.             for i=1, #windows do
  986.                 local window = windows[i]
  987.                 if window:onClick(button, x, y, i)== 'focusme' then
  988.                     table.insert(windows, 1, table.remove(windows, i))
  989.                     return
  990.                 end
  991.             end
  992.            
  993.             if button == 2 then
  994.                 openMenu(x, y, bgMenu(), x, y)
  995.             end
  996.         end)()
  997.     elseif ev == 'mouse_drag' then
  998.         for i=1, #windows do
  999.             windows[i]:onDrag(p1, p2, p3)
  1000.         end
  1001.     elseif ev == 'key' then
  1002.         windows[1]:onKey(p1)
  1003.         if p1 == keys.tab then
  1004.             table.insert(windows, table.remove(windows, 1))
  1005.         end
  1006.     elseif ev == 'char' then
  1007.         windows[1]:onChar(p1)
  1008.     elseif ev == 'timer' then
  1009.         for i=1, #windows do
  1010.             windows[i]:onTimer(p1)
  1011.         end
  1012.        
  1013.         if p1 == warningTimer then
  1014.             warningMsg = ''
  1015.         end
  1016.     end
  1017.    
  1018.     if ev == 'key' and p1 == keys.leftAlt or p1 == keys.rightAlt then
  1019.         altmode = true
  1020.     else
  1021.         altmode = false
  1022.     end
  1023.    
  1024.     for i=#windows, 1, -1 do
  1025.         if windows[i].remove == true then
  1026.             table.remove(windows, i)
  1027.         end
  1028.     end
  1029.    
  1030.     --[[
  1031.     if ev == 'key' and p1 == keys.backspace then
  1032.         running = false
  1033.     end
  1034.    
  1035.     if ev == 'terminate' then
  1036.         running = false
  1037.     end
  1038.     --]]
  1039. end
  1040.  
  1041. function draw()
  1042.     local t = term
  1043.     local w,h = term.getSize()
  1044.    
  1045.     t.setBackgroundColor(colors.blue)
  1046.     t.clear()
  1047.    
  1048.     if warningMsg ~= '' then
  1049.         t.setCursorPos(1,1)
  1050.         t.setBackgroundColor(colors.gray)
  1051.         t.setTextColor(colors.white)
  1052.         t.clearLine()
  1053.         t.write(warningMsg)
  1054.     end
  1055.    
  1056.     for i=#windows, 1, -1 do
  1057.         windows[i]:draw()
  1058.     end
  1059. end
  1060.  
  1061. function exit()
  1062.     local t = term
  1063.     t.setCursorPos(1,1)
  1064.     t.setBackgroundColor(colors.black)
  1065.     t.setTextColor(colors.white)
  1066.     t.clear()
  1067.     print 'Thanks for using canvas! -Kingdaro'
  1068. end
  1069.  
  1070.  
  1071. local function main()
  1072.     init()
  1073.     while running do
  1074.         draw()
  1075.         update(os.pullEventRaw())
  1076.     end
  1077.     exit()
  1078. end
  1079.  
  1080. main()
Add Comment
Please, Sign In to add comment