LoveFrames/loveframes/libraries/utils.lua
Centauri Soldier 5e86bbb0b6 Added State Change Callback Functions
Each state now has an OnOpen and OnClose callback function. These are available to be set by the client but not required. If not set, they will be ignored.
2021-04-07 12:52:43 -07:00

509 lines
16 KiB
Lua

--[[------------------------------------------------
-- Love Frames - A GUI library for LOVE --
-- Copyright (c) 2012-2014 Kenny Shields --
--]]------------------------------------------------
return function(loveframes)
---------- module start ----------
-- util library
--local util = {}
local statecallbacks = {};
--[[---------------------------------------------------------
- func: SetState(name)
- desc: sets the current state
--]]---------------------------------------------------------
function loveframes.SetState(name)
if (loveframes.state ~= name) then
--fire the closing function of the soon-to-be-closed state
if (type(statecallbacks[loveframes.state]) == "table" and
type(statecallbacks[loveframes.state].onclose) == "function") then
--callback must accept the name of the current state and soon-to-be-opened state as args
statecallbacks[loveframes.state].onclose(loveframes.state, name);
end
local oldstate = loveframes.state;
loveframes.state = name
loveframes.base.state = name
--fire the opening function of the newly-opened state
if (type(statecallbacks[loveframes.state]) == "table" and
type(statecallbacks[loveframes.state].onopen) == "function") then
--callback must accept the name of the current state and previous state as args
statecallbacks[loveframes.state].onopen(name, oldstate);
end
end
end
--[[---------------------------------------------------------
- func: SetStateOnOpenCallback(name, func)
- desc: sets a state's opening callback function
--]]---------------------------------------------------------
function loveframes.SetStateOnOpenCallback(name, func)
if (type(name) == "string" and name:gsub("%s", '') ~= '' and
type(func) == "function") then
if (not (statecallbacks[name])) then
statecallbacks[name] = {};
end
statecallbacks[name].onopen = func;
end
end
--[[---------------------------------------------------------
- func: SetStateOnOpenCallback(name, func)
- desc: sets a state's closing callback function
--]]---------------------------------------------------------
function loveframes.SetStateOnCloseCallback(name, func)
if (type(name) == "string" and name:gsub("%s", '') ~= '' and
type(func) == "function") then
if (not (statecallbacks[name])) then
statecallbacks[name] = {};
end
statecallbacks[name].onclose = func;
end
end
--[[---------------------------------------------------------
- func: GetState()
- desc: gets the current state
--]]---------------------------------------------------------
function loveframes.GetState()
return loveframes.state
end
--[[---------------------------------------------------------
- func: SetActiveSkin(name)
- desc: sets the active skin
--]]---------------------------------------------------------
function loveframes.SetActiveSkin(name)
local skin = name and loveframes.skins[name]
if not skin then print("SetActiveSkin: no such skin") return end
loveframes.config["ACTIVESKIN"] = name
local object = loveframes.base
object:SetSkin(name)
end
--[[---------------------------------------------------------
- func: GetActiveSkin()
- desc: gets the active skin
--]]---------------------------------------------------------
function loveframes.GetActiveSkin()
local index = loveframes.config["ACTIVESKIN"]
return loveframes.skins[index]
end
--[[---------------------------------------------------------
- func: BoundingBox(x1, x2, y1, y2, w1, w2, h1, h2)
- desc: checks for a collision between two boxes
- note: I take no credit for this function
--]]---------------------------------------------------------
function loveframes.BoundingBox(x1, x2, y1, y2, w1, w2, h1, h2)
if x1 > x2 + w2 - 1 or y1 > y2 + h2 - 1 or x2 > x1 + w1 - 1 or y2 > y1 + h1 - 1 then
return false
else
return true
end
end
--[[---------------------------------------------------------
- func: GetCollisions(object, table)
- desc: gets all objects colliding with the mouse
--]]---------------------------------------------------------
function loveframes.GetCollisions(object, t)
local x, y = love.mouse.getPosition()
local curstate = loveframes.state
local object = object or loveframes.base
local visible = object.visible
local children = object.children
local internals = object.internals
local objectstate = object.state
local t = t or {}
if objectstate == curstate and visible then
local objectx = object.x
local objecty = object.y
local objectwidth = object.width
local objectheight = object.height
local col = loveframes.BoundingBox(x, objectx, y, objecty, 1, objectwidth, 1, objectheight)
local collide = object.collide
if col and collide then
local clickbounds = object.clickbounds
if clickbounds then
local cx = clickbounds.x
local cy = clickbounds.y
local cwidth = clickbounds.width
local cheight = clickbounds.height
local clickcol = loveframes.BoundingBox(x, cx, y, cy, 1, cwidth, 1, cheight)
if clickcol then
table.insert(t, object)
end
else
table.insert(t, object)
end
end
if children then
for k, v in ipairs(children) do
loveframes.GetCollisions(v, t)
end
end
if internals then
for k, v in ipairs(internals) do
local type = v.type
if type ~= "tooltip" then
loveframes.GetCollisions(v, t)
end
end
end
end
return t
end
--[[---------------------------------------------------------
- func: GetAllObjects(object, table)
- desc: gets all active objects
--]]---------------------------------------------------------
function loveframes.GetAllObjects(object, t)
local object = object or loveframes.base
local internals = object.internals
local children = object.children
local t = t or {}
table.insert(t, object)
if internals then
for k, v in ipairs(internals) do
loveframes.GetAllObjects(v, t)
end
end
if children then
for k, v in ipairs(children) do
loveframes.GetAllObjects(v, t)
end
end
return t
end
--[[---------------------------------------------------------
- func: GetDirectoryContents(directory, table)
- desc: gets the contents of a directory and all of
its subdirectories
--]]---------------------------------------------------------
function loveframes.GetDirectoryContents(dir, t)
local dir = dir
local t = t or {}
local dirs = {}
local files = love.filesystem.getDirectoryItems(dir)
for k, v in ipairs(files) do
local isdir = love.filesystem.getInfo(dir.. "/" ..v) ~= nil and love.filesystem.getInfo(dir.. "/" ..v)["type"] == "directory" --love.filesystem.isDirectory(dir.. "/" ..v)
if isdir == true then
table.insert(dirs, dir.. "/" ..v)
else
local parts = loveframes.SplitString(v, "([.])")
local extension = #parts > 1 and parts[#parts]
if #parts > 1 then
parts[#parts] = nil
end
local name = table.concat(parts, ".")
table.insert(t, {
path = dir,
fullpath = dir.. "/" ..v,
requirepath = loveframes.utf8.gsub(dir, "/", ".") .. "." ..name,
name = name,
extension = extension
})
end
end
for k, v in ipairs(dirs) do
t = loveframes.GetDirectoryContents(v, t)
end
return t
end
--[[---------------------------------------------------------
- func: Round(num, idp)
- desc: rounds a number based on the decimal limit
- note: I take no credit for this function
--]]---------------------------------------------------------
function loveframes.Round(num, idp)
local mult = 10^(idp or 0)
if num >= 0 then
return math.floor(num * mult + 0.5) / mult
else
return math.ceil(num * mult - 0.5) / mult
end
end
--[[---------------------------------------------------------
- func: SplitString(string, pattern)
- desc: splits a string into a table based on a given pattern
- note: I take no credit for this function
--]]---------------------------------------------------------
function loveframes.SplitString(str, pat)
local t = {} -- NOTE: use {n = 0} in Lua-5.0
if pat == " " then
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = loveframes.utf8.find(str, fpat, 1)
while s do
if s ~= #str then
cap = cap .. " "
end
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = loveframes.utf8.find(str, fpat, last_end)
end
if last_end <= #str then
cap = loveframes.utf8.sub(str, last_end)
table.insert(t, cap)
end
else
local fpat = "(.-)" .. pat
local last_end = 1
local s, e, cap = loveframes.utf8.find(str, fpat, 1)
while s do
if s ~= 1 or cap ~= "" then
table.insert(t,cap)
end
last_end = e+1
s, e, cap = loveframes.utf8.find(str, fpat, last_end)
end
if last_end <= #str then
cap = loveframes.utf8.sub(str, last_end)
table.insert(t, cap)
end
end
return t
end
--[[---------------------------------------------------------
- func: RemoveAll()
- desc: removes all gui elements
--]]---------------------------------------------------------
function loveframes.RemoveAll()
loveframes.base.children = {}
loveframes.base.internals = {}
loveframes.hoverobject = false
loveframes.downobject = false
loveframes.modalobject = false
loveframes.inputobject = false
loveframes.hover = false
end
--[[---------------------------------------------------------
- func: TableHasValue(table, value)
- desc: checks to see if a table has a specific value
--]]---------------------------------------------------------
function loveframes.TableHasValue(table, value)
for k, v in pairs(table) do
if v == value then
return true
end
end
return false
end
--[[---------------------------------------------------------
- func: TableHasKey(table, key)
- desc: checks to see if a table has a specific key
--]]---------------------------------------------------------
function loveframes.TableHasKey(table, key)
return table[key] ~= nil
end
--[[---------------------------------------------------------
- func: Error(message)
- desc: displays a formatted error message
--]]---------------------------------------------------------
function loveframes.Error(message)
error("[Love Frames] " ..message)
end
--[[---------------------------------------------------------
- func: GetCollisionCount()
- desc: gets the total number of objects colliding with
the mouse
--]]---------------------------------------------------------
function loveframes.GetCollisionCount()
return loveframes.collisioncount
end
--[[---------------------------------------------------------
- func: GetHover()
- desc: returns loveframes.hover, can be used to check
if the mouse is colliding with a visible
Love Frames object
--]]---------------------------------------------------------
function loveframes.GetHover()
return loveframes.hover
end
--[[---------------------------------------------------------
- func: RectangleCollisionCheck(rect1, rect2)
- desc: checks for a collision between two rectangles
based on two tables containing rectangle sizes
and positions
--]]---------------------------------------------------------
function loveframes.RectangleCollisionCheck(rect1, rect2)
return loveframes.BoundingBox(rect1.x, rect2.x, rect1.y, rect2.y, rect1.width, rect2.width, rect1.height, rect2.height)
end
--[[---------------------------------------------------------
- func: DeepCopy(orig)
- desc: copies a table
- note: I take not credit for this function
--]]---------------------------------------------------------
function loveframes.DeepCopy(orig)
local orig_type = type(orig)
local copy
if orig_type == 'table' then
copy = {}
for orig_key, orig_value in next, orig, nil do
copy[loveframes.DeepCopy(orig_key)] = loveframes.DeepCopy(orig_value)
end
setmetatable(copy, loveframes.DeepCopy(getmetatable(orig)))
else -- number, string, boolean, etc
copy = orig
end
return copy
end
--[[---------------------------------------------------------
- func: GetHoverObject()
- desc: returns loveframes.hoverobject
--]]---------------------------------------------------------
function loveframes.GetHoverObject()
return loveframes.hoverobject
end
--[[---------------------------------------------------------
- func: IsCtrlDown()
- desc: checks for ctrl, for use with multiselect, copy,
paste, and such. On OS X it actually looks for cmd.
--]]---------------------------------------------------------
function loveframes.IsCtrlDown()
if love._os == "OS X" then
return love.keyboard.isDown("lgui") or love.keyboard.isDown("rgui")
end
return love.keyboard.isDown("lctrl") or love.keyboard.isDown("rctrl")
end
function loveframes.Color(s, a)
local r, g, b = string.match(s, '#?(%x%x)(%x%x)(%x%x)')
if r == nil then return end
return tonumber(r, 16) / 0xFF, tonumber(g, 16) / 0xFF, tonumber(b, 16) / 0xFF, a or 1
end
--[[---------------------------------------------------------
- func: draw()
- desc: draws debug information
--]]---------------------------------------------------------
function loveframes.DebugDraw()
local infox = 5
local infoy = 40
local topcol = {type = "None", children = {}, x = 0, y = 0, width = 0, height = 0}
local hoverobject = loveframes.hoverobject
--local objects = loveframes.GetAllObjects()
local version = loveframes.version
local stage = loveframes.stage
local basedir = loveframes.config["DIRECTORY"]
local loveversion = love._version
local fps = love.timer.getFPS()
local deltatime = love.timer.getDelta()
local font = loveframes.basicfontsmall
if hoverobject then
topcol = hoverobject
end
-- show frame docking zones
if topcol.type == "frame" then
for k, v in pairs(topcol.dockzones) do
love.graphics.setLineWidth(1)
love.graphics.setColor(255/255, 0, 0, 100/255)
love.graphics.rectangle("fill", v.x, v.y, v.width, v.height)
love.graphics.setColor(255/255, 0, 0, 255/255)
love.graphics.rectangle("line", v.x, v.y, v.width, v.height)
end
end
-- outline the object that the mouse is hovering over
love.graphics.setColor(255/255, 204/255, 51/255, 255/255)
love.graphics.setLineWidth(2)
love.graphics.rectangle("line", topcol.x - 1, topcol.y - 1, topcol.width + 2, topcol.height + 2)
-- draw main debug box
love.graphics.setFont(font)
love.graphics.setColor(0, 0, 0, 200/255)
love.graphics.rectangle("fill", infox, infoy, 200, 70)
love.graphics.setColor(255/255, 0, 0, 255/255)
love.graphics.print("Love Frames - Debug (" ..version.. " - " ..stage.. ")", infox + 5, infoy + 5)
love.graphics.setColor(255/255, 255/255, 255/255, 255/255)
love.graphics.print("LOVE Version: " ..loveversion, infox + 10, infoy + 20)
love.graphics.print("FPS: " ..fps, infox + 10, infoy + 30)
love.graphics.print("Delta Time: " ..deltatime, infox + 10, infoy + 40)
love.graphics.print("Total Objects: " ..loveframes.objectcount, infox + 10, infoy + 50)
-- draw object information if needed
if topcol.type ~= "base" then
love.graphics.setColor(0, 0, 0, 200/255)
love.graphics.rectangle("fill", infox, infoy + 75, 200, 100)
love.graphics.setColor(255/255, 0, 0, 255/255)
love.graphics.print("Object Information", infox + 5, infoy + 80)
love.graphics.setColor(255/255, 255/255, 255/255, 255/255)
love.graphics.print("Type: " ..topcol.type, infox + 10, infoy + 95)
if topcol.children then
love.graphics.print("# of children: " .. #topcol.children, infox + 10, infoy + 105)
else
love.graphics.print("# of children: 0", infox + 10, infoy + 105)
end
if topcol.internals then
love.graphics.print("# of internals: " .. #topcol.internals, infox + 10, infoy + 115)
else
love.graphics.print("# of internals: 0", infox + 10, infoy + 115)
end
love.graphics.print("X: " ..topcol.x, infox + 10, infoy + 125)
love.graphics.print("Y: " ..topcol.y, infox + 10, infoy + 135)
love.graphics.print("Width: " ..topcol.width, infox + 10, infoy + 145)
love.graphics.print("Height: " ..topcol.height, infox + 10, infoy + 155)
end
end
--return util
---------- module end ----------
end