usage: require("gui")
this is probably the most useful library in SComputers
it makes it easy to create graphical interfaces and even windows
the library supports automatic calculation of the size and position of elements
the library supports only the basic objects, but you are not restricted by them! you can create your own objects and add them to the GUI
there is also an objs library in the mod that contains ready-made additional GUI objects
there is also a styles library in mod that contains styles for objects that can be applied to any object and its drawer callback will be replaced with a style
methods:
constants:
guiinstance methods:
guiscene methods:
any object callbacks (you can implement this in the object after creation):
scene callbacks (you can implement this in the object after creation):
window callbacks (you can implement this in the object after creation):
scene/window callbacks (you can implement this in the object after creation):
button callbacks (you can implement this in the object after creation):
scene/window methods:
window object methods:
button object methods:
image object methods:
label object methods:
text object methods:
textbox object methods:
any object methods (these methods are relevant for any type of object, including custom and window):
functions for specifying the position of objects:
--example for display 128x128
local display = getComponents("display")[1]
display.reset()
display.clearClicks()
display.setClicksAllowed(true)
local rx, ry = display.getWidth(), display.getHeight()
local gui = require("gui").new(display)
local styles = require("styles")
local objs = require("objs")
local scene = gui:createScene("777777")
local horizontalTabBar = scene:createCustom(0, 0, rx, 8, objs.tabbar, 0x444444, false, nil, 3)
for ix = 1, 4 do
local window = horizontalTabBar:createOtherspaceWindow()
local verticleTabBar = window:createCustom(0, 0, 26, ry - horizontalTabBar.sizeY, objs.tabbar, 0x444444, true, nil, 3)
for iy = 1, 4 do
local window2 = verticleTabBar:createOtherspaceWindow()
verticleTabBar:addTab("VTAB" .. iy, window2)
window2:createLabel(0, 0, window2.sizeX, window2.sizeY, "HTAB: " .. ix .. "\n" .. "VTAB: " .. iy, 0x000088, 0xffffff)
end
horizontalTabBar:addTab("HTAB" .. ix, window)
end
function callback_loop()
if _endtick then
display.clear()
display.flush()
return
end
gui:tick()
if gui:needFlush() then
gui:draw()
display.flush()
end
end
{
useWindow = false, --by default, false, if set to true, your object will be created as a window, which will allow you to create child objects on it
layerMode = 1, --default layerMode. by default 1
autoViewport = false, --the default is false, but you can set this to true to limit your object rendering to the border of your object
handlerLocalPosition = false, --by default, this is false, but you can set this to true so that the values in the "handler" are local to the object
handlerAllClicks = false, --by default, false. if set to true, your handler will receive all clicks in general, even if they are in no way related to your object
handlerOutsideDrag = false, --by default, false. if set to true, you will receive drag events, including outside your object, if the first click was inside your object
--It makes sense to enable this flag even if you are using handlerAllClicks, as this will tell the gui library that your object remains "grappable" even when the user removes the cursor but does not release the button.
init = function(self, ...) --you can execute code when creating an object, and you can also receive arguments
local args = {...}
--carefully create parameters in yourself to avoid conflicts with the gui library itself.
--for example, names like "state" and "touchX" are already occupied, which may not be obvious
--to avoid conflicts for sure, it's better to create a separate table in self to store the parameters of your object
self.myVars = {creationArgs = args, example = 1, color = 0xff0000}
end,
drawer = function(self) --implement the rendering of your object here
self.display.fillRect(self.x, self.y, self.sizeX, self.sizeY, self.myVars.color) --example
end,
handler = function(self, x, y, clickType, button, nickname, inZone, elementCapture) --handles item touches. please note that by default you get the global coordinates of the display!
--please note that you may receive parameters: -1, -1, "released", -1, "unknown"
--this happens if the object was activated during scene switching
--you can also get a position outside of your object during "released" in cases where the user clicked on the object and released the touch outside the object, you will still receive an event, but it will be outside the object
--if you have the handlerOutsideDrag flag set, you will receive drag events outside your object if the first click was inside your object
--inZone is set to true if the click occurs inside the object, it works independently of handlerLocalPosition and handlerAllClicks
--elementCapture is a flag that is used if your custom object is running in useWindow mode, it is set to true if it is currently interacting with a child object
if clickType == "pressed" then
self.myVars.color = math.random(0, 0xffffff)
self:update() --you can return true from this function instead of explicitly calling self:update()
end
end,
destroyHandler = function(self) --called when your object is deleted
end,
calculateSize = function(self) --you can define this function to automatically calculate the size of your object. you can write it yourself or use a ready-made function from gui library
return 16, 16
end,
methods = { --you can implement custom functions for your component here
}
}
local display = getComponent("display")
display.reset()
display.clearClicks()
display.setClicksAllowed(true)
local rx, ry = display.getWidth(), display.getHeight()
local gui = require("gui").new(display)
local scene = gui:createScene(0x0000ff)
local object = scene:createCustom(nil, nil, 64, 64, {
autoViewport = true, --set the render to the size of the object
handlerLocalPosition = true,
init = function(self)
self.myVars = {creationArgs = args, example = 1, color = 0xff0000}
end,
drawer = function(self)
self.display.fillCircle(self.x, self.y, self.sizeX, self.myVars.color)
end,
handler = function(self, x, y, clickType, button, nickname)
if clickType == "pressed" then
self.myVars.color = math.random(0, 0xffffff)
self:update()
end
end
})
object:setCenter()
function callback_loop()
if _endtick then
display.clear()
display.flush()
return
end
gui:tick()
if gui:needFlush() then
gui:draw()
display.flush()
end
end
--the example was created for a 128x128 screen
local display = getComponents("display")[1]
display.reset()
display.clearClicks()
display.setClicksAllowed(true)
local rx, ry = display.getWidth(), display.getHeight()
local gui = require("gui").new(display)
local styles = require("styles")
local scene = gui:createScene("777777")
local function addWindow()
local window = scene:createWindow(16, 16, 64, 64, "2d2d2d")
window:upPanel("058db8", "ffffff", "test window", true)
window:setDraggable(true)
local closeButton = window:panelButton(7, false, "X", "00a2d5", "0054a1", "00c2ff", "0085ff")
closeButton:attachCallback(function(self, state, inZone)
if not state and inZone then
window:destroy()
end
end)
local oldText
for i = 1, 4 do
local text = window:createText(nil, nil, "switch " .. i .. ": ")
if oldText then text:setDown(oldText) end
local switch = window:createButton(nil, nil, 8, 4, true, nil, "444444", "ffffff", "44b300", "ffffff")
switch:setCustomStyle(styles.switch)
switch:setRight(text)
oldText = text
end
local window2 = window:createWindow(nil, nil, 32, 16, "333377")
window2:setDown(oldText)
window2:upPanel("058db8", "ffffff", "test", true)
window2:minimize(true)
local switch = window2:createButton(nil, nil, 14, 8, true, nil, "444444", "ffffff", "44b300", "ffffff")
switch:setCustomStyle(styles.switch)
function switch:onTick()
switch:setBgColor(math.random(0, 0xffffff))
end
end
local addWindowButton = scene:createButton(nil, nil, 32, 32, false, "WINDOW")
addWindowButton:attachCallback(function(self, state, inZone)
if state then
addWindow()
end
end)
function scene:onTick()
addWindowButton:setBgColor(math.random(0, 0xffffff))
end
function callback_loop()
if _endtick then
display.clear()
display.flush()
out(false)
return
end
gui:tick()
if gui:needFlush() then
gui:draw()
display.flush()
end
end
--example for display 64x64
local display = getComponent("display")
local width, height = display.getSize()
display.reset()
display.clearClicks()
display.setClicksAllowed(true)
display.clear()
display.flush()
local gui = require("gui").new(display)
local scene = gui:createScene(0x444444)
local rgbButtons = {
init = function(self, partsX, partsY)
self.colors = {}
self.partsX = partsX
self.partsY = partsY
self.partSizeX = math.floor((self.sizeX / partsX) + 0.5)
self.partSizeY = math.floor((self.sizeY / partsY) + 0.5)
for ix = 0, partsX - 1 do
self.colors[ix] = {}
for iy = 0, partsY - 1 do
self.colors[ix][iy] = 0
end
end
end,
drawer = function(self)
self:clear(0x000000)
for ix = 0, self.partsX - 1 do
for iy = 0, self.partsY - 1 do
local color
local colorIdx = self.colors[ix][iy]
if colorIdx == 0 then
color = 0xff0000
elseif colorIdx == 1 then
color = 0x00ff00
elseif colorIdx == 2 then
color = 0x0000ff
end
self.display.fillRect(self.x + (ix * self.partSizeX) + 1, self.y + (iy * self.partSizeY) + 1, self.partSizeX - 1, self.partSizeY - 1, color)
end
end
end,
handlerLocalPosition = true, --tells the library that you would like to get the click position relative to your object
handler = function(self, x, y, action, button) -- if the object was clicked and then the scene switched, the method will be called with the parameters: self, -1, -1, "released", -1
if action == "pressed" then
self:togglePartIndex(math.floor(x / self.partSizeX), math.floor(y / self.partSizeY))
self:update()
end
end,
methods = { --here you can implement your own methods that the user of the component can call
togglePartIndex = function(self, x, y)
local colorIdx = self:getPartIndex(x, y)
if not colorIdx then return end
colorIdx = colorIdx + 1
if colorIdx > 2 then
colorIdx = 0
end
self:setPartIndex(x, y, colorIdx)
end,
setPartIndex = function(self, x, y, colorIdx)
self.colors[x][y] = colorIdx
end,
getPartIndex = function(self, x, y)
if not self.colors[x] then
return
end
return self.colors[x][y]
end
}
}
--creating 4 instances of custom "rgbButtons" objects
local sizeX, sizeY = (width / 2) - 3, (height / 2) - 3
local rgbButtons1 = scene:createCustom(2, 2, sizeX, sizeY, rgbButtons, 4, 4)
local rgbButtons2 = scene:createCustom(nil, nil, sizeX, sizeY, rgbButtons, 4, 4)
rgbButtons2:setRight(rgbButtons1, 2)
local rgbButtons3 = scene:createCustom(nil, nil, sizeX, sizeY, rgbButtons, 4, 4)
rgbButtons3:setDown(rgbButtons1, 2)
local rgbButtons4 = scene:createCustom(nil, nil, sizeX, sizeY, rgbButtons, 4, 4)
rgbButtons4:setRight(rgbButtons3, 2)
rgbButtons2:setPartIndex(2, 2, 1)
rgbButtons4:setPartIndex(1, 1, 2)
scene:select()
function callback_loop()
if _endtick then
display.clear()
display.flush()
return
end
gui:tick()
if gui:needFlush() then
gui:draw()
display.flush()
end
end
--example for display 128x128
local display = getComponent("display")
local camera = getComponent("camera")
local width, height = display.getSize()
display.reset()
display.clearClicks()
display.setClicksAllowed(true)
display.clear()
display.flush()
local gui = require("gui").new(display)
local scene = gui:createScene(function()
camera.drawAdvanced(display, true)
end)
local rgbButtons = {
init = function(self, partsX, partsY)
self.colors = {}
self.partsX = partsX
self.partsY = partsY
self.partSizeX = math.floor((self.sizeX / partsX) + 0.5)
self.partSizeY = math.floor((self.sizeY / partsY) + 0.5)
for ix = 0, partsX - 1 do
self.colors[ix] = {}
for iy = 0, partsY - 1 do
self.colors[ix][iy] = 0
end
end
end,
drawer = function(self)
self:clear(0x000000)
for ix = 0, self.partsX - 1 do
for iy = 0, self.partsY - 1 do
local color
local colorIdx = self.colors[ix][iy]
if colorIdx == 0 then
color = 0xff0000
elseif colorIdx == 1 then
color = 0x00ff00
elseif colorIdx == 2 then
color = 0x0000ff
end
self.display.fillRect(self.x + (ix * self.partSizeX) + 1, self.y + (iy * self.partSizeY) + 1, self.partSizeX - 1, self.partSizeY - 1, color)
end
end
end,
handlerLocalPosition = true, --tells the library that you would like to get the click position relative to your object
handler = function(self, x, y, action, button) -- if the object was clicked and then the scene switched, the method will be called with the parameters: self, -1, -1, "released", -1
if action == "pressed" then
self:togglePartIndex(math.floor(x / self.partSizeX), math.floor(y / self.partSizeY))
self:update()
end
end,
methods = { --here you can implement your own methods that the user of the component can call
togglePartIndex = function(self, x, y)
local colorIdx = self:getPartIndex(x, y)
if not colorIdx then return end
colorIdx = colorIdx + 1
if colorIdx > 2 then
colorIdx = 0
end
self:setPartIndex(x, y, colorIdx)
end,
setPartIndex = function(self, x, y, colorIdx)
self.colors[x][y] = colorIdx
end,
getPartIndex = function(self, x, y)
if not self.colors[x] then
return
end
return self.colors[x][y]
end
}
}
local rgbButtons1 = scene:createCustom(2, 2, 29, 29, rgbButtons, 4, 4)
scene:select()
function callback_loop()
if _endtick then
display.clear()
display.flush()
return
end
gui:tick()
gui:drawForce()
display.flush()
end
--example for a 256x256 display
local fonts = require("fonts")
local display = getComponent("display")
display.reset()
display.clearClicks()
display.setClicksAllowed(true)
display.setFont(fonts.impact_32)
local width, height = display.getWidth(), display.getHeight()
local fwidth, fheight = display.getFontWidth(), display.getFontHeight()
local gui = require("gui").new(display)
------------------------------------------
local acceptCode
local acceptScene = gui:createScene(sm.color.new("#e530ff"))
local acceptLabel = acceptScene:createLabel(4, 4, width - 8, fheight * 1.2, nil, 0x000000, 0xff0000)
local accept = acceptScene:createButton(nil, nil, width - 8, fheight * 1.4, false, "accept")
accept:setDown(acceptLabel, 4)
accept:attachCallback(function (_, newstate, isZone)
if not newstate and isZone then
acceptCode()
mainScene:select()
end
end)
local cancel = acceptScene:createButton(nil, nil, width - 8, fheight * 1.4, false, "cancel")
cancel:setDown(accept, 4)
cancel:attachCallback(function (_, newstate, isZone)
if not newstate and isZone then
mainScene:select()
end
end)
------------------------------------------
mainScene = gui:createScene(sm.color.new("#03a4ff"))
local label = mainScene:createLabel(4, 4, width - 8, fheight * 1.2, "what to delete?", 0x000000, 0xff0000)
local action1 = mainScene:createButton(nil, nil, width - 8, fheight * 1.4, false, "all bodies")
action1:setDown(label, 4)
action1:attachCallback(function (_, newstate, isZone)
if global and not newstate and isZone then
acceptLabel.text = "del all bodies"
acceptCode = function()
for _, body in ipairs(sm.body.getAllBodies()) do
for _, shape in ipairs(body:getShapes()) do
shape:destroyShape()
end
end
end
acceptScene:select()
end
end)
local action2 = mainScene:createButton(nil, nil, width - 8, fheight * 1.4, false, "loose bodies")
action2:setDown(action1, 4)
action2:attachCallback(function (_, newstate, isZone)
if global and not newstate and isZone then
acceptLabel.text = "del loose bodies"
acceptCode = function()
for _, body in ipairs(sm.body.getAllBodies()) do
if body:isDynamic() then
for _, shape in ipairs(body:getShapes()) do
shape:destroyShape()
end
end
end
end
acceptScene:select()
end
end)
local action3 = mainScene:createButton(nil, nil, width - 8, fheight * 1.4, false, "units")
action3:setDown(action2, 4)
action3:attachCallback(function (_, newstate, isZone)
if global and not newstate and isZone then
acceptLabel.text = "del units"
acceptCode = function()
for _, unit in ipairs(sm.unit.getAllUnits()) do
unit:destroy()
end
end
acceptScene:select()
end
end)
------------------------------------------
local needUnsafeMode = gui:createScene(sm.color.new("#ff9d03"))
needUnsafeMode:createLabel(4, 4, width - 8, height - 8, "to use it, you need to activate unsafe mode", 0x000000, 0xffffff)
local _globalState
local function checkUnsafeMode()
local globalState = not not global
if globalState ~= _globalState then
if globalState then
mainScene:select()
else
needUnsafeMode:select()
end
end
_globalState = globalState
end
checkUnsafeMode()
function callback_loop()
if _endtick then
display.clear()
display.flush()
return
end
checkUnsafeMode()
gui:tick()
if gui:needFlush() then
gui:draw()
display.flush()
end
end
local display = getComponent("display")
display.reset()
display.clearClicks()
display.setSkipAtLags(false)
display.setClicksAllowed(true)
local rx, ry = display.getWidth(), display.getHeight()
local gui = require("gui").new(display)
local styles = require("styles")
local scene = gui:createScene("0000ff")
local switch = scene:createButton(4, (ry / 2) - 16, rx - 8, 32, true, nil, "444444", "ffffff", "44b300", "ffffff")
switch:setCustomStyle(styles.switch)
scene:select()
function callback_loop()
if _endtick then
display.clear()
display.flush()
out(false)
return
end
gui:tick()
out(switch:getState())
if gui:needFlush() then
gui:draw()
display.flush()
end
end
--the example was created for a 128x128 screen
local display = getComponent("display")
display.reset()
display.clearClicks()
display.setSkipAtLags(false)
display.setClicksAllowed(true)
local rx, ry = display.getWidth(), display.getHeight()
local gui = require("gui").new(display)
local styles = require("styles")
local scene = gui:createScene("777777")
local function addWindow()
local window = scene:createWindow(16, 16, 64, 64, "2d2d2d")
window:upPanel("058db8", "ffffff", "test window", true)
window:setDraggable(true)
local closeButton = window:panelButton(7, false, "X", "00a2d5", "0054a1", "00c2ff", "0085ff")
closeButton:attachCallback(function(self, state, inZone)
if not state and inZone then
window:destroy()
end
end)
local oldText
for i = 1, 4 do
local text = window:createText(nil, nil, "switch " .. i .. ": ")
if oldText then text:setDown(oldText) end
local switch = window:createButton(nil, nil, 8, 4, true, nil, "444444", "ffffff", "44b300", "ffffff")
switch:setCustomStyle(styles.switch)
switch:setRight(text)
oldText = text
end
local window2 = window:createWindow(nil, nil, 32, 16, "333377")
window2:setDown(oldText)
window2:upPanel("058db8", "ffffff", "test", true)
window2:minimize(true)
local switch = window2:createButton(nil, nil, 14, 8, true, nil, "444444", "ffffff", "44b300", "ffffff")
switch:setCustomStyle(styles.switch)
end
local addWindowButton = scene:createButton(nil, nil, 32, 32, false, "WINDOW")
addWindowButton:attachCallback(function(self, state, inZone)
if state then
addWindow()
end
end)
function callback_loop()
if _endtick then
display.clear()
display.flush()
out(false)
return
end
gui:tick()
if gui:needFlush() then
gui:draw()
display.flush()
end
end