mirror of
https://github.com/linux-man/LoveFrames.git
synced 2024-12-11 07:24:24 +00:00
edab2be4c7
- Grid span feature - Added offset from parent to update each element (for correct positioning relative to each parent) - OnClick event rework - Multichoice: new function GetChoiceIndex - Added positioning of items in the grid using the align parameter in AddItem - Correct positioning of multichoice-arrow image - Fixed stencil test for list - Fixed a bug that can trigger OnClick events again, even if the mouse cursor is not already hovering over the object - Fixed a bug where "backspace" leads to an incorrect positioning of the entry if the text is overflowed 'textinput' element
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
|