2016-04-18 06:22:40 +00:00
|
|
|
pop = {
|
2016-04-20 21:12:32 +00:00
|
|
|
_VERSION: 'Pop.Box v0.0.0'
|
|
|
|
_DESCRIPTION: 'GUI library for LOVE, designed for ease of use'
|
|
|
|
_URL: 'http://github.com/Guard13007/Pop.Box'
|
|
|
|
_LICENSE: 'The MIT License (MIT)'
|
|
|
|
_AUTHOR: 'Paul Liverman III'
|
2016-04-18 06:22:40 +00:00
|
|
|
}
|
|
|
|
|
2016-04-02 03:45:43 +00:00
|
|
|
unless love.getVersion
|
2016-04-05 03:59:42 +00:00
|
|
|
error "Pop.Box only supports LOVE versions >= 0.9.1"
|
2016-04-02 03:45:43 +00:00
|
|
|
|
2016-02-23 03:55:05 +00:00
|
|
|
import filesystem, graphics from love
|
|
|
|
import insert from table
|
2016-04-02 03:45:43 +00:00
|
|
|
import inheritsFromElement from require "#{...}/util"
|
2016-02-23 03:55:05 +00:00
|
|
|
|
|
|
|
path = ...
|
|
|
|
|
2016-02-25 01:02:56 +00:00
|
|
|
pop.elements = {}
|
2016-03-29 00:59:12 +00:00
|
|
|
pop.skins = {}
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-04-17 06:43:15 +00:00
|
|
|
pop.screen = false -- initialized in pop.load()
|
2016-03-30 17:50:33 +00:00
|
|
|
pop.focused = false
|
2016-03-29 00:59:12 +00:00
|
|
|
|
|
|
|
-- loads elements and skins, creates pop.screen (intended to only be called once at the beginning)
|
2016-02-25 01:02:56 +00:00
|
|
|
pop.load = ->
|
|
|
|
elements = filesystem.getDirectoryItems "#{path}/elements"
|
2016-03-30 21:01:15 +00:00
|
|
|
|
2016-02-25 01:02:56 +00:00
|
|
|
for i = 1, #elements
|
2016-03-29 00:59:12 +00:00
|
|
|
-- only attempt to load lua files
|
|
|
|
unless elements[i]\sub(-4) == ".lua"
|
|
|
|
continue
|
|
|
|
|
|
|
|
-- load into pop.elements table
|
2016-02-25 01:02:56 +00:00
|
|
|
name = elements[i]\sub 1, -5
|
|
|
|
pop.elements[name] = require "#{path}/elements/#{name}"
|
2016-04-03 07:27:15 +00:00
|
|
|
|
|
|
|
if pop.elements[name].load
|
|
|
|
pop.elements[name].load pop
|
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
print "element loaded: \"#{name}\""
|
|
|
|
|
|
|
|
-- create pop.element() wrapper if possible
|
|
|
|
unless pop[name]
|
|
|
|
if pop.elements[name].wrap
|
|
|
|
pop[name] = pop.elements[name].wrap pop
|
|
|
|
else
|
|
|
|
pop[name] = (...) ->
|
|
|
|
return pop.create(name, ...)
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
print "wrapper created: \"pop.#{name}()\""
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- works just like above, except no load calls or wrappers
|
2016-03-29 00:59:12 +00:00
|
|
|
skins = filesystem.getDirectoryItems "#{path}/skins"
|
2016-03-30 21:01:15 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
for i = 1, #skins
|
|
|
|
unless skins[i]\sub(-4) == ".lua"
|
|
|
|
continue
|
2016-03-30 21:01:15 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
name = skins[i]\sub 1, -5
|
|
|
|
pop.skins[name] = require "#{path}/skins/#{name}"
|
2016-03-30 21:01:15 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
print "skin loaded: \"#{name}\""
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- (again, similar) load extensions by just running them via require
|
2016-04-01 21:14:24 +00:00
|
|
|
extensions = filesystem.getDirectoryItems "#{path}/extensions"
|
|
|
|
|
|
|
|
for i = 1, #extensions
|
|
|
|
unless extensions[i]\sub(-4) == ".lua"
|
|
|
|
continue
|
|
|
|
|
|
|
|
name = extensions[i]\sub 1, -5
|
|
|
|
require "#{path}/extensions/#{name}"
|
|
|
|
|
2016-04-02 03:45:43 +00:00
|
|
|
print "extension loaded: \"#{name}\""
|
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- GUI screen area
|
2016-03-29 00:59:12 +00:00
|
|
|
pop.screen = pop.create("element", false)\setSize(graphics.getWidth!, graphics.getHeight!)
|
|
|
|
print "created \"pop.screen\""
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- creates an element (parent is an element, false, or nil (defaults to pop.screen))
|
2016-03-29 00:59:12 +00:00
|
|
|
pop.create = (element, parent=pop.screen, ...) ->
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if valid parent element
|
2016-04-02 03:45:43 +00:00
|
|
|
if inheritsFromElement parent
|
2016-04-03 06:33:18 +00:00
|
|
|
element = pop.elements[element](parent, ...)
|
2016-04-01 22:48:58 +00:00
|
|
|
insert parent.child, element
|
2016-04-17 06:43:15 +00:00
|
|
|
-- if explicitly no parent
|
2016-04-02 03:45:43 +00:00
|
|
|
elseif parent == false
|
2016-04-03 06:33:18 +00:00
|
|
|
element = pop.elements[element](false, ...)
|
2016-04-20 21:12:32 +00:00
|
|
|
-- else use pop.screen, and "parent" is actually first argument
|
2016-04-01 01:59:16 +00:00
|
|
|
else
|
2016-04-03 06:33:18 +00:00
|
|
|
element = pop.elements[element](pop.screen, parent, ...)
|
2016-04-01 22:48:58 +00:00
|
|
|
insert pop.screen.child, element
|
2016-03-29 00:59:12 +00:00
|
|
|
|
|
|
|
return element
|
|
|
|
|
|
|
|
pop.update = (dt, element=pop.screen) ->
|
|
|
|
unless element.excludeUpdate
|
2016-02-25 01:02:56 +00:00
|
|
|
if element.update
|
|
|
|
element\update dt
|
|
|
|
for i = 1, #element.child
|
|
|
|
pop.update dt, element.child[i]
|
2016-02-23 03:55:05 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
pop.draw = (element=pop.screen) ->
|
|
|
|
unless element.excludeDraw
|
2016-02-25 01:02:56 +00:00
|
|
|
if element.draw
|
2016-03-29 00:59:12 +00:00
|
|
|
element\draw!
|
2016-02-25 01:02:56 +00:00
|
|
|
for i = 1, #element.child
|
2016-03-29 00:59:12 +00:00
|
|
|
pop.draw element.child[i]
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
--TODO implement a way for an element to attach itself to mousemoved events
|
2016-04-17 08:27:06 +00:00
|
|
|
pop.mousemoved = (x, y, dx, dy) ->
|
2016-04-02 03:45:43 +00:00
|
|
|
if pop.focused and pop.focused.mousemoved
|
|
|
|
return pop.focused\mousemoved x, y, dx, dy
|
|
|
|
|
|
|
|
return false
|
|
|
|
|
|
|
|
pop.mousepressed = (x, y, button, element) ->
|
2016-04-20 21:12:32 +00:00
|
|
|
-- start at the screen, print that we received an event
|
2016-04-02 03:45:43 +00:00
|
|
|
unless element
|
|
|
|
print "mousepressed", x, y, button
|
|
|
|
element = pop.screen
|
2016-03-30 21:38:46 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- have we handled the event?
|
2016-03-30 17:50:33 +00:00
|
|
|
handled = false
|
2016-04-02 03:45:43 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if it was inside the current element..
|
2016-03-30 17:50:33 +00:00
|
|
|
if (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h)
|
2016-04-20 21:12:32 +00:00
|
|
|
-- check its child elements in reverse order, returning if something handles it
|
2016-04-18 04:57:32 +00:00
|
|
|
for i = #element.child, 1, -1
|
2016-04-20 21:12:32 +00:00
|
|
|
if handled = pop.mousepressed x, y, button, element.child[i]
|
|
|
|
return handled
|
2016-04-18 04:57:32 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if a child hasn't handled it yet
|
2016-04-18 04:57:32 +00:00
|
|
|
unless handled
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if we can handle it and are visible, try to handle it, and set pop.focused
|
2016-04-18 04:57:32 +00:00
|
|
|
if element.mousepressed and (not element.excludeDraw)
|
|
|
|
if handled = element\mousepressed x - element.x, y - element.y, button
|
|
|
|
pop.focused = element
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-04-27 17:53:19 +00:00
|
|
|
-- return whether or not we have handled the event
|
2016-04-02 03:45:43 +00:00
|
|
|
return handled
|
2016-03-30 21:38:46 +00:00
|
|
|
|
2016-04-18 03:37:55 +00:00
|
|
|
pop.mousereleased = (x, y, button, element) ->
|
2016-04-20 21:12:32 +00:00
|
|
|
-- we are trying to handle a clicked or mousereleased event
|
2016-04-02 03:45:43 +00:00
|
|
|
clickedHandled = false
|
|
|
|
mousereleasedHandled = false
|
2016-03-30 21:38:46 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if we have an element, and are within its bounds
|
2016-04-18 03:37:55 +00:00
|
|
|
if element
|
|
|
|
if (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h)
|
2016-04-20 21:12:32 +00:00
|
|
|
-- check its children in reverse for handling a clicked or mousereleased event
|
2016-04-18 04:57:32 +00:00
|
|
|
for i = #element.child, 1, -1
|
|
|
|
clickedHandled, mousereleasedHandled = pop.mousereleased x, y, button, element.child[i]
|
|
|
|
if clickedHandled or mousereleasedHandled
|
2016-04-20 21:12:32 +00:00
|
|
|
return clickedHandled, mousereleasedHandled
|
2016-04-18 03:37:55 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if that doesn't work, we try to handle it ourselves
|
2016-04-18 04:57:32 +00:00
|
|
|
unless clickedHandled or mousereleasedHandled
|
2016-04-20 21:12:32 +00:00
|
|
|
-- clicked only happens on visible elements, mousereleased happens either way
|
2016-04-18 04:57:32 +00:00
|
|
|
if element.clicked and (not element.excludeDraw)
|
|
|
|
clickedHandled = element\clicked x - element.x, y - element.y, button
|
|
|
|
if element.mousereleased
|
|
|
|
mousereleasedHandled = element\mousereleased x - element.x, y - element.y, button
|
2016-03-30 21:38:46 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- if we clicked, we're focused!
|
2016-04-18 04:57:32 +00:00
|
|
|
if clickedHandled
|
|
|
|
pop.focused = element
|
2016-04-27 17:53:19 +00:00
|
|
|
--NOTE this might cause an error in the above for loop!
|
|
|
|
-- basically, move focused element to front of its parent's child
|
|
|
|
--element.parent\focusChild element
|
|
|
|
--table.insert element.parent, element.parent\removeChild(element),
|
2016-04-18 03:37:55 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- else, default to pop.screen to begin! (and print that we received an event)
|
|
|
|
else
|
|
|
|
print "mousereleased", x, y, button
|
|
|
|
pop.mousereleased x, y, button, pop.screen
|
2016-03-30 21:38:46 +00:00
|
|
|
|
2016-04-02 03:45:43 +00:00
|
|
|
return clickedHandled, mousereleasedHandled
|
2016-02-25 01:02:56 +00:00
|
|
|
|
|
|
|
pop.keypressed = (key) ->
|
2016-03-29 00:59:12 +00:00
|
|
|
print "keypressed", key
|
2016-04-17 07:29:27 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- keypressed events must be on visible elements
|
2016-04-17 07:29:27 +00:00
|
|
|
element = pop.focused
|
|
|
|
if element and element.keypressed and (not element.excludeDraw)
|
|
|
|
return element.keypressed key
|
|
|
|
|
|
|
|
return false
|
2016-02-25 01:02:56 +00:00
|
|
|
|
|
|
|
pop.keyreleased = (key) ->
|
2016-03-29 00:59:12 +00:00
|
|
|
print "keyreleased", key
|
2016-04-17 07:29:27 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- keyreleased events are always called
|
2016-04-17 07:29:27 +00:00
|
|
|
element = pop.focused
|
|
|
|
if element and element.keyreleased
|
|
|
|
return element.keyreleased key
|
|
|
|
|
|
|
|
return false
|
2016-02-25 01:02:56 +00:00
|
|
|
|
|
|
|
pop.textinput = (text) ->
|
2016-03-29 00:59:12 +00:00
|
|
|
print "textinput", text
|
2016-04-17 07:29:27 +00:00
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
-- textinput events must be on visible elements
|
2016-04-17 07:29:27 +00:00
|
|
|
element = pop.focused
|
|
|
|
if element and element.textinput and (not element.excludeDraw)
|
|
|
|
return element.textinput text
|
|
|
|
|
|
|
|
return false
|
2016-03-29 00:59:12 +00:00
|
|
|
|
2016-04-04 08:16:32 +00:00
|
|
|
--TODO rewrite skin system to not rely on knowing internals of elements,
|
|
|
|
-- instead call functions like setColor and setBackground
|
2016-03-29 00:59:12 +00:00
|
|
|
-- skins an element (and its children unless depth == true or 0)
|
|
|
|
-- depth can be an integer for how many levels to go down when skinning
|
|
|
|
-- defaults to pop.screen and the default skin
|
|
|
|
pop.skin = (element=pop.screen, skin=pop.skins.default, depth) ->
|
|
|
|
if element.background and skin.background
|
2016-02-25 01:02:56 +00:00
|
|
|
element.background = skin.background
|
2016-03-29 00:59:12 +00:00
|
|
|
if element.color and skin.color
|
2016-02-25 01:02:56 +00:00
|
|
|
element.color = skin.color
|
2016-03-29 00:59:12 +00:00
|
|
|
if element.font and skin.font
|
2016-02-25 01:02:56 +00:00
|
|
|
element.font = skin.font
|
2016-02-23 03:55:05 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
unless depth or (depth == 0)
|
|
|
|
if depth == tonumber depth
|
|
|
|
for i = 1, #element.child
|
|
|
|
pop.skin element.child[i], skin, depth - 1
|
|
|
|
else
|
|
|
|
for i = 1, #element.child
|
|
|
|
pop.skin element.child[i], skin, false
|
2016-02-25 01:02:56 +00:00
|
|
|
|
2016-03-29 00:59:12 +00:00
|
|
|
pop.debugDraw = (element=pop.screen) ->
|
2016-02-25 01:02:56 +00:00
|
|
|
if element.debugDraw
|
|
|
|
element\debugDraw!
|
|
|
|
else
|
|
|
|
graphics.setLineWidth 1
|
2016-03-29 00:59:12 +00:00
|
|
|
graphics.setLineColor 0, 0, 0, 100
|
2016-02-25 01:02:56 +00:00
|
|
|
graphics.rectangle "fill", element.x, element.y, element.w, element.h
|
|
|
|
graphics.setColor 150, 150, 150, 150
|
|
|
|
graphics.rectangle "line", element.x, element.y, element.w, element.h
|
|
|
|
graphics.setColor 200, 200, 200, 255
|
|
|
|
graphics.print ".", element.x, element.y
|
|
|
|
|
|
|
|
for i = 1, #element.child
|
|
|
|
pop.debugDraw element.child[i]
|
|
|
|
|
2016-04-20 21:12:32 +00:00
|
|
|
pop.printElementTree = (element=pop.screen, depth=0) ->
|
2016-04-18 04:57:32 +00:00
|
|
|
cls = element.__class.__name
|
|
|
|
|
|
|
|
if cls == "text"
|
|
|
|
cls = cls .. " (\"#{element\getText!\gsub "\n", "\\n"}\")"
|
|
|
|
elseif cls == "box"
|
|
|
|
bg = element\getBackground!
|
|
|
|
|
|
|
|
if type(bg) == "table"
|
|
|
|
bg = "#{bg[1]}, #{bg[2]}, #{bg[3]}, #{bg[4]}"
|
|
|
|
|
|
|
|
cls = cls .. " (#{bg})"
|
|
|
|
|
|
|
|
print string.rep("-", depth) .. " #{cls}"
|
|
|
|
|
|
|
|
for i = 1, #element.child
|
|
|
|
pop.printElementStack element.child[i], depth + 1
|
|
|
|
|
2016-02-25 01:02:56 +00:00
|
|
|
pop.load!
|
|
|
|
|
|
|
|
return pop
|