Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- --[[
- Micropaint
- A paint program that lets you utilize the new computercraft charset for detailed painting
- Written by: Nitrogen Fingers
- ]]--
- --[[
- Operation:
- The smaller pixels allow a single ComputerCraft pixel to be divided into six subpixels, in a 3x2 pattern. For each pixel, only two colours can be used in these sub-pixels, one for the background and one for the foreground, although the program does it's best to abstract this information out when using.
- As the mouse events do not have enough precision to target these sub-pixels, and each pixel can potentially have different fore and background colours, editing of these subpixels is done by selecting the desired pixel with the 'magnify' tool, or the right mouse button on the main canvas. In the smaller canvas on the top-right, a representation of that pixel is displayed in a zoomed-in fashion. Changing the colours on this magnify tool correspondingly update the larger image.
- Images start filled with an alpha layer- this is filled by default with black when images are saved and loaded. When an image is 'cropped', as much of this as possible is removed.
- Colours can be changed with the provided pallette. There are currently no other tools to make use of.
- ]]
- local w,h = term.getSize()
- --A very quick button class, for pallette and tools
- local _button = {
- x = nil;
- y = nil;
- text = nil;
- colour = nil;
- textcol = "f";
- draw = function(self)
- term.setCursorPos(self.x, self.y)
- term.blit(self.text, self.textcol:rep(#self.text), self.colour:rep(#self.text))
- end;
- is_pressed = function(self, mx, my)
- return mx >= self.x and mx < self.x + #self.text and my == self.y
- end;
- on_pressed = function() print("not implemented yet") end;
- }
- _button.__index = function(t, k) return _button[k] end
- _button.new = function(x, y, text, colour)
- local new = { x = x; y = y; text = text; colour = colour; }
- setmetatable(new, _button)
- return new
- end
- --A kind of button that uses images rather than text
- local _imgbutton = {
- x = nil;
- y = nil;
- img = nil;
- is_pressed = function(self, mx, my)
- return mx >= self.x and mx <= self.x + self.img.width and
- my >= self.y and my <= self.y + self.img.height
- end;
- on_pressed = function() print("not implemented yet") end;
- change_colour = function(self, old_col, new_col)
- for i=1, self.img.height do
- self.img.fg[i]:gsub(old_col, new_col)
- self.img.bg[i]:gsub(old_col, new_col)
- end
- end;
- }
- _imgbutton.__index = function(t, k) return _imgbutton[k] end
- _imgbutton.new = function(x, y, image)
- local new = { x = x, y = y, img = image }
- setmetatable(new, _imgbutton)
- return new
- end
- --Draws an image format of given width and height to the screen
- --This won't work for our painting image, as the lines aren't concatenated
- local function drawImage(image, x, y)
- for i = 1, image.height do
- term.setCursorPos(x, y + i - 1)
- term.blit(image.text[i], image.fg[i], image.bg[i])
- end
- end
- _imgbutton.draw = function(self, x, y) drawImage(self.img, self.x, self.y) end
- --The full image. Contains width and height information, as well as three distinct layers, stored in a 2D array:
- -- text: The character used to display the pixel. This is a char between 128 and 159, and can be nil.
- -- fg: The colouring of that character
- -- bg: The colouring of the background behind the character
- local img = { }
- --A map of the magnify tool. This is a 3x2 map of single-character colour codes, integer-indexed
- --Left and right are part of the painting tool.
- --pixelX and pixelY are markers to the index of the img table being edited
- local magnify = { }
- --The offset from the top left corner to draw the image
- local imgoffX, imgoffY = 2, 2
- --The offset from the top left corner to draw the magnify tool
- local magoffX, magoffY = 0, 0
- --The colour underlying the null characters
- local alpha = "f"
- --The character representing null characters
- local nullchar = "\127"
- --The maximum size of t a canvas. For the present- scrolling later perhaps
- local maxWidth, maxHeight = w - 12, h - 4
- --The minimum size of a canvas
- local minWidth, minHeight = 1, 1
- --The animation timer used to update 'ticking' interface elements (select boxes, magnify etc. etc.)
- local tickState = 0
- --The interval for the tick timer
- local tickInterval = 0.5
- --The tick timer
- local tickTimer = os.startTimer(tickInterval)
- --The name of the file being edited, if named
- local filename = nil
- --The file path itself
- local sPath = nil
- --Whether or not a saveable change has been made since the program started
- local sChange = false
- --Allows the run loop to proceed
- local programRunning = true
- --Whether or not the code should be shown
- local showMagCode = false
- --Whether or not the control key is down
- local ctrl_down = false
- --The order of our pallette. Done manually just to make it look a little nicer
- local colourder = {"f", "7", "8", "0", "e", "a", "b", "c", "1", "2", "9", "d", "4", "6", "3", "5"}
- --A list of buttons that can be pressed to change the colour of the paint tools
- local pallette = {}
- --A button drawn near the paint pallette that switches colours left and right
- local pswitch = _button.new(0, 0, "\18", "8")
- --Buttons for the file and edit menu
- local fileButton = _button.new(2, 1, "File", "b")
- fileButton.textcol = "0"
- local editButton = _button.new(8, 1, "Edit", "b")
- editButton.textcol = "0"
- --The file and edit menu
- local fMenu = { name = "File", x = fileButton.x, y = fileButton.y }
- local eMenu = { name = "Edit", x = editButton.x, y = editButton.y }
- --The list of each of the tool buttons, displayed on the other side of the magnifying image
- local tools = {}
- --The currently selected tool. A string.
- local selTool = "paint"
- --The select box details
- local selboxX1, selboxY1 = nil, nil
- local selboxX2, selboxY2 = nil, nil
- --Where the cursor tool is currently working from
- local cursorX, cursorY = nil, nil
- --The clipboard
- local clipboard = nil
- --Draws a very thin 2D border around the specified rectangle. fg is the border colour, bg the background
- local function drawEnclosingBorder(x, y, w, h, bg, fg)
- term.setCursorPos(x-1, y-1)
- local topborder = "\159"..("\143"):rep(w).."\144"
- term.blit(topborder, bg:rep(#topborder-1)..fg, fg:rep(#topborder-1)..bg)
- for iy = y,y+h-1 do
- term.setCursorPos(x-1, iy)
- term.blit("\149", bg, fg)
- term.setCursorPos(x+w, iy)
- term.blit("\149", fg, bg)
- end
- term.setCursorPos(x-1, y+h)
- local botborder = "\130"..("\131"):rep(w).."\129"
- term.blit(botborder, fg:rep(#botborder), bg:rep(#botborder))
- end
- --Tries to preserve the colour settings picked by the user- but changes them if necessary
- local function updateMagColours(fg, bg, fused)
- if not fused then
- if bg ~= magnify.left and bg ~= magnify.right then return magnify.left, bg
- else return magnify.left, magnify.right end
- else
- if (bg == magnify.left or bg == magnify.right) and (fg == magnify.left or fg == magnify.right) then
- return magnify.left, magnify.right
- elseif bg == magnify.left then return magnify.left, fg
- elseif fg == magnify.left then return magnify.left, bg
- elseif bg == magnify.right then return fg, magnify.right
- elseif fg == magnify.right then return bg, magnify.right
- else return fg, bg end
- end
- end
- --[[The following two functions belong to the BLittle API by BombBloke, which can be viewed here:
- http://www.computercraft.info/forums2/index.php?/topic/25354-cc-176-blittle-api/
- For comments or question, please contact the author directly.
- ]]--
- function saveBLittle()
- local output = fs.open(sPath, "wb")
- if not output then error("Can't open "..output.." for output.") end
- local function writeInt(num)
- output.write(bit.band(num, 255))
- output.write(bit.brshift(num, 8))
- end
- writeInt(img.width)
- writeInt(img.height)
- local imgComponents = {"text", "fg", "bg"}
- local defComponent = { "\128", "0", "f" }
- for ind,i in ipairs(imgComponents) do
- local thisSet = img[i]
- for y = 1, img.height do
- local thisRow = thisSet[y]
- for x = 1, img.width do
- output.write((thisRow[x] or defComponent[ind]):byte())
- end
- end
- end
- output.close()
- sChange = false
- end
- function loadBLittle(_filename)
- local input = fs.open(_filename, "rb")
- if not input then error("Can't open "..input.." for input.") end
- local function readInt()
- local result = input.read()
- return result + bit.blshift(input.read(), 8)
- end
- local image = {}
- image.width = readInt()
- image.height = readInt()
- local imgComponents = {"text", "fg", "bg"}
- for _,i in ipairs(imgComponents) do
- local thisSet = {}
- for y = 1, image.height do
- local thisRow = {}
- for x = 1, image.width do thisRow[x] = string.char(input.read()) end
- thisSet[y] = thisRow
- end
- image[i] = thisSet
- end
- input.close()
- sChange = false
- return image
- end
- --Takes a 3x2 map of colours and turns it into character with colours
- local function encodeChar(magimage)
- --The binary 'score' of the iamge that determines the pixel
- local score = 0
- --We use the bottom-right colour as our baseline
- local brcol = magimage[3][2]
- local fcol = nil
- for i = 0,4 do
- if magimage[math.floor(i/2)+1][(i%2)+1] ~= brcol then
- score = score + math.pow(2,i)
- fcol = magimage[math.floor(i/2)+1][(i%2)+1]
- end
- end
- fcol = fcol or magnify.left
- return string.char(128 + score), brcol, fcol
- end
- --Takes a character from the image and decodes it into a 3x2 map
- local function decodeChar(x, y)
- local chr, fg, bg = img.text[y][x], img.fg[y][x], img.bg[y][x]
- local score = chr and string.byte(chr) - 128 or 0
- local fused = false
- local magimage = { { }, { }, { } }
- for i = 0, 4 do
- if bit.band(score, math.pow(2, i)) == 0 then
- magimage[math.floor(i/2)+1][(i%2)+1] = bg
- else
- magimage[math.floor(i/2)+1][(i%2)+1] = fg
- fused = true
- end
- end
- magimage[3][2] = bg
- magimage.left, magimage.right = updateMagColours(fg, bg, fused)
- assert(magimage.left)
- assert(magimage.right)
- magimage.pixelX, magimage.pixelY = x, y
- return magimage
- end
- --Draws a single character from the image on the screen
- local function drawChar(x, y)
- term.setCursorPos(imgoffX + x, imgoffY + y)
- if img.bg[y][x] == nil then
- term.blit(nullchar, "7", "f")
- else
- term.blit(img.text[y][x] and img.text[y][x] or " ",
- img.fg[y][x] and img.fg[y][x] or img.bg[y][x], img.bg[y][x])
- end
- end
- --Flashes to draw attention to what pixel is currently being magnified
- local function drawMagnifyIcon()
- if tickState % 2 == 0 and magnify[1] then
- term.setCursorPos(imgoffX + magnify.pixelX, imgoffY + magnify.pixelY)
- local fg = string.format("%x", 15 - tonumber(img.bg[magnify.pixelY][magnify.pixelX], 16))
- local bg = img.bg[magnify.pixelY][magnify.pixelX]..""
- assert(type(nullchar) == "string")
- assert(type(fg) == "string")
- assert(type(bg) == "string")
- term.blit(nullchar, fg, bg)
- elseif magnify[1] then
- drawChar(magnify.pixelX, magnify.pixelY)
- end
- end
- --Draws the code used by the magnify image
- local function drawMagCode()
- if not showMagCode then return end
- term.setCursorPos(magoffX + 1, magoffY + 6)
- local msg = magnify[1] and "\\"..string.byte(img.text[magnify.pixelY][magnify.pixelX]) or " "
- term.blit(msg, ("0"):rep(#msg), ("7"):rep(#msg))
- end
- --Draws a graphic of the left and right paint colours beneath the magnify tool
- local function drawMagColours()
- pswitch:draw()
- local mcx, mcy = magoffX + 2, magoffY + 8
- term.setCursorPos(mcx, mcy)
- local l, r = magnify.left or alpha, magnify.right or alpha
- term.blit(("\143"):rep(2).." ", "8"..r:rep(2), l:rep(2)..r)
- term.setCursorPos(mcx, mcy + 1)
- term.blit(" ".."\131", l:rep(2)..r, l:rep(2).."8")
- end
- --Draws the entire magnify tool, including the colour pallette and the display
- local function drawMagnify()
- drawMagColours()
- if not magnify[1] then
- for y = magoffY, magoffY + 5 do
- term.setCursorPos(magoffX, y)
- term.blit(nullchar:rep(6), "777777", "ffffff")
- end
- return
- end
- for y = 1, 3 do
- for x = 1, 2 do
- local a, c = nullchar:rep(3), "fff"
- if magnify[y][x] then a, c = " ", magnify[y][x]:rep(3) end
- term.setCursorPos(x*3 + magoffX - 3, y*2 + magoffY - 2)
- term.blit(a, "777", c)
- term.setCursorPos(x*3 + magoffX - 3, y*2 + 1 + magoffY - 2)
- term.blit(a, "777", c)
- end
- end
- end
- --Draws the selection box, if it's visible
- local function drawSelectionBox()
- --Captures if there's no box to draw (null or too small)
- if selboxX1 == selboxX2 or selboxY1 == selboxY2 then return end
- local minX, maxX = math.min(selboxX1, selboxX2), math.max(selboxX1, selboxX2)
- local minY, maxY = math.min(selboxY1, selboxY2), math.max(selboxY1, selboxY2)
- local intv = (minX + minY + tickState) % 2 == 0
- if intv == 1 and clipboard and clipboard.projected then
- drawClipboard()
- end
- local border = ""
- term.setCursorPos(minX + imgoffX, minY + imgoffY)
- if intv then
- border = "\145"..("\129"):rep(maxX - minX - 1).."\137"
- term.blit(border, ("8"):rep(#border), ("f"):rep(#border))
- else
- border = "\134"..("\130"):rep(maxX - minX - 1).."\157"
- term.blit(border, ("8"):rep(#border-1).."f", ("f"):rep(#border-1).."8")
- end
- intv = (minX + maxY + tickState) % 2 == 0
- term.setCursorPos(minX + imgoffX, maxY + imgoffY)
- if intv then
- border = "\145"..("\144"):rep(maxX - minX - 1).."\152"
- term.blit(border, ("8"):rep(#border), ("f"):rep(#border))
- else
- border = "\155"..("\159"):rep(maxX - minX - 1).."\157"
- term.blit(border, ("f"):rep(#border), ("8"):rep(#border))
- end
- for iy = minY + 1, maxY - 1 do
- --Left side
- term.setCursorPos(minX + imgoffX, iy + imgoffY)
- intv = (iy + minX + tickState) % 2 == 0
- term.blit(intv and "\145" or "\132", "8", "f")
- --Right side
- term.setCursorPos(maxX + imgoffX, iy + imgoffY)
- intv = (iy + minX + tickState) % 2 == 0
- term.blit(intv and "\136" or "\157", intv and "8" or "f", intv and "f" or "8")
- end
- end
- --Helper function to draw entire image to screen
- local function drawImage()
- for y = 1, img.height do
- for x = 1, img.width do
- drawChar(x, y)
- end
- end
- drawSelectionBox()
- end
- --Draws all the tools
- local function drawTools()
- for k,v in pairs(tools) do
- v:draw()
- if k == selTool then
- drawEnclosingBorder(v.x, v.y, v.img.width, v.img.height, "8", "e")
- end
- end
- end
- --Updates changes to the magnify tool to the image itself
- local function updateImage()
- img.text[magnify.pixelY][magnify.pixelX],
- img.bg[magnify.pixelY][magnify.pixelX],
- img.fg[magnify.pixelY][magnify.pixelX] = encodeChar(magnify)
- drawMagnifyIcon()
- drawMagCode()
- end
- --Updates the magnify tool by changes made to the main canvas or colour pallette
- local function updateMagnify(ocol, ncol)
- if not magnify[3] or ocol == magnify.left or ocol == magnify.right then
- drawMagnify()
- return
- end
- for y = 1, 3 do
- for x = 1, 2 do
- if magnify[y][x] == ocol then magnify[y][x] = ncol end
- end
- end
- drawMagnify()
- updateImage()
- end
- --Copies all data currently under the selection. Can cut with optional argument
- local function copySelection(cut)
- if selboxX1 == selboxX2 or selboxY1 == selboxY2 then return end
- local swidth, sheight = math.abs(selboxX2 - selboxX1), math.abs(selboxY2 - selboxY1)
- local minX, minY = math.min(selboxX1, selboxX2), math.min(selboxY1, selboxY2)
- local maxX, maxY = math.max(selboxX1, selboxX2), math.max(selboxY1, selboxY2)
- clipboard = {
- width = swidth;
- height = sheight;
- projected = false;
- text = { }; fg = { }; bg = { }; }
- for y = 1, sheight do
- clipboard.text[y] = { }
- clipboard.fg[y] = { }
- clipboard.bg[y] = { }
- for x = 1, swidth do
- clipboard.text[y][x] = img.text[y + minY - 1][x + minX - 1]
- clipboard.fg[y][x] = img.fg[y + minY - 1][x + minX - 1]
- clipboard.bg[y][x] = img.bg[y + minY - 1][x + minX - 1]
- if cut then
- img.text[y + minY - 1][x + minX - 1] = nil
- img.fg[y + minY - 1][x + minX - 1] = nil
- img.bg[y + minY - 1][x + minX - 1] = nil
- end
- end
- end
- end
- --Pastes data in the projected clipboard onto the screen
- local function pasteSelection()
- if clipboard == nil or clipboard.projected ~= true then return end
- for x = 1, clipboard.width do
- for y = 1, clipboard.height do
- end
- end
- end
- --The step before pasting. This recreates the selection box and has the clipboard drawn inside-
- --the user can then move the image around before deciding on where to place it and striking a key or
- --changing tools.
- local function projectClipboard()
- if clipboard == nil then return end
- selboxX1, selboxX2 = 1, clipboard.width
- selboxY1, selboxY2 = 1, clipboard.height
- clipboard.projected = true
- end
- --Applies a colour to a given index of the magnify map
- local function paintMagnify(x, y, col)
- magnify[math.floor(y/2) + 1][math.floor(x/3) + 1] = col
- drawMagnify()
- updateImage()
- end
- --Switches the left and right paint pallette.
- local function switchPaintColours()
- local bag = magnify.left
- magnify.left = magnify.right
- magnify.right = bag
- drawMagColours()
- end
- --Helper methods to ensure no OOB errors
- local function inCanvasBounds(x, y)
- return x > imgoffX and x <= imgoffX + img.width and y > imgoffY and y <= imgoffY + img.height
- end
- local function inMagnifyBounds(x, y)
- return x >= magoffX and x <= magoffX + 5 and y >= magoffY and y <= magoffY + 5
- end
- --Draws the menu bar
- local function drawMenuBar()
- term.setCursorPos(2,1)
- term.setBackgroundColour(colours.blue)
- term.clearLine()
- fileButton:draw()
- editButton:draw()
- local title = filename or "Untitled"
- term.setCursorPos(w/2 - #title/2 + 1, 1)
- term.blit(title, (filename and "0" or "3"):rep(#title), ("b"):rep(#title))
- end
- --Draws the entire scene fresh
- local function drawScene()
- term.setBackgroundColour(colours.lightGrey)
- term.clear()
- --The canvas and magnify tool
- drawImage()
- drawMagnify()
- drawTools()
- for _, p in ipairs(pallette) do
- p:draw()
- end
- drawMenuBar()
- --The borders for each tool
- drawEnclosingBorder(magoffX, magoffY, 6, 6, "8", "7")
- drawEnclosingBorder(imgoffX + 1, imgoffY + 1, img.width, img.height, "8", "7")
- drawMagCode()
- end
- --Repositions the tools to be adjacent to the canvas. Avoids overlapping and so on.
- local function setToolPositions()
- magoffX, magoffY = imgoffX + img.width + 4, 3
- pswitch.x, pswitch.y = imgoffX + img.width + 4, magoffY + 8
- for i,c in ipairs(colourder) do
- pallette[i].x = imgoffX + img.width + 1 + math.ceil(i/4) * 2
- pallette[i].y = magoffY + 11 + ((i-1) % 4)
- end
- local ind = 1
- for k,v in pairs(tools) do
- tools[k].x = magoffX + 9
- assert(tools[k].img)
- tools[k].y = 3 + (tools[k].img.height + 1) * (ind - 1)
- ind = ind + 1
- end
- end
- --Helper for resizing canvases. Shifts a table's indices left
- local function shiftIndexes(val)
- local startx = val > 0 and img.width or 1
- local endx = val > 0 and 1 or img.width
- for y = 1, img.height do
- for x = startx, endx, -(val/math.abs(val)) do
- img.text[y][x + val] = img.text[y][x]
- img.text[y][x] = nil
- img.fg[y][x + val] = img.fg[y][x]
- img.fg[y][x] = nil
- img.bg[y][x + val] = img.bg[y][x]
- img.bg[y][x] = nil
- end
- end
- end
- local function resize(nw, nh, left, bottom)
- local wdiff, hdiff = nw - img.width, nh - img.height
- local top = hdiff - bottom
- local right = wdiff - left
- if right < 0 then
- for y = 1, img.height do
- for x = img.width, img.width + right + 1, -1 do
- img.text[y][x] = nil
- img.fg[y][x] = nil
- img.bg[y][x] = nil
- end
- end
- end
- shiftIndexes(left)
- local tablefunc = hdiff > 0 and table.insert or table.remove
- for i = 1, math.abs(top) do
- tablefunc(img.text, 1, {})
- tablefunc(img.fg, 1, {})
- tablefunc(img.bg, 1, {})
- end
- for i = 1, math.abs(bottom) do
- tablefunc(img.text, #img.text, {})
- tablefunc(img.fg, #img.fg, {})
- tablefunc(img.bg, #img.bg, {})
- end
- img.width = nw
- img.height = nh
- selboxX1, selboxX2, selboxY1, selboxY2 = nil, nil, nil, nil
- setToolPositions()
- drawScene()
- end
- --Resizes the canvas to be bigger or smaller, then redraws the scene
- --I is the direction to preserve the piece, as an inverse numberpad.
- local function resizeCanvas(nw, nh, i)
- local wdiff, hdiff = nw - img.width, nh - img.height
- local left = wdiff - math.ceil(wdiff - (wdiff/2) * ((i-1)%3))
- local bottom = math.ceil(hdiff - (hdiff/2) * math.floor((i-1)/3))
- resize(nw, nh, left, bottom)
- end
- --Changes the image width and height to match the area captured in the
- local function cropToSelection()
- if selboxX1 == selboxX2 or selboxY1 == selboxY2 then return end
- local xmin, xmax = math.min(selboxX1, selboxX2), math.max(selboxX1, selboxX2)
- local ymin, ymax = math.min(selboxY1, selboxY2), math.max(selboxY1, selboxY2)
- local nw, nh = xmax - xmin + 1, ymax - ymin + 1
- local left = 1 - xmin
- local bottom = ymax - img.height
- resize(nw, nh, left, bottom)
- end
- --An off-center print function.
- local function wprintOffCenter(msg, height, width, offset)
- local inc = 0
- local ops = 1
- while #msg - ops > width do
- local nextspace = 0
- while string.find(msg, " ", ops + nextspace) and
- string.find(msg, " ", ops + nextspace) - ops < width do
- nextspace = string.find(msg, " ", nextspace + ops) + 1 - ops
- end
- local ox,oy = term.getCursorPos()
- term.setCursorPos(width/2 - (nextspace)/2 + offset, height + inc)
- inc = inc + 1
- term.write(string.sub(msg, ops, nextspace + ops - 1))
- ops = ops + nextspace
- end
- term.setCursorPos(width/2 - #string.sub(msg, ops)/2 + offset, height + inc)
- term.write(string.sub(msg, ops))
- return inc + 1
- end
- --[[ Gets the previous file in the path, if there is one ]]--
- local function getPreviousDir(_path)
- if _path == "" or _path == "/" then return path
- else
- _path = _path:reverse()
- return _path:sub(_path:find("/.*")+1):reverse()
- end
- end
- --Simple confirm/deny messages
- local function displayConfirmDialogue(ctitle, msg, ...)
- local _doffX, _doffY = 8, 5
- local menuBG = colours.white
- --We actually print twice- once to get the lines, second time to print proper. Easier this way.
- local lines = wprintOffCenter(msg, _doffY, w - (_doffX+2) * 2, _doffX + 2)
- drawScene()
- term.setCursorPos(_doffX, _doffY - 2)
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(string.rep(" ", w - _doffX * 2))
- term.setCursorPos(w/2 - #ctitle/2, _doffY - 2)
- term.write(ctitle)
- term.setTextColour(colours.black)
- term.setBackgroundColour(menuBG)
- term.setCursorPos(_doffX, 4)
- term.write(string.rep(" ", w - _doffX * 2))
- for i = _doffY, _doffY + lines do
- term.setCursorPos(_doffX, i)
- term.write(" "..string.rep(" ", w - (_doffX) * 2 - 2).." ")
- end
- wprintOffCenter(msg, _doffY, w - (_doffX+2) * 2, _doffX + 2)
- if arg then
- term.setCursorPos(_doffX, _doffY + lines + 1)
- term.write(string.rep(" ", w - _doffX * 2))
- term.setCursorPos(_doffX, _doffY + lines + 2)
- term.write(string.rep(" ", w - _doffX * 2))
- local _sspace = 0
- for _k,_v in ipairs(arg) do _sspace = _sspace + #_v[1] end
- _sspace = (w - (_doffX * 2) - _sspace - (2 * #arg))/(#arg)
- if _sspace <= #arg - 1 then assert(false, "Too little space: needed "..(#arg - 1)..", got ".._sspace) end
- term.setTextColour(colours.black)
- term.setCursorPos(_doffX + 1, _doffY + lines + 1)
- local _spart = false
- for i=1,#arg do
- _v = arg[i]
- arg[i][3] = term.getCursorPos()
- term.setBackgroundColour(_v[2])
- term.write(" ".._v[1].." ")
- local _vspace = math.floor(_sspace)
- if i >= #arg/2 and not _spart then _vspace = math.ceil(_sspace) _spart = true end
- local _x,_y = term.getCursorPos()
- term.setCursorPos(_x + _vspace, _y)
- end
- end
- --In the event of a message, the player hits anything to continue
- while true do
- local _id,_p1,_p2,_p3 = os.pullEvent()
- if (not arg or #arg == 0) and (id == "key" or id == "mouse_click" or id == "mouse_drag") then
- break
- elseif _id == "key" then
- if _p1 == keys.enter then return 1 end
- if _p1 == keys.backspace then return 2 end
- elseif _id == "mouse_click" and _p3 == _doffY + lines + 1 then
- for i=1,#arg do
- _v = arg[i]
- if _p2 >= _v[3] and _p2 <= _v[3] + #_v[1] + 1 then return i end
- end
- elseif _id == "timer" and _p1 == tickTimer then tickTimer = os.startTimer(tickInterval) end
- end
- end
- --Displays and handles events for drop-down menus
- local function displayDropDown(x, y, options, noTitle)
- inDropDown = true
- if noTitle then y = y - 1 end
- local menuBG = colours.white
- --Figures out the dimensions of our thing
- local longestX = #options.name
- for i=1,#options do
- local currVal = options[i]
- if type(currVal) == "table" then currVal = currVal.name end
- longestX = math.max(longestX, #currVal)
- end
- local xOffset = math.max(0, longestX - ((w-2) - x) + 1)
- local yOffset = math.max(0, #options - ((h-1) - y))
- local clickTimes = 0
- local tid = nil
- local selection = nil
- while clickTimes < 4 do
- if not noTitle then
- term.setCursorPos(x-xOffset,y-yOffset)
- term.setBackgroundColour(colours.lightBlue)
- term.setTextColour(colours.black)
- term.write(options.name.." ")
- end
- for i=1,#options do
- term.setCursorPos(x-xOffset, y-yOffset+i)
- local currVal = options[i]
- if type(currVal.value) == "table" then
- currVal.enabled = #currVal.value > 0
- end
- if i==selection and clickTimes % 2 == 0 then
- term.setBackgroundColour(colours.blue)
- if options[selection].enabled then term.setTextColour(colours.white)
- else term.setTextColour(colours.grey) end
- else
- term.setBackgroundColour(menuBG)
- local _tcol = colours.black
- if not currVal.enabled then _tcol = colours.lightGrey end
- term.setTextColour(_tcol)
- end
- if type(currVal.value) == "table" then
- term.write(currVal.name..string.rep(" ", longestX-#currVal.name))
- term.setBackgroundColour(colours.blue)
- term.setTextColour(colours.white)
- term.write(">")
- else
- term.write(currVal.name..string.rep(" ", longestX-#currVal.name + 1))
- end
- if (i~= selection or clickTimes %2 == 1) and currVal.key and currVal.enabled then
- term.setTextColour(colours.blue)
- term.setBackgroundColour(menuBG)
- local co = currVal.name:find(currVal.key)
- if not co then co = longestX else co = co - 1 end
- term.setCursorPos(x-xOffset+co, y-yOffset + i)
- term.write(currVal.key)
- end
- end
- local id, p1, p2, p3 = os.pullEvent()
- if id == "timer" then
- if p1 == tid then
- clickTimes = clickTimes + 1
- if clickTimes > 2 then
- break
- else
- tid = os.startTimer(0.1)
- end
- elseif p1 == tickTimer then
- tickTimer = os.startTimer(tickInterval)
- tickState = (tickState + 1) % 4
- end
- elseif id == "key" and not tid then
- if p1 == keys.leftCtrl then
- selection = ""
- break
- elseif p1 == keys.down and (not selection or selection < #options) then
- selection = selection or 0
- _os = selection
- repeat selection = selection + 1
- until selection == #options + 1 or options[selection].enabled
- --if selection == #options + 1 then selection = _os end
- elseif p1 == keys.up and (not selection or selection > 1) then
- selection = selection or #options + 1
- _os = selection
- repeat selection = selection - 1
- until selection == 0 or options[selection].enabled
- if selection == 0 then selection = _os end
- elseif p1 == keys.enter and selection and options[selection].enabled then
- tid = os.startTimer(0.1)
- clickTimes = clickTimes - 1
- end
- elseif id == "mouse_click" and not tid then
- local _xp, _yp = x - xOffset, y - yOffset
- if p2 >= _xp and p2 <= _xp + longestX + 1 and
- p3 >= _yp+1 and p3 <= _yp+#options then
- if options[p3 - _yp].enabled then
- selection = p3-(_yp)
- tid = os.startTimer(0.1)
- end
- else
- selection = ""
- break
- end
- elseif id == "char" and not tid then
- for k,v in ipairs(options) do
- if v.key and v.key:lower() == p1:lower() and options[k].enabled then
- selection = k
- tid = os.startTimer(0.1)
- break
- end
- end
- end
- end
- local _val = selection
- if type(selection) == "number" then
- selection = options[selection].value
- end
- if type(selection) == "string" then
- inDropDown = false
- return selection
- elseif type(selection) == "table" then
- return displayDropDown(x + longestX + 1, y + _val, selection, true)
- elseif type(selection) == "function" then
- local _rval = selection()
- if not _rval then return "" else return _rval end
- end
- end
- --A built-in form designed to let users create new documents of a given size
- local function displaySizeSettings(_flag)
- local title = _flag == "n" and "New Image" or "Resize Image"
- local _w, _h = _flag == "n" and 16 or 24, 8
- local _x, _y = math.ceil(w/2 - _w/2), 4
- local _wopt, _hopt = { name = "Width:", y = 2, val = "", sel = false }, { name = "Height:", y = 4, val = "", sel = false }
- local _fx, _self = 9, nil
- local _fg, _bg, _hfg, _hbg = "f", "0", "0", "b"
- local _by = 6
- local _okay = _button.new(_x + 1, _y + _by, "Create", "5")
- local _invalid = _button.new(_x + 1, _y + _by, "Invalid", "8")
- _invalid.textcol = "7"
- local _cancel = _button.new(_x + #_invalid.text + 2, _y + _by, "Cancel", "e")
- drawScene()
- if _flag == "r" then
- _wopt.val = img.width..""
- _hopt.val = img.height..""
- end
- local _resizeList = { }
- for i = 1, 9 do
- _resizeList[i] = _button.new(_x + _w - (6 - ((i - 1) % 3) * 2), _y + math.ceil(i/3) * 2, " ", "7")
- _resizeList[i].textcol = "0"
- end
- _resizeList[2].text = "\24"
- _resizeList[4].text = "\27"
- _resizeList[6].text = "\26"
- _resizeList[8].text = "\25"
- _resizeList[5].colour = "5"
- _rsSel = 5
- local function drawWindow()
- local _ws = _w - #title - 1
- term.setCursorPos(_x, _y)
- term.blit((" "):rep(math.floor(_ws/2))..title..(" "):rep(math.ceil(_ws/2)).."x", ("0"):rep(_w), ("b"):rep(_w-1).."e")
- term.setCursorPos(_x, _y + 1)
- term.setBackgroundColour(colours.white)
- term.setTextColour(colours.black)
- term.write((" "):rep(_w))
- term.setCursorPos(_x, _y + 2)
- local _ws = _w - #_wopt.name - 1
- term.write(" ".._wopt.name..(" "):rep(_ws))
- term.setCursorPos(_x, _y + 3)
- term.write((" "):rep(_w))
- term.setCursorPos(_x, _y + 4)
- local _ws = _w - #_hopt.name - 1
- term.write(" ".._hopt.name..(" "):rep(_ws))
- for i=5,_h-1 do
- term.setCursorPos(_x, _y + i)
- term.write((" "):rep(_w))
- end
- end
- local function valsValid()
- local nw, nh = tonumber(_wopt.val), tonumber(_hopt.val)
- return type(nw) == "number" and type(nh) == "number" and nw >= minWidth and nw <= maxWidth and
- nh >= minHeight and nh <= maxHeight
- end
- local function drawFields()
- term.setCursorPos(_x + _fx, _y + _wopt.y)
- term.setBackgroundColour(colours.lightGrey)
- term.setTextColour(colours.black)
- term.write(_wopt.val..(" "):rep(3 - #_wopt.val))
- term.setCursorPos(_x + _fx, _y + _hopt.y)
- term.write(_hopt.val..(" "):rep(3 - #_hopt.val))
- end
- local function updateBlink()
- if _self == nil then
- term.setCursorBlink(false)
- else
- term.setCursorPos(_x + _fx + #_self.val, _y + _self.y)
- term.setCursorBlink(true)
- end
- end
- local function drawButtons()
- term.setCursorPos(_x + 1, _y + _by)
- term.setBackgroundColour(colours.white)
- term.write((" "):rep(#_invalid.text))
- if valsValid() then
- _okay:draw()
- else
- _invalid:draw()
- end
- _cancel:draw()
- end
- local function displayResizePositions()
- for i,b in ipairs(_resizeList) do
- b.colour = i == _rsSel and "5" or "7"
- b:draw()
- end
- end
- drawWindow()
- drawFields()
- drawButtons()
- if _flag == "r" then displayResizePositions() end
- _menuComplete = false
- repeat
- local _id, _p1, _p2, _p3 = os.pullEvent()
- if _id == "timer" then
- if _p1 == tickTimer then tickTimer = os.startTimer(tickInterval) end
- elseif _id == "mouse_click" then
- if _cancel:is_pressed(_p2, _p3) or (_p2 == _x + _w - 1 and _p3 == _y) then
- _menuComplete = true
- elseif _okay:is_pressed(_p2, _p3) and valsValid() then
- term.setCursorBlink(false)
- return tonumber(_wopt.val), tonumber(_hopt.val), _rsSel
- elseif _p2 >= _x + _fx and _p2 <= _x + _fx + 3 and _p3 == _y + _wopt.y then
- _self = _wopt
- updateBlink()
- elseif _p2 >= _x + _fx and _p2 <= _x + _fx + 3 and _p3 == _y + _hopt.y then
- _self = _hopt
- updateBlink()
- elseif _flag == "r" then
- local pressed = false
- for i, b in ipairs(_resizeList) do
- if b:is_pressed(_p2, _p3) then
- _rsSel = i
- pressed = true
- break
- end
- end
- displayResizePositions()
- if not pressed then
- _self = nil
- end
- updateBlink()
- else
- _self = nil
- updateBlink()
- end
- elseif _id == "char" then
- if _self and #_self.val < 2 then
- _self.val = _self.val.._p1
- drawFields()
- drawButtons()
- updateBlink()
- end
- elseif _id == "key" then
- if _self and _p1 == keys.backspace then
- _self.val = _self.val:sub(0, #_self.val-1)
- drawFields()
- drawButtons()
- updateBlink()
- end
- end
- until _menuComplete
- term.setCursorBlink(false)
- end
- --A more elaborate save menu than my first effort. Directory navigation and election. It works well enough.
- local function displayFileBrowser(_flag,_h,_w,_y,_x)
- local _mt = { ["-s"] = "Save As", ["-b"] = "Browse Files", ["-l"] = "Load File" }
- if not _h then _h = math.floor(h/2) else _h = math.floor(_h) end
- if not _w then _w = math.floor(w/2) else _w = math.floor(_w) end
- if not _x then _x = math.floor(w/2-_w/2) + 1 else _x = math.floor(_x) end
- if not _y then _y = math.floor(h/2-_h/2) end
- local _bc,_tc = colours.white, colours.black
- local _btc,_ttc = colours.blue, colours.white
- local _bfc,_tfc,_tdc = colours.black, colours.white, colours.green
- local _fname = sName or ""
- --This is a nasty timesaver.
- local _cpath = sPath or "/"..shell.resolve(".")
- if _cpath == "/" then _cpath = "" end
- if not _cpath:find("/") then
- _cpath = ""
- elseif sPath then
- _cpath = "/"..getPreviousDir(_cpath)
- end
- local _rlist = fs.list(_cpath)
- if _cpath ~= "/" and _cpath ~= "" then table.insert(_rlist, 1, "..") end
- local _scr = 0
- local _abmsg = { ["-l"] = " Open ", ["-s"] = " Save As ",
- [1] = " Invalid ", [2] = " Overwrite " }
- local _labmsg = ""
- local _winh = _h - 7
- local _cpos = 0
- drawScene()
- --Some dedicated internal draw functions (to speed things up)
- local function _drawWindow()
- --Permanent parts of the window
- term.setBackgroundColour(_btc)
- term.setTextColour(_ttc)
- term.setCursorPos(_x,_y)
- term.write(string.rep(" ", math.floor(_w/2) - math.floor(#_mt[_flag]/2))
- .._mt[_flag]..string.rep(" ", math.ceil(_w/2) -
- math.ceil(#_mt[_flag]/2)-1))
- term.setBackgroundColour(colours.red)
- term.setTextColour(colours.white)
- term.write("x")
- term.setBackgroundColour(_bc)
- term.setTextColour(_tc)
- for i=1,_h do
- term.setCursorPos(_x,_y+i)
- term.write(string.rep(" ", _w))
- end
- term.setTextColour(colours.black)
- term.setBackgroundColour(colours.red)
- term.setCursorPos(_x + _w - 10, _y + _h - 1)
- term.write(" Cancel ")
- end
- local function _drawBrowser()
- term.setBackgroundColour(_bc)
- term.setTextColour(_tc)
- local _dpath = _cpath
- if #_dpath > _w-4 then
- while _dpath:find("/") do
- local _ind = _dpath:find("/") + 1
- _dpath = _dpath:sub(_ind)
- if #_dpath < _w-7 then
- _dpath = "...".._dpath
- break
- end
- end
- end
- if #_dpath > _w-4 then _dpath = "...".._dpath:sub(_w-4-#_dpath)
- elseif #_dpath == 0 then _dpath = "/" end
- term.setCursorPos(_x+2,_y+2)
- term.write(_dpath..string.rep(" ", _w-4-#_dpath))
- term.setBackgroundColour(_bfc)
- for i = 1 + _scr, _winh + _scr do
- _pth = _rlist[i] or ""
- if #_pth > _w - 5 then _pth = _pth:sub(1,(_w - 8)).."..." end
- term.setCursorPos(_x + 2, _y + 2 + i - _scr)
- if fs.isDir(_cpath.."/".._pth) then
- term.setTextColour(_tdc)
- else term.setTextColour(_tfc) end
- term.write(" ".._pth..string.rep(" ", _w - 5 - #_pth))
- end
- end
- local function _drawActivationButton()
- _owrite = 0
- local _val = _cpath.."/".._fname
- if (not fs.exists(_val) and _flag == "-l") or
- (fs.exists(_val) and fs.isDir(_val)) then
- _owrite = 1
- elseif fs.exists(_val) and _flag == "-s" then _owrite = 2 end
- term.setTextColour(colours.black)
- term.setCursorPos(_x + 2, _y + _h - 1)
- if _owrite == 1 and _flag ~= "-b" then
- term.setBackgroundColour(colours.lightGrey)
- term.setTextColour(colours.grey)
- _labmsg = " Invalid "
- elseif _owrite == 2 then
- term.setBackgroundColour(colours.orange)
- _labmsg = " Overwrite "
- elseif _flag == "-s" then
- term.setBackgroundColour(colours.green)
- _labmsg = " Save "
- elseif _flag == "-l" then
- term.setBackgroundColour(colours.green)
- _labmsg = " Open "
- end
- term.write(_labmsg)
- term.setBackgroundColour(_bc)
- term.write(string.rep(" ", #" Overwrite " - #_labmsg))
- end
- local function _drawWriteBar()
- term.setCursorPos(_x + 2, _y + _h - 3)
- term.setTextColour(colours.black)
- term.setBackgroundColour(colours.white)
- local _pm = "Save As "
- if _flag == "-l" then _pm = "Open " end
- term.write(_pm)
- local _msg = _fname
- if #_msg > _w - 8 - #_pm then _msg = _msg:sub(#_msg - (_w - 8 - #_pm)) end
- term.setBackgroundColour(colours.lightGrey)
- term.write(" ".._msg)
- _cpos = term.getCursorPos()
- term.write(string.rep(" ", math.max(_w - 5 - #_pm - #_msg, 1)))
- end
- _drawWindow()
- _drawActivationButton()
- _drawBrowser()
- _drawWriteBar()
- while true do
- term.setTextColour(colours.black)
- term.setCursorPos(_cpos, _y + _h - 3)
- term.setCursorBlink(true)
- local _id,_p1,_p2,_p3 = os.pullEvent()
- if _id == "key" then
- if _p1 == keys.backspace and #_fname > 0 then
- _fname = _fname:sub(1,#_fname-1)
- _drawActivationButton()
- _drawWriteBar()
- elseif _p1 == keys.up and _scr > 0 then
- _scr = _scr - 1
- _drawBrowser()
- elseif _p1 == keys.down and _scr < #_rlist - _winh then
- _scr = _scr + 1
- _drawBrowser()
- elseif _p1 == keys.enter then
- local _val = _cpath.."/".._fname
- if (_flag == "-l" and fs.exists(_val) and not fs.isDir(_val))
- or(_flag == "-s" and not fs.isDir(_val)) then
- break
- end
- end
- elseif _id == "char" then
- _fname = _fname.._p1
- _drawActivationButton()
- _drawWriteBar()
- elseif _id == "mouse_click" then
- if _p2 == _x + _w - 1 and _p3 == _y then
- _fname = nil
- break
- elseif _p2 >= _x + 2 and _p2 <= _x + _w - 2 and _p3 >= _y + 3 and
- _p3 < _y + 3 + _winh then
- _p3 = _p3 - _y - 2
- local _val = _rlist[_p3 + _scr]
- if _val == ".." then
- --Wow why is there no reverse find
- _cpath = getPreviousDir(_cpath)
- _rlist = fs.list(_cpath)
- if _cpath ~= "/" and _cpath ~= "" then table.insert(_rlist, 1, "..") end
- _scr = 0
- _drawBrowser()
- _drawActivationButton()
- elseif fs.isDir(_cpath.."/".._val) then
- if _cpath == "/" then _cpath = _cpath.._val
- else _cpath = _cpath.."/".._val end
- _rlist = fs.list(_cpath)
- if _cpath ~= "/" and _cpath ~= "" then table.insert(_rlist, 1, "..") end
- _scr = 0
- _drawBrowser()
- _drawActivationButton()
- else
- _fname = _val or _fname
- _drawActivationButton()
- _drawWriteBar()
- end
- elseif _p3 == _y + _h - 1 and _p2 >= _x + 2 and _p2 < _x + 2 + # _labmsg and _labmsg ~= _abmsg[1] then
- break
- elseif _p3 == _y + _h - 1 and _p2 >= _x + _w - 2 - #" Cancel " and
- _p2 < _x + _w - 2 then
- _fname = nil
- term.setCursorBlink(false)
- return false
- end
- elseif _id == "mouse_scroll" then
- _scr = math.min(_scr, #_rlist - _winh - 1)
- _scr = math.max(_scr + _p1, 0)
- _drawBrowser()
- elseif _id == "timer" and _p1 == tickTimer then
- tickTimer = os.startTimer(tickInterval)
- end
- end
- term.setCursorBlink(false)
- if _fname then return (_cpath.."/".._fname):sub(2),_fname else return nil end
- end
- --Sets up canvas features etc. for a loaded image
- local function createLoadedImage(path)
- path = path or sPath
- img = loadBLittle(path)
- magnify = {
- left = "0";
- right = "f";
- pixelX = nil;
- pixelY = nil;
- }
- setToolPositions()
- drawScene()
- end
- --Starts a blank canvas.
- local function createNewImage(iw, ih)
- img = {
- width = iw or 20;
- height = ih or 15;
- text = { };
- fg = { };
- bg = { };
- }
- for y = 1, img.height do
- img.text[y] = { }
- img.fg[y] = { }
- img.bg[y] = { }
- end
- magnify = {
- left = "0";
- right = "f";
- pixelX = nil;
- pixelY = nil;
- }
- setToolPositions()
- sPath = nil
- sChange = false
- filename = nil
- drawScene()
- end
- --Save a document from the menu
- local function menuSaveAs()
- local _sp,sn = displayFileBrowser("-s", 14, 24, 3)
- if _sp then
- sPath = _sp
- sName = _sn
- saveBLittle(img, sPath)
- end
- end
- --Checks to ensure no changes have been made that are unsaved
- local function checkSave()
- if not sChange then return true end
- drawScene()
- local _val = displayConfirmDialogue("Unsaved Changes", "Your document contains unsaved changes. Are you sure you want to do this?", { [1] = "Save"; [2] = colours.green }, { [1] = "Discard"; [2] = colours.orange; }, { [1] = "Cancel";
- [2] = colours.red; })
- if _val == 1 then
- if sPath then saveBLittle()
- else menuSaveAs() end
- return true
- elseif _val == 2 then
- return true
- else
- return false
- end
- end
- --Handles the events for the program
- local function run()
- while programRunning do
- local id, p1, p2, p3 = os.pullEvent()
- if id == "key" then
- if p1 == keys.leftCtrl then
- ctrl_down = true
- elseif ctrl_down then
- local found = false
- for _, option in ipairs(fMenu) do
- if p1 == keys[string.lower(option.key or "-")] and option.enabled then
- found = true
- option.value()
- drawScene()
- break
- end
- end
- if not found then for _, option in ipairs(eMenu) do
- if p1 == keys[string.lower(option.key or "-")] and option.enabled then
- found = true
- option.value()
- drawScene()
- break
- end
- end end
- ctrl_down = false
- end
- elseif id == "key_up" then
- if p1 == keys.leftCtrl then
- ctrl_down = false
- end
- elseif id == "timer" then
- if p1 == tickTimer then
- tickTimer = os.startTimer(tickInterval)
- tickState = (tickState + 1) % 4
- drawMagnifyIcon()
- drawSelectionBox()
- end
- elseif id == "mouse_click" or id == "mouse_drag" then
- local ix, iy = p2 - imgoffX, p3 - imgoffY
- local mx, my = p2 - magoffX, p3 - magoffY
- if p1 == 1 then
- --Canvas
- if inCanvasBounds(p2, p3) then
- if selTool == "paint" then
- img.text[iy][ix] = "\128"
- img.fg[iy][ix] = magnify.left
- img.bg[iy][ix] = magnify.left
- drawChar(ix, iy)
- if ix == magnify.pixelX and iy == magnify.pixelY then
- magnify = decodeChar(ix, iy)
- drawMagnify()
- drawMagCode()
- end
- sChange = true
- elseif selTool == "magnify" and img.text[iy][ix] ~= nil then
- local ox, oy = magnify.pixelX, magnify.pixelY
- magnify = decodeChar(ix, iy)
- drawMagnify()
- drawMagCode()
- if ox and oy then drawChar(ox, oy) end
- drawMagnifyIcon()
- elseif selTool == "selectbox" then
- if id == "mouse_click" then
- selboxX1, selboxY1 = ix, iy
- end
- selboxX2, selboxY2 = ix, iy
- drawImage()
- elseif selTool == "cursor" then
- if id == "mouse_drag" and selboxX1 ~= selboxX2 and selboxY1 ~= selboxY2 then
- local diffX, diffY = ix - cursorX, iy - cursorY
- local bwidth, bheight = math.abs(selboxX2 - selboxX1), math.abs(selboxY2 - selboxY1)
- if selboxX2 then
- selboxX2 = math.max(math.min(selboxX2 + diffX, img.width), bwidth + 1)
- selboxX1 = math.max(math.min(selboxX1 + diffX, img.width - bwidth ), 1)
- selboxY2 = math.max(math.min(selboxY2 + diffY, img.height), bheight + 1)
- selboxY1 = math.max(math.min(selboxY1 + diffY, img.height - bheight), 1)
- drawImage()
- end
- end
- cursorX, cursorY = ix, iy
- end
- elseif magnify[1] and inMagnifyBounds(p2, p3) then
- paintMagnify(mx, my, magnify.left)
- sChange = true
- elseif pswitch:is_pressed(p2, p3) then pswitch:on_pressed()
- elseif fileButton:is_pressed(p2, p3) then fileButton:on_pressed()
- elseif editButton:is_pressed(p2, p3) then editButton:on_pressed()
- else
- local _pressed = false
- for _, cbutton in ipairs(pallette) do
- if cbutton:is_pressed(p2, p3) then
- cbutton:on_pressed(p1)
- _pressed = true
- break
- end
- end
- --if _pressed then break end
- for _, tbutton in pairs(tools) do
- if tbutton:is_pressed(p2, p3) then
- tbutton:on_pressed()
- _pressed = true
- break
- end
- end
- end
- elseif p1 == 2 then
- if inCanvasBounds(p2, p3) then
- if selTool == "paint" then
- img.text[iy][ix] = "\128"
- img.fg[iy][ix] = magnify.right
- img.bg[iy][ix] = magnify.right
- drawChar(ix, iy)
- if ix == magnify.pixelX and iy == magnify.pixelY then
- magnify = decodeChar(ix, iy)
- drawMagnify()
- drawMagCode()
- end
- sChange = true
- elseif selTool == "magnify" and img.text[iy][ix] ~= nil then
- local ox, oy = magnify.pixelX, magnify.pixelY
- magnify = decodeChar(ix, iy)
- drawMagnify()
- drawMagCode()
- if ox and oy then drawChar(ox, oy) end
- drawMagnifyIcon()
- elseif selTool == "selectbox" then
- elseif selTool == "cursor" then
- end
- elseif magnify[1] and inMagnifyBounds(p2, p3) then
- paintMagnify(mx, my, magnify.right)
- sChange = true
- else
- local bpressed = false
- for _, cbutton in ipairs(pallette) do
- if cbutton:is_pressed(p2, p3) then
- cbutton:on_pressed(p1)
- bpressed = true
- break
- end
- end
- if not bpressed and id ~= "mouse_drag" then
- local ox, oy = magnify.pixelX, magnify.pixelY
- magnify = { left = magnify.left, right = magnify.right }
- drawMagnify()
- drawMagCode()
- if ox and oy then drawChar(ox, oy) end
- end
- end
- end
- elseif id == "mouse_scroll" then
- switchPaintColours()
- end
- if selTool == "paint" or selTool == "magnify" and selboxX1 then
- selboxX1, selboxY1, selboxX2, selboxY2 = nil, nil, nil
- drawImage()
- end
- end
- end
- --Interface specifications etc.
- local pallette_pressed = function(self, mb)
- local ocol = (mb == 1 and magnify.left or magnify.right)
- if mb == 1 then magnify.left = self.colour
- else magnify.right = self.colour end
- updateMagnify(ocol, self.colour)
- end
- for i,c in ipairs(colourder) do
- nb = _button.new(w-1, i, " ", c)
- nb.on_pressed = pallette_pressed
- table.insert(pallette, nb)
- end
- pswitch.on_pressed = switchPaintColours
- fileButton.on_pressed = function()
- fMenu[3].enabled = sChange and sPath
- fMenu[4].enabled = sChange and sPath
- displayDropDown(fMenu.x, fMenu.y, fMenu)
- drawScene()
- end
- editButton.on_pressed = function()
- displayDropDown(eMenu.x, eMenu.y, eMenu)
- drawScene()
- end
- table.insert(fMenu, { name = "New", key = "N", enabled = true, value = function()
- if checkSave() then
- local nw, nh = displaySizeSettings("n")
- if nw and nh then createNewImage(nw, nh)
- end end end })
- table.insert(fMenu, { name = "Open", key = "O", enabled = true, value = function()
- if checkSave() then
- local _sp,_sn = displayFileBrowser("-l", 14, 24, 3)
- if _sp then
- createLoadedImage(_sp)
- sPath = _sp
- filename = _sn
- assert(sPath)
- end end end })
- table.insert(fMenu, { name = "Revert", key = "R", enabled = false, value = function()
- createLoadedImage(sPath)
- end })
- table.insert(fMenu, { name = "Save", key = "S", enabled = false, value = saveBLittle })
- table.insert(fMenu, { name = "Save As", key = "A", enabled = true, value = menuSaveAs })
- table.insert(fMenu, { name = "-------", enabled = false })
- --table.insert(fMenu, { name = "About", key = "b", enabled = false, value = nil })
- table.insert(fMenu, { name = "Quit", key = "Q", enabled = true, value = function()
- if checkSave() then programRunning = false end end })
- table.insert(eMenu, { name = "Cut", key = 'x', enabled = false })
- table.insert(eMenu, { name = "Copy", key = 'C', enabled = false })
- table.insert(eMenu, { name = "Paste", key = 'v', enabled = false })
- table.insert(eMenu, { name = "---------", enabled = false })
- table.insert(eMenu, { name = "Resize", key = "R", enabled = true, value = function()
- local nw, nh, i = displaySizeSettings("r")
- if nw and nh then resizeCanvas(nw, nh, i) end
- end})
- table.insert(eMenu, { name = "Crop", key = "p", enabled = true, value = cropToSelection })
- table.insert(eMenu, { name = "Show Code", enabled = true, value = function()
- showMagCode = not showMagCode
- eMenu[#eMenu].name = showMagCode and "Hide Code" or "Show Code"
- end
- })
- --A list of the images we use for each tool
- local toolImages = {
- paint = {
- width = 3, height = 2,
- text = { "\128\135\135", "\142\133\128" },
- fg = { "00c", "ff0" },
- bg = { "0c0", "000" }
- },
- magnify = {
- width = 3, height = 2,
- text = { "\151\128\148", "\128\131\146" },
- fg = { "033", "090" },
- bg = { "930", "00f" }
- },
- selectbox = {
- width = 3, height = 2,
- text = { "\136\136\144", "\130\132\132" },
- fg = { "888", "888" },
- bg = { "000", "000" }
- },
- cursor = {
- width = 3, height = 2,
- text = { "\128\143\144", "\128\135\132" },
- fg = { "00f", "0ff" },
- bg = { "0f0", "000" }
- }
- }
- for k,v in pairs(toolImages) do
- local newButton = _imgbutton.new(1, 1, v)
- newButton.name = k
- newButton.on_pressed = function(self)
- if tools[selTool] then tools[selTool]:change_colour("4", "0") end
- local oldSel = tools[selTool]
- drawEnclosingBorder(oldSel.x, oldSel.y, oldSel.img.width, oldSel.img.height, "8", "8")
- selTool = self.name
- self:change_colour("0", "4")
- drawTools()
- end
- tools[k] = newButton
- end
- local tArgs = {...}
- if tArgs[1] then
- filename = tArgs[1]
- sPath = shell.resolve(tArgs[1])
- if fs.exists(sPath) then
- createLoadedImage(sPath)
- else
- createNewImage(10,10)
- end
- else
- createNewImage(10,10)
- end
- run()
- term.setBackgroundColour(colours.black)
- shell.run("clear")
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement