mirror of
https://github.com/linux-man/LoveFrames.git
synced 2024-11-26 05:14:21 +00:00
5e86bbb0b6
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.
509 lines
16 KiB
Lua
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
|