Posted 01 February 2013 - 08:44 AM
This utility allows you to easily create advanced GUIs, with textfields, textboxes, buttons, tabs, scrollbars, etc. It is in very early development stages right now, though.
So far there is not any documentation, as it is very simple to use.
This is the code, if you want to use it:
This is an example of how to use it(login screen)
I hope that this will come to good use :)/>
So far there is not any documentation, as it is very simple to use.
This is the code, if you want to use it:
Spoiler
http://pastebin.com/j0i5fXAs
local S_WIDTH, S_HEIGHT = term.getSize()
local function cleanExit()
term.setBackgroundColor(colors.black)
term.clear()
term.setCursorPos(1, 1)
term.setTextColor(colors.yellow)
print(os.version())
error()
end
local function inheritsFrom(baseClass)
local new_class = {}
local class_mt = {__index = new_class}
new_class.new = function()
local newinst = {}
setmetatable(newinst, class_mt)
return newinst
end
if baseClass then
setmetatable(new_class, {__index = baseClass})
end
return new_class
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Event class --
-------------------------------------------------------------
-------------------------------------------------------------
local BUTTON_LEFT = 1;
local BUTTON_RIGHT = 2;
local BUTTON_W_UP = 3;
local BUTTON_W_DOWN = 4;
local BUTTON_W = 5;
local gui_event = {
x = 0;
y = 0;
button = BUTTON_LEFT;
key = 0;
}
gui_event.__index = gui_event
gui_event.new = function()
local t = {}
setmetatable(t, gui_event)
return t
end
local gui_base = {
width = 0;
height = 0;
x = 0;
y = 0;
hasBgColor = true;
visible = true;
bgColor = colors.lightBlue;
}
gui_base.__index = gui_base
function gui_base:onMouseEvent(ev)
end
function gui_base:onKeyEvent(ev)
end
function gui_base:draw()
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Button class --
-------------------------------------------------------------
-------------------------------------------------------------
local gui_button = inheritsFrom(gui_base)
function gui_button:init(text, x, y, w, h)
self.textColor = colors.black
self.bgColor = colors.white
self.text = text
self.x = x
self.y = y
self.width = w
self.height = h
self.onClick = function(x, y) end
self.centerText = true
self.clickable = true
end
function gui_button:draw()
if self.visible then
term.setTextColor(self.textColor)
if self.hasBgColor then
term.setBackgroundColor(self.bgColor)
local w, h = self.width, self.height
local x, y = math.floor(self.x), math.floor(self.y)
-- print("x, y = " .. x .. ", " .. y)
for _x = 0, w - 1 do
for _y = 0, h - 1 do
-- print("Drawing at (" .. _x + x .. ", " .. _y + y .. ")")
term.setCursorPos(_x + x, _y + y)
io.write(" ")
end
end
end
local pX, pY = math.floor(self.x), math.floor(self.y)
local w, h = self.width, self.height
local cX, cY = (self.centerText and math.floor(pX + w / 2) - self.text:len() / 2 or pX), (self.centerText and math.floor(pY + h / 2) or pY)
term.setCursorPos(cX, cY)
if self.text:len() == 1 then
term.setCursorPos(cX + 1, cY)
io.write(self.text)
else
io.write(self.text:sub(1, (self.text:len() > w - 1 and w - 1 or self.text:len())))
end
end
end
function gui_button:onMouseEvent(ev)
if self.visible then
local _x, _y = math.floor(self.x), math.floor(self.y)
local w, h = self.width, self.height
-- _x, _y = _x - 1, _y - 1
-- print("(" .. tostring(ev.x) .. ", " .. tostring(ev.y) .. ")")
if self.clickable and ev.x >= _x and ev.x < _x + w and ev.y >= _y and ev.y < _y + h then
self.onClick(self, ev.x, ev.y)
end
end
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Textbox class --
-------------------------------------------------------------
-------------------------------------------------------------
local gui_textfield = inheritsFrom(gui_base)
function gui_textfield:init(text, x, y, w, m_ch)
self.text = text
self.textColor = colors.black
self.bgColor = colors.white
self.replaceChar = nil
self.chars = {}
self.editable = true
for i = 1, text:len() do
table.insert(self.chars, text:sub(i, i))
end
local _w, h = term.getSize()
self.x = x
self.y = y
self.width = w or 30
self.height = 1
--[[
local sW, sH = self.width, self.height
while sH ~= 1 do
self.height = self.height - 0.2
sW, sH = self.width, self.height
end
]]
self.m_ch = m_ch or false
self.__onClick = function(x, y)
self:edit()
end
end
function gui_textfield:draw()
if self.visible then
term.setTextColor(self.textColor)
if self.hasBgColor then
term.setBackgroundColor(self.bgColor)
local w, h = self.width, self.height
local x, y = math.floor(self.x), math.floor(self.y)
-- print("x, y = " .. x .. ", " .. y)
for _x = 0, w - 1 do
for _y = 0, h - 1 do
-- print("Drawing at (" .. _x + x .. ", " .. _y + y .. ")")
term.setCursorPos(_x + x, _y + y)
io.write(" ")
end
end
end
local pX, pY = math.floor(self.x), math.floor(self.y)
-- pY = pY - 1
-- print(pX .. ", " .. pY)
local w, h = self.width, self.height
local cX, cY = pX, math.floor(pY + h / 2)
term.setCursorPos(cX, cY)
local line = self.text
local _line = line
if self.replaceChar then
line = ""
for i = 1, _line:len() do
line = line .. self.replaceChar:sub(1, 1)
end
end
io.write(line:sub(1, (line:len() > w - 1 and w - 1 or line:len())))
end
end
function gui_textfield:edit()
if self.visible and self.editable then
local pX, pY = math.floor(self.x), math.floor(self.y)
-- pX, pY = pX, pY - 1
-- print(pX .. ", " .. pY)
local w, h = self.width, self.height
term.setCursorPos(pX, math.floor(pY + h / 2))
term.setCursorBlink(true)
local line = self.text
local cursorX = line:len()
local chars = {}
for i = 1, self.text:len() do
table.insert(chars, self.text:sub(i, i))
end
local function redraw(r_ch)
term.setTextColor(self.textColor)
term.setBackgroundColor(self.bgColor)
term.setCursorPos(pX, math.floor(pY + h / 2))
line = ""
for _, c in ipairs(chars) do
line = line .. c
end
local _line = line
if self.replaceChar then
line = ""
for i = 1, _line:len() do
line = line .. self.replaceChar:sub(1, 1)
end
end
for i = 1, w do
io.write(" ")
end
term.setCursorPos(pX, math.floor(pY + h / 2))
local currentX = pX
if line:len() >= w - 1 and cursorX >= w - 1 then
io.write(line:sub(cursorX - (w - 2), cursorX))
currentX = pX + cursorX - line:sub(1, cursorX - (w - 1)):len()
elseif line:len() >= w - 1 and cursorX < w - 1 then
io.write(line:sub(1, w - 1))
currentX = pX + cursorX
else
io.write(line)
currentX = pX + cursorX
end
line = _line
term.setCursorPos(currentX, math.floor(pY + h / 2))
end
redraw(self.replaceChar)
while true do
local ev, key = os.pullEvent()
if ev == "key" and (m_ch and #chars < m_ch or true) then
if key == keys.left then
if cursorX > 0 then
cursorX = cursorX - 1
end
elseif key == keys.right then
if cursorX < line:len() then
cursorX = cursorX + 1
end
elseif key == keys.enter then
break
elseif key == keys.backspace then
if cursorX > 0 then
table.remove(chars, cursorX)
cursorX = cursorX - 1
end
elseif key == keys.delete then
if cursorX < line:len() then
table.remove(chars, cursorX + 1)
end
end
elseif ev == "char" then
table.insert(chars, cursorX + 1, key)
cursorX = cursorX + 1
end
redraw()
end
term.setCursorBlink(false)
self.text = line
end
end
function gui_textfield:setText(t)
self.text = t
for i = 1, t:len() do
self.chars[i] = t:sub(i, i)
end
end
function gui_textfield:onMouseEvent(ev)
if self.visible then
local _x, _y = math.floor(self.x), math.floor(self.y)
local w, h = self.width, self.height
if self.editable and ev.x >= _x and ev.x < _x + w and ev.y >= _y and ev.y < _y + h then
self.__onClick(ev.x, ev.y)
end
end
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Label class --
-------------------------------------------------------------
-------------------------------------------------------------
local gui_label = inheritsFrom(gui_base)
function gui_label:init(text, x, y)
self.textColor = colors.black
self.bgColor = colors.white
self.text = text
self.x = x
self.y = y
self.centerText = false
self.width = text:len() + 1
self.height = 1
end
function gui_label:draw()
if self.visible then
term.setTextColor(self.textColor)
if self.hasBgColor then
term.setBackgroundColor(self.bgColor)
local w, h = self.width, self.height
local x, y = math.floor(self.x), math.floor(self.y)
-- print("x, y = " .. x .. ", " .. y)
for _x = 0, w - 1 do
for _y = 0, h - 1 do
-- print("Drawing at (" .. _x + x .. ", " .. _y + y .. ")")
term.setCursorPos(_x + x, _y + y)
io.write(" ")
end
end
end
local pX, pY = math.floor(self.x), math.floor(self.y)
-- print(pX .. ", " .. pY)
local w, h = self.width, self.height
local cX, cY = (self.centerText and math.floor(pX + w / 2) - self.text:len() / 2 or pX), (self.centerText and math.floor(pY + h / 2) or pY)
term.setCursorPos(cX, cY)
if self.text:len() == 1 then
term.setCursorPos(cX + 1, cY)
io.write(self.text)
else
io.write(self.text:sub(1, (self.text:len() > w - 1 and w - 1 or self.text:len())))
end
end
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Rectangle class --
-------------------------------------------------------------
-------------------------------------------------------------
local gui_rect = inheritsFrom(gui_base)
function gui_rect:init(x, y, w, h, c)
self.x = x
self.y = y
self.width = w
self.height = h
self.bgColor = c
end
function gui_rect:draw()
if self.visible then
if self.hasBgColor then
term.setBackgroundColor(self.bgColor)
local w, h = self.width, self.height
local x, y = math.floor(self.x), math.floor(self.y)
-- print("x, y = " .. x .. ", " .. y)
for _x = 0, w - 1 do
for _y = 0, h - 1 do
-- print("Drawing at (" .. _x + x .. ", " .. _y + y .. ")")
term.setCursorPos(_x + x, _y + y)
io.write(" ")
end
end
end
end
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Image class --
-------------------------------------------------------------
-------------------------------------------------------------
local gui_image = inheritsFrom(gui_base)
function gui_image:init(path, x, y)
self.path = path
self.x = x or 0
self.y = y or 0
self.imgData = nil
end
function gui_image:load()
self.imgData = paintutils.loadImage(self.path)
if self.imgData == false then
return false
end
return true
end
function gui_image:draw()
paintutils.drawImage(self.imgData, self.x, self.y)
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Utilities --
-------------------------------------------------------------
-------------------------------------------------------------
local function createMessageBox(title, _msg, _type)
local host = {}
local msgBox = gui_rect.new()
msgBox:init(S_WIDTH / 2 - 19, S_HEIGHT / 2 - 5, 20 * 2, 10)
msgBox.index = #host + 1
host.msgBox = msgBox
local msgBoxTitleBar = gui_rect.new()
msgBoxTitleBar:init(S_WIDTH / 2 - 19, S_HEIGHT / 2 - 5, 20 * 2, 1)
msgBoxTitleBar.bgColor = colors.lightGray
host.msgBoxTitleBar = msgBoxTitleBar
local msgBoxTitle = gui_label.new()
msgBoxTitle:init(title, S_WIDTH / 2 - 19, S_HEIGHT / 2 - 5)
msgBoxTitle.bgColor = msgBoxTitleBar.bgColor
host.msgBoxTitle = msgBoxTitle
local msgBoxQuit = gui_button.new()
msgBoxQuit:init("X", S_WIDTH / 2 - 19 + 20 * 2 - 1, S_HEIGHT / 2 - 5, 1, 1)
msgBoxQuit.bgColor = colors.red
msgBoxQuit.textColor = colors.white
msgBoxQuit.onClick = function(self, x, y)
for _, el in pairs(host) do
el.visible = false
end
end
host.msgBoxQuit = msgBoxQuit
local msgLabel = gui_label.new()
msgLabel:init(_msg, S_WIDTH / 2 - _msg:len() / 2, S_HEIGHT / 2, _msg:len(), 1)
msgLabel.bgColor = msgBox.bgColor
msgLabel.textColor = colors.black
if _type:lower() == "error" then
msgLabel.bgColor = colors.red
end
host.msgLabel = msgLabel
return host
end
local function guiPromt(title, _msg, options)
local host = {}
local msgBox = gui_rect.new()
msgBox:init(S_WIDTH / 2 - 19, S_HEIGHT / 2 - 5, 20 * 2, 10 + 1)
msgBox.index = #host + 1
host.msgBox = msgBox
local msgBoxTitleBar = gui_rect.new()
msgBoxTitleBar:init(S_WIDTH / 2 - 19, S_HEIGHT / 2 - 5, 20 * 2, 1)
msgBoxTitleBar.bgColor = colors.lightGray
host.msgBoxTitleBar = msgBoxTitleBar
local msgBoxTitle = gui_label.new()
msgBoxTitle:init(title, S_WIDTH / 2 - 19, S_HEIGHT / 2 - 5)
msgBoxTitle.bgColor = msgBoxTitleBar.bgColor
host.msgBoxTitle = msgBoxTitle
local msgBoxQuit = gui_button.new()
msgBoxQuit:init("X", S_WIDTH / 2 - 19 + 20 * 2 - 1, S_HEIGHT / 2 - 5, 1, 1)
msgBoxQuit.bgColor = colors.red
msgBoxQuit.textColor = colors.white
msgBoxQuit.onClick = function(self, x, y)
for _, el in pairs(host) do
el.visible = false
end
end
host.msgBoxQuit = msgBoxQuit
local msgLabel = gui_label.new()
msgLabel:init(_msg, S_WIDTH / 2 - _msg:len() / 2, S_HEIGHT / 2, _msg:len(), 1)
msgLabel.bgColor = msgBox.bgColor
msgLabel.textColor = colors.black
host.msgLabel = msgLabel
local maxLen = options.t:len() > options.f:len() and options.t:len() or options.f:len()
---[[
local trueButton = gui_button.new()
trueButton:init(options.t, S_WIDTH / 2 - 1 - maxLen - 2, S_HEIGHT / 2 + 2, maxLen + 2, 1)
trueButton.onClick = msgBoxQuit.onClick
trueButton.bgColor = colors.green
host.trueButton = trueButton
local falseButton = gui_button.new()
falseButton:init(options.f, S_WIDTH / 2 + 1, S_HEIGHT / 2 + 2, maxLen + 2, 1)
falseButton.onClick = msgBoxQuit.onClick
falseButton.bgColor = colors.red
host.falseButton = falseButton
--]]
return host
end
This is an example of how to use it(login screen)
Spoiler
http://pastebin.com/46hv2KBP
local guiElements = {}
local loginScreen = {}
local function initLoginScreen()
local bg = gui_rect.new()
bg:init(0, 0, S_WIDTH + 1, S_HEIGHT + 1)
bg.bgColor = colors.cyan
local userLabel = gui_label.new()
userLabel:init("Username: ", S_WIDTH / 2 - 10, S_HEIGHT / 2 + 2, 10, 1)
userLabel.bgColor = bg.bgColor
local userField = gui_textfield.new()
userField:init("", S_WIDTH / 2 + 1, S_HEIGHT / 2 + 2, 15, 1)
local passLabel = gui_label.new()
passLabel:init("Password: ", S_WIDTH / 2 - 10, S_HEIGHT / 2 + 2 + 2, 10, 1)
passLabel.bgColor = bg.bgColor
local passField = gui_textfield.new()
passField:init("", S_WIDTH / 2 + 1, S_HEIGHT / 2 + 2 + 2, 15, 1)
passField.replaceChar = "*"
table.insert(loginScreen, bg)
table.insert(loginScreen, userLabel)
table.insert(loginScreen, userField)
table.insert(loginScreen, passLabel)
table.insert(loginScreen, passField)
local cancelButton = gui_button.new()
cancelButton:init("Cancel", S_WIDTH / 2 - 10, S_HEIGHT / 2 + 2 + 2 + 2, 8, 3)
cancelButton.onClick = function(self, x, y)
cleanExit()
end
cancelButton.textColor = bg.bgColor
table.insert(loginScreen, cancelButton)
local loginButton = gui_button.new()
loginButton:init("Log in", S_WIDTH / 2 + 8, S_HEIGHT / 2 + 2 + 2 + 2, 8, 3)
loginButton.onClick = function(loginB, x, y)
-- PERFORM LOGIN STUFF
if success then
-- Perform success stuff
else
userField.editable = false
passField.editable = false
loginButton.clickable = false
cancelButton.clickable = false
local msg = createMessageBox("Failure", errorMessage, "error")
msg.msgBoxQuit.onClick = function(self, x, y)
userField.editable = true
passField.editable = true
loginButton.clickable = true
cancelButton.clickable = true
for _, el in pairs(msg) do
el.visible = false
end
end
for _, el in pairs(msg) do
table.insert(loginScreen, el)
end
end
end
loginButton.textColor = bg.bgColor
table.insert(loginScreen, loginButton)
end
initLoginScreen()
guiElements = loginScreen
for _, el in ipairs(guiElements) do
el:draw()
end
-------------------------------------------------------------
-------------------------------------------------------------
-- GUI Control loop --
-------------------------------------------------------------
-------------------------------------------------------------
while true do
local ev, p1, p2, p3, p4 = os.pullEvent()
local gui_ev = gui_event.new()
if ev == "mouse_click" then
gui_ev.button = p1
if p1 == 3 then
gui_ev.button = BUTTON_W
end
gui_ev.x = p2
gui_ev.y = p3
for _, el in ipairs(guiElements) do
el:onMouseEvent(gui_ev)
end
elseif ev == "mouse_scroll" then
gui_ev.button = p1 == 1 and BUTTON_W_DOWN or BUTTON_W_UP
gui_ev.x = p2
gui_ev.y = p3
for _, el in ipairs(guiElements) do
el:onMouseEvent(gui_ev)
end
elseif ev == "key" then
gui_ev.key = p1
for _, el in ipairs(guiElements) do
el:onKeyEvent(gui_ev)
end
end
for _, el in ipairs(guiElements) do
el:draw()
end
end
I hope that this will come to good use :)/>