if OneOS then --running under OneOS OneOS.ToolBarColour = colours.grey OneOS.ToolBarTextColour = colours.white end colours.transparent = -1 colors.transparent = -1 --APIS-- --This is my drawing API, is is pretty much identical to what drives OneOS, PearOS, etc. local _w, _h = term.getSize() local round = function(num, idp) local mult = 10^(idp or 0) return math.floor(num * mult + 0.5) / mult end Clipboard = { Content = nil, Type = nil, IsCut = false, Empty = function() Clipboard.Content = nil Clipboard.Type = nil Clipboard.IsCut = false end, isEmpty = function() return Clipboard.Content == nil end, Copy = function(content, _type) Clipboard.Content = content Clipboard.Type = _type or 'generic' Clipboard.IsCut = false end, Cut = function(content, _type) Clipboard.Content = content Clipboard.Type = _type or 'generic' Clipboard.IsCut = true end, Paste = function() local c, t = Clipboard.Content, Clipboard.Type if Clipboard.IsCut then Clipboard.Empty() end return c, t end } if OneOS and OneOS.Clipboard then Clipboard = OneOS.Clipboard end Drawing = { Screen = { Width = _w, Height = _h }, DrawCharacters = function (x, y, characters, textColour,bgColour) Drawing.WriteStringToBuffer(x, y, characters, textColour, bgColour) end, DrawBlankArea = function (x, y, w, h, colour) Drawing.DrawArea (x, y, w, h, " ", 1, colour) end, DrawArea = function (x, y, w, h, character, textColour, bgColour) --width must be greater than 1, other wise we get a stack overflow if w < 0 then w = w * -1 elseif w == 0 then w = 1 end for ix = 1, w do local currX = x + ix - 1 for iy = 1, h do local currY = y + iy - 1 Drawing.WriteToBuffer(currX, currY, character, textColour, bgColour) end end end, DrawImage = function(_x,_y,tImage, w, h) if tImage then for y = 1, h do if not tImage[y] then break end for x = 1, w do if not tImage[y][x] then break end local bgColour = tImage[y][x] local textColour = tImage.textcol[y][x] or colours.white local char = tImage.text[y][x] Drawing.WriteToBuffer(x+_x-1, y+_y-1, char, textColour, bgColour) end end elseif w and h then Drawing.DrawBlankArea(x, y, w, h, colours.green) end end, --using .nft LoadImage = function(path) local image = { text = {}, textcol = {} } local _fs = fs if OneOS then _fs = OneOS.FS end if _fs.exists(path) then local _open = io.open if OneOS then _open = OneOS.IO.open end local file = _open(path, "r") local sLine = file:read() local num = 1 while sLine do table.insert(image, num, {}) table.insert(image.text, num, {}) table.insert(image.textcol, num, {}) --As we're no longer 1-1, we keep track of what index to write to local writeIndex = 1 --Tells us if we've hit a 30 or 31 (BG and FG respectively)- next char specifies the curr colour local bgNext, fgNext = false, false --The current background and foreground colours local currBG, currFG = nil,nil for i=1,#sLine do local nextChar = string.sub(sLine, i, i) if nextChar:byte() == 30 then bgNext = true elseif nextChar:byte() == 31 then fgNext = true elseif bgNext then currBG = Drawing.GetColour(nextChar) bgNext = false elseif fgNext then currFG = Drawing.GetColour(nextChar) fgNext = false else if nextChar ~= " " and currFG == nil then currFG = colours.white end image[num][writeIndex] = currBG image.textcol[num][writeIndex] = currFG image.text[num][writeIndex] = nextChar writeIndex = writeIndex + 1 end end num = num+1 sLine = file:read() end file:close() end return image end, DrawCharactersCenter = function(x, y, w, h, characters, textColour,bgColour) w = w or Drawing.Screen.Width h = h or Drawing.Screen.Height x = x or 0 y = y or 0 x = math.ceil((w - #characters) / 2) + x y = math.floor(h / 2) + y Drawing.DrawCharacters(x, y, characters, textColour, bgColour) end, GetColour = function(hex) if hex == ' ' then return colours.transparent end local value = tonumber(hex, 16) if not value then return nil end value = math.pow(2,value) return value end, Clear = function (_colour) _colour = _colour or colours.black Drawing.ClearBuffer() Drawing.DrawBlankArea(1, 1, Drawing.Screen.Width, Drawing.Screen.Height, _colour) end, Buffer = {}, BackBuffer = {}, DrawBuffer = function() for y,row in pairs(Drawing.Buffer) do for x,pixel in pairs(row) do local shouldDraw = true local hasBackBuffer = true if Drawing.BackBuffer[y] == nil or Drawing.BackBuffer[y][x] == nil or #Drawing.BackBuffer[y][x] ~= 3 then hasBackBuffer = false end if hasBackBuffer and Drawing.BackBuffer[y][x][1] == Drawing.Buffer[y][x][1] and Drawing.BackBuffer[y][x][2] == Drawing.Buffer[y][x][2] and Drawing.BackBuffer[y][x][3] == Drawing.Buffer[y][x][3] then shouldDraw = false end if shouldDraw then term.setBackgroundColour(pixel[3]) term.setTextColour(pixel[2]) term.setCursorPos(x, y) term.write(pixel[1]) end end end Drawing.BackBuffer = Drawing.Buffer Drawing.Buffer = {} term.setCursorPos(1,1) end, ClearBuffer = function() Drawing.Buffer = {} end, WriteStringToBuffer = function (x, y, characters, textColour,bgColour) for i = 1, #characters do local character = characters:sub(i,i) Drawing.WriteToBuffer(x + i - 1, y, character, textColour, bgColour) end end, WriteToBuffer = function(x, y, character, textColour,bgColour) x = round(x) y = round(y) if bgColour == colours.transparent then Drawing.Buffer[y] = Drawing.Buffer[y] or {} Drawing.Buffer[y][x] = Drawing.Buffer[y][x] or {"", colours.white, colours.black} Drawing.Buffer[y][x][1] = character Drawing.Buffer[y][x][2] = textColour else Drawing.Buffer[y] = Drawing.Buffer[y] or {} Drawing.Buffer[y][x] = {character, textColour, bgColour} end end, } --Colour Deffitions-- UIColours = { Toolbar = colours.grey, ToolbarText = colours.lightGrey, ToolbarSelected = colours.lightBlue, ControlText = colours.white, ToolbarItemTitle = colours.black, Background = colours.lightGrey, MenuBackground = colours.white, MenuText = colours.black, MenuSeparatorText = colours.grey, MenuDisabledText = colours.lightGrey, Shadow = colours.grey, TransparentBackgroundOne = colours.white, TransparentBackgroundTwo = colours.lightGrey, MenuBarActive = colours.white } --Lists-- Current = { Artboard = nil, Layer = nil, Tool = nil, ToolSize = 1, Toolbar = nil, Colour = colours.lightBlue, Menu = nil, MenuBar = nil, Window = nil, Input = nil, CursorPos = {1,1}, CursorColour = colours.black, InterfaceVisible = true, Selection = {}, SelectionDrawTimer = nil, HandDragStart = {}, Modified = false, } local isQuitting = false function PrintCentered(text, y) local w, h = term.getSize() x = math.ceil(math.ceil((w / 2) - (#text / 2)), 0)+1 term.setCursorPos(x, y) print(text) end function DoVanillaClose() term.setBackgroundColour(colours.black) term.setTextColour(colours.white) term.clear() term.setCursorPos(1, 1) PrintCentered("Thanks for using Sketch!", (Drawing.Screen.Height/2)-1) term.setTextColour(colours.lightGrey) PrintCentered("Photoshop Inspired Image Editor for ComputerCraft", (Drawing.Screen.Height/2)) term.setTextColour(colours.white) PrintCentered("(c) oeed 2013 - 2014", (Drawing.Screen.Height/2)+3) term.setCursorPos(1, Drawing.Screen.Height) error('', 0) end function Close() if isQuitting or not Current.Artboard or not Current.Modified then if not OneOS then DoVanillaClose() end return true else local _w = ButtonDialougeWindow:Initialise('Quit Sketch?', 'You have unsaved changes, do you want to quit anyway?', 'Quit', 'Cancel', function(window, success) if success then if OneOS then OneOS.Close(true) else DoVanillaClose() end end window:Close() Draw() end):Show() --it's hacky but it works os.queueEvent('mouse_click', 1, _w.X, _w.Y) return false end end if OneOS then OneOS.CanClose = function() return Close() end end Lists = { Artboards = {}, Interface = { Toolbars = {} } } Events = { } --Setters-- function SetColour(colour) Current.Colour = colour Draw() end function SetTool(tool) if tool and tool.Select and tool:Select() then Current.Input = nil Current.Tool = tool return true end return false end function GetAbsolutePosition(object) local obj = object local i = 0 local x = 1 local y = 1 while true do x = x + obj.X - 1 y = y + obj.Y - 1 if not obj.Parent then return {X = x, Y = y} end obj = obj.Parent if i > 32 then return {X = 1, Y = 1} end i = i + 1 end end --Object Defintions-- Pixel = { TextColour = colours.black, BackgroundColour = colours.white, Character = " ", Layer = nil, Draw = function(self, x, y) if self.BackgroundColour ~= colours.transparent or self.Character ~= ' ' then Drawing.WriteToBuffer(self.Layer.Artboard.X + x - 1, self.Layer.Artboard.Y + y - 1, self.Character, self.TextColour, self.BackgroundColour) end end, Initialise = function(self, textColour, backgroundColour, character, layer) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.TextColour = textColour or self.TextColour new.BackgroundColour = backgroundColour or self.BackgroundColour new.Character = character or self.Character new.Layer = layer return new end, Set = function(self, textColour, backgroundColour, character) self.TextColour = textColour or self.TextColour self.BackgroundColour = backgroundColour or self.BackgroundColour self.Character = character or self.Character end } Layer = { Name = "", Pixels = { }, Artboard = nil, BackgroundColour = colours.white, Visible = true, Index = 1, Draw = function(self) if self.Visible then for x = 1, self.Artboard.Width do for y = 1, self.Artboard.Height do self.Pixels[x][y]:Draw(x, y) end end end end, Remove = function(self) for i, v in ipairs(self.Artboard.Layers) do if v == Current.Layer then Current.Artboard.Layers[i] = nil Current.Layer = Current.Artboard.Layers[1] ModuleNamed('Layers'):Update() end end end, Initialise = function(self, name, backgroundColour, artboard, index, pixels) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Name = name new.Pixels = {} new.BackgroundColour = backgroundColour new.Artboard = artboard new.Index = index or #artboard.Layers + 1 if not pixels then new:MakeAllBlankPixels() else new:MakeAllBlankPixels() for x, col in ipairs(pixels) do for y, pixel in ipairs(col) do new:SetPixel(x, y, pixel.TextColour, pixel.BackgroundColour, pixel.Character) end end end return new end, SetPixel = function(self, x, y, textColour, backgroundColour, character) textColour = textColour or Current.Colour backgroundColour = backgroundColour or Current.Colour character = character or " " if x < 1 or y < 1 or x > self.Artboard.Width or y > self.Artboard.Height then return end if self.Pixels[x][y] then self.Pixels[x][y]:Set(textColour, backgroundColour, character) self.Pixels[x][y]:Draw(x,y) end end, MakePixel = function(self, x, y, backgroundColour) backgroundColour = backgroundColour or self.BackgroundColour self.Pixels[x][y] = Pixel:Initialise(nil, backgroundColour, nil, self) end, MakeColumn = function(self, x) self.Pixels[x] = {} end, MakeAllBlankPixels = function(self) for x = 1, self.Artboard.Width do if not self.Pixels[x] then self:MakeColumn(x) end for y = 1, self.Artboard.Height do if not self.Pixels[x][y] then self:MakePixel(x, y) end end end end, PixelsInSelection = function(self, cut) local pixels = {} if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then local point1 = Current.Selection[1] local point2 = Current.Selection[2] local size = point2 - point1 local cornerX = point1.x local cornerY = point1.y for x = 1, size.x + 1 do for y = 1, size.y + 1 do if not pixels[x] then pixels[x] = {} end if not self.Pixels[cornerX + x - 1] or not self.Pixels[cornerX + x - 1][cornerY + y - 1] then break end local pixel = self.Pixels[cornerX + x - 1][cornerY + y - 1] pixels[x][y] = Pixel:Initialise(pixel.TextColour, pixel.BackgroundColour, pixel.Character, Current.Layer) if cut then Current.Layer:SetPixel(cornerX + x - 1, cornerY + y - 1, nil, Current.Layer.BackgroundColour, nil) end end end end return pixels end, EraseSelection = function(self) if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then local point1 = Current.Selection[1] local point2 = Current.Selection[2] local size = point2 - point1 local cornerX = point1.x local cornerY = point1.y for x = 1, size.x + 1 do for y = 1, size.y + 1 do Current.Layer:SetPixel(cornerX + x - 1, cornerY + y - 1, nil, Current.Layer.BackgroundColour, nil) end end end end, InsertPixels = function(self, pixels) local cornerX = Current.Selection[1].x local cornerY = Current.Selection[1].y for x, col in ipairs(pixels) do for y, pixel in ipairs(col) do Current.Layer:SetPixel(cornerX + x - 1, cornerY + y - 1, pixel.TextColour, pixel.BackgroundColour, pixel.Character) end end end } Artboard = { X = 0, Y = 0, Name = "", Path = "", Width = 1, Height = 1, Layers = {}, Format = nil, SelectionIsBlack = true, Draw = function(self) Drawing.DrawBlankArea(self.X + 1, self.Y + 1, self.Width, self.Height, UIColours.Shadow) local odd for x = 1, self.Width do odd = x % 2 if odd == 1 then odd = true else odd = false end for y = 1, self.Height do if odd then Drawing.WriteToBuffer(self.X + x - 1, self.Y + y - 1, ":", UIColours.TransparentBackgroundTwo, UIColours.TransparentBackgroundOne) else Drawing.WriteToBuffer(self.X + x - 1, self.Y + y - 1, ":", UIColours.TransparentBackgroundOne, UIColours.TransparentBackgroundTwo) end odd = not odd end end for i, layer in ipairs(self.Layers) do layer:Draw() end if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then local point1 = Current.Selection[1] local point2 = Current.Selection[2] local size = point2 - point1 local isBlack = self.SelectionIsBlack local function c() local c = colours.white if isBlack then c = colours.black end isBlack = not isBlack return c end function horizontal(y) Drawing.WriteToBuffer(self.X - 1 + point1.x, self.Y - 1 + y, '+', c(), colours.transparent) if size.x > 0 then for i = 1, size.x - 1 do Drawing.WriteToBuffer(self.X - 1 + point1.x + i, self.Y - 1 + y, '-', c(), colours.transparent) end else for i = 1, (-1 * size.x) - 1 do Drawing.WriteToBuffer(self.X - 1 + point1.x - i, self.Y - 1 + y, '-', c(), colours.transparent) end end Drawing.WriteToBuffer(self.X - 1 + point1.x + size.x, self.Y - 1 + y, '+', c(), colours.transparent) end function vertical(x) if size.y < 0 then for i = 1, (-1 * size.y) - 1 do Drawing.WriteToBuffer(self.X - 1 + x, self.Y - 1 + point1.y - i, '|', c(), colours.transparent) end else for i = 1, size.y - 1 do Drawing.WriteToBuffer(self.X - 1 + x, self.Y - 1 + point1.y + i, '|', c(), colours.transparent) end end end horizontal(point1.y) vertical(point1.x) horizontal(point1.y + size.y) vertical(point1.x + size.x) end end, Initialise = function(self, name, path, width, height, format, backgroundColour, layers) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Y = 3 new.X = 2 new.Name = name new.Path = path new.Width = width new.Height = height new.Format = format new.Layers = {} if not layers then new:MakeLayer('Background', backgroundColour) else for i, layer in ipairs(layers) do new:MakeLayer(layer.Name, layer.BackgroundColour, layer.Index, layer.Pixels) new.Layers[i].Visible = layer.Visible end Current.Layer = new.Layers[#new.Layers] end return new end, Resize = function(self, top, bottom, left, right) self.Height = self.Height + top + bottom self.Width = self.Width + left + right for i, layer in ipairs(self.Layers) do if left < 0 then for x = 1, -left do table.remove(layer.Pixels, 1) end end if right < 0 then for x = 1, -right do table.remove(layer.Pixels, #layer.Pixels) end end for x = 1, left do table.insert(layer.Pixels, 1, {}) for y = 1, self.Height do layer:MakePixel(1, y) end end for x = 1, right do table.insert(layer.Pixels, {}) for y = 1, self.Height do layer:MakePixel(#layer.Pixels, y) end end for y = 1, top do for x = 1, self.Width do table.insert(layer.Pixels[x], 1, {}) layer:MakePixel(x, 1) end end for y = 1, bottom do for x = 1, self.Width do table.insert(layer.Pixels[x], {}) layer:MakePixel(x, #layer.Pixels[x]) end end if top < 0 then for y = 1, -top do for x = 1, self.Width do table.remove(layer.Pixels[x], 1) end end end if bottom < 0 then for y = 1, -bottom do for x = 1, self.Width do table.remove(layer.Pixels[x], #layer.Pixels[x]) end end end end end, MakeLayer = function(self, name, backgroundColour, index, pixels) backgroundColour = backgroundColour or colours.white name = name or "Layer" local layer = Layer:Initialise(name, backgroundColour, self, index, pixels) table.insert(self.Layers, layer) Current.Layer = layer ModuleNamed('Layers'):Update() return layer end, New = function(self, name, path, width, height, format, backgroundColour, layers) local new = self:Initialise(name, path, width, height, format, backgroundColour, layers) table.insert(Lists.Artboards, new) Current.Artboard = new --new:Save() return new end, Save = function(self, path) Current.Artboard = self path = path or self.Path local _open = io.open if OneOS then _open = OneOS.IO.open end local file = _open(path, "w", true) if self.Format == '.skch' then file:write(textutils.serialize(SaveSKCH())) else local lines = {} if self.Format == '.nfp' then lines = SaveNFP() elseif self.Format == '.nft' then lines = SaveNFT() end for i, line in ipairs(lines) do file:write(line.."\n") end end file:close() Current.Modified = false end, Click = function(self, side, x, y, drag) if Current.Tool and Current.Layer and Current.Layer.Visible then Current.Tool:Use(x, y, side, drag) Current.Modified = true return true end end } Toolbar = { X = 0, Y = 0, Width = 0, ExpandedWidth = 14, ClosedWidth = 2, Height = 0, Expanded = true, ToolbarItems = {}, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) self:CalculateToolbarItemPositions() --Drawing.DrawArea(self.X - 1, self.Y, 1, self.Height, "|", UIColours.ToolbarText, UIColours.Background) --if not Current.Window then Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.Toolbar) --else -- Drawing.DrawArea(self.X, self.Y, self.Width, self.Height, '|', colours.lightGrey, UIColours.Toolbar) --end for i, toolbarItem in ipairs(self.ToolbarItems) do toolbarItem:Draw() end end, Initialise = function(self, side, expanded) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Expanded = expanded if expanded then new.Width = new.ExpandedWidth else new.Width = new.ClosedWidth end if side == 'right' then new.X = Drawing.Screen.Width - new.Width + 1 end if side == 'right' or side == 'left' then new.Height = Drawing.Screen.Width end new.Y = 1 return new end, AddToolbarItem = function(self, item) table.insert(self.ToolbarItems, item) self:CalculateToolbarItemPositions() end, CalculateToolbarItemPositions = function(self) local currY = 1 for i, toolbarItem in ipairs(self.ToolbarItems) do toolbarItem.Y = currY currY = currY + toolbarItem.Height end end, Update = function(self) for i, toolbarItem in ipairs(self.ToolbarItems) do if toolbarItem.Module.Update then toolbarItem.Module:Update(toolbarItem) end end end, New = function(self, side, expanded) local new = self:Initialise(side, expanded) --new:AddToolbarItem(ToolbarItem:Initialise("Colours", nil, true, new)) --new:AddToolbarItem(ToolbarItem:Initialise("IDK", true, new)) table.insert(Lists.Interface.Toolbars, new) return new end, Click = function(self, side, x, y) return false end } ToolbarItem = { X = 0, Y = 0, Width = 0, Height = 0, ExpandedHeight = 5, Expanded = true, Toolbar = nil, Title = "", MenuIcon = "=", ExpandedIcon = "+", ContractIcon = "-", ContentView = nil, Module = nil, MenuItems = nil, Draw = function(self) Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, UIColours.ToolbarItemTitle) Drawing.DrawCharacters(self.X + 1, self.Y, self.Title, UIColours.ToolbarText, UIColours.ToolbarItemTitle) Drawing.DrawCharacters(self.X + self.Width - 1, self.Y, self.MenuIcon, UIColours.ToolbarText, UIColours.ToolbarItemTitle) local expandContractIcon = self.ContractIcon if not self.Expanded then expandContractIcon = self.ExpandedIcon end if self.Expanded and self.ContentView then self.ContentView:Draw() end Drawing.DrawCharacters(self.X + self.Width - 2, self.Y, expandContractIcon, UIColours.ToolbarText, UIColours.ToolbarItemTitle) end, Initialise = function(self, module, height, expanded, toolbar, menuItems) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Expanded = expanded new.Title = module.Title new.Width = toolbar.Width new.Height = height or 5 new.Module = module new.MenuItems = menuItems or {} table.insert(new.MenuItems, { Title = 'Shrink', Click = function() new:ToggleExpanded() end }) new.ExpandedHeight = height or 5 new.Y = 1 new.X = toolbar.X new.ContentView = ContentView:Initialise(1, 2, new.Width, new.Height - 1, nil, new) new.Toolbar = toolbar return new end, ToggleExpanded = function(self) self.Expanded = not self.Expanded if self.Expanded then self.Height = self.ExpandedHeight else self.Height = 1 end end, Click = function(self, side, x, y) local pos = GetAbsolutePosition(self) if x == self.Width and y == 1 then local expandContract = "Shrink" if not self.Expanded then expandContract = "Expand" end self.MenuItems[#self.MenuItems].Title = expandContract Menu:New(pos.X + x, pos.Y + y, self.MenuItems, self) return true elseif x == self.Width - 1 and y == 1 then self:ToggleExpanded() return true elseif y ~= 1 then return self.ContentView:Click(side, x - self.ContentView.X + 1, y - self.ContentView.Y + 1) end return false end } ContentView = { X = 1, Y = 1, Width = 0, Height = 0, Parent = nil, Views = {}, AbsolutePosition = function(self) return self.Parent:AbsolutePosition() end, Draw = function(self) for i, view in ipairs(self.Views) do view:Draw() end end, Initialise = function(self, x, y, width, height, views, parent) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = width new.Height = height new.Y = y new.X = x new.Views = views or {} new.Parent = parent return new end, Click = function(self, side, x, y) for k, view in pairs(self.Views) do if DoClick(view, side, x, y) then return true end end end } Button = { X = 1, Y = 1, Width = 0, Height = 0, BackgroundColour = colours.lightGrey, TextColour = colours.white, ActiveBackgroundColour = colours.lightGrey, Text = "", Parent = nil, _Click = nil, Toggle = nil, AbsolutePosition = function(self) return self.Parent:AbsolutePosition() end, Draw = function(self) local bg = self.BackgroundColour local tc = self.TextColour if type(bg) == 'function' then bg = bg() end if self.Toggle then tc = UIColours.MenuBarActive bg = self.ActiveBackgroundColour end local pos = GetAbsolutePosition(self) Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, bg) Drawing.DrawCharactersCenter(pos.X, pos.Y, self.Width, self.Height, self.Text, tc, bg) end, Initialise = function(self, x, y, width, height, backgroundColour, parent, click, text, textColour, toggle, activeBackgroundColour) local new = {} -- the new instance setmetatable( new, {__index = self} ) height = height or 1 new.Width = width or #text + 2 new.Height = height new.Y = y new.X = x new.Text = text or "" new.BackgroundColour = backgroundColour or colours.lightGrey new.TextColour = textColour or colours.white new.ActiveBackgroundColour = activeBackgroundColour or colours.lightGrey new.Parent = parent new._Click = click new.Toggle = toggle return new end, Click = function(self, side, x, y) if self._Click then if self:_Click(side, x, y, not self.Toggle) ~= false and self.Toggle ~= nil then self.Toggle = not self.Toggle Draw() end return true else return false end end } TextBox = { X = 1, Y = 1, Width = 0, Height = 0, BackgroundColour = colours.lightGrey, TextColour = colours.black, Parent = nil, TextInput = nil, AbsolutePosition = function(self) return self.Parent:AbsolutePosition() end, Draw = function(self) local pos = GetAbsolutePosition(self) Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, self.BackgroundColour) local text = self.TextInput.Value if #text > (self.Width - 2) then text = text:sub(#text-(self.Width - 3)) if Current.Input == self.TextInput then Current.CursorPos = {pos.X + 1 + self.Width-2, pos.Y} end else if Current.Input == self.TextInput then Current.CursorPos = {pos.X + 1 + self.TextInput.CursorPos, pos.Y} end end Drawing.DrawCharacters(pos.X + 1, pos.Y, text, self.TextColour, self.BackgroundColour) term.setCursorBlink(true) Current.CursorColour = self.TextColour end, Initialise = function(self, x, y, width, height, parent, text, backgroundColour, textColour, done, numerical) local new = {} -- the new instance setmetatable( new, {__index = self} ) height = height or 1 new.Width = width or #text + 2 new.Height = height new.Y = y new.X = x new.TextInput = TextInput:Initialise(text or '', function(key) if done then done(key) end Draw() end, numerical) new.BackgroundColour = backgroundColour or colours.lightGrey new.TextColour = textColour or colours.black new.Parent = parent return new end, Click = function(self, side, x, y) Current.Input = self.TextInput self:Draw() end } TextInput = { Value = "", Change = nil, CursorPos = nil, Numerical = false, Initialise = function(self, value, change, numerical) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Value = value new.Change = change new.CursorPos = #value new.Numerical = numerical return new end, Char = function(self, char) if self.Numerical then char = tostring(tonumber(char)) end if char == 'nil' then return end self.Value = string.sub(self.Value, 1, self.CursorPos ) .. char .. string.sub( self.Value, self.CursorPos + 1 ) self.CursorPos = self.CursorPos + 1 self.Change(key) end, Key = function(self, key) if key == keys.enter then self.Change(key) elseif key == keys.left then -- Left if self.CursorPos > 0 then self.CursorPos = self.CursorPos - 1 self.Change(key) end elseif key == keys.right then -- Right if self.CursorPos < string.len(self.Value) then self.CursorPos = self.CursorPos + 1 self.Change(key) end elseif key == keys.backspace then -- Backspace if self.CursorPos > 0 then self.Value = string.sub( self.Value, 1, self.CursorPos - 1 ) .. string.sub( self.Value, self.CursorPos + 1 ) self.CursorPos = self.CursorPos - 1 end self.Change(key) elseif key == keys.home then -- Home self.CursorPos = 0 self.Change(key) elseif key == keys.delete then if self.CursorPos < string.len(self.Value) then self.Value = string.sub( self.Value, 1, self.CursorPos ) .. string.sub( self.Value, self.CursorPos + 2 ) self.Change(key) end elseif key == keys["end"] then -- End self.CursorPos = string.len(self.Value) self.Change(key) end end } LayerItem = { X = 1, Y = 1, Parent = nil, Layer = nil, Draw = function(self) self.Y = self.Layer.Index local pos = GetAbsolutePosition(self) local tc = colours.lightGrey if Current.Layer == self.Layer then tc = colours.white end Drawing.DrawBlankArea(pos.X, pos.Y, self.Width, self.Height, UIColours.Toolbar) Drawing.DrawCharacters(pos.X + 3, pos.Y, self.Layer.Name, tc, UIColours.Toolbar) if self.Layer.Visible then Drawing.DrawCharacters(pos.X + 1, pos.Y, "@", tc, UIColours.Toolbar) else Drawing.DrawCharacters(pos.X + 1, pos.Y, "X", tc, UIColours.Toolbar) end end, Initialise = function(self, layer, parent) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = parent.Width new.Height = 1 new.Y = 1 new.X = 1 new.Layer = layer new.Parent = parent return new end, Click = function(self, side, x, y) if x == 2 then self.Layer.Visible = not self.Layer.Visible else Current.Layer = self.Layer end return true end } Menu = { X = 0, Y = 0, Width = 0, Height = 0, Owner = nil, Items = {}, RemoveTop = false, Draw = function(self) Drawing.DrawBlankArea(self.X + 1, self.Y + 1, self.Width, self.Height, UIColours.Shadow) if not self.RemoveTop then Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.MenuBackground) for i, item in ipairs(self.Items) do if item.Separator then Drawing.DrawArea(self.X, self.Y + i, self.Width, 1, '-', colours.grey, UIColours.MenuBackground) else local textColour = UIColours.MenuText if (item.Enabled and type(item.Enabled) == 'function' and item.Enabled() == false) or item.Enabled == false then textColour = UIColours.MenuDisabledText end Drawing.DrawCharacters(self.X + 1, self.Y + i, item.Title, textColour, UIColours.MenuBackground) end end else Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.MenuBackground) for i, item in ipairs(self.Items) do if item.Separator then Drawing.DrawArea(self.X, self.Y + i - 1, self.Width, 1, '-', colours.grey, UIColours.MenuBackground) else local textColour = UIColours.MenuText if (item.Enabled and type(item.Enabled) == 'function' and item.Enabled() == false) or item.Enabled == false then textColour = UIColours.MenuDisabledText end Drawing.DrawCharacters(self.X + 1, self.Y + i - 1, item.Title, textColour, UIColours.MenuBackground) Drawing.DrawCharacters(self.X - 1 + self.Width-#item.KeyName, self.Y + i - 1, item.KeyName, textColour, UIColours.MenuBackground) end end end end, NameForKey = function(self, key) if key == keys.leftCtrl then return '^' elseif key == keys.tab then return 'Tab' elseif key == keys.delete then return 'Delete' elseif key == keys.n then return 'N' elseif key == keys.s then return 'S' elseif key == keys.o then return 'O' elseif key == keys.z then return 'Z' elseif key == keys.y then return 'Y' elseif key == keys.c then return 'C' elseif key == keys.x then return 'X' elseif key == keys.v then return 'V' elseif key == keys.r then return 'R' elseif key == keys.l then return 'L' elseif key == keys.t then return 'T' elseif key == keys.h then return 'H' elseif key == keys.e then return 'E' elseif key == keys.p then return 'P' elseif key == keys.f then return 'F' elseif key == keys.m then return 'M' else return '?' end end, Initialise = function(self, x, y, items, owner, removeTop) local new = {} -- the new instance setmetatable( new, {__index = self} ) if not owner then return end local keyNames = {} for i, v in ipairs(items) do items[i].KeyName = '' if v.Keys then for _i, key in ipairs(v.Keys) do items[i].KeyName = items[i].KeyName .. self:NameForKey(key) end end if items[i].KeyName ~= '' then table.insert(keyNames, items[i].KeyName) end end local keysLength = LongestString(keyNames) if keysLength > 0 then keysLength = keysLength + 2 end new.Width = LongestString(items, 'Title') + 2 + keysLength if new.Width < 10 then new.Width = 10 end new.Height = #items + 2 new.RemoveTop = removeTop or false if removeTop then new.Height = new.Height - 1 end if y < 1 then y = 1 end if x < 1 then x = 1 end if y + new.Height > Drawing.Screen.Height + 1 then y = Drawing.Screen.Height - new.Height end if x + new.Width > Drawing.Screen.Width + 1 then x = Drawing.Screen.Width - new.Width end new.Y = y new.X = x new.Items = items new.Owner = owner return new end, New = function(self, x, y, items, owner, removeTop) if Current.Menu and Current.Menu.Owner == owner then Current.Menu = nil return end local new = self:Initialise(x, y, items, owner, removeTop) Current.Menu = new return new end, Click = function(self, side, x, y) local i = y-1 if self.RemoveTop then i = y end if i >= 1 and y < self.Height then if not ((self.Items[i].Enabled and type(self.Items[i].Enabled) == 'function' and self.Items[i].Enabled() == false) or self.Items[i].Enabled == false) and self.Items[i].Click then self.Items[i]:Click() if Current.Menu.Owner and Current.Menu.Owner.Toggle then Current.Menu.Owner.Toggle = false end Current.Menu = nil self = nil end return true end end } MenuBar = { X = 1, Y = 1, Width = Drawing.Screen.Width, Height = 1, MenuBarItems = {}, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) --Drawing.DrawArea(self.X - 1, self.Y, 1, self.Height, "|", UIColours.ToolbarText, UIColours.Background) Drawing.DrawBlankArea(self.X, self.Y, self.Width, self.Height, UIColours.Toolbar) for i, button in ipairs(self.MenuBarItems) do button:Draw() end end, Initialise = function(self, items) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.X = 1 new.Y = 1 new.MenuBarItems = items return new end, AddToolbarItem = function(self, item) table.insert(self.ToolbarItems, item) self:CalculateToolbarItemPositions() end, CalculateToolbarItemPositions = function(self) local currY = 1 for i, toolbarItem in ipairs(self.ToolbarItems) do toolbarItem.Y = currY currY = currY + toolbarItem.Height end end, Click = function(self, side, x, y) for i, item in ipairs(self.MenuBarItems) do if item.X <= x and item.X + item.Width > x then if item:Click(item, side, x - item.X + 1, 1) then break end end end return false end } --Modules-- Modules = { { Title = "Colours", ToolbarItem = nil, Initialise = function(self) self.ToolbarItem = ToolbarItem:Initialise(self, nil, true, Current.Toolbar) local buttons = {} local i = 0 local coloursWidth = 8 local _colours = { colours.brown, colours.yellow, colours.orange, colours.red, colours.green, colours.lime, colours.magenta, colours.pink, colours.purple, colours.blue, colours.cyan, colours.lightBlue, colours.lightGrey, colours.grey, colours.black, colours.white } for k, colour in pairs(_colours) do if type(colour) == 'number' and colour ~= -1 then i = i + 1 local y = math.floor(i/(coloursWidth/2)) local x = (i%(coloursWidth/2)) if x == 0 then x = (coloursWidth/2) y = y -1 end table.insert(buttons, { X = x*2 - 2 + self.ToolbarItem.Width - coloursWidth, Y = y+1, Width = 2, Height = 1, BackgroundColour = colour, Click = function(self, side, x, y) SetColour(self.BackgroundColour) end } ) end end for i, button in ipairs(buttons) do table.insert(self.ToolbarItem.ContentView.Views, Button:Initialise(button.X, button.Y, button.Width, button.Height, button.BackgroundColour, self.ToolbarItem.ContentView, button.Click)) end table.insert(self.ToolbarItem.ContentView.Views, Button:Initialise(1, 1, 4, 3, function()return Current.Colour end, self.ToolbarItem.ContentView, nil)) Current.Toolbar:AddToolbarItem(self.ToolbarItem) end }, { Title = "Tools", ToolbarItem = nil, Update = function(self) for i, view in ipairs(self.ToolbarItem.ContentView.Views) do if (Current.Tool and Current.Tool.Name == view.Text) then view.TextColour = colours.white else view.TextColour = colours.lightGrey end end self.ToolbarItem.ContentView.Views[1].Text = 'Size: '..Current.ToolSize end, Initialise = function(self) self.ToolbarItem = ToolbarItem:Initialise(self, #Tools+2, true, Current.Toolbar, {{ Title = "Change Tool Size", Click = function() DisplayToolSizeWindow() end, }}) table.insert(self.ToolbarItem.ContentView.Views, Button:Initialise(1, 1, self.ToolbarItem.Width, 1, UIColours.Toolbar, self.ToolbarItem.ContentView, DisplayToolSizeWindow, 'Size: '..Current.ToolSize)) local y = 2 for i, tool in ipairs(Tools) do table.insert(self.ToolbarItem.ContentView.Views, Button:Initialise(1, y, self.ToolbarItem.Width, 1, UIColours.Toolbar, self.ToolbarItem.ContentView, function() SetTool(tool) self:Update(self.ToolbarItem) end, tool.Name)) y = y + 1 end self:Update(self.ToolbarItem) Current.Toolbar:AddToolbarItem(self.ToolbarItem) end }, { Title = "Layers", ToolbarItem = nil, Update = function(self) if Current.Artboard then self.ToolbarItem.ContentView.Views = {} for i = 1, #Current.Artboard.Layers do table.insert(self.ToolbarItem.ContentView.Views, LayerItem:Initialise(Current.Artboard.Layers[#Current.Artboard.Layers-i+1], self.ToolbarItem.ContentView)) end end end, Initialise = function(self) self.ToolbarItem = ToolbarItem:Initialise(self, nil, true, Current.Toolbar, {{ Title = "New Layer", Click = function() MakeNewLayer() end, Enabled = function() return CheckOpenArtboard() end }, { Title = 'Delete Layer', Click = function() DeleteLayer() end, Enabled = function() return CheckSelectedLayer() end }, { Title = 'Rename Layer...', Click = function() RenameLayer() end, Enabled = function() return CheckSelectedLayer() end }}) self:Update() Current.Toolbar:AddToolbarItem(self.ToolbarItem) end } } function ModuleNamed(name) for i, v in ipairs(Modules) do if v.Title == name then return v end end end --Tools-- function ToolAffectedPixels(x, y) if not CheckSelectedLayer() then return {} end if Current.ToolSize == 1 then if Current.Layer.Pixels[x] and Current.Layer.Pixels[x][y] then return {{Current.Layer.Pixels[x][y], x, y}} end else local pixels = {} local cornerX = x - math.ceil(Current.ToolSize/2) local cornerY = y - math.ceil(Current.ToolSize/2) for _x = 1, Current.ToolSize do for _y = 1, Current.ToolSize do if Current.Layer.Pixels[cornerX + _x] and Current.Layer.Pixels[cornerX + _x][cornerY + _y] then table.insert(pixels, {Current.Layer.Pixels[cornerX + _x][cornerY + _y], cornerX + _x, cornerY + _y}) end end end return pixels end end local moveStartPoint = {} Tools = { { Name = "Hand", Use = function(self, x, y, side, drag) Current.Input = nil if drag and Current.HandDragStart and Current.HandDragStart[1] and Current.HandDragStart[2] then local deltaX = x - Current.HandDragStart[1] local deltaY = y - Current.HandDragStart[2] Current.Artboard.X = Current.Artboard.X + deltaX Current.Artboard.Y = Current.Artboard.Y + deltaY else Current.HandDragStart = {x, y} end sleep(0) end, Select = function(self) return true end }, { Name = "Pencil", Use = function(self, _x, _y, side, artboard) Current.Input = nil for i, pixel in ipairs(ToolAffectedPixels(_x, _y)) do if side == 1 then pixel[1].BackgroundColour = Current.Colour elseif side == 2 then pixel[1].TextColour = Current.Colour end pixel[1]:Draw(pixel[2], pixel[3]) end end, Select = function(self) return true end }, { Name = "Eraser", Use = function(self, x, y, side) Current.Input = nil Current.Layer:SetPixel(x, y, nil, Current.Layer.BackgroundColour, nil) for i, pixel in ipairs(ToolAffectedPixels(x, y)) do Current.Layer:SetPixel(pixel[2], pixel[3], nil, Current.Layer.BackgroundColour, nil) end end, Select = function(self) return true end }, { Name = "Fill Bucket", Use = function(self, x, y, side) local replaceColour = Current.Layer.Pixels[x][y].BackgroundColour if side == 2 then replaceColour = Current.Layer.Pixels[x][y].TextColour end local nodes = {{X = x, Y = y}} while #nodes > 0 do local node = nodes[1] if Current.Layer.Pixels[node.X] and Current.Layer.Pixels[node.X][node.Y] then local replacing = Current.Layer.Pixels[node.X][node.Y].BackgroundColour if side == 2 then replacing = Current.Layer.Pixels[node.X][node.Y].TextColour end if replacing == replaceColour and replacing ~= Current.Colour then if side == 1 then Current.Layer.Pixels[node.X][node.Y].BackgroundColour = Current.Colour elseif side == 2 then Current.Layer.Pixels[node.X][node.Y].TextColour = Current.Colour end table.insert(nodes, {X = node.X, Y = node.Y + 1}) table.insert(nodes, {X = node.X + 1, Y = node.Y}) if x > 1 then table.insert(nodes, {X = node.X - 1, Y = node.Y}) end if y > 1 then table.insert(nodes, {X = node.X, Y = node.Y - 1}) end end end table.remove(nodes, 1) end Draw() end, Select = function(self) return true end }, { Name = "Select", Use = function(self, x, y, side, drag) Current.Input = nil if not drag then Current.Selection[1] = vector.new(x, y, 0) Current.Selection[2] = nil else Current.Selection[2] = vector.new(x, y, 0) end end, Select = function(self) return true end }, { Name = "Move", Use = function(self, x, y, side, drag) Current.Input = nil if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then if drag and moveStartPoint then local pixels = Current.Layer:PixelsInSelection(true) local size = Current.Selection[1] - Current.Selection[2] Current.Selection[1] = vector.new(x-moveStartPoint[1], y-moveStartPoint[2], 0) Current.Selection[2] = vector.new(x-moveStartPoint[1]-size.x, y-moveStartPoint[2]-size.y, 0) Current.Layer:InsertPixels(pixels) else moveStartPoint = {x-Current.Selection[1].x, y-Current.Selection[1].y} end end end, Select = function(self) return true end }, { Name = "Text", Use = function(self, x, y) Current.Input = TextInput:Initialise('', function(key) if key == keys.delete or key == keys.backspace then if #Current.Input.Value == 0 then if Current.Layer.Pixels[x] and Current.Layer.Pixels[x][y] then Current.Layer.Pixels[x][y]:Set(nil, nil, ' ') local newPos = Current.CursorPos[1] - Current.Artboard.X if newPos < Current.Artboard.X - 1 then newPos = Current.Artboard.X - 1 end Current.Tool:Use(newPos, Current.CursorPos[2] - Current.Artboard.Y + 1) Draw() end return else if Current.Layer.Pixels[x+#Current.Input.Value] and Current.Layer.Pixels[x+#Current.Input.Value][y] then Current.Layer.Pixels[x+#Current.Input.Value][y]:Set(nil, nil, ' ') end end else local i = #Current.Input.Value if Current.Layer.Pixels[x+i-1] then Current.Layer.Pixels[x+i-1][y]:Set(Current.Colour, nil, Current.Input.Value:sub(i,i)) Current.Layer.Pixels[x+i-1][y]:Draw(x+i-1, y) end end local newPos = x+Current.Input.CursorPos if newPos > Current.Artboard.Width then Current.Input.CursorPos = Current.Input.CursorPos - 1 end Current.CursorPos = {x+Current.Input.CursorPos + Current.Artboard.X - 1, y + Current.Artboard.Y - 1} Current.CursorColour = Current.Colour Draw() end) Current.CursorPos = {x + Current.Artboard.X - 1, y + Current.Artboard.Y - 1} Current.CursorColour = Current.Colour end, Select = function(self) if Current.Artboard.Format == '.nfp' then ButtonDialougeWindow:Initialise('NFP does not support text!', 'The format you are using, NFP, does not support text. Use NFT or SKCH to use text.', 'Ok', nil, function(window) window:Close() end):Show() return false else return true end end } } function ToolNamed(name) for i, v in ipairs(Tools) do if v.Name == name then return v end end end --Windows-- NewDocumentWindow = { X = 1, Y = 1, Width = 0, Height = 0, CursorPos = 1, Visible = true, Return = nil, OkButton = nil, Format = '.skch', ImageBackgroundColour = colours.white, NameLabelHighlight = false, SizeLabelHighlight = false, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) if not self.Visible then return end Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) local nameLabelColour = colours.black if self.NameLabelHighlight then nameLabelColour = colours.red end Drawing.DrawCharacters(self.X+1, self.Y+2, "Name", nameLabelColour, colours.white) Drawing.DrawCharacters(self.X+1, self.Y+4, "Type", colours.black, colours.white) local sizeLabelColour = colours.black if self.SizeLabelHighlight then sizeLabelColour = colours.red end Drawing.DrawCharacters(self.X+1, self.Y+6, "Size", sizeLabelColour, colours.white) Drawing.DrawCharacters(self.X+11, self.Y+6, "x", colours.black, colours.white) Drawing.DrawCharacters(self.X+1, self.Y+8, "Background", colours.black, colours.white) self.OkButton:Draw() self.CancelButton:Draw() self.SKCHButton:Draw() self.NFTButton:Draw() self.NFPButton:Draw() self.PathTextBox:Draw() self.WidthTextBox:Draw() self.HeightTextBox:Draw() self.WhiteButton:Draw() self.BlackButton:Draw() self.TransparentButton:Draw() end, Initialise = function(self, returnFunc) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = 32 new.Height = 13 new.Return = returnFunc new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) new.Title = 'New Document' new.Visible = true new.NameLabelHighlight = false new.SizeLabelHighlight = false new.Format = '.skch' new.OkButton = Button:Initialise(new.Width - 4, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) local path = new.PathTextBox.TextInput.Value local ok = true new.NameLabelHighlight = false new.SizeLabelHighlight = false local _fs = fs if OneOS then _fs = OneOS.FS end if path:sub(-1) == '/' or _fs.isDir(path) or #path == 0 then ok = false new.NameLabelHighlight = true end if #new.WidthTextBox.TextInput.Value == 0 or tonumber(new.WidthTextBox.TextInput.Value) <= 0 then ok = false new.SizeLabelHighlight = true end if #new.HeightTextBox.TextInput.Value == 0 or tonumber(new.HeightTextBox.TextInput.Value) <= 0 then ok = false new.SizeLabelHighlight = true end if ok then returnFunc(new, true, path, tonumber(new.WidthTextBox.TextInput.Value), tonumber(new.HeightTextBox.TextInput.Value), new.Format, new.ImageBackgroundColour) else Draw() end end, 'Ok', colours.black) new.CancelButton = Button:Initialise(new.Width - 13, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle)returnFunc(new, false)end, 'Cancel', colours.black) new.SKCHButton = Button:Initialise(7, 5, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) new.NFTButton.Toggle = false new.NFPButton.Toggle = false self.Toggle = false new.Format = '.skch' end, '.skch', colours.black, true, colours.lightBlue) new.NFTButton = Button:Initialise(15, 5, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) new.SKCHButton.Toggle = false new.NFPButton.Toggle = false self.Toggle = false new.Format = '.nft' end, '.nft', colours.black, false, colours.lightBlue) new.NFPButton = Button:Initialise(22, 5, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) new.SKCHButton.Toggle = false new.NFTButton.Toggle = false self.Toggle = false new.Format = '.nfp' end, '.nfp', colours.black, false, colours.lightBlue) local path = '' if OneOS then path = '/Desktop/' end new.PathTextBox = TextBox:Initialise(7, 3, new.Width - 7, 1, new, path, nil, nil, function(key) if key == keys.enter or key == keys.tab then Current.Input = new.WidthTextBox.TextInput end end) new.WidthTextBox = TextBox:Initialise(7, 7, 4, 1, new, tostring(15), nil, nil, function() if key == keys.enter or key == keys.tab then Current.Input = new.HeightTextBox.TextInput end end, true) new.HeightTextBox = TextBox:Initialise(14, 7, 4, 1, new, tostring(10), nil, nil, function() if key == keys.enter or key == keys.tab then Current.Input = new.PathTextBox.TextInput end end, true) Current.Input = new.PathTextBox.TextInput new.WhiteButton = Button:Initialise(2, 10, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) new.TransparentButton.Toggle = false new.BlackButton.Toggle = false self.Toggle = false new.ImageBackgroundColour = colours.white end, 'White', colours.black, true, colours.lightBlue) new.BlackButton = Button:Initialise(10, 10, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) new.TransparentButton.Toggle = false new.WhiteButton.Toggle = false self.Toggle = false new.ImageBackgroundColour = colours.black end, 'Black', colours.black, false, colours.lightBlue) new.TransparentButton = Button:Initialise(18, 10, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) new.WhiteButton.Toggle = false new.BlackButton.Toggle = false self.Toggle = false new.ImageBackgroundColour = colours.transparent end, 'Transparent', colours.black, false, colours.lightBlue) return new end, Show = function(self) Current.Window = self return self end, Close = function(self) Current.Input = nil Current.Window = nil self = nil end, Flash = function(self) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() sleep(0.15) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() end, ButtonClick = function(self, button, x, y) if button.X <= x and button.Y <= y and button.X + button.Width > x and button.Y + button.Height > y then button:Click() end end, Click = function(self, side, x, y) local items = {self.OkButton, self.CancelButton, self.SKCHButton, self.NFTButton, self.NFPButton, self.PathTextBox, self.WidthTextBox, self.HeightTextBox, self.WhiteButton, self.BlackButton, self.TransparentButton} for i, v in ipairs(items) do if CheckClick(v, x, y) then v:Click(side, x, y) end end return true end } local TidyPath = function(path) path = '/'..path local _fs = fs if OneOS then _fs = OneOS.FS end if _fs.isDir(path) then path = path .. '/' end path, n = path:gsub("//", "/") while n > 0 do path, n = path:gsub("//", "/") end return path end local WrapText = function(text, maxWidth) local lines = {''} for word, space in text:gmatch('(%S+)(%s*)') do local temp = lines[#lines] .. word .. space:gsub('\n','') if #temp > maxWidth then table.insert(lines, '') end if space:find('\n') then lines[#lines] = lines[#lines] .. word space = space:gsub('\n', function() table.insert(lines, '') return '' end) else lines[#lines] = lines[#lines] .. word .. space end end return lines end OpenDocumentWindow = { X = 1, Y = 1, Width = 0, Height = 0, CursorPos = 1, Visible = true, Return = nil, OpenButton = nil, PathTextBox = nil, CurrentDirectory = '/', Scroll = 0, MaxScroll = 0, GoUpButton = nil, SelectedFile = '', Files = {}, Typed = false, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) if not self.Visible then return end Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) Drawing.DrawBlankArea(self.X, self.Y, self.Width, 3, colours.lightGrey) Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-6, colours.white) Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) Drawing.DrawBlankArea(self.X, self.Y + self.Height - 5, self.Width, 5, colours.lightGrey) self:DrawFiles() local _fs = fs if OneOS then _fs = OneOS.FS end if (_fs.exists(self.PathTextBox.TextInput.Value)) or (self.SelectedFile and #self.SelectedFile > 0 and _fs.exists(self.CurrentDirectory .. self.SelectedFile)) then self.OpenButton.TextColour = colours.black else self.OpenButton.TextColour = colours.lightGrey end self.PathTextBox:Draw() self.OpenButton:Draw() self.CancelButton:Draw() self.GoUpButton:Draw() end, DrawFiles = function(self) local _fs = fs if OneOS then _fs = OneOS.FS end for i, file in ipairs(self.Files) do if i > self.Scroll and i - self.Scroll <= 11 then if file == self.SelectedFile then Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.white, colours.lightBlue) elseif string.find(file, '%.skch') or string.find(file, '%.nft') or string.find(file, '%.nfp') or _fs.isDir(self.CurrentDirectory .. file) then Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.black, colours.white) else Drawing.DrawCharacters(self.X + 1, self.Y + i - self.Scroll, file, colours.grey, colours.white) end end end self.MaxScroll = #self.Files - 11 if self.MaxScroll < 0 then self.MaxScroll = 0 end end, Initialise = function(self, returnFunc) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = 32 new.Height = 17 new.Return = returnFunc new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) new.Title = 'Open Document' new.Visible = true new.CurrentDirectory = '/' new.SelectedFile = nil if OneOS then new.CurrentDirectory = '/Desktop/' end local _fs = fs if OneOS then _fs = OneOS.FS end new.OpenButton = Button:Initialise(new.Width - 6, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) if _fs.exists(new.PathTextBox.TextInput.Value) and self.TextColour == colours.black and not _fs.isDir(new.PathTextBox.TextInput.Value) then returnFunc(new, true, TidyPath(new.PathTextBox.TextInput.Value)) elseif new.SelectedFile and self.TextColour == colours.black and _fs.isDir(new.CurrentDirectory .. new.SelectedFile) then new:GoToDirectory(new.CurrentDirectory .. new.SelectedFile) elseif new.SelectedFile and self.TextColour == colours.black then returnFunc(new, true, TidyPath(new.CurrentDirectory .. '/' .. new.SelectedFile)) end end, 'Open', colours.black) new.CancelButton = Button:Initialise(new.Width - 15, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) returnFunc(new, false) end, 'Cancel', colours.black) new.GoUpButton = Button:Initialise(2, new.Height - 1, nil, nil, colours.white, new, function(self, side, x, y, toggle) local folderName = _fs.getName(new.CurrentDirectory) local parentDirectory = new.CurrentDirectory:sub(1, #new.CurrentDirectory-#folderName-1) new:GoToDirectory(parentDirectory) end, 'Go Up', colours.black) new.PathTextBox = TextBox:Initialise(2, new.Height - 3, new.Width - 2, 1, new, new.CurrentDirectory, colours.white, colours.black) new:GoToDirectory(new.CurrentDirectory) return new end, Show = function(self) Current.Window = self return self end, Close = function(self) Current.Input = nil Current.Window = nil self = nil end, GoToDirectory = function(self, path) path = TidyPath(path) self.CurrentDirectory = path self.Scroll = 0 self.SelectedFile = nil self.Typed = false self.PathTextBox.TextInput.Value = path local _fs = fs if OneOS then _fs = OneOS.FS end self.Files = _fs.list(self.CurrentDirectory) Draw() end, Flash = function(self) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() sleep(0.15) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() end, Click = function(self, side, x, y) local items = {self.OpenButton, self.CancelButton, self.PathTextBox, self.GoUpButton} local found = false for i, v in ipairs(items) do if CheckClick(v, x, y) then v:Click(side, x, y) found = true end end if not found then if y <= 12 then local _fs = fs if OneOS then _fs = OneOS.FS end self.SelectedFile = _fs.list(self.CurrentDirectory)[y-1] self.PathTextBox.TextInput.Value = TidyPath(self.CurrentDirectory .. '/' .. self.SelectedFile) Draw() end end return true end } ButtonDialougeWindow = { X = 1, Y = 1, Width = 0, Height = 0, CursorPos = 1, Visible = true, CancelButton = nil, OkButton = nil, Lines = {}, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) if not self.Visible then return end Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) for i, text in ipairs(self.Lines) do Drawing.DrawCharacters(self.X + 1, self.Y + 1 + i, text, colours.black, colours.white) end self.OkButton:Draw() if self.CancelButton then self.CancelButton:Draw() end end, Initialise = function(self, title, message, okText, cancelText, returnFunc) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = 28 new.Lines = WrapText(message, new.Width - 2) new.Height = 5 + #new.Lines new.Return = returnFunc new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) new.Title = title new.Visible = true new.Visible = true new.OkButton = Button:Initialise(new.Width - #okText - 2, new.Height - 1, nil, 1, nil, new, function() returnFunc(new, true) end, okText) if cancelText then new.CancelButton = Button:Initialise(new.Width - #okText - 2 - 1 - #cancelText - 2, new.Height - 1, nil, 1, nil, new, function() returnFunc(new, false) end, cancelText) end return new end, Show = function(self) Current.Window = self return self end, Close = function(self) Current.Window = nil self = nil end, Flash = function(self) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() sleep(0.15) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() end, Click = function(self, side, x, y) local items = {self.OkButton, self.CancelButton} local found = false for i, v in ipairs(items) do if CheckClick(v, x, y) then v:Click(side, x, y) found = true end end return true end } TextDialougeWindow = { X = 1, Y = 1, Width = 0, Height = 0, CursorPos = 1, Visible = true, CancelButton = nil, OkButton = nil, Lines = {}, TextInput = nil, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) if not self.Visible then return end Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) for i, text in ipairs(self.Lines) do Drawing.DrawCharacters(self.X + 1, self.Y + 1 + i, text, colours.black, colours.white) end Drawing.DrawBlankArea(self.X + 1, self.Y + self.Height - 4, self.Width - 2, 1, colours.lightGrey) Drawing.DrawCharacters(self.X + 2, self.Y + self.Height - 4, self.TextInput.Value, colours.black, colours.lightGrey) Current.CursorPos = {self.X + 2 + self.TextInput.CursorPos, self.Y + self.Height - 4} Current.CursorColour = colours.black self.OkButton:Draw() if self.CancelButton then self.CancelButton:Draw() end end, Initialise = function(self, title, message, okText, cancelText, returnFunc, numerical) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = 28 new.Lines = WrapText(message, new.Width - 2) new.Height = 7 + #new.Lines new.Return = returnFunc new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) new.Title = title new.Visible = true new.Visible = true new.OkButton = Button:Initialise(new.Width - #okText - 2, new.Height - 1, nil, 1, nil, new, function() if #new.TextInput.Value > 0 then returnFunc(new, true, new.TextInput.Value) end end, okText) if cancelText then new.CancelButton = Button:Initialise(new.Width - #okText - 2 - 1 - #cancelText - 2, new.Height - 1, nil, 1, nil, new, function() returnFunc(new, false) end, cancelText) end new.TextInput = TextInput:Initialise('', function(enter) if enter then new.OkButton:Click() end Draw() end, numerical) Current.Input = new.TextInput return new end, Show = function(self) Current.Window = self return self end, Close = function(self) Current.Window = nil Current.Input = nil self = nil end, Flash = function(self) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() sleep(0.15) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() end, Click = function(self, side, x, y) local items = {self.OkButton, self.CancelButton} local found = false for i, v in ipairs(items) do if CheckClick(v, x, y) then v:Click(side, x, y) found = true end end return true end } ResizeDocumentWindow = { X = 1, Y = 1, Width = 0, Height = 0, CursorPos = 1, Visible = true, Return = nil, OkButton = nil, AnchorPosition = 5, WidthLabelHighlight = false, HeightLabelHighlight = false, AbsolutePosition = function(self) return {X = self.X, Y = self.Y} end, Draw = function(self) if not self.Visible then return end Drawing.DrawBlankArea(self.X + 1, self.Y+1, self.Width, self.Height, colours.grey) Drawing.DrawBlankArea(self.X, self.Y, self.Width, 1, colours.lightGrey) Drawing.DrawBlankArea(self.X, self.Y+1, self.Width, self.Height-1, colours.white) Drawing.DrawCharactersCenter(self.X, self.Y, self.Width, 1, self.Title, colours.black, colours.lightGrey) Drawing.DrawCharacters(self.X+1, self.Y+2, "New Size", colours.lightGrey, colours.white) if (#self.WidthTextBox.TextInput.Value > 0 and tonumber(self.WidthTextBox.TextInput.Value) < Current.Artboard.Width) or (#self.HeightTextBox.TextInput.Value > 0 and tonumber(self.HeightTextBox.TextInput.Value) < Current.Artboard.Height) then Drawing.DrawCharacters(self.X+1, self.Y+8, "Clipping will occur!", colours.red, colours.white) end local widthLabelColour = colours.black if self.WidthLabelHighlight then widthLabelColour = colours.red end local heightLabelColour = colours.black if self.HeightLabelHighlight then heightLabelColour = colours.red end Drawing.DrawCharacters(self.X+1, self.Y+4, "Width", widthLabelColour, colours.white) Drawing.DrawCharacters(self.X+1, self.Y+6, "Height", heightLabelColour, colours.white) Drawing.DrawCharacters(self.X+14, self.Y+2, "Anchor", colours.lightGrey, colours.white) self.WidthTextBox:Draw() self.HeightTextBox:Draw() self.OkButton:Draw() self.Anchor1:Draw() self.Anchor2:Draw() self.Anchor3:Draw() self.Anchor4:Draw() self.Anchor5:Draw() self.Anchor6:Draw() self.Anchor7:Draw() self.Anchor8:Draw() self.Anchor9:Draw() end, Initialise = function(self, returnFunc) local new = {} -- the new instance setmetatable( new, {__index = self} ) new.Width = 27 new.Height = 10 new.Return = returnFunc new.X = math.ceil((Drawing.Screen.Width - new.Width) / 2) new.Y = math.ceil((Drawing.Screen.Height - new.Height) / 2) new.Title = 'Resize Document' new.Visible = true new.WidthTextBox = TextBox:Initialise(9, 5, 4, 1, new, tostring(Current.Artboard.Width), nil, nil, function() new:UpdateAnchorButtons() end, true) new.HeightTextBox = TextBox:Initialise(9, 7, 4, 1, new, tostring(Current.Artboard.Height), nil, nil, function() new:UpdateAnchorButtons() end, true) new.OkButton = Button:Initialise(new.Width - 4, new.Height - 1, nil, nil, colours.lightGrey, new, function(self, side, x, y, toggle) local ok = true new.WidthLabelHighlight = false new.HeightLabelHighlight = false if #new.WidthTextBox.TextInput.Value == 0 or tonumber(new.WidthTextBox.TextInput.Value) <= 0 then ok = false new.WidthLabelHighlight = true end if #new.HeightTextBox.TextInput.Value == 0 or tonumber(new.HeightTextBox.TextInput.Value) <= 0 then ok = false new.HeightLabelHighlight = true end if ok then returnFunc(new, tonumber(new.WidthTextBox.TextInput.Value), tonumber(new.HeightTextBox.TextInput.Value), new.AnchorPosition) else Draw() end end, 'Ok', colours.black) local anchorX = 15 local anchorY = 5 new.Anchor1 = Button:Initialise(anchorX, anchorY, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 1 new:UpdateAnchorButtons() end, ' ', colours.black) new.Anchor2 = Button:Initialise(anchorX+1, anchorY, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 2 new:UpdateAnchorButtons() end, '^', colours.black) new.Anchor3 = Button:Initialise(anchorX+2, anchorY, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 3 new:UpdateAnchorButtons() end, ' ', colours.black) new.Anchor4 = Button:Initialise(anchorX, anchorY+1, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 4 new:UpdateAnchorButtons() end, '<', colours.black) new.Anchor5 = Button:Initialise(anchorX+1, anchorY+1, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 5 new:UpdateAnchorButtons() end, '#', colours.black) new.Anchor6 = Button:Initialise(anchorX+2, anchorY+1, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 6 new:UpdateAnchorButtons() end, '>', colours.black) new.Anchor7 = Button:Initialise(anchorX, anchorY+2, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 7 new:UpdateAnchorButtons() end, ' ', colours.black) new.Anchor8 = Button:Initialise(anchorX+1, anchorY+2, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 8 new:UpdateAnchorButtons() end, 'v', colours.black) new.Anchor9 = Button:Initialise(anchorX+2, anchorY+2, 1, 1, colours.lightGrey, new, function(self, side, x, y, toggle)new.AnchorPosition = 9 new:UpdateAnchorButtons() end, ' ', colours.black) return new end, UpdateAnchorButtons = function(self) local anchor1 = ' ' local anchor2 = ' ' local anchor3 = ' ' local anchor4 = ' ' local anchor5 = ' ' local anchor6 = ' ' local anchor7 = ' ' local anchor8 = ' ' local anchor9 = ' ' self.AnchorPosition = self.AnchorPosition or 5 if self.AnchorPosition == 1 then anchor1 = '#' anchor2 = '>' anchor4 = 'v' elseif self.AnchorPosition == 2 then anchor1 = '<' anchor2 = '#' anchor3 = '>' anchor5 = 'v' elseif self.AnchorPosition == 3 then anchor2 = '<' anchor3 = '#' anchor6 = 'v' elseif self.AnchorPosition == 4 then anchor1 = '^' anchor4 = '#' anchor5 = '>' anchor7 = 'v' elseif self.AnchorPosition == 5 then anchor2 = '^' anchor4 = '<' anchor5 = '#' anchor6 = '>' anchor8 = 'v' elseif self.AnchorPosition == 6 then anchor3 = '^' anchor6 = '#' anchor5 = '<' anchor9 = 'v' elseif self.AnchorPosition == 7 then anchor4 = '^' anchor7 = '#' anchor8 = '>' elseif self.AnchorPosition == 8 then anchor5 = '^' anchor8 = '#' anchor7 = '<' anchor9 = '>' elseif self.AnchorPosition == 9 then anchor6 = '^' anchor9 = '#' anchor8 = '<' end if #self.HeightTextBox.TextInput.Value > 0 and Current.Artboard.Height > tonumber(self.HeightTextBox.TextInput.Value) then local r = function(str) if string.find(str, "%^") then str = str:gsub('%^','v') elseif string.find(str, "v") then str = str:gsub('v','%^') end return str end anchor1 = r(anchor1) anchor2 = r(anchor2) anchor3 = r(anchor3) anchor4 = r(anchor4) anchor5 = r(anchor5) anchor6 = r(anchor6) anchor7 = r(anchor7) anchor8 = r(anchor8) anchor9 = r(anchor9) end if #self.WidthTextBox.TextInput.Value > 0 and Current.Artboard.Width > tonumber(self.WidthTextBox.TextInput.Value) then local r = function(str) if string.find(str, ">") then str = str:gsub('>','<') elseif string.find(str, "<") then str = str:gsub('<','>') end return str end anchor1 = r(anchor1) anchor2 = r(anchor2) anchor3 = r(anchor3) anchor4 = r(anchor4) anchor5 = r(anchor5) anchor6 = r(anchor6) anchor7 = r(anchor7) anchor8 = r(anchor8) anchor9 = r(anchor9) end self.Anchor1.Text = anchor1 self.Anchor2.Text = anchor2 self.Anchor3.Text = anchor3 self.Anchor4.Text = anchor4 self.Anchor5.Text = anchor5 self.Anchor6.Text = anchor6 self.Anchor7.Text = anchor7 self.Anchor8.Text = anchor8 self.Anchor9.Text = anchor9 end, Show = function(self) Current.Window = self return self end, Close = function(self) Current.Input = nil Current.Window = nil self = nil end, Flash = function(self) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() sleep(0.15) self.Visible = false Draw() sleep(0.15) self.Visible = true Draw() end, ButtonClick = function(self, button, x, y) if button.X <= x and button.Y <= y and button.X + button.Width > x and button.Y + button.Height > y then button:Click() end end, Click = function(self, side, x, y) local items = {self.OkButton, self.WidthTextBox, self.HeightTextBox, self.Anchor1, self.Anchor2, self.Anchor3, self.Anchor4, self.Anchor5, self.Anchor6, self.Anchor7, self.Anchor8, self.Anchor9} for i, v in ipairs(items) do if CheckClick(v, x, y) then v:Click(side, x, y) end end return true end } ---------------------- function CheckOpenArtboard() if Current.Artboard then return true else return false end end function CheckSelectedLayer() if Current.Artboard and Current.Layer then return true else return false end end function DisplayNewDocumentWindow() NewDocumentWindow:Initialise(function(self, success, path, width, height, format, backgroundColour) if success then if path:sub(-4) ~= format then path = path .. format end local oldWindow = self Current.Input = nil Current.Window = nil makeDocument = function()oldWindow:Close()NewDocument(path, width, height, format, backgroundColour)end local _fs = fs if OneOS then _fs = OneOS.FS end if _fs.exists(path) then ButtonDialougeWindow:Initialise('File Exists', path..' already exists! Use a different name and try again.', 'Ok', nil, function(window, ok) window:Close() oldWindow:Show() end):Show() elseif format == '.nfp' then Current.Window = nil ButtonDialougeWindow:Initialise('Use NFP?', 'The NFT format does not support text or layers, if you use it you will only be able to use 1 layer and not have any text.', 'Use NFP', 'Cancel', function(window, ok) window:Close() if ok then makeDocument() else oldWindow:Show() end end):Show() elseif format == '.nft' then ButtonDialougeWindow:Initialise('Use NFT?', 'The NFT format does not support layers, if you use it you will only be able to use 1 layer.', 'Use NFT', 'Cancel', function(window, ok) window:Close() if ok then makeDocument() else oldWindow:Show() end end):Show() else makeDocument() end else self:Close() end end):Show() end function NewDocument(path, width, height, format, backgroundColour) local _fs = fs if OneOS then _fs = OneOS.FS end ab = Artboard:New(_fs.getName(path), path, width, height, format, backgroundColour) Current.Tool = Tools[2] Current.Toolbar:Update() Current.Modified = false Draw() end function DisplayToolSizeWindow() if not CheckOpenArtboard() then return end TextDialougeWindow:Initialise('Change Tool Size', 'Enter the new tool size you\'d like to use.', 'Ok', 'Cancel', function(window, success, value) if success then Current.ToolSize = math.ceil(tonumber(value)) if Current.ToolSize < 1 then Current.ToolSize = 1 elseif Current.ToolSize > 50 then Current.ToolSize = 50 end ModuleNamed('Tools'):Update() end window:Close() end, true):Show() end --[[ Attempt to figure out what format the image is if it doesn't have an extension ]]-- function GetFormat(path) local _fs = fs if OneOS then _fs = OneOS.FS end local file = _fs.open(path, 'r') local content = file.readAll() file.close() if type(textutils.unserialize(content)) == 'table' then -- It's a serlized table, asume sketch return '.skch' elseif string.find(content, string.char(30)) or string.find(content, string.char(31)) then -- Contains the characters that set colours, asume nft return '.nft' else -- Otherwise asume nfp return '.nfp' end end function DisplayOpenDocumentWindow() OpenDocumentWindow:Initialise(function(self, success, path) self:Close() if success then OpenDocument(path) end end):Show() end local function Extension(path, addDot) if not path then return nil elseif not string.find(fs.getName(path), '%.') then if not addDot then return fs.getName(path) else return '' end else local _path = path if path:sub(#path) == '/' then _path = path:sub(1,#path-1) end local extension = _path:gmatch('\.[0-9a-z]+$')() if extension then extension = extension:sub(2) else --extension = nil return '' end if addDot then extension = '.'..extension end return extension:lower() end end local RemoveExtension = function(path) if path:sub(1,1) == '.' then return path end local extension = Extension(path) if extension == path then return fs.getName(path) end return string.gsub(path, extension, ''):sub(1, -2) end --[[ Open a documet at a given path ]]-- function OpenDocument(path) local _fs = fs if OneOS then _fs = OneOS.FS end if _fs.exists(path) and not _fs.isDir(path) then local format = Extension(path, true) if (not format or format == '') and (format ~= '.nfp' and format ~= '.nft' and format ~= '.skch') then format = GetFormat(path) end local layers = {} if format == '.nfp' then layers = ReadNFP(path) elseif format == '.nft' then layers = ReadNFT(path) elseif format == '.skch' then layers = ReadSKCH(path) end for i, layer in ipairs(layers) do if layer.Visible == nil then layer.Visible = true end if layer.Index == nil then layer.Index = 1 end if layer.Name == nil then if layer.Index == 1 then layer.Name = 'Background' else layer.Name = 'Layer' end end if layer.BackgroundColour == nil then layer.BackgroundColour = colours.white end end if not layers[1] then --log('File could not be read.') return end local width = #layers[1].Pixels local height = #layers[1].Pixels[1] Current.Artboard = nil local _fs = fs if OneOS then _fs = OneOS.FS end ab = Artboard:New(_fs.getName('Image'), path, width, height, format, nil, layers) Current.Tool = Tools[2] Current.Toolbar:Update() Current.Modified = false Draw() end end function MakeNewLayer() if not CheckOpenArtboard() then return end if Current.Artboard.Format == '.skch' then TextDialougeWindow:Initialise('New Layer Name', 'Enter the name you want for the next layer.', 'Ok', 'Cancel', function(window, success, value) if success then Current.Artboard:MakeLayer(value, colours.transparent) end window:Close() end):Show() else local format = 'NFP' if Current.Artboard.Format == '.nft' then format = 'NFT' end ButtonDialougeWindow:Initialise(format..' does not support layers!', 'The format you are using, '..format..', does not support multiple layers. Use SKCH to have more than one layer.', 'Ok', nil, function(window) window:Close() end):Show() end end function ResizeDocument() if not CheckOpenArtboard() then return end ResizeDocumentWindow:Initialise(function(window, width, height, anchor) window:Close() local topResize = 0 local rightResize = 0 local bottomResize = 0 local leftResize = 0 if anchor == 1 then rightResize = 1 bottomResize = 1 elseif anchor == 2 then rightResize = 0.5 leftResize = 0.5 bottomResize = 1 elseif anchor == 3 then leftResize = 1 bottomResize = 1 elseif anchor == 4 then rightResize = 1 bottomResize = 0.5 topResize = 0.5 elseif anchor == 5 then rightResize = 0.5 leftResize = 0.5 bottomResize = 0.5 topResize = 0.5 elseif anchor == 6 then leftResize = 1 bottomResize = 0.5 topResize = 0.5 elseif anchor == 7 then rightResize = 1 topResize = 1 elseif anchor == 8 then rightResize = 0.5 leftResize = 0.5 topResize = 1 elseif anchor == 9 then leftResize = 1 topResize = 1 end topResize = topResize * (height - Current.Artboard.Height) if topResize > 0 then topResize = math.floor(topResize) else topResize = math.ceil(topResize) end bottomResize = bottomResize * (height - Current.Artboard.Height) if bottomResize > 0 then bottomResize = math.ceil(bottomResize) else bottomResize = math.floor(bottomResize) end leftResize = leftResize * (width - Current.Artboard.Width) if leftResize > 0 then leftResize = math.floor(leftResize) else leftResize = math.ceil(leftResize) end rightResize = rightResize * (width - Current.Artboard.Width) if rightResize > 0 then rightResize = math.ceil(rightResize) else rightResize = math.floor(rightResize) end Current.Artboard:Resize(topResize, bottomResize, leftResize, rightResize) end):Show() end function RenameLayer() if not CheckOpenArtboard() then return end if Current.Artboard.Format == '.skch' then TextDialougeWindow:Initialise("Rename Layer '"..Current.Layer.Name.."'", 'Enter the new name you want the layer to be called.', 'Ok', 'Cancel', function(window, success, value) if success then Current.Layer.Name = value end window:Close() end):Show() else local format = 'NFP' if Current.Artboard.Format == '.nft' then format = 'NFT' end ButtonDialougeWindow:Initialise(format..' does not support layers!', 'The format you are using, '..format..', does not support renaming layers. Use SKCH to rename layers.', 'Ok', nil, function(window) window:Close() end):Show() end end function DeleteLayer() if not CheckOpenArtboard() then return end if Current.Artboard.Format == '.skch' then if #Current.Artboard.Layers > 1 then ButtonDialougeWindow:Initialise("Delete Layer '"..Current.Layer.Name.."'?", 'Are you sure you want delete the layer?', 'Ok', 'Cancel', function(window, success) if success then Current.Layer:Remove() end window:Close() end):Show() else ButtonDialougeWindow:Initialise('Can not delete layer!', 'You can not delete the last layer of an image! Make another layer to delete this one.', 'Ok', nil, function(window) window:Close() end):Show() end else local format = 'NFP' if Current.Artboard.Format == '.nft' then format = 'NFT' end ButtonDialougeWindow:Initialise(format..' does not support layers!', 'The format you are using, '..format..', does not support deleting layers. Use SKCH to deleting layers.', 'Ok', nil, function(window) window:Close() end):Show() end end needsDraw = false isDrawing = false function Draw() if isDrawing then needsDraw = true return end needsDraw = false isDrawing = true if not Current.Window then Drawing.Clear(UIColours.Background) else Drawing.DrawArea(1, 2, Drawing.Screen.Width, Drawing.Screen.Height, '|', colours.black, colours.lightGrey) end if Current.Artboard then ab:Draw() end if Current.InterfaceVisible then Current.MenuBar:Draw() Current.Toolbar.Width = Current.Toolbar.ExpandedWidth Current.Toolbar:Draw() else Current.Toolbar.Width = Current.Toolbar.ExpandedWidth end if Current.InterfaceVisible and Current.Menu then Current.Menu:Draw() end if Current.Window then Current.Window:Draw() end if not Current.InterfaceVisible then ShowInterfaceButton:Draw() end Drawing.DrawBuffer() if Current.Input and not Current.Menu then term.setCursorPos(Current.CursorPos[1], Current.CursorPos[2]) term.setCursorBlink(true) term.setTextColour(Current.CursorColour) else term.setCursorBlink(false) end if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then Current.SelectionDrawTimer = os.startTimer(0.5) end isDrawing = false if needsDraw then Draw() end end function LoadMenuBar() Current.MenuBar = MenuBar:Initialise({ Button:Initialise(1, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) if toggle then Menu:New(1, 2, { { Title = "New...", Click = function() DisplayNewDocumentWindow() end, Keys = { keys.leftCtrl, keys.n } }, { Title = 'Open...', Click = function() DisplayOpenDocumentWindow() end, Keys = { keys.leftCtrl, keys.o } }, { Separator = true }, { Title = 'Save...', Click = function() Current.Artboard:Save() end, Keys = { keys.leftCtrl, keys.s }, Enabled = function() return CheckOpenArtboard() end }, { Separator = true }, { Title = 'Quit', Click = function() if Close() then OneOS.Close() end end }, --[[ { Title = 'Save As...', Click = function() end } ]]-- }, self, true) else Current.Menu = nil end return true end, 'File', colours.lightGrey, false), Button:Initialise(7, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) if not self.Toggle then Menu:New(7, 2, { --[[ { Title = "Undo", Click = function() end, Keys = { keys.leftCtrl, keys.z }, Enabled = function() return false end }, { Title = 'Redo', Click = function() end, Keys = { keys.leftCtrl, keys.y }, Enabled = function() return false end }, { Separator = true }, ]]-- { Title = 'Cut', Click = function() Clipboard.Cut(Current.Layer:PixelsInSelection(true), 'sketchpixels') end, Keys = { keys.leftCtrl, keys.x }, Enabled = function() return Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil end }, { Title = 'Copy', Click = function() Clipboard.Copy(Current.Layer:PixelsInSelection(), 'sketchpixels') end, Keys = { keys.leftCtrl, keys.c }, Enabled = function() return Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil end }, { Title = 'Paste', Click = function() Current.Layer:InsertPixels(Clipboard.Paste()) end, Keys = { keys.leftCtrl, keys.v }, Enabled = function() return (not Clipboard.isEmpty()) and Clipboard.Type == 'sketchpixels' end } }, self, true) else Current.Menu = nil end return true end, 'Edit', colours.lightGrey, false), Button:Initialise(13, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) if toggle then Menu:New(13, 2, { { Title = "Resize...", Click = function() ResizeDocument() end, Keys = { keys.leftCtrl, keys.r }, Enabled = function() return CheckOpenArtboard() end }, { Title = "Crop", Click = function() local top = 0 local left = 0 local bottom = 0 local right = 0 if Current.Selection[1].x < Current.Selection[2].x then left = Current.Selection[1].x - 1 right = Current.Artboard.Width - Current.Selection[2].x else left = Current.Selection[2].x - 1 right = Current.Artboard.Width - Current.Selection[1].x end if Current.Selection[1].y < Current.Selection[2].y then top = Current.Selection[1].y - 1 bottom = Current.Artboard.Height - Current.Selection[2].y else top = Current.Selection[2].y - 1 bottom = Current.Artboard.Height - Current.Selection[1].y end Current.Artboard:Resize(-1*top, -1*bottom, -1*left, -1*right) Current.Selection[2] = nil end, Enabled = function() if CheckSelectedLayer() and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then return true else return false end end }, { Separator = true }, { Title = 'New Layer...', Click = function() MakeNewLayer() end, Keys = { keys.leftCtrl, keys.l }, Enabled = function() return CheckOpenArtboard() end }, { Title = 'Delete Layer', Click = function() DeleteLayer() end, Enabled = function() return CheckSelectedLayer() end }, { Title = 'Rename Layer...', Click = function() RenameLayer() end, Enabled = function() return CheckSelectedLayer() end }, { Separator = true }, { Title = 'Erase Selection', Click = function() Current.Layer:EraseSelection() end, Keys = { keys.delete }, Enabled = function() if CheckSelectedLayer() and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then return true else return false end end }, { Separator = true }, { Title = 'Hide Interface', Click = function() Current.InterfaceVisible = not Current.InterfaceVisible end, Keys = { keys.tab } } }, self, true) else Current.Menu = nil end return true end, 'Image', colours.lightGrey, false), Button:Initialise(20, 1, nil, nil, colours.grey, Current.MenuBar, function(self, side, x, y, toggle) if toggle then local menuItems = {{ Title = "Change Size", Click = function() DisplayToolSizeWindow() end, Keys = { keys.leftCtrl, keys.t } }, { Separator = true } } local _keys = {'h','p','e','f','s','m','t'} for i, tool in ipairs(Tools) do table.insert(menuItems, { Title = tool.Name, Click = function() SetTool(tool) local m = ModuleNamed('Tools') m:Update(m.ToolbarItem) end, Keys = { keys[_keys[i]] }, Enabled = function() return CheckOpenArtboard() end }) end Menu:New(20, 2, menuItems, self, true) else Current.Menu = nil end return true end, 'Tools', colours.lightGrey, false), }) end function Timer(event, timer) if timer == Current.ControlPressedTimer then Current.ControlPressedTimer = nil elseif timer == Current.SelectionDrawTimer then if Current.Artboard then Current.Artboard.SelectionIsBlack = not Current.Artboard.SelectionIsBlack Draw() end end end function Initialise(arg) if not OneOS then SplashScreen() end EventRegister('mouse_click', TryClick) EventRegister('mouse_drag', function(event, side, x, y)TryClick(event, side, x, y, true)end) EventRegister('mouse_scroll', Scroll) EventRegister('key', HandleKey) EventRegister('char', HandleKey) EventRegister('timer', Timer) EventRegister('terminate', function(event) if Close() then error( "Terminated", 0 ) end end) Current.Toolbar = Toolbar:New('right', true) for k, v in pairs(Modules) do v:Initialise() end --term.setBackgroundColour(UIColours.Background) --term.clear() LoadMenuBar() local _fs = fs if OneOS then _fs = OneOS.FS end if arg and _fs.exists(arg) then OpenDocument(arg) else DisplayNewDocumentWindow() Current.Window.Visible = false end ShowInterfaceButton = Button:Initialise(Drawing.Screen.Width - 15, 1, nil, 1, colours.grey, nil, function(self) Current.InterfaceVisible = true Draw() end, 'Show Interface') Draw() if Current.Window then Current.Window.Visible = true Draw() end EventHandler() end function SplashScreen() local splashIcon = {{1,1,1,256,256,256,256,256,256,256,256,1,1,1,},{1,256,256,8,8,8,8,8,8,8,8,256,256,1,},{256,8,8,8,8,8,8,8,8,8,8,8,8,256,},{256,256,256,8,8,8,8,8,8,8,8,256,256,256,},{256,256,256,256,256,256,256,256,256,256,256,256,256,256,},{2048,2048,256,256,256,256,256,256,256,256,256,256,2048,2048,},{2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,},{2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,},{2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,},{2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,},{256,256,2048,2048,2048,2048,2048,2048,2048,2048,2048,2048,256,256,},{1,256,256,256,256,256,256,256,256,256,256,256,256,1,},{1,1,1,256,256,256,256,256,256,256,256,1,1,1,},["text"]={{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," ","S","k","e","t","c","h"," "," "," "," ",},{" "," "," "," "," "," ","b","y"," "," "," "," "," "," ",},{" "," "," "," "," ","o","e","e","d"," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},{" "," "," "," "," "," "," "," "," "," "," "," "," "," ",},},["textcol"]={{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,256,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,1,1,1,1,1,1,32768,32768,32768,32768,},{32768,32768,32768,32768,8,8,8,8,8,8,8,32768,32768,32768,},{32768,32768,32768,32768,1,1,1,1,1,32768,8,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},{32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,32768,},},} Drawing.Clear(colours.white) Drawing.DrawImage((Drawing.Screen.Width - 14)/2, (Drawing.Screen.Height - 13)/2, splashIcon, 14, 13) Drawing.DrawBuffer() parallel.waitForAny(function()sleep(1)end, function()os.pullEvent('mouse_click')end) end LongestString = function(input, key) local length = 0 for i = 1, #input do local value = input[i] if key then if value[key] then value = value[key] else value = '' end end local titleLength = string.len(value) if titleLength > length then length = titleLength end end return length end function HandleKey(...) local args = {...} local event = args[1] local keychar = args[2] if event == 'key' and Current.Tool and Current.Tool.Name == 'Text' and Current.Input and (keychar == keys.up or keychar == keys.down or keychar == keys.left or keychar == keys.right) then local currentPos = {Current.CursorPos[1] - Current.Artboard.X + 1, Current.CursorPos[2] - Current.Artboard.Y + 1} if keychar == keys.up then currentPos[2] = currentPos[2] - 1 elseif keychar == keys.down then currentPos[2] = currentPos[2] + 1 elseif keychar == keys.left then currentPos[1] = currentPos[1] - 1 elseif keychar == keys.right then currentPos[1] = currentPos[1] + 1 end if currentPos[1] < 1 then currentPos[1] = 1 end if currentPos[1] > Current.Artboard.Width then currentPos[1] = Current.Artboard.Width end if currentPos[2] < 1 then currentPos[2] = 1 end if currentPos[2] > Current.Artboard.Height then currentPos[2] = Current.Artboard.Height end Current.Tool:Use(currentPos[1], currentPos[2]) Current.Modified = true Draw() elseif Current.Input then if event == 'char' then Current.Input:Char(keychar) elseif event == 'key' then Current.Input:Key(keychar) end elseif event == 'key' then CheckKeyboardShortcut(keychar) end end function Scroll(event, direction, x, y) if Current.Window and Current.Window.OpenButton then Current.Window.Scroll = Current.Window.Scroll + direction if Current.Window.Scroll < 0 then Current.Window.Scroll = 0 elseif Current.Window.Scroll > Current.Window.MaxScroll then Current.Window.Scroll = Current.Window.MaxScroll end end Draw() end function CheckKeyboardShortcut(key) local shortcuts = {} if key == keys.leftCtrl then Current.ControlPressedTimer = os.startTimer(0.5) return end if Current.ControlPressedTimer then shortcuts[keys.n] = function() DisplayNewDocumentWindow() end shortcuts[keys.o] = function() DisplayOpenDocumentWindow() end shortcuts[keys.s] = function() Current.Artboard:Save() end shortcuts[keys.x] = function() if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then Clipboard.Cut(Current.Layer:PixelsInSelection(true), 'sketchpixels') end end shortcuts[keys.c] = function() if Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then Clipboard.Copy(Current.Layer:PixelsInSelection(), 'sketchpixels') end end shortcuts[keys.v] = function() if (not Clipboard.isEmpty()) and Clipboard.Type == 'sketchpixels' then Current.Layer:InsertPixels(Clipboard.Paste()) end end shortcuts[keys.r] = function() ResizeDocument() end shortcuts[keys.l] = function() MakeNewLayer() end end shortcuts[keys.delete] = function() if CheckSelectedLayer() and Current.Selection and Current.Selection[1] and Current.Selection[2] ~= nil then Current.Layer:EraseSelection() Draw() end end shortcuts[keys.backspace] = shortcuts[keys.delete] shortcuts[keys.tab] = function() Current.InterfaceVisible = not Current.InterfaceVisible Draw() end shortcuts[keys.h] = function() SetTool(ToolNamed('Hand')) ModuleNamed('Tools'):Update() Draw() end shortcuts[keys.e] = function() SetTool(ToolNamed('Eraser')) ModuleNamed('Tools'):Update() Draw() end shortcuts[keys.p] = function() SetTool(ToolNamed('Pencil')) ModuleNamed('Tools'):Update() Draw() end shortcuts[keys.f] = function() SetTool(ToolNamed('Fill Bucket')) ModuleNamed('Tools'):Update() Draw() end shortcuts[keys.m] = function() SetTool(ToolNamed('Move')) ModuleNamed('Tools'):Update() Draw() end shortcuts[keys.s] = function() SetTool(ToolNamed('Select')) ModuleNamed('Tools'):Update() Draw() end shortcuts[keys.t] = function() SetTool(ToolNamed('Text')) ModuleNamed('Tools'):Update() Draw() end if shortcuts[key] then shortcuts[key]() return true else return false end end --[[ Check if the given object falls under the click coordinates ]]-- function CheckClick(object, x, y) if object.X <= x and object.Y <= y and object.X + object.Width > x and object.Y + object.Height > y then return true end end --[[ Attempt to clicka given object ]]-- function DoClick(object, side, x, y, drag) if object and CheckClick(object, x, y) then return object:Click(side, x - object.X + 1, y - object.Y + 1, drag) end end --[[ Try to click at the given coordinates ]]-- function TryClick(event, side, x, y, drag) if Current.InterfaceVisible and Current.Menu then if DoClick(Current.Menu, side, x, y, drag) then Draw() return else if Current.Menu.Owner and Current.Menu.Owner.Toggle then Current.Menu.Owner.Toggle = false end Current.Menu = nil Draw() return end elseif Current.Window then if DoClick(Current.Window, side, x, y, drag) then Draw() return else Current.Window:Flash() return end end local interfaceElements = {} if Current.InterfaceVisible then table.insert(interfaceElements, Current.MenuBar) else table.insert(interfaceElements, ShowInterfaceButton) end for i, v in ipairs(Lists.Interface.Toolbars) do for i, v2 in ipairs(v.ToolbarItems) do table.insert(interfaceElements, v2) end table.insert(interfaceElements, v) end table.insert(interfaceElements, Current.Artboard) for i, object in ipairs(interfaceElements) do if DoClick(object, side, x, y, drag) then Draw() return end end Draw() end --[[ Registers functions to run on certain events ]]-- function EventRegister(event, func) if not Events[event] then Events[event] = {} end table.insert(Events[event], func) end --[[ The main loop event handler, runs registered event functinos ]]-- function EventHandler() while true do local event, arg1, arg2, arg3, arg4 = os.pullEventRaw() if Events[event] then for i, e in ipairs(Events[event]) do e(event, arg1, arg2, arg3, arg4) end end end end --[[ Thanks to NitrogenFingers for the colour functions and NFT + NFP read/write functions ]]-- --[[ Gets the hex value from a colour ]]-- local hexnums = { [10] = "a", [11] = "b", [12] = "c", [13] = "d", [14] = "e" , [15] = "f" } local function getHexOf(colour) if colour == colours.transparent or not colour or not tonumber(colour) then return " " end local value = math.log(colour)/math.log(2) if value > 9 then value = hexnums[value] end return value end --[[ Gets the colour from a hex value ]]-- local function getColourOf(hex) if hex == ' ' then return colours.transparent end local value = tonumber(hex, 16) if not value then return nil end value = math.pow(2,value) return value end --[[ Saves the current artboard in .skch format ]]-- function SaveSKCH() local layers = {} for i, l in ipairs(Current.Artboard.Layers) do local pixels = SaveNFT(i) local layer = { Name = l.Name, Pixels = pixels, BackgroundColour = l.BackgroundColour, Visible = l.Visible, Index = l.Index, } table.insert(layers, layer) end return layers end --[[ Saves the current artboard in .nft format ]]-- function SaveNFT(layer) layer = layer or 1 local lines = {} local width = Current.Artboard.Width local height = Current.Artboard.Height for y = 1, height do local line = '' local currentBackgroundColour = nil local currentTextColour = nil for x = 1, width do local pixel = Current.Artboard.Layers[layer].Pixels[x][y] if pixel.BackgroundColour ~= currentBackgroundColour then line = line..string.char(30)..getHexOf(pixel.BackgroundColour) currentBackgroundColour = pixel.BackgroundColour end if pixel.TextColour ~= currentTextColour then line = line..string.char(31)..getHexOf(pixel.TextColour) currentTextColour = pixel.TextColour end line = line .. pixel.Character end table.insert(lines, line) end return lines end --[[ Saves the current artboard in .nfp format ]]-- function SaveNFP() local lines = {} local width = Current.Artboard.Width local height = Current.Artboard.Height for y = 1, height do local line = '' for x = 1, width do line = line .. getHexOf(Current.Artboard.Layers[1].Pixels[x][y].BackgroundColour) end table.insert(lines, line) end return lines end --[[ Reads a .nfp file from the given path ]]-- function ReadNFP(path) local pixels = {} local _fs = fs if OneOS then _fs = OneOS.FS end local file = _fs.open(path, 'r') local line = file.readLine() local y = 1 while line do for x = 1, #line do if not pixels[x] then pixels[x] = {} end pixels[x][y] = {BackgroundColour = getColourOf(line:sub(x,x))} end y = y + 1 line = file.readLine() end file.close() return {{Pixels = pixels}} end --[[ Reads a .nft file from the given path ]]-- function ReadNFT(path) local _fs = fs if OneOS then _fs = OneOS.FS end local file = _fs.open(path, 'r') local line = file.readLine() local lines = {} while line do table.insert(lines, line) line = file.readLine() end file.close() return {{Pixels = ParseNFT(lines)}} end --[[ Converts the lines of an .nft document to readble pixel data ]]-- function ParseNFT(lines) local pixels = {} for y, line in ipairs(lines) do local bgNext, fgNext = false, false local currBG, currFG = nil,nil local writePosition = 1 for x = 1, #line do if not pixels[writePosition] then pixels[writePosition] = {} end local nextChar = string.sub(line, x, x) if nextChar:byte() == 30 then bgNext = true elseif nextChar:byte() == 31 then fgNext = true elseif bgNext then currBG = getColourOf(nextChar) if currBG == nil then currBG = colours.transparent end bgNext = false elseif fgNext then currFG = getColourOf(nextChar) fgNext = false else if nextChar ~= " " and currFG == nil then currFG = colours.white end pixels[writePosition][y] = {BackgroundColour = currBG, TextColour = currFG, Character = nextChar} writePosition = writePosition + 1 end end end return pixels end --[[ Read a .skch file from the given path ]]-- function ReadSKCH(path) local _fs = fs if OneOS then _fs = OneOS.FS end local file = _fs.open(path, 'r') local _layers = textutils.unserialize(file.readAll()) file.close() local layers = {} for i, l in ipairs(_layers) do local layer = { Name = l.Name, Pixels = ParseNFT(l.Pixels), BackgroundColour = l.BackgroundColour, Visible = l.Visible, Index = l.Index, } table.insert(layers, layer) end return layers end --[[ Start the program after all functions and tables are loaded ]]-- if term.isColor and term.isColor() then Initialise(...) else print('Sorry, but Sketch only works on Advanced (gold) Computers') end