Last active
August 29, 2015 14:16
-
-
Save skyem123/529593c86a36f1d38a83 to your computer and use it in GitHub Desktop.
skyem's GUI and Integrated Circuits editor for OpenComputers for Minecraft
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- GUILIB INDEV by Skye Macdonald | |
-- CC0 Licence | |
-- For debug purposes | |
local serialization = require("serialization") | |
local component = require("component") | |
local colour = require("colors") | |
local gpu = component.gpu | |
local unicode = require("unicode") | |
local event = require("event") | |
local guiLib = {math = { ceil = math.ceil, floor = math.floor, max = math.max, min = math.min}} | |
function guiLib.math.round(num, idp) | |
local mult = 10^(idp or 0) | |
return guiLib.math.floor(num * mult + 0.5) / mult | |
end | |
guiLib.colour = { | |
white = 0xFFFFFF, | |
orange = 0xFFA500, | |
magenta = 0xFF00FF, | |
lightblue = 0xADD8E6, | |
yellow = 0xFFFF00, | |
lime = 0x00FF00, | |
pink = 0xFFC0CB, | |
gray = 0x808080, | |
silver = 0xC0C0C0, | |
cyan = 0x00FFFF, | |
purple = 0x800080, | |
blue = 0x0000FF, | |
brown = 0xA52A2A, | |
green = 0x008000, | |
red = 0xFF0000, | |
black = 0x000000, | |
} | |
do | |
local keys = {} | |
for k in pairs(guiLib.colour) do | |
table.insert(keys, k) | |
end | |
for _, k in pairs(keys) do | |
guiLib.colour[guiLib.colour[k]] = k | |
end | |
end | |
guiLib.colour["grey"] = guiLib.colour["gray"] | |
guiLib.color = guiLib.colour | |
function guiLib.colourFormatConvTo(colourFrom) | |
checkArg(1, colourFrom, "number", "string") | |
if type(colourFrom) == "number" then | |
return guiLib.colour[colour[colourFrom]] | |
else | |
return guiLib.colour[colourFrom] | |
end | |
end | |
function guiLib.colourFormatConvFrom(colourFrom) | |
checkArg(1, colourFrom, "number", "string") | |
if type(colourFrom) == "number" then | |
return colour[guiLib.colour[colourFrom]] | |
else | |
return guiLib.colour[colourFrom] | |
end | |
end | |
guiLib.colorFormatConvTo = guiLib.colourFormatConvTo | |
guiLib.colorFormatConvFrom = guiLib.colourFormatConvFrom | |
function guiLib.getSize() return gpu.getResolution() end | |
-- Puts a character (or a string) onto the screen, (x: number, y: number, value: string[, vertical:boolean, foreColor: number, backColor: number]): boolean | |
function guiLib.putChar(x,y, character, ...) | |
print("putChar", x, y, character, ...) | |
checkArg(1, x, "number") | |
checkArg(2, y, "number") | |
checkArg(3, character, "string") | |
local extraParams = {...} | |
local extraParamNum = 1 | |
local vertical = false | |
if type(extraParams[extraParamNum]) == "boolean" then | |
vertical = extraParams[extraParamNum] | |
extraParamNum = extraParamNum + 1 | |
end | |
local foreColour, backColour -- just to let Lua know they exist | |
local colourChange = false | |
if type(extraParams[extraParamNum]) == "number" then | |
checkArg(extraParamNum + 3, extraParams[extraParamNum], "number") | |
foreColour, backColour = extraParams[extraParamNum], extraParams[extraParamNum + 1] | |
extraParamNum = extraParamNum + 2 | |
colourChange = true | |
else checkArg(extraParamNum + 4, extraParams[extraParamNum], "number", "nil") end | |
local currentForeColour, currentForeIndex = false, false | |
local currentBackColour, currentBackIndex = false, false | |
if colourChange then -- Set the colour of the char that we are going to set | |
currentForeColour, currentForeIndex = gpu.setForeground(foreColour) | |
currentBackColour, currentBackIndex = gpu.setBackground(backColour) | |
end | |
-- Set the char(s) | |
assert(gpu.set(x, y, character, vertical), "Unable to set character(s) at (" .. x,y .. ")") | |
if colourChange then -- Revert the colour | |
gpu.setForeground(currentForeIndex or currentForeColour, not not currentForeIndex) | |
gpu.setBackground(currentBackIndex or currentBackColour, not not currentBackIndex) | |
end | |
end | |
-- Draws a line given a start and end point | |
function guiLib.drawLine(x1,y1, x2,y2, ...) | |
checkArg(1, x1, "number") | |
checkArg(2, y1, "number") | |
checkArg(3, x2, "number") | |
checkArg(4, y2, "number") | |
local extraParams = {...} | |
local extraParamNum = 1 | |
local foreColour, backColour | |
local charIter | |
if type(extraParams[extraParamNum]) == "number" then -- We have colour choices | |
checkArg(5 + extraParamNum, extraParams[extraParamNum], "number") | |
foreColour, backColour = extraParams[extraParamNum], extraParams[extraParamNum + 1] | |
extraParamNum = extraParamNum + 2 | |
elseif type(extraParams[extraParamNum]) == "function" then -- We have a function that will pick colours AND the character | |
charIter = extraParams[extraParamNum] | |
extraParamNum = extraParamNum + 1 | |
end | |
local character = unicode.char(0x2588) | |
if type(extraParams[extraParamNum]) == "string" and not charIter then | |
character = extraParams[extraParamNum] | |
extraParamNum = extraParamNum + 1 | |
end | |
if x1 > x2 then x1, x2 = x2, x1 end | |
if y1 > y2 then y1, y2 = y2, y1 end | |
-- Is it a simple (non diagonal) line? | |
if y1 == y2 then -- This is a horizontal line | |
if charIter then for character, foreColor, backColor in charIter(x1,x2, y1,y2) do guiLib.putChar(x1, y1, unicode.sub(character:rep(math.ceil((x2 - x1) / unicode.len(character))), 1, (x2 + 1) - x1), false, foreColour, backColour) end | |
else guiLib.putChar(x1, y1, unicode.sub(character:rep(math.ceil((x2 - x1) / unicode.len(character))), 1, (x2 + 1) - x1), false, foreColour, backColour) end | |
elseif x1 == x2 then -- This is a vertical line | |
if charIter then for character, foreColor, backColor in charIter(x1,x2, y1,y2) do guiLib.putChar(x1, y1, unicode.sub(character:rep(math.ceil((x2 - x1) / unicode.len(character))), 1, (x2 + 1) - x1), false, foreColour, backColour) end | |
else guiLib.putChar(x1, y1, unicode.sub(character:rep(math.ceil((y2 - y1) / unicode.len(character))), 1, (y2 + 1) - y1), true, foreColour, backColour) end | |
else error("Diagonal line drawing is not yet implemented because skyem is too dumb.", 1) | |
end | |
end | |
local guiObj = {} | |
function guiObj:init() | |
self.registeredClicks = {} | |
self.eventTable = {} | |
self.quit = false | |
end | |
function guiObj:new(object, ...) | |
object = object or {} -- create object if user does not provide one | |
setmetatable(object, self) | |
self.__index = self | |
object:init(...) | |
return object | |
end | |
function guiObj:drawAll() end | |
function guiObj:registerClick(x1,y1, x2,y2, callback, clickType) | |
clickType = clickType or "single" | |
table.insert(self.registeredClicks, {x1 = x1, x2 = x2, y1 = y1, y2 = y2, clickType = clickType, callback = callback}) | |
end | |
function guiObj:eventListen(name, callback) | |
-- Create an callback table if it doesn't exist for this event | |
if self.eventTable[name] == nil then self.eventTable[name] = {} end | |
-- Add the callback the the end of the callback table for this event | |
table.insert(self.eventTable[name], callback) | |
-- Return if it was inserted into the event table | |
return self.eventTable[name][#self.eventTable[name]] and true | |
end | |
function guiObj:eventIgnore(name, callback) | |
-- If the name for the event doesn't exit, we can do nothing | |
if self.eventTable[name] == nil then return end | |
for i = 1, #self.eventTable[name] do | |
if self.eventTable[name][i] == callback then | |
table.remove(self.eventTable[name], i) | |
if #self.eventTable[name] == 0 then | |
self.eventTable[name] = nil | |
end | |
return true | |
end | |
end | |
-- Return if it was inserted into the event table | |
return self.eventTable[name][#self.eventTable[name]] and true | |
end | |
-- DO NOT CALL DIRECTLY! | |
-- This may crash and leave unwanted stuff on your system | |
function guiObj:unsafeLoop() | |
local function singleClick(_, _, x,y, _, _) | |
for _, click in ipairs(self.registeredClicks) do | |
--print(x,click.x1,click.x2, y,click.y1,click.y2) | |
if click.clickType == "single" then | |
-- Do a bounds check | |
--print(serialization.serialize(click, true), "\n", x,y) | |
if click.x1 <= x and click.x2 >= x and click.y1 <= y and click.y2 >= y then | |
click.callback(x - click.x1, y - click.y1) -- If it's within the bounds of the button, call it. | |
end | |
end | |
end | |
end | |
self:eventListen("touch", singleClick) | |
while not self.quit do -- Event loop | |
-- Pull and event, wait forever for it if there is no sleepTime set (nil means wait forever) | |
gotEvent = table.pack(event.pull(sleepTime)) | |
-- Loop through the events registered | |
for event, callbacks in pairs(self.eventTable) do | |
-- If the event we got is registered | |
if gotEvent[1] == event then | |
for _, callback in ipairs(callbacks) do | |
callback(table.unpack(gotEvent)) | |
end | |
end | |
end | |
end | |
end | |
-- Safe loop that cleans up after the unsafe loop | |
function guiObj:mainLoop(...) | |
-- Safely call the main loop | |
retVal = table.pack(pcall(self.unsafeLoop, self, ...)) | |
local success = retVal[1] | |
-- If there is an error, log it to stderr | |
if not success then io.stderr:write("Error: " .. tostring(retVal[2]) .. "\n" .. debug.traceback() .. "\n") end | |
-- Return the return value and error | |
return table.unpack(retVal) | |
end | |
function guiObj:placeComponent(item, foreColour, backColour) | |
local width, height = guiLib.getSize() | |
-- TODO: Make error more informative | |
if not (item.relX and item.relY) then error("No position for item!",1) end | |
local x, y, item_height, item_width | |
if item.relX == 0 then x = 1 else x = guiLib.math.round(width * item.relX) end | |
if item.relY == 0 then y = 1 else y = guiLib.math.round(height * item.relY) end | |
if x == 0 then x = 1 end | |
if y == 0 then y = 1 end | |
if item.height then item_height = item.height else item_height = 1 end | |
if item.width then item_width = item.width else item_width = 1 end | |
print(x,y) | |
if item.itemType == "text" then | |
local length = item.length or unicode.len(item.text) | |
if item.alignment == "centre" then | |
x = x - guiLib.math.floor(length / 2) | |
end | |
guiLib.putChar(x, y, item.text, item.foreColour or foreColour, item.backColour or backColour) | |
end | |
if item.itemType == "button" then | |
guiLib.putChar(x, y, item.text, item.foreColour or foreColour, item.backColour or backColour) | |
local length = item.length or unicode.len(item.text) | |
self:registerClick(x,y, x + length - 1, y + (item.height or 1), item.action) | |
end | |
end | |
-- (Sets and) draws the title bar | |
function guiObj:drawTitleBar(setTitle) | |
if setTitle then self.title = setTitle end | |
if self.title == nil then return end -- Exit if there is no title bar set | |
local width, _ = guiLib.getSize() | |
guiLib.drawLine(1,1, width +1 ,1, self.title.backColour, self.title.backColour) | |
local position = 1 | |
for key, item in ipairs(self.title) do | |
item.relY = 0 | |
item.height = 1 | |
self:placeComponent(item, self.title.foreColour, self.title.backColour) | |
end | |
end | |
function guiLib.getObject(...) | |
return guiObj:new({}, ...) | |
end | |
return guiLib |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
local term = require("term") | |
term.clear() | |
print() | |
local serialization = require("serialization") | |
require("package").loaded.guiLib = nil | |
local guiLib = require("guiLib") | |
local guiObj = guiLib.getObject() | |
local colour = guiLib.colour | |
local unicode = require("unicode") | |
print(serialization.serialize(guiObj, true)) | |
print(serialization.serialize(guiObj.__index, true)) | |
print(serialization.serialize(guiLib, true)) | |
--guiLib.putChar(1,2, "H") | |
--guiLib.putChar(2,2, "e", colour.white, colour.red) | |
--guiLib.putChar(3,2, unicode.char(0x2588), colour.red, colour.white) | |
--guiLib.putChar(1,3, "Hello!", colour.white, colour.red) | |
--guiLib.drawLine(1,5, 10,5) | |
--guiLib.drawLine(1,7, 1,10, colour.red, colour.white) | |
--guiLib.drawLine(3,7, 3,10, colour.red, colour.white, "Hi ") | |
print(pcall(guiLib.drawLine, 40,1, 20,30)) | |
guiObj:drawTitleBar({ | |
{itemType = "button", text = "X", foreColour = colour.white, backColour = colour.red, relX = 1, action = function() guiObj.quit = true end }, | |
{itemType = "text", text = "ICs OC 0.1", relX = 0.5, alignment = "centre"}, | |
foreColour = colour.white, backColour = colour.blue}) | |
guiObj:placeComponent({itemType = "button", text = "Hello!", foreColour = colour.white, backColour = colour.red, relX = 0.5, relY = 0.5, action = function() print("Hello, world!") end }, colour.blue, colour.white) | |
guiObj:placeComponent({itemType = "text", text = "I am some text.", foreColour = colour.black, backColour = colour.white, relX = 0.7, relY = 0.7}, colour.blue, colour.white) | |
guiLib.putChar(50, 25, "a") | |
print(guiObj.mainLoop(guiObj)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment