diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..d6a337f
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015-2016 Paul Liverman III
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..4f26852
--- /dev/null
+++ b/README.md
@@ -0,0 +1,52 @@
+# Pop.Box
+
+*Do not mix with [Cola][1].*
+
+Pop.Box is a GUI library for use in the [LÖVE][2] engine, designed to be easy to
+use and require as little code as possible to set up. It is primarily designed
+to make it easy to experiment with GUIs during development.
+
+Supports LÖVE versions 0.9.1 and higher.
+
+## Features
+
+- Quickly set up and align GUI elements.
+- Fully customizable alignment / styling.
+- Moving/resizing elements takes alignment into account.
+- Mouse and key input handling. (**Note**: Work in progress.)
+- Extensible: Make your own elements, skins, extensions, and everything is
+ automatically loaded.
+
+## Usage
+
+The basics:
+
+```lua
+local pop = require "pop"
+-- define LÖVE callbacks here (update, draw, textinput, mouse/key events)
+local window = pop.window():align("center"):setTitle("Welcome!")
+window:addChild(pop.text("Welcome to Pop.Box()!"))
+```
+
+**Note**: Due to this being so early in development...the above example doesn't
+actually work as expected. `window` is a very new element.
+
+For more examples, see the code in `demo`. For documentation, see `docs`.
+
+# Documentation
+
+**Note**: Docs not written just yet. Will be soon.
+
+- [Pop Module][3] (The main module/interface.)
+- [Elements][4] (Basic features of elements/types of elements.)
+- [Skins][5] (A basic system for quickly applying settings to many elements.)
+- [Extensions][7] (A way to load custom code in.)
+- [Drawables][6] (Reference for what can be used as a background/color.)
+
+[1]: https://en.wikipedia.org/wiki/Cola_(programming_language)
+[2]: https://love2d.org/
+[3]: ./docs/Pop.md
+[4]: ./docs/Elements.md
+[5]: ./docs/Skins.md
+[6]: ./docs/Drawables.md
+[7]: ./docs/Extensions.md
diff --git a/build.sh b/build.sh
new file mode 100755
index 0000000..d9ec838
--- /dev/null
+++ b/build.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+cd src
+moonc -t ../lib .
+cd ..
+cp -rf ./lib/pop/* ./demo/pop/
diff --git a/demo/debug-lib/inspect.lua b/demo/debug-lib/inspect.lua
new file mode 100644
index 0000000..4b654b2
--- /dev/null
+++ b/demo/debug-lib/inspect.lua
@@ -0,0 +1,344 @@
+local inspect ={
+ _VERSION = 'inspect.lua 3.0.3',
+ _URL = 'http://github.com/kikito/inspect.lua',
+ _DESCRIPTION = 'human-readable representations of tables',
+ _LICENSE = [[
+ MIT LICENSE
+
+ Copyright (c) 2013 Enrique García Cota
+
+ Permission is hereby granted, free of charge, to any person obtaining a
+ copy of this software and associated documentation files (the
+ "Software"), to deal in the Software without restriction, including
+ without limitation the rights to use, copy, modify, merge, publish,
+ distribute, sublicense, and/or sell copies of the Software, and to
+ permit persons to whom the Software is furnished to do so, subject to
+ the following conditions:
+
+ The above copyright notice and this permission notice shall be included
+ in all copies or substantial portions of the Software.
+
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ ]]
+}
+
+inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end})
+inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end})
+
+-- Apostrophizes the string if it has quotes, but not aphostrophes
+-- Otherwise, it returns a regular quoted string
+local function smartQuote(str)
+ if str:match('"') and not str:match("'") then
+ return "'" .. str .. "'"
+ end
+ return '"' .. str:gsub('"', '\\"') .. '"'
+end
+
+local controlCharsTranslation = {
+ ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n",
+ ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v"
+}
+
+local function escape(str)
+ local result = str:gsub("\\", "\\\\"):gsub("(%c)", controlCharsTranslation)
+ return result
+end
+
+local function isIdentifier(str)
+ return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" )
+end
+
+local function isSequenceKey(k, sequenceLength)
+ return type(k) == 'number'
+ and 1 <= k
+ and k <= sequenceLength
+ and math.floor(k) == k
+end
+
+local defaultTypeOrders = {
+ ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4,
+ ['function'] = 5, ['userdata'] = 6, ['thread'] = 7
+}
+
+local function sortKeys(a, b)
+ local ta, tb = type(a), type(b)
+
+ -- strings and numbers are sorted numerically/alphabetically
+ if ta == tb and (ta == 'string' or ta == 'number') then return a < b end
+
+ local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb]
+ -- Two default types are compared according to the defaultTypeOrders table
+ if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb]
+ elseif dta then return true -- default types before custom ones
+ elseif dtb then return false -- custom types after default ones
+ end
+
+ -- custom types are sorted out alphabetically
+ return ta < tb
+end
+
+-- For implementation reasons, the behavior of rawlen & # is "undefined" when
+-- tables aren't pure sequences. So we implement our own # operator.
+local function getSequenceLength(t)
+ local len = 1
+ local v = rawget(t,len)
+ while v ~= nil do
+ len = len + 1
+ v = rawget(t,len)
+ end
+ return len - 1
+end
+
+local function getNonSequentialKeys(t)
+ local keys = {}
+ local sequenceLength = getSequenceLength(t)
+ for k,_ in pairs(t) do
+ if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end
+ end
+ table.sort(keys, sortKeys)
+ return keys, sequenceLength
+end
+
+local function getToStringResultSafely(t, mt)
+ local __tostring = type(mt) == 'table' and rawget(mt, '__tostring')
+ local str, ok
+ if type(__tostring) == 'function' then
+ ok, str = pcall(__tostring, t)
+ str = ok and str or 'error: ' .. tostring(str)
+ end
+ if type(str) == 'string' and #str > 0 then return str end
+end
+
+local maxIdsMetaTable = {
+ __index = function(self, typeName)
+ rawset(self, typeName, 0)
+ return 0
+ end
+}
+
+local idsMetaTable = {
+ __index = function (self, typeName)
+ local col = {}
+ rawset(self, typeName, col)
+ return col
+ end
+}
+
+local function countTableAppearances(t, tableAppearances)
+ tableAppearances = tableAppearances or {}
+
+ if type(t) == 'table' then
+ if not tableAppearances[t] then
+ tableAppearances[t] = 1
+ for k,v in pairs(t) do
+ countTableAppearances(k, tableAppearances)
+ countTableAppearances(v, tableAppearances)
+ end
+ countTableAppearances(getmetatable(t), tableAppearances)
+ else
+ tableAppearances[t] = tableAppearances[t] + 1
+ end
+ end
+
+ return tableAppearances
+end
+
+local copySequence = function(s)
+ local copy, len = {}, #s
+ for i=1, len do copy[i] = s[i] end
+ return copy, len
+end
+
+local function makePath(path, ...)
+ local keys = {...}
+ local newPath, len = copySequence(path)
+ for i=1, #keys do
+ newPath[len + i] = keys[i]
+ end
+ return newPath
+end
+
+local function processRecursive(process, item, path, visited)
+
+ if item == nil then return nil end
+ if visited[item] then return visited[item] end
+
+ local processed = process(item, path)
+ if type(processed) == 'table' then
+ local processedCopy = {}
+ visited[item] = processedCopy
+ local processedKey
+
+ for k,v in pairs(processed) do
+ processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited)
+ if processedKey ~= nil then
+ processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited)
+ end
+ end
+
+ local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited)
+ setmetatable(processedCopy, mt)
+ processed = processedCopy
+ end
+ return processed
+end
+
+
+
+-------------------------------------------------------------------
+
+local Inspector = {}
+local Inspector_mt = {__index = Inspector}
+
+function Inspector:puts(...)
+ local args = {...}
+ local buffer = self.buffer
+ local len = #buffer
+ for i=1, #args do
+ len = len + 1
+ buffer[len] = tostring(args[i])
+ end
+end
+
+function Inspector:down(f)
+ self.level = self.level + 1
+ f()
+ self.level = self.level - 1
+end
+
+function Inspector:tabify()
+ self:puts(self.newline, string.rep(self.indent, self.level))
+end
+
+function Inspector:alreadyVisited(v)
+ return self.ids[type(v)][v] ~= nil
+end
+
+function Inspector:getId(v)
+ local tv = type(v)
+ local id = self.ids[tv][v]
+ if not id then
+ id = self.maxIds[tv] + 1
+ self.maxIds[tv] = id
+ self.ids[tv][v] = id
+ end
+ return id
+end
+
+function Inspector:putKey(k)
+ if isIdentifier(k) then return self:puts(k) end
+ self:puts("[")
+ self:putValue(k)
+ self:puts("]")
+end
+
+function Inspector:putTable(t)
+ if t == inspect.KEY or t == inspect.METATABLE then
+ self:puts(tostring(t))
+ elseif self:alreadyVisited(t) then
+ self:puts('
')
+ elseif self.level >= self.depth then
+ self:puts('{...}')
+ else
+ if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end
+
+ local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t)
+ local mt = getmetatable(t)
+ local toStringResult = getToStringResultSafely(t, mt)
+
+ self:puts('{')
+ self:down(function()
+ if toStringResult then
+ self:puts(' -- ', escape(toStringResult))
+ if sequenceLength >= 1 then self:tabify() end
+ end
+
+ local count = 0
+ for i=1, sequenceLength do
+ if count > 0 then self:puts(',') end
+ self:puts(' ')
+ self:putValue(t[i])
+ count = count + 1
+ end
+
+ for _,k in ipairs(nonSequentialKeys) do
+ if count > 0 then self:puts(',') end
+ self:tabify()
+ self:putKey(k)
+ self:puts(' = ')
+ self:putValue(t[k])
+ count = count + 1
+ end
+
+ if mt then
+ if count > 0 then self:puts(',') end
+ self:tabify()
+ self:puts(' = ')
+ self:putValue(mt)
+ end
+ end)
+
+ if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing }
+ self:tabify()
+ elseif sequenceLength > 0 then -- array tables have one extra space before closing }
+ self:puts(' ')
+ end
+
+ self:puts('}')
+ end
+end
+
+function Inspector:putValue(v)
+ local tv = type(v)
+
+ if tv == 'string' then
+ self:puts(smartQuote(escape(v)))
+ elseif tv == 'number' or tv == 'boolean' or tv == 'nil' then
+ self:puts(tostring(v))
+ elseif tv == 'table' then
+ self:putTable(v)
+ else
+ self:puts('<',tv,' ',self:getId(v),'>')
+ end
+end
+
+-------------------------------------------------------------------
+
+function inspect.inspect(root, options)
+ options = options or {}
+
+ local depth = options.depth or math.huge
+ local newline = options.newline or '\n'
+ local indent = options.indent or ' '
+ local process = options.process
+
+ if process then
+ root = processRecursive(process, root, {}, {})
+ end
+
+ local inspector = setmetatable({
+ depth = depth,
+ buffer = {},
+ level = 0,
+ ids = setmetatable({}, idsMetaTable),
+ maxIds = setmetatable({}, maxIdsMetaTable),
+ newline = newline,
+ indent = indent,
+ tableAppearances = countTableAppearances(root)
+ }, Inspector_mt)
+
+ inspector:putValue(root)
+
+ return table.concat(inspector.buffer)
+end
+
+setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end })
+
+return inspect
+
diff --git a/demo/main.lua b/demo/main.lua
index e0f79b2..ff5e1f0 100644
--- a/demo/main.lua
+++ b/demo/main.lua
@@ -1,8 +1,14 @@
local lg = love.graphics
-local pop
+local pop, inspect
+
+local debugDraw = false
function love.load()
+ print(love.getVersion())
+
+ inspect = require "debug-lib/inspect"
pop = require "pop"
+ ---[[
local c = pop.box():align("center", "center"):setSize(300, 300)
pop.box(c, {255, 0, 0, 255}):setSize(100, 50)
pop.box(c, {0, 255, 0, 255}):align("center"):setSize(100, 100)
@@ -17,6 +23,45 @@ function love.load()
pop.skin(pop.text("Here's easier-to-code test text in the center!"):align("center", "center", true)) -- 'true' means align to pixel!
w = pop.box(nil, {255, 255, 255, 255}):align(false, "bottom"):setSize(150, 150)
b = pop.box(w, {0, 0, 0, 255}):setMargin(5):setSize(100, 100)
+ --]]
+
+ --c:move(100)
+ pop.box({255, 0, 0, 255}):position(50, 500) -- testing streamlined_get_set extension & optional parents
+ --b:margin(2) -- testing streamlined_get_set extension
+ b:fill() -- testing fill!
+
+ ---[[
+ w2 = pop.window(nil, "Window")
+ w2:move(100, 100)
+ w2:setWidth(500)
+ w2:move(-50, 80)
+ w2:setHeight(500)
+ w2:move(0, -175)
+ w2.title:align("center")
+ w2:position(0, 0)
+ --w2:setAlignment("right")
+ w2:size(200, 120):position(90, 70)
+ --w2:align("center")
+ --w2:setAlignment("center"):align("center")
+
+ --w2.child[1]:setBackground {100, 100, 100, 255}
+ --w2.child[3]:setBackground {160, 140, 40, 255}
+ --]]
+
+ local test = lg.newImage("test.png")
+ G = pop.element():align("right"):move(-2, 2)
+ a = pop.box(G, test):align("right")
+ b = pop.box(G, test):align("right"):move(-25):setWidth(40)
+ c = pop.box(G, test):align("right"):move(0, 25):setHeight(40)
+
+ print(a.horizontal, a.vertical)
+ print(b.horizontal, b.vertical)
+ print(c.horizontal, c.vertical)
+
+ local window = pop.window():align("center", "center"):setTitle("Welcome!")
+ --window:addChild(pop.text("Welcome to Pop.Box()!"))
+
+ --TODO make debugDraw better
end
function love.update(dt)
@@ -25,7 +70,15 @@ end
function love.draw()
pop.draw()
- --pop.debugDraw()
+
+ if debugDraw then
+ pop.debugDraw()
+ --w2:debugDraw()
+ end
+end
+
+function love.mousemoved(x, y, dx, dy)
+ pop.mousemoved(x, y, dx, dy)
end
function love.mousepressed(x, y, button)
@@ -39,6 +92,10 @@ end
function love.keypressed(key)
local handled = pop.keypressed(key)
+ if (key == "d") and (not handled) then
+ debugDraw = not debugDraw
+ end
+
if (key == "escape") and (not handled) then
love.event.quit()
end
diff --git a/demo/pop/elements/box.lua b/demo/pop/elements/box.lua
index bd95152..6b9fb2f 100644
--- a/demo/pop/elements/box.lua
+++ b/demo/pop/elements/box.lua
@@ -21,6 +21,7 @@ do
local w, h = self.background:getDimensions()
w = self.w / w
h = self.h / h
+ graphics.setColor(255, 255, 255, 255)
graphics.draw(self.background, self.x, self.y, 0, w, h)
end
end
@@ -70,11 +71,13 @@ do
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
- __init = function(self, pop, parent, background)
+ __init = function(self, parent, background)
if background == nil then
background = false
end
- _class_0.__parent.__init(self, pop, parent)
+ _class_0.__parent.__init(self, parent)
+ self.w = 20
+ self.h = 20
self.background = background
end,
__base = _base_0,
diff --git a/demo/pop/elements/element.lua b/demo/pop/elements/element.lua
index 163d7a3..e0d5301 100644
--- a/demo/pop/elements/element.lua
+++ b/demo/pop/elements/element.lua
@@ -16,6 +16,14 @@ do
graphics.print("e", self.x, self.y)
return self
end,
+ addChild = function(self, child)
+ self.child[#self.child + 1] = child
+ child.parent = self
+ return self
+ end,
+ getChildren = function(self)
+ return self.child
+ end,
move = function(self, x, y)
if x then
self.x = self.x + x
@@ -105,6 +113,32 @@ do
getSize = function(self)
return self.w, self.h
end,
+ setWidth = function(self, w)
+ local _exp_0 = self.horizontal
+ if "center" == _exp_0 then
+ self.x = self.x - ((w - self.w) / 2)
+ elseif "right" == _exp_0 then
+ self.x = self.x - (w - self.w)
+ end
+ self.w = w
+ return self
+ end,
+ getWidth = function(self)
+ return self.w
+ end,
+ setHeight = function(self, h)
+ local _exp_0 = self.vertical
+ if "center" == _exp_0 then
+ self.y = self.y - ((h - self.h) / 2)
+ elseif "bottom" == _exp_0 then
+ self.y = self.y - (h - self.h)
+ end
+ self.h = h
+ return self
+ end,
+ getHeight = function(self)
+ return self.h
+ end,
adjustSize = function(self, w, h)
local W, H = self:getSize()
if w then
@@ -136,7 +170,7 @@ do
elseif "bottom" == _exp_1 then
self.y = self.y + (self.parent.h - self.h - self.margin)
end
- if toPixel then
+ if toPixel or (toPixel == nil) then
self.x = floor(self.x)
self.y = floor(self.y)
end
@@ -158,6 +192,9 @@ do
end
return self
end,
+ getAlignment = function(self)
+ return self.horizontal, self.vertical
+ end,
setMargin = function(self, margin)
self.margin = margin
self:align()
@@ -165,25 +202,31 @@ do
end,
getMargin = function(self)
return self.margin
+ end,
+ fill = function(self)
+ self.x = self.parent.x + self.margin
+ self.y = self.parent.y + self.margin
+ self.w = self.parent.w - self.margin * 2
+ self.h = self.parent.h - self.margin * 2
end
}
_base_0.__index = _base_0
_class_0 = setmetatable({
- __init = function(self, pop, parent)
+ __init = function(self, parent)
self.parent = parent
self.child = { }
+ self.w = 0
+ self.h = 0
+ self.margin = 0
if parent then
- self.x = parent.x or 0
- self.y = parent.y or 0
+ self.x = parent.x
+ self.y = parent.y
else
self.x = 0
self.y = 0
end
- self.w = 10
- self.h = 10
self.horizontal = "left"
self.vertical = "top"
- self.margin = 0
end,
__base = _base_0,
__name = "element"
diff --git a/demo/pop/elements/text.lua b/demo/pop/elements/text.lua
index ba1cd78..9d5df18 100644
--- a/demo/pop/elements/text.lua
+++ b/demo/pop/elements/text.lua
@@ -12,19 +12,10 @@ do
local _class_0
local _parent_0 = element
local _base_0 = {
- wrap = function(pop)
- return function(parent, ...)
- if type(parent) == "string" then
- return pop.create("text", nil, parent, ...)
- else
- return pop.create("text", parent, ...)
- end
- end
- end,
draw = function(self)
graphics.setColor(self.color)
graphics.setFont(self.font)
- graphics.print(self.text, self.x, self.y)
+ graphics.print(self.txt, self.x, self.y)
return self
end,
debugDraw = function(self)
@@ -38,8 +29,8 @@ do
return self
end,
setSize = function(self)
- local w = self.font:getWidth(self.text)
- local h = self.font:getHeight() * (select(2, self.text:gsub("\n", "\n")) + 1)
+ local w = self.font:getWidth(self.txt)
+ local h = self.font:getHeight() * (select(2, self.txt:gsub("\n", "\n")) + 1)
local _exp_0 = self.horizontal
if "center" == _exp_0 then
self.x = self.x - ((w - self.w) / 2)
@@ -56,16 +47,24 @@ do
self.h = h
return self
end,
+ setWidth = function(self)
+ self:setSize()
+ return self
+ end,
+ setHeight = function(self)
+ self:setSize()
+ return self
+ end,
setText = function(self, text)
if text == nil then
text = ""
end
- self.text = text
+ self.txt = text
self:setSize()
return self
end,
getText = function(self)
- return self.text
+ return self.txt
end,
setFont = function(self, font)
self.font = font
@@ -98,7 +97,7 @@ do
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
- __init = function(self, pop, parent, text, color)
+ __init = function(self, parent, text, color)
if text == nil then
text = ""
end
@@ -110,7 +109,7 @@ do
255
}
end
- _class_0.__parent.__init(self, pop, parent)
+ _class_0.__parent.__init(self, parent)
self.font = graphics.newFont(14)
self:setText(text)
self.color = color
@@ -137,6 +136,16 @@ do
end
})
_base_0.__class = _class_0
+ local self = _class_0
+ self.wrap = function(pop)
+ return function(parent, ...)
+ if type(parent) == "string" then
+ return pop.create("text", nil, parent, ...)
+ else
+ return pop.create("text", parent, ...)
+ end
+ end
+ end
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
diff --git a/demo/pop/elements/window.lua b/demo/pop/elements/window.lua
new file mode 100644
index 0000000..72b2033
--- /dev/null
+++ b/demo/pop/elements/window.lua
@@ -0,0 +1,251 @@
+local graphics
+graphics = love.graphics
+local sub, len
+do
+ local _obj_0 = string
+ sub, len = _obj_0.sub, _obj_0.len
+end
+local path = sub(..., 1, len(...) - len("/window"))
+local element = require(tostring(path) .. "/element")
+local box = require(tostring(path) .. "/box")
+local text = require(tostring(path) .. "/text")
+local left = 1
+local mousemoved_event = true
+do
+ local major, minor, revision = love.getVersion()
+ if (major == 0) and (minor == 10) and ((revision == 0) or (revision == 1)) then
+ left = 1
+ elseif (major == 0) and (minor == 9) then
+ left = "l"
+ if revision == 1 then
+ mousemoved_event = false
+ end
+ else
+ print("elements/window: unrecognized LÖVE version: " .. tostring(major) .. "." .. tostring(minor) .. "." .. tostring(revision))
+ print(" assuming LÖVE version > 0.10.1 (there may be bugs)")
+ end
+end
+local pop_ref = false
+local window
+do
+ local _class_0
+ local _parent_0 = element
+ local _base_0 = {
+ load = function(pop)
+ pop_ref = pop
+ end,
+ debugDraw = function(self)
+ graphics.setLineWidth(0.5)
+ graphics.setColor(0, 0, 0, 100)
+ graphics.rectangle("fill", self.x, self.y, self.w, self.h)
+ graphics.setColor(200, 0, 200, 200)
+ graphics.rectangle("line", self.x, self.y, self.w, self.h)
+ graphics.setColor(255, 200, 255, 255)
+ graphics.print("w", self.x, self.y)
+ return self
+ end,
+ addChild = function(self, child)
+ self.window.child[#self.window.child + 1] = child
+ child.parent = self.window
+ return self
+ end,
+ getChildren = function(self)
+ return self.window.child
+ end,
+ align = function(self, horizontal, vertical, toPixel)
+ _class_0.__parent.__base.align(self, horizontal, vertical, toPixel)
+ for i = 1, #self.child do
+ self.child[i]:align()
+ end
+ self.window:move(nil, self.head:getHeight())
+ return self
+ end,
+ setSize = function(self, w, h)
+ local x = 0
+ local y = 0
+ if w then
+ local _exp_0 = self.horizontal
+ if "center" == _exp_0 then
+ x = x - ((w - self.w) / 2)
+ elseif "right" == _exp_0 then
+ x = x - (w - self.w)
+ end
+ self.head:setWidth(w)
+ self.window:setWidth(w)
+ self.w = w
+ self.x = self.x + x
+ self.title:align()
+ end
+ if h then
+ h = h - self.head:getHeight()
+ local _exp_0 = self.vertical
+ if "center" == _exp_0 then
+ y = y - ((h - self.h) / 2)
+ elseif "right" == _exp_0 then
+ y = y - (h - self.h)
+ end
+ self.window:setHeight(h)
+ self.h = h + self.head:getHeight()
+ self.y = self.y + y
+ end
+ self.head:move(x, y)
+ self.window:move(x, y)
+ return self
+ end,
+ setWidth = function(self, w)
+ local x = 0
+ local _exp_0 = self.horizontal
+ if "center" == _exp_0 then
+ x = x - ((w - self.w) / 2)
+ elseif "right" == _exp_0 then
+ x = x - (w - self.w)
+ end
+ self.head:setWidth(w)
+ self.window:setWidth(w)
+ self.w = w
+ self.x = self.x + x
+ self.title:align()
+ self.head:move(x)
+ self.window:move(x)
+ return self
+ end,
+ setHeight = function(self, h)
+ local y = 0
+ h = h - self.head:getHeight()
+ local _exp_0 = self.vertical
+ if "center" == _exp_0 then
+ y = y - ((h - self.h) / 2)
+ elseif "right" == _exp_0 then
+ y = y - (h - self.h)
+ end
+ self.window:setHeight(h)
+ self.h = h + self.head:getHeight()
+ self.y = self.y + y
+ self.head:move(nil, y)
+ self.title:move(nil, y)
+ self.window:move(nil, y)
+ return self
+ end,
+ setTitle = function(self, title)
+ self.title:setText(title)
+ return self
+ end
+ }
+ _base_0.__index = _base_0
+ setmetatable(_base_0, _parent_0.__base)
+ _class_0 = setmetatable({
+ __init = function(self, parent, title, tBackground, tColor, wBackground)
+ if title == nil then
+ title = "window"
+ end
+ if tBackground == nil then
+ tBackground = {
+ 25,
+ 180,
+ 230,
+ 255
+ }
+ end
+ if tColor == nil then
+ tColor = {
+ 255,
+ 255,
+ 255,
+ 255
+ }
+ end
+ if wBackground == nil then
+ wBackground = {
+ 200,
+ 200,
+ 210,
+ 255
+ }
+ end
+ _class_0.__parent.__init(self, parent)
+ self.head = box(self, tBackground)
+ self.title = text(self, title, tColor)
+ self.window = box(self, wBackground)
+ local height = self.title:getHeight()
+ self.head:setSize(self.w, height)
+ self.window:move(nil, height)
+ self:setSize(100, 80)
+ self.child = {
+ self.head,
+ self.title,
+ self.window
+ }
+ self.head.selected = false
+ if mousemoved_event then
+ self.head.mousemoved = function(self, x, y, dx, dy)
+ if self.selected then
+ self.parent:move(y, dx)
+ return true
+ end
+ return false
+ end
+ self.head.mousepressed = function(self, x, y, button)
+ if button == left then
+ self.selected = true
+ return true
+ end
+ return false
+ end
+ self.head.mousereleased = function(self, x, y, button)
+ if button == left then
+ self.selected = false
+ pop_ref.focused = false
+ return true
+ end
+ return false
+ end
+ else
+ self.head.mx = 0
+ self.head.my = 0
+ self.head.update = function(self)
+ return false
+ end
+ self.head.mousepressed = function(self, x, y, button)
+ if button == left then
+ self.selected = true
+ self.mx = x
+ self.my = y
+ end
+ end
+ self.head.mousereleased = function(self, x, y, button)
+ if button == left then
+ self.selected = false
+ return true
+ end
+ return false
+ end
+ end
+ end,
+ __base = _base_0,
+ __name = "window",
+ __parent = _parent_0
+ }, {
+ __index = function(cls, name)
+ local val = rawget(_base_0, name)
+ if val == nil then
+ local parent = rawget(cls, "__parent")
+ if parent then
+ return parent[name]
+ end
+ else
+ return val
+ end
+ end,
+ __call = function(cls, ...)
+ local _self_0 = setmetatable({}, _base_0)
+ cls.__init(_self_0, ...)
+ return _self_0
+ end
+ })
+ _base_0.__class = _class_0
+ if _parent_0.__inherited then
+ _parent_0.__inherited(_parent_0, _class_0)
+ end
+ window = _class_0
+ return _class_0
+end
diff --git a/demo/pop/extensions/streamlined_get_set.lua b/demo/pop/extensions/streamlined_get_set.lua
new file mode 100644
index 0000000..b23a6d2
--- /dev/null
+++ b/demo/pop/extensions/streamlined_get_set.lua
@@ -0,0 +1,51 @@
+local graphics
+graphics = love.graphics
+local sub, len
+do
+ local _obj_0 = string
+ sub, len = _obj_0.sub, _obj_0.len
+end
+local path = sub(..., 1, len(...) - len("/extensions/streamlined_get_set"))
+local element = require(tostring(path) .. "/elements/element")
+element.__base.position = function(self, x, y)
+ if x or y then
+ return self:setPosition(x, y)
+ else
+ return self:getPosition()
+ end
+end
+element.__base.size = function(self, w, h)
+ if w or h then
+ return self:setSize(w, h)
+ else
+ return self:getSize()
+ end
+end
+element.__base.width = function(self, w)
+ if w then
+ return self:setWidth(w)
+ else
+ return self:getWidth()
+ end
+end
+element.__base.height = function(self, h)
+ if h then
+ return self:setHeight(h)
+ else
+ return self:getHeight()
+ end
+end
+element.__base.alignment = function(self, horizontal, vertical)
+ if horizontal or vertical then
+ return self:setAlignment(horizontal, vertical)
+ else
+ return self:getAlignment()
+ end
+end
+element.__base.margin = function(self, m)
+ if m then
+ return self:setMargin(m)
+ else
+ return self:getMargin()
+ end
+end
diff --git a/demo/pop/init.lua b/demo/pop/init.lua
index d77f6cc..fc09351 100644
--- a/demo/pop/init.lua
+++ b/demo/pop/init.lua
@@ -1,3 +1,6 @@
+if not (love.getVersion) then
+ error("Pop.Box only supports LÖVE versions >= 0.9.1")
+end
local filesystem, graphics
do
local _obj_0 = love
@@ -5,11 +8,15 @@ do
end
local insert
insert = table.insert
+local inheritsFromElement
+inheritsFromElement = require(tostring(...) .. "/util").inheritsFromElement
local path = ...
local pop = { }
pop.elements = { }
pop.skins = { }
+pop.events = { }
pop.screen = false
+pop.focused = false
pop.load = function()
local elements = filesystem.getDirectoryItems(tostring(path) .. "/elements")
for i = 1, #elements do
@@ -21,6 +28,9 @@ pop.load = function()
end
local name = elements[i]:sub(1, -5)
pop.elements[name] = require(tostring(path) .. "/elements/" .. tostring(name))
+ if pop.elements[name].load then
+ pop.elements[name].load(pop)
+ end
print("element loaded: \"" .. tostring(name) .. "\"")
if not (pop[name]) then
if pop.elements[name].wrap then
@@ -55,6 +65,23 @@ pop.load = function()
break
end
end
+ local extensions = filesystem.getDirectoryItems(tostring(path) .. "/extensions")
+ for i = 1, #extensions do
+ local _continue_0 = false
+ repeat
+ if not (extensions[i]:sub(-4) == ".lua") then
+ _continue_0 = true
+ break
+ end
+ local name = extensions[i]:sub(1, -5)
+ require(tostring(path) .. "/extensions/" .. tostring(name))
+ print("extension loaded: \"" .. tostring(name) .. "\"")
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
pop.screen = pop.create("element", false):setSize(graphics.getWidth(), graphics.getHeight())
return print("created \"pop.screen\"")
end
@@ -62,9 +89,14 @@ pop.create = function(element, parent, ...)
if parent == nil then
parent = pop.screen
end
- element = pop.elements[element](pop, parent, ...)
- if parent then
+ if inheritsFromElement(parent) then
+ element = pop.elements[element](parent, ...)
insert(parent.child, element)
+ elseif parent == false then
+ element = pop.elements[element](false, ...)
+ else
+ element = pop.elements[element](pop.screen, parent, ...)
+ insert(pop.screen.child, element)
end
return element
end
@@ -94,19 +126,62 @@ pop.draw = function(element)
end
end
end
-pop.mousepressed = function(x, y, button, element)
- if element == nil then
- element = pop.screen
+pop.mousemoved = function(self, x, y, dx, dy)
+ if pop.focused and pop.focused.mousemoved then
+ return pop.focused:mousemoved(x, y, dx, dy)
end
- print("mousepressed", x, y, button, element)
return false
end
-pop.mousereleased = function(x, y, button, element)
- if element == nil then
+pop.mousepressed = function(x, y, button, element)
+ if not (element) then
+ print("mousepressed", x, y, button)
element = pop.screen
end
- print("mousereleased", x, y, button, element)
- return false
+ local handled = false
+ if (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h) then
+ if element.mousepressed then
+ handled = element:mousepressed(x - element.x, y - element.y, button)
+ end
+ if handled then
+ pop.focused = element
+ pop.events[button] = element
+ else
+ for i = 1, #element.child do
+ handled = pop.mousepressed(x, y, button, element.child[i])
+ if handled then
+ break
+ end
+ end
+ end
+ end
+ return handled
+end
+pop.mousereleased = function(x, y, button)
+ print("mousereleased", x, y, button)
+ local clickedHandled = false
+ local mousereleasedHandled = false
+ do
+ local element = pop.events[button]
+ if element then
+ if element.clicked and (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h) then
+ do
+ clickedHandled = element:clicked(x - element.x, y - element.y, button)
+ if clickedHandled then
+ pop.events[button] = nil
+ end
+ end
+ end
+ if element.mousereleased then
+ do
+ mousereleasedHandled = element:mousereleased(x - element.x, y - element.y, button)
+ if mousereleasedHandled then
+ pop.events[button] = nil
+ end
+ end
+ end
+ end
+ end
+ return clickedHandled, mousereleasedHandled
end
pop.keypressed = function(key)
print("keypressed", key)
diff --git a/demo/pop/util.lua b/demo/pop/util.lua
new file mode 100644
index 0000000..0804d8b
--- /dev/null
+++ b/demo/pop/util.lua
@@ -0,0 +1,19 @@
+local inheritsFromElement
+inheritsFromElement = function(object)
+ if object and object.__class then
+ local cls = object.__class
+ if cls.__name == "element" then
+ return true
+ end
+ while cls.__parent do
+ cls = cls.__parent
+ if cls.__name == "element" then
+ return true
+ end
+ end
+ end
+ return false
+end
+return {
+ inheritsFromElement = inheritsFromElement
+}
diff --git a/demo/test.ogv b/demo/test.ogv
new file mode 100644
index 0000000..1dcdf39
Binary files /dev/null and b/demo/test.ogv differ
diff --git a/demo/test.png b/demo/test.png
new file mode 100644
index 0000000..bf19d3b
Binary files /dev/null and b/demo/test.png differ
diff --git a/docs/Drawables.md b/docs/Drawables.md
new file mode 100644
index 0000000..b1ed96e
--- /dev/null
+++ b/docs/Drawables.md
@@ -0,0 +1,15 @@
+# Supported Drawables
+
+Pop.Box supports three [Drawables][1]: Canvas, Image, and Video.
+
+**Note**: Video and Canvas support are untested.
+
+Additionally, in the place of a Drawable, you can use `false` to not render
+anything, or a table of color values. The color values should be in the format
+LÖVE uses (`{red, green, blue, alpha}`, see [love.graphics.setColor][2]).
+
+(The alpha value is optional and will default to `255`, but not using an alpha
+ is likely to mess up your rendering if an alpha value is used *anywhere else*.)
+
+[1]: https://love2d.org/wiki/Drawable
+[2]: https://love2d.org/wiki/love.graphics.setColor
diff --git a/docs/Elements.md b/docs/Elements.md
new file mode 100644
index 0000000..cc38df4
--- /dev/null
+++ b/docs/Elements.md
@@ -0,0 +1,31 @@
+# Elements
+
+Elements are the core of Pop.Box.
+
+Once `pop` has been required, you can create Elements and interact with them.
+Most elements can be created like this: `local box = pop.box(...)`
+
+However, if an element's name clashes with a function name used in Pop.Box, you
+will have to use `pop.create(type, ...)` where `type` is a string naming the
+element type. (No standard elements do this.)
+
+When creating an element, the first argument is its parent element. If the first
+argument is not an element, it will be treated as the second argument. If it is
+`false`, then an element with no parent will be created.
+
+When no parent is specified, an element's parent is `pop.screen`, which is the
+top-level element of Pop.Box. (This behavior can be modified by custom elements,
+so check their documentation.)
+
+## Available Elements
+
+- [Element][1] (The base of all elements, and useful for alignment.)
+- [Box][2] (A box, can be colored, or display a [supported Drawable][3].)
+- [Text][4] (Plain text, no special features. Useful for basic labels and such.)
+- [Window][5] (A moveable window. Has a title and area for other elements.)
+
+[1]: ./elements/element.md
+[2]: ./elements/box.md
+[3]: ./Drawables.md
+[4]: ./elements/text.md
+[5]: ./elements/window.md
diff --git a/docs/Extensions.md b/docs/Extensions.md
new file mode 100644
index 0000000..778c7b4
--- /dev/null
+++ b/docs/Extensions.md
@@ -0,0 +1,14 @@
+# Extensions
+
+Extensions are simply a way for custom code to be run after loading elements and
+skins. Simply place a `.lua` file in the `extensions` directory, and it will be
+required.
+
+## Standard Extensions
+
+There is only one standard extension, which modifies element classes to add a
+more convenient getter/setter method to most get/set methods. For example,
+instead of `element:getSize()`, just call `element:size()`. Instead of
+`element:setSize(w, h)`, call `element:size(w, h)`.
+
+This is mostly for a demo of what can be possible, but also might be useful.
diff --git a/docs/Pop.md b/docs/Pop.md
new file mode 100644
index 0000000..79d1b69
--- /dev/null
+++ b/docs/Pop.md
@@ -0,0 +1,54 @@
+# Pop Module
+
+This is the main module that allows you to access everything else in Pop.Box.
+Simply require it (`local pop = require "pop"`) and define LÖVE callbacks for:
+
+- `pop.update(dt)`
+- `pop.draw()`
+- `pop.mousemoved(x, y, dx, dy)` (when using LÖVE 0.10.0 or later)
+- `pop.mousepressed(x, y, button)`
+- `pop.mousereleased(x, y, button)`
+- `pop.keypressed(key)`
+- `pop.keyreleased(key)`
+- `pop.textinput(text)`
+
+Every callback returns `true`/`false` for whether or not the event was handled.
+For example, using the `mousepressed` event handler:
+
+```lua
+function love.mousepressed(x, y, button)
+ local handled = pop.mousepressed(x, y, button)
+ if not handled then
+ -- do something useful
+ end
+end
+```
+
+## Creating Elements
+
+Once `pop` has been required, you can create [Elements][1] and interact with
+them. Most elements can be created like this: `local box = pop.box(...)`
+
+For more information, see the [Elements documentation][1].
+
+## Skinning Elements
+
+See the [Skins][2] documentation.
+
+## Custom Elements/Skins/Extensions
+
+Any `.lua` file placed in the `elements`, `skins`, and `extensions` directories
+within the module will be loaded and available as appropriate. See the
+documentation on each for how to make them:
+
+- [Elements][1]
+- [Skins][2]
+- [Extensions][3]
+
+Also of use, there is a separate set of docs about how Pop.Box works under the
+surface: [Pop Module (dev)][4]
+
+[1]: ./Elements.md
+[2]: ./Skins.md
+[3]: ./Extensions.md
+[4]: ./dev/Pop.md
diff --git a/docs/Skins.md b/docs/Skins.md
new file mode 100644
index 0000000..9fdcf04
--- /dev/null
+++ b/docs/Skins.md
@@ -0,0 +1,19 @@
+# Skins
+
+**Note**: This system is mostly an after-thought right now, and will probably
+be replaced with something else entirely.
+
+Skins are simple tables containing information to style a variety of elements.
+Use `pop.skin()` to apply a skin to an element and its children (see
+[Pop Module][3] documentation). Skins are loaded from the `skins` directory.
+
+- `color` - A table of RGBA values (see [love.graphics.setColor][2]), used as a
+ foreground color (currently for `text` elements only).
+- `background` - A [supported Drawable][4], used for backgrounds (currently
+ used on `box` elements only).
+- `font` - A [Font][5].
+
+[2]: https://love2d.org/wiki/love.graphics.setColor
+[3]: ./Pop.md
+[4]: ./Drawables.md
+[5]: https://love2d.org/wiki/Font
diff --git a/docs/dev/Pop.md b/docs/dev/Pop.md
new file mode 100644
index 0000000..36602fc
--- /dev/null
+++ b/docs/dev/Pop.md
@@ -0,0 +1,8 @@
+# Pop Module (dev)
+
+(This document focuses on the structure and code of Pop.Box, not how to use it.
+ See the [regular documentation][1] for that.)
+
+TODO: Write me.
+
+[1]: ../Pop.md
diff --git a/docs/elements/box.md b/docs/elements/box.md
new file mode 100644
index 0000000..e2ec304
--- /dev/null
+++ b/docs/elements/box.md
@@ -0,0 +1,17 @@
+# box
+
+The box element is a rectangle that has a [supported Drawable][1] as its
+background.
+
+## Methods
+
+- `setBackground(background)` Using a supported Drawable (see above), set the
+ background.
+- `getBackground()` Returns the background in use.
+- `setColor(red, green, blue, alpha)` Sets the background to the specified
+ color. `alpha` is optional and defaults to `255`. Alternately, pass a table of
+ color values (ex: `{red, green, blue, alpha}`).
+- `getColor()` Returns red, green, blue, and alpha color values of background.
+ Errors if the background is not a color.
+
+[1]: ../Drawables.md
diff --git a/docs/elements/element.md b/docs/elements/element.md
new file mode 100644
index 0000000..eb87609
--- /dev/null
+++ b/docs/elements/element.md
@@ -0,0 +1,53 @@
+# element
+
+This is the base of all elements, and useful for alignment purposes. It is also
+the element type used for `pop.screen` (the top-level element of Pop.Box). All
+methods here are available on all other elements (any differences are noted on
+their documentation pages).
+
+## Alignment
+
+All elements have a `horizontal` and `vertical` alignment property. These modify
+how elements are aligned and how positions are handled. For example, an element
+aligned to the top-right will return the position of the top-right corner when
+calling `getPosition()`.
+
+- `horizontal` can be `left`, `center`, or `right`. Defaults to `left`.
+- `vertical` can be `top`, `center`, or `bottom`. Defaults to `top`.
+
+## Methods
+
+- `addChild(child)` Adds a child element.
+- `getChildren()` Returns a numerically indexed table of child elements.
+- `move(x, y)` Moves the element (and its children) by specified values.
+ Parameters optional.
+- `setPosition(x, y)` Sets position of the element (and its children) based on
+ the alignment of the element. Parameters optional.
+- `getPosition()` Returns x/y position of the element based on its alignment.
+- `setSize(w, h)` Sets the width/height of the element, element keeps "in-place"
+ based on its alignment. (For example, a right-aligned element will grow to the
+ left.) Parameters optional.
+- `getSize()` Returns the width/height of the element.
+- `setWidth(w)` Sets the width of the element. Element stays "in-place" based on
+ its alignment.
+- `getWidth()` Returns the width of the element.
+- `setHeight(h)` Sets the height of the element. Element stays "in-place" based
+ on its alignment.
+- `getHeight()` Returns the height of the element.
+- `adjustSize(w, h)` Grows the element by a relative width/height. Element stays
+ "in-place" based on its alignment.
+- `align(horizontal, vertical, toPixel)` Aligns the element based on its margin
+ and parent. `toPixel` is a boolean for pixel-perfect alignment, defaulting to
+ true. See above section about alignment for valid values of `horizontal` and
+ `vertical`. A parent element is required for this method.
+- `alignTo(element, horizontal, vertical, toPixel)` Works just like `align()`,
+ except that alignment is based on a specific element instead of the parent.
+ Does not require a parent element.
+- `setAlignment(horizontal, vertical)` Sets alignment values on the element
+ *without* moving the element.
+- `getAlignment()` Returns the `horizontal` and `vertical` alignment of the
+ element.
+- `setMargin(m)` Sets a margin to be used when aligning the element.
+- `getMargin()` Returns the current margin value.
+- `fill()` Resizes and aligns the element to fill its parent's area, with the
+ element's margin taken into account on all sides.
diff --git a/docs/elements/text.md b/docs/elements/text.md
new file mode 100644
index 0000000..fd68c2b
--- /dev/null
+++ b/docs/elements/text.md
@@ -0,0 +1,22 @@
+# text
+
+The text element is plain text in one color. Nothing special.
+
+(**Note**: Other, more advanced text elements are planned, with support for
+ things like line-wrapping and custom coloring and formatting of text.)
+
+## Methods
+
+- `setSize()` Unlike other elements, a size cannot be set. If this is called, it
+ will fix the size of the text if it somehow was modified incorrectly.
+- `setWidth()` Width cannot be set. If called, will fix the size of the text.
+- `setHeight()` Height cannot be set. If called, will fix the size of the text.
+- `setText(text)` Sets the text of the element. Will resize element to fit text.
+ Newlines are supported. Defaults to an empty string.
+- `getText()` Returns the text of the element.
+- `setFont()` Sets the font to be used on this element. Will resize to fit the
+ text and font.
+- `setColor(red, green, blue, alpha)` Sets color of text. `alpha` is optional
+ and defaults to `255`. Alternately, pass a table of color values (ex: `{red,
+ green, blue, alpha}`).
+- `getColor()` Returns red, green, blue, and alpha values of color.
diff --git a/docs/elements/window.md b/docs/elements/window.md
new file mode 100644
index 0000000..dc443bd
--- /dev/null
+++ b/docs/elements/window.md
@@ -0,0 +1,4 @@
+# window
+
+Documentation has not been written yet, as this element is still under heavy
+development.
diff --git a/dummy/main.lua b/dummy/main.lua
new file mode 100644
index 0000000..c7744af
--- /dev/null
+++ b/dummy/main.lua
@@ -0,0 +1,34 @@
+local lg = love.graphics
+local pop, parts
+
+-- pretend parts has been defined
+
+function love.load()
+ pop = require "lib.pop"
+ local width4 = lg.getWidth()/4
+ local height2 = lg.getHeight()/2
+ local PartList = pop.scrollbox():setSize(width4, height2)--:setSizeControl(true) --defaults to true
+ local PartInfo = pop.scrollbox():setSize(width4, height2):move(nil, height2)
+ local CraftInfo = pop.box():setSize(width4, height2):move(lg.getWidth()*3/4)
+ local columns = math.floor(PartList:getWidth()/128)
+ local rows = math.floor(#parts/columns)
+ local grid = pop.grid(PartList, columns, rows)
+ for i = 1, #parts do
+ -- pretend that parts.gui is a box designed to fix in here properly
+ grid:add(parts[i].gui) -- pretend by default, adding something to a grid like this adds it to first available spot
+ -- also, grids auto-resize their children
+ end
+ PartList:add(grid) -- BULLSHIT ?!
+end
+
+-- parts.gui is something like this:
+
+gui = pop.box(newImage()) -- assumes a box can take 'userdata' as first arg and know it is an image
+gui.clicked = function(x, y, button)
+ -- don't care about x/y
+ if button == "l" then --left
+ selected = gui.partReference -- or something
+ elseif button == "r" then --right
+ displayPartInfo() -- something happens to display it in the proper spot
+ end
+end
diff --git a/lib/pop/elements/box.lua b/lib/pop/elements/box.lua
index bd95152..6b9fb2f 100644
--- a/lib/pop/elements/box.lua
+++ b/lib/pop/elements/box.lua
@@ -21,6 +21,7 @@ do
local w, h = self.background:getDimensions()
w = self.w / w
h = self.h / h
+ graphics.setColor(255, 255, 255, 255)
graphics.draw(self.background, self.x, self.y, 0, w, h)
end
end
@@ -70,11 +71,13 @@ do
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
- __init = function(self, pop, parent, background)
+ __init = function(self, parent, background)
if background == nil then
background = false
end
- _class_0.__parent.__init(self, pop, parent)
+ _class_0.__parent.__init(self, parent)
+ self.w = 20
+ self.h = 20
self.background = background
end,
__base = _base_0,
diff --git a/lib/pop/elements/element.lua b/lib/pop/elements/element.lua
index 163d7a3..e0d5301 100644
--- a/lib/pop/elements/element.lua
+++ b/lib/pop/elements/element.lua
@@ -16,6 +16,14 @@ do
graphics.print("e", self.x, self.y)
return self
end,
+ addChild = function(self, child)
+ self.child[#self.child + 1] = child
+ child.parent = self
+ return self
+ end,
+ getChildren = function(self)
+ return self.child
+ end,
move = function(self, x, y)
if x then
self.x = self.x + x
@@ -105,6 +113,32 @@ do
getSize = function(self)
return self.w, self.h
end,
+ setWidth = function(self, w)
+ local _exp_0 = self.horizontal
+ if "center" == _exp_0 then
+ self.x = self.x - ((w - self.w) / 2)
+ elseif "right" == _exp_0 then
+ self.x = self.x - (w - self.w)
+ end
+ self.w = w
+ return self
+ end,
+ getWidth = function(self)
+ return self.w
+ end,
+ setHeight = function(self, h)
+ local _exp_0 = self.vertical
+ if "center" == _exp_0 then
+ self.y = self.y - ((h - self.h) / 2)
+ elseif "bottom" == _exp_0 then
+ self.y = self.y - (h - self.h)
+ end
+ self.h = h
+ return self
+ end,
+ getHeight = function(self)
+ return self.h
+ end,
adjustSize = function(self, w, h)
local W, H = self:getSize()
if w then
@@ -136,7 +170,7 @@ do
elseif "bottom" == _exp_1 then
self.y = self.y + (self.parent.h - self.h - self.margin)
end
- if toPixel then
+ if toPixel or (toPixel == nil) then
self.x = floor(self.x)
self.y = floor(self.y)
end
@@ -158,6 +192,9 @@ do
end
return self
end,
+ getAlignment = function(self)
+ return self.horizontal, self.vertical
+ end,
setMargin = function(self, margin)
self.margin = margin
self:align()
@@ -165,25 +202,31 @@ do
end,
getMargin = function(self)
return self.margin
+ end,
+ fill = function(self)
+ self.x = self.parent.x + self.margin
+ self.y = self.parent.y + self.margin
+ self.w = self.parent.w - self.margin * 2
+ self.h = self.parent.h - self.margin * 2
end
}
_base_0.__index = _base_0
_class_0 = setmetatable({
- __init = function(self, pop, parent)
+ __init = function(self, parent)
self.parent = parent
self.child = { }
+ self.w = 0
+ self.h = 0
+ self.margin = 0
if parent then
- self.x = parent.x or 0
- self.y = parent.y or 0
+ self.x = parent.x
+ self.y = parent.y
else
self.x = 0
self.y = 0
end
- self.w = 10
- self.h = 10
self.horizontal = "left"
self.vertical = "top"
- self.margin = 0
end,
__base = _base_0,
__name = "element"
diff --git a/lib/pop/elements/text.lua b/lib/pop/elements/text.lua
index ba1cd78..9d5df18 100644
--- a/lib/pop/elements/text.lua
+++ b/lib/pop/elements/text.lua
@@ -12,19 +12,10 @@ do
local _class_0
local _parent_0 = element
local _base_0 = {
- wrap = function(pop)
- return function(parent, ...)
- if type(parent) == "string" then
- return pop.create("text", nil, parent, ...)
- else
- return pop.create("text", parent, ...)
- end
- end
- end,
draw = function(self)
graphics.setColor(self.color)
graphics.setFont(self.font)
- graphics.print(self.text, self.x, self.y)
+ graphics.print(self.txt, self.x, self.y)
return self
end,
debugDraw = function(self)
@@ -38,8 +29,8 @@ do
return self
end,
setSize = function(self)
- local w = self.font:getWidth(self.text)
- local h = self.font:getHeight() * (select(2, self.text:gsub("\n", "\n")) + 1)
+ local w = self.font:getWidth(self.txt)
+ local h = self.font:getHeight() * (select(2, self.txt:gsub("\n", "\n")) + 1)
local _exp_0 = self.horizontal
if "center" == _exp_0 then
self.x = self.x - ((w - self.w) / 2)
@@ -56,16 +47,24 @@ do
self.h = h
return self
end,
+ setWidth = function(self)
+ self:setSize()
+ return self
+ end,
+ setHeight = function(self)
+ self:setSize()
+ return self
+ end,
setText = function(self, text)
if text == nil then
text = ""
end
- self.text = text
+ self.txt = text
self:setSize()
return self
end,
getText = function(self)
- return self.text
+ return self.txt
end,
setFont = function(self, font)
self.font = font
@@ -98,7 +97,7 @@ do
_base_0.__index = _base_0
setmetatable(_base_0, _parent_0.__base)
_class_0 = setmetatable({
- __init = function(self, pop, parent, text, color)
+ __init = function(self, parent, text, color)
if text == nil then
text = ""
end
@@ -110,7 +109,7 @@ do
255
}
end
- _class_0.__parent.__init(self, pop, parent)
+ _class_0.__parent.__init(self, parent)
self.font = graphics.newFont(14)
self:setText(text)
self.color = color
@@ -137,6 +136,16 @@ do
end
})
_base_0.__class = _class_0
+ local self = _class_0
+ self.wrap = function(pop)
+ return function(parent, ...)
+ if type(parent) == "string" then
+ return pop.create("text", nil, parent, ...)
+ else
+ return pop.create("text", parent, ...)
+ end
+ end
+ end
if _parent_0.__inherited then
_parent_0.__inherited(_parent_0, _class_0)
end
diff --git a/lib/pop/elements/window.lua b/lib/pop/elements/window.lua
new file mode 100644
index 0000000..72b2033
--- /dev/null
+++ b/lib/pop/elements/window.lua
@@ -0,0 +1,251 @@
+local graphics
+graphics = love.graphics
+local sub, len
+do
+ local _obj_0 = string
+ sub, len = _obj_0.sub, _obj_0.len
+end
+local path = sub(..., 1, len(...) - len("/window"))
+local element = require(tostring(path) .. "/element")
+local box = require(tostring(path) .. "/box")
+local text = require(tostring(path) .. "/text")
+local left = 1
+local mousemoved_event = true
+do
+ local major, minor, revision = love.getVersion()
+ if (major == 0) and (minor == 10) and ((revision == 0) or (revision == 1)) then
+ left = 1
+ elseif (major == 0) and (minor == 9) then
+ left = "l"
+ if revision == 1 then
+ mousemoved_event = false
+ end
+ else
+ print("elements/window: unrecognized LÖVE version: " .. tostring(major) .. "." .. tostring(minor) .. "." .. tostring(revision))
+ print(" assuming LÖVE version > 0.10.1 (there may be bugs)")
+ end
+end
+local pop_ref = false
+local window
+do
+ local _class_0
+ local _parent_0 = element
+ local _base_0 = {
+ load = function(pop)
+ pop_ref = pop
+ end,
+ debugDraw = function(self)
+ graphics.setLineWidth(0.5)
+ graphics.setColor(0, 0, 0, 100)
+ graphics.rectangle("fill", self.x, self.y, self.w, self.h)
+ graphics.setColor(200, 0, 200, 200)
+ graphics.rectangle("line", self.x, self.y, self.w, self.h)
+ graphics.setColor(255, 200, 255, 255)
+ graphics.print("w", self.x, self.y)
+ return self
+ end,
+ addChild = function(self, child)
+ self.window.child[#self.window.child + 1] = child
+ child.parent = self.window
+ return self
+ end,
+ getChildren = function(self)
+ return self.window.child
+ end,
+ align = function(self, horizontal, vertical, toPixel)
+ _class_0.__parent.__base.align(self, horizontal, vertical, toPixel)
+ for i = 1, #self.child do
+ self.child[i]:align()
+ end
+ self.window:move(nil, self.head:getHeight())
+ return self
+ end,
+ setSize = function(self, w, h)
+ local x = 0
+ local y = 0
+ if w then
+ local _exp_0 = self.horizontal
+ if "center" == _exp_0 then
+ x = x - ((w - self.w) / 2)
+ elseif "right" == _exp_0 then
+ x = x - (w - self.w)
+ end
+ self.head:setWidth(w)
+ self.window:setWidth(w)
+ self.w = w
+ self.x = self.x + x
+ self.title:align()
+ end
+ if h then
+ h = h - self.head:getHeight()
+ local _exp_0 = self.vertical
+ if "center" == _exp_0 then
+ y = y - ((h - self.h) / 2)
+ elseif "right" == _exp_0 then
+ y = y - (h - self.h)
+ end
+ self.window:setHeight(h)
+ self.h = h + self.head:getHeight()
+ self.y = self.y + y
+ end
+ self.head:move(x, y)
+ self.window:move(x, y)
+ return self
+ end,
+ setWidth = function(self, w)
+ local x = 0
+ local _exp_0 = self.horizontal
+ if "center" == _exp_0 then
+ x = x - ((w - self.w) / 2)
+ elseif "right" == _exp_0 then
+ x = x - (w - self.w)
+ end
+ self.head:setWidth(w)
+ self.window:setWidth(w)
+ self.w = w
+ self.x = self.x + x
+ self.title:align()
+ self.head:move(x)
+ self.window:move(x)
+ return self
+ end,
+ setHeight = function(self, h)
+ local y = 0
+ h = h - self.head:getHeight()
+ local _exp_0 = self.vertical
+ if "center" == _exp_0 then
+ y = y - ((h - self.h) / 2)
+ elseif "right" == _exp_0 then
+ y = y - (h - self.h)
+ end
+ self.window:setHeight(h)
+ self.h = h + self.head:getHeight()
+ self.y = self.y + y
+ self.head:move(nil, y)
+ self.title:move(nil, y)
+ self.window:move(nil, y)
+ return self
+ end,
+ setTitle = function(self, title)
+ self.title:setText(title)
+ return self
+ end
+ }
+ _base_0.__index = _base_0
+ setmetatable(_base_0, _parent_0.__base)
+ _class_0 = setmetatable({
+ __init = function(self, parent, title, tBackground, tColor, wBackground)
+ if title == nil then
+ title = "window"
+ end
+ if tBackground == nil then
+ tBackground = {
+ 25,
+ 180,
+ 230,
+ 255
+ }
+ end
+ if tColor == nil then
+ tColor = {
+ 255,
+ 255,
+ 255,
+ 255
+ }
+ end
+ if wBackground == nil then
+ wBackground = {
+ 200,
+ 200,
+ 210,
+ 255
+ }
+ end
+ _class_0.__parent.__init(self, parent)
+ self.head = box(self, tBackground)
+ self.title = text(self, title, tColor)
+ self.window = box(self, wBackground)
+ local height = self.title:getHeight()
+ self.head:setSize(self.w, height)
+ self.window:move(nil, height)
+ self:setSize(100, 80)
+ self.child = {
+ self.head,
+ self.title,
+ self.window
+ }
+ self.head.selected = false
+ if mousemoved_event then
+ self.head.mousemoved = function(self, x, y, dx, dy)
+ if self.selected then
+ self.parent:move(y, dx)
+ return true
+ end
+ return false
+ end
+ self.head.mousepressed = function(self, x, y, button)
+ if button == left then
+ self.selected = true
+ return true
+ end
+ return false
+ end
+ self.head.mousereleased = function(self, x, y, button)
+ if button == left then
+ self.selected = false
+ pop_ref.focused = false
+ return true
+ end
+ return false
+ end
+ else
+ self.head.mx = 0
+ self.head.my = 0
+ self.head.update = function(self)
+ return false
+ end
+ self.head.mousepressed = function(self, x, y, button)
+ if button == left then
+ self.selected = true
+ self.mx = x
+ self.my = y
+ end
+ end
+ self.head.mousereleased = function(self, x, y, button)
+ if button == left then
+ self.selected = false
+ return true
+ end
+ return false
+ end
+ end
+ end,
+ __base = _base_0,
+ __name = "window",
+ __parent = _parent_0
+ }, {
+ __index = function(cls, name)
+ local val = rawget(_base_0, name)
+ if val == nil then
+ local parent = rawget(cls, "__parent")
+ if parent then
+ return parent[name]
+ end
+ else
+ return val
+ end
+ end,
+ __call = function(cls, ...)
+ local _self_0 = setmetatable({}, _base_0)
+ cls.__init(_self_0, ...)
+ return _self_0
+ end
+ })
+ _base_0.__class = _class_0
+ if _parent_0.__inherited then
+ _parent_0.__inherited(_parent_0, _class_0)
+ end
+ window = _class_0
+ return _class_0
+end
diff --git a/lib/pop/extensions/streamlined_get_set.lua b/lib/pop/extensions/streamlined_get_set.lua
new file mode 100644
index 0000000..b23a6d2
--- /dev/null
+++ b/lib/pop/extensions/streamlined_get_set.lua
@@ -0,0 +1,51 @@
+local graphics
+graphics = love.graphics
+local sub, len
+do
+ local _obj_0 = string
+ sub, len = _obj_0.sub, _obj_0.len
+end
+local path = sub(..., 1, len(...) - len("/extensions/streamlined_get_set"))
+local element = require(tostring(path) .. "/elements/element")
+element.__base.position = function(self, x, y)
+ if x or y then
+ return self:setPosition(x, y)
+ else
+ return self:getPosition()
+ end
+end
+element.__base.size = function(self, w, h)
+ if w or h then
+ return self:setSize(w, h)
+ else
+ return self:getSize()
+ end
+end
+element.__base.width = function(self, w)
+ if w then
+ return self:setWidth(w)
+ else
+ return self:getWidth()
+ end
+end
+element.__base.height = function(self, h)
+ if h then
+ return self:setHeight(h)
+ else
+ return self:getHeight()
+ end
+end
+element.__base.alignment = function(self, horizontal, vertical)
+ if horizontal or vertical then
+ return self:setAlignment(horizontal, vertical)
+ else
+ return self:getAlignment()
+ end
+end
+element.__base.margin = function(self, m)
+ if m then
+ return self:setMargin(m)
+ else
+ return self:getMargin()
+ end
+end
diff --git a/lib/pop/init.lua b/lib/pop/init.lua
index d77f6cc..fc09351 100644
--- a/lib/pop/init.lua
+++ b/lib/pop/init.lua
@@ -1,3 +1,6 @@
+if not (love.getVersion) then
+ error("Pop.Box only supports LÖVE versions >= 0.9.1")
+end
local filesystem, graphics
do
local _obj_0 = love
@@ -5,11 +8,15 @@ do
end
local insert
insert = table.insert
+local inheritsFromElement
+inheritsFromElement = require(tostring(...) .. "/util").inheritsFromElement
local path = ...
local pop = { }
pop.elements = { }
pop.skins = { }
+pop.events = { }
pop.screen = false
+pop.focused = false
pop.load = function()
local elements = filesystem.getDirectoryItems(tostring(path) .. "/elements")
for i = 1, #elements do
@@ -21,6 +28,9 @@ pop.load = function()
end
local name = elements[i]:sub(1, -5)
pop.elements[name] = require(tostring(path) .. "/elements/" .. tostring(name))
+ if pop.elements[name].load then
+ pop.elements[name].load(pop)
+ end
print("element loaded: \"" .. tostring(name) .. "\"")
if not (pop[name]) then
if pop.elements[name].wrap then
@@ -55,6 +65,23 @@ pop.load = function()
break
end
end
+ local extensions = filesystem.getDirectoryItems(tostring(path) .. "/extensions")
+ for i = 1, #extensions do
+ local _continue_0 = false
+ repeat
+ if not (extensions[i]:sub(-4) == ".lua") then
+ _continue_0 = true
+ break
+ end
+ local name = extensions[i]:sub(1, -5)
+ require(tostring(path) .. "/extensions/" .. tostring(name))
+ print("extension loaded: \"" .. tostring(name) .. "\"")
+ _continue_0 = true
+ until true
+ if not _continue_0 then
+ break
+ end
+ end
pop.screen = pop.create("element", false):setSize(graphics.getWidth(), graphics.getHeight())
return print("created \"pop.screen\"")
end
@@ -62,9 +89,14 @@ pop.create = function(element, parent, ...)
if parent == nil then
parent = pop.screen
end
- element = pop.elements[element](pop, parent, ...)
- if parent then
+ if inheritsFromElement(parent) then
+ element = pop.elements[element](parent, ...)
insert(parent.child, element)
+ elseif parent == false then
+ element = pop.elements[element](false, ...)
+ else
+ element = pop.elements[element](pop.screen, parent, ...)
+ insert(pop.screen.child, element)
end
return element
end
@@ -94,19 +126,62 @@ pop.draw = function(element)
end
end
end
-pop.mousepressed = function(x, y, button, element)
- if element == nil then
- element = pop.screen
+pop.mousemoved = function(self, x, y, dx, dy)
+ if pop.focused and pop.focused.mousemoved then
+ return pop.focused:mousemoved(x, y, dx, dy)
end
- print("mousepressed", x, y, button, element)
return false
end
-pop.mousereleased = function(x, y, button, element)
- if element == nil then
+pop.mousepressed = function(x, y, button, element)
+ if not (element) then
+ print("mousepressed", x, y, button)
element = pop.screen
end
- print("mousereleased", x, y, button, element)
- return false
+ local handled = false
+ if (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h) then
+ if element.mousepressed then
+ handled = element:mousepressed(x - element.x, y - element.y, button)
+ end
+ if handled then
+ pop.focused = element
+ pop.events[button] = element
+ else
+ for i = 1, #element.child do
+ handled = pop.mousepressed(x, y, button, element.child[i])
+ if handled then
+ break
+ end
+ end
+ end
+ end
+ return handled
+end
+pop.mousereleased = function(x, y, button)
+ print("mousereleased", x, y, button)
+ local clickedHandled = false
+ local mousereleasedHandled = false
+ do
+ local element = pop.events[button]
+ if element then
+ if element.clicked and (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h) then
+ do
+ clickedHandled = element:clicked(x - element.x, y - element.y, button)
+ if clickedHandled then
+ pop.events[button] = nil
+ end
+ end
+ end
+ if element.mousereleased then
+ do
+ mousereleasedHandled = element:mousereleased(x - element.x, y - element.y, button)
+ if mousereleasedHandled then
+ pop.events[button] = nil
+ end
+ end
+ end
+ end
+ end
+ return clickedHandled, mousereleasedHandled
end
pop.keypressed = function(key)
print("keypressed", key)
diff --git a/lib/pop/util.lua b/lib/pop/util.lua
new file mode 100644
index 0000000..0804d8b
--- /dev/null
+++ b/lib/pop/util.lua
@@ -0,0 +1,19 @@
+local inheritsFromElement
+inheritsFromElement = function(object)
+ if object and object.__class then
+ local cls = object.__class
+ if cls.__name == "element" then
+ return true
+ end
+ while cls.__parent do
+ cls = cls.__parent
+ if cls.__name == "element" then
+ return true
+ end
+ end
+ end
+ return false
+end
+return {
+ inheritsFromElement = inheritsFromElement
+}
diff --git a/src/pop/elements/box.moon b/src/pop/elements/box.moon
index c70b496..2450eb3 100644
--- a/src/pop/elements/box.moon
+++ b/src/pop/elements/box.moon
@@ -5,8 +5,11 @@ path = sub ..., 1, len(...) - len "/box"
element = require "#{path}/element"
class box extends element
- new: (pop, parent, background=false) =>
- super pop, parent
+ new: (parent, background=false) =>
+ super parent
+
+ @w = 20
+ @h = 20
@background = background
@@ -19,6 +22,7 @@ class box extends element
w, h = @background\getDimensions!
w = @w / w
h = @h / h
+ graphics.setColor 255, 255, 255, 255
graphics.draw @background, @x, @y, 0, w, h
return @
diff --git a/src/pop/elements/element.moon b/src/pop/elements/element.moon
index 9a4265b..be36652 100644
--- a/src/pop/elements/element.moon
+++ b/src/pop/elements/element.moon
@@ -2,23 +2,29 @@ import graphics from love
import floor from math
class element
- new: (pop, parent) =>
+ new: (parent) =>
@parent = parent
@child = {}
+ @w = 0
+ @h = 0
+
+ @margin = 0
+
if parent
- @x = parent.x or 0
- @y = parent.y or 0
+ @x = parent.x
+ @y = parent.y
+ --@horizontal = parent.horizontal
+ --@vertical = parent.vertical
+ --@align!
else
@x = 0
@y = 0
-
- @w = 10
- @h = 10
+ --@horizontal = "left"
+ --@vertical = "top"
@horizontal = "left"
@vertical = "top"
- @margin = 0
debugDraw: =>
graphics.setLineWidth 0.5
@@ -31,6 +37,15 @@ class element
return @
+ addChild: (child) =>
+ @child[#@child+1] = child
+ child.parent = @
+
+ return @
+
+ getChildren: =>
+ return @child
+
move: (x, y) =>
if x
@x = @x + x
@@ -117,6 +132,34 @@ class element
getSize: =>
return @w, @h
+ setWidth: (w) =>
+ switch @horizontal
+ when "center"
+ @x -= (w - @w)/2
+ when "right"
+ @x -= w - @w
+
+ @w = w
+
+ return @
+
+ getWidth: =>
+ return @w
+
+ setHeight: (h) =>
+ switch @vertical
+ when "center"
+ @y -= (h - @h)/2
+ when "bottom"
+ @y -= h - @h
+
+ @h = h
+
+ return @
+
+ getHeight: =>
+ return @h
+
adjustSize: (w, h) =>
W, H = @getSize!
@@ -130,7 +173,7 @@ class element
return @
--TODO note that align requires a parent!
- align: (horizontal, vertical, toPixel) =>
+ align: (horizontal, vertical, toPixel=true) =>
@setAlignment horizontal, vertical
@x = @parent.x
@@ -158,11 +201,11 @@ class element
return @
- alignTo: (element, horizontal, vertical) =>
+ alignTo: (element, horizontal, vertical, toPixel=true) =>
parent = @parent
@parent = element
- @align horizontal, vertical
+ @align horizontal, vertical, toPixel
@parent = parent
@@ -176,6 +219,9 @@ class element
return @
+ getAlignment: =>
+ return @horizontal, @vertical
+
setMargin: (margin) =>
@margin = margin
@align!
@@ -183,3 +229,9 @@ class element
getMargin: =>
return @margin
+
+ fill: =>
+ @x = @parent.x + @margin
+ @y = @parent.y + @margin
+ @w = @parent.w - @margin*2
+ @h = @parent.h - @margin*2
diff --git a/src/pop/elements/text.moon b/src/pop/elements/text.moon
index fc7e6af..80d611e 100644
--- a/src/pop/elements/text.moon
+++ b/src/pop/elements/text.moon
@@ -5,15 +5,16 @@ path = sub ..., 1, len(...) - len "/box"
element = require "#{path}/element"
class text extends element
- wrap: (pop) ->
+ -- this should be completely unneccessary, but I'm keeping it just in case
+ @wrap = (pop) ->
return (parent, ...) ->
if type(parent) == "string"
return pop.create("text", nil, parent, ...)
else
return pop.create("text", parent, ...)
- new: (pop, parent, text="", color={255,255,255,255}) =>
- super pop, parent
+ new: (parent, text="", color={255,255,255,255}) =>
+ super parent
@font = graphics.newFont 14
@setText text
@@ -22,7 +23,7 @@ class text extends element
draw: =>
graphics.setColor @color
graphics.setFont @font
- graphics.print @text, @x, @y
+ graphics.print @txt, @x, @y
return @
@@ -39,8 +40,8 @@ class text extends element
-- unlike most elements, you cannot set a size for text elements
setSize: =>
- w = @font\getWidth @text
- h = @font\getHeight! * (select(2, @text\gsub("\n", "\n")) + 1) --hack to get height of multiple lines
+ w = @font\getWidth @txt
+ h = @font\getHeight! * (select(2, @txt\gsub("\n", "\n")) + 1) --hack to get height of multiple lines
switch @horizontal
when "center"
@@ -59,13 +60,23 @@ class text extends element
return @
+ -- cannot set width!
+ setWidth: =>
+ @setSize!
+ return @
+
+ -- cannot set height!
+ setHeight: =>
+ @setSize!
+ return @
+
setText: (text="") =>
- @text = text
+ @txt = text
@setSize!
return @
getText: =>
- return @text
+ return @txt
setFont: (font) =>
@font = font
diff --git a/src/pop/elements/window.moon b/src/pop/elements/window.moon
new file mode 100644
index 0000000..9a4aa8c
--- /dev/null
+++ b/src/pop/elements/window.moon
@@ -0,0 +1,223 @@
+import graphics from love
+import sub, len from string
+
+path = sub ..., 1, len(...) - len "/window"
+element = require "#{path}/element"
+box = require "#{path}/box"
+text = require "#{path}/text"
+
+-- version compatibility
+left = 1 -- what is the left mouse button?
+mousemoved_event = true -- is the mousemoved event available?
+
+do
+ major, minor, revision = love.getVersion!
+ if (major == 0) and (minor == 10) and ((revision == 0) or (revision == 1))
+ left = 1 -- redundant, but whatever
+ elseif (major == 0) and (minor == 9)
+ left = "l"
+ if revision == 1
+ mousemoved_event = false
+ else
+ print "elements/window: unrecognized LOVE version: #{major}.#{minor}.#{revision}"
+ print " assuming LOVE version > 0.10.1 (there may be bugs)"
+
+pop_ref = false -- reference to pop, loaded by pop.load!
+
+class window extends element
+ load: (pop) ->
+ pop_ref = pop
+
+ --wrap: (pop) ->
+ -- pop_ref = pop -- set our reference to pop (needed for mouse handling)
+ -- return (...) -> -- standard wrapper, nothing special needed
+ -- return pop.create("window", ...)
+
+ new: (parent, title="window", tBackground={25, 180, 230, 255}, tColor={255, 255, 255, 255}, wBackground={200, 200, 210, 255}) =>
+ super parent
+
+ @head = box @, tBackground -- title box at top
+ @title = text @, title, tColor -- text at top
+ @window = box @, wBackground -- main window area
+
+ -- correct placement / sizes of elements
+ height = @title\getHeight!
+ @head\setSize @w, height
+ @window\move nil, height
+ @setSize 100, 80
+
+ -- our child elements are still child elements
+ --TODO change title to be a child of head ?
+ @child = {
+ @head, @title, @window
+ }
+
+ --@selected = false -- whether or not the window title (and thus, the window) has been selected
+ --NOTE all of these commented out, because I realized these event handlers should be attached to the title element
+
+ @head.selected = false -- whether or not the window title (and thus, the window) has been selected
+
+ if mousemoved_event
+ @head.mousemoved = (x, y, dx, dy) =>
+ if @selected
+ -- for some reason, y and dx are actually dx and dy...what the fuck? (note: in version 0.10.0)
+ @parent\move y, dx --dx, dy
+ return true
+ return false
+
+ @head.mousepressed = (x, y, button) =>
+ if button == left
+ @selected = true
+ return true
+ return false
+
+ @head.mousereleased = (x, y, button) =>
+ if button == left
+ @selected = false
+ pop_ref.focused = false -- clear our focus
+ return true
+ return false
+
+ else
+ @head.mx = 0 -- local mouse coordinates when selected
+ @head.my = 0
+
+ @head.update = =>
+ --TODO write me!
+ return false
+
+ @head.mousepressed = (x, y, button) =>
+ if button == left
+ @selected = true
+ @mx = x
+ @my = y
+
+ @head.mousereleased = (x, y, button) => -- this is actually the same for both versions...
+ if button == left
+ @selected = false
+ return true
+ return false
+
+ debugDraw: =>
+ graphics.setLineWidth 0.5
+ graphics.setColor 0, 0, 0, 100
+ graphics.rectangle "fill", @x, @y, @w, @h
+ graphics.setColor 200, 0, 200, 200
+ graphics.rectangle "line", @x, @y, @w, @h
+ graphics.setColor 255, 200, 255, 255
+ graphics.print "w", @x, @y
+
+ return @
+
+ addChild: (child) =>
+ @window.child[#@window.child+1] = child
+ child.parent = @window
+
+ return @
+
+ getChildren: =>
+ return @window.child
+
+ align: (horizontal, vertical, toPixel) =>
+ super horizontal, vertical, toPixel
+
+ for i = 1, #@child
+ @child[i]\align!
+
+ @window\move nil, @head\getHeight!
+
+ return @
+
+ --update: =>
+ -- if selected, set position based on current mouse position relative to position it was when mousepressed
+
+ --mousemoved: (x, y, dx, dy) =>
+ -- if selected, set position based on new mouse position relative to position it was when mousepressed
+
+ --mousepressed: (x, y, button) =>
+ -- if button == "l" -> selected = true, mouse position saved
+
+ --mousereleased: (x, y, button) =>
+ -- if button == "l" -> set position based on position relative to when mousepressed, selected == false
+
+ setSize: (w, h) =>
+ x = 0
+ y = 0
+
+ if w
+ switch @horizontal
+ when "center"
+ x -= (w - @w)/2
+ when "right"
+ x -= w - @w
+
+ @head\setWidth w
+ @window\setWidth w
+ @w = w
+ @x += x
+
+ @title\align!
+
+ if h
+ h = h - @head\getHeight!
+ switch @vertical
+ when "center"
+ y -= (h - @h)/2
+ when "right"
+ y -= h - @h
+
+ @window\setHeight h
+ @h = h + @head\getHeight!
+ @y += y
+
+ @head\move x, y
+ --@title\move x, y
+ @window\move x, y
+
+ return @
+
+ setWidth: (w) =>
+ x = 0
+
+ switch @horizontal
+ when "center"
+ x -= (w - @w)/2
+ when "right"
+ x -= w - @w
+
+ @head\setWidth w
+ @window\setWidth w
+ @w = w
+ @x += x
+
+ @title\align!
+
+ @head\move x
+ --@title\move x
+ @window\move x
+
+ return @
+
+ setHeight: (h) =>
+ y = 0
+
+ h = h - @head\getHeight!
+ switch @vertical
+ when "center"
+ y -= (h - @h)/2
+ when "right"
+ y -= h - @h
+
+ @window\setHeight h
+ @h = h + @head\getHeight!
+ @y += y
+
+ @head\move nil, y
+ @title\move nil, y
+ @window\move nil, y
+
+ return @
+
+ setTitle: (title) =>
+ @title\setText title
+ return @
diff --git a/src/pop/extensions/streamlined_get_set.moon b/src/pop/extensions/streamlined_get_set.moon
new file mode 100644
index 0000000..5391baf
--- /dev/null
+++ b/src/pop/extensions/streamlined_get_set.moon
@@ -0,0 +1,86 @@
+-- adds methods to elements using a single function for get and set operations
+-- ex: instead of getWidth() and setWidth(val), use width() and width(val)
+
+import graphics from love
+import sub, len from string
+
+path = sub ..., 1, len(...) - len "/extensions/streamlined_get_set"
+element = require "#{path}/elements/element"
+box = require "#{path}/elements/box"
+text = require "#{path}/elements/text"
+
+element.__base.position = (x, y) =>
+ if x or y
+ return @setPosition x, y
+ else
+ return @getPosition!
+
+element.__base.size = (w, h) =>
+ if w or h
+ return @setSize w, h
+ else
+ return @getSize!
+
+element.__base.width = (w) =>
+ if w
+ return @setWidth w
+ else
+ return @getWidth!
+
+element.__base.height = (h) =>
+ if h
+ return @setHeight h
+ else
+ return @getHeight!
+
+element.__base.alignment = (horizontal, vertical) =>
+ if horizontal or vertical
+ return @setAlignment horizontal, vertical
+ else
+ return @getAlignment!
+
+-- why is this bit here? Oo
+element.__base.margin = (m) =>
+ if m
+ return @setMargin m
+ else
+ return @getMargin!
+
+--oldinit = element.__init
+--
+--element.__init = (...) ->
+-- object = oldinit ...
+-- value = object.margin
+--
+-- object.margin = setmetatable {:value}, {
+-- __call: (...) ->
+-- print ...
+-- }
+
+element.__base.resize = element.__base.adjustSize
+
+-- box.__base.background -- can't be done!
+
+box.__base.color = (r, g, b, a) =>
+ if r or g or b or a
+ return @setColor r, g, b, a
+ else
+ return @getColor!
+
+text.__base.text = (text) =>
+ if text
+ return @setText text
+ else
+ return @getText!
+
+text.__base.font = (font) =>
+ if font
+ return @setFont font
+ else
+ return @getFont!
+
+text.__base.color = (r, g, b, a) =>
+ if r or g or b or a
+ return @setColor r, g, b, a
+ else
+ return @getColor!
diff --git a/src/pop/init.moon b/src/pop/init.moon
index 8420818..73c8370 100644
--- a/src/pop/init.moon
+++ b/src/pop/init.moon
@@ -1,5 +1,9 @@
+unless love.getVersion
+ error "Pop.Box only supports LOVE versions >= 0.9.1"
+
import filesystem, graphics from love
import insert from table
+import inheritsFromElement from require "#{...}/util"
path = ...
@@ -7,13 +11,15 @@ pop = {}
pop.elements = {}
pop.skins = {}
+pop.events = {}
pop.screen = false -- initialized in pop.load()
---pop.focused ?
+pop.focused = false
-- loads elements and skins, creates pop.screen (intended to only be called once at the beginning)
pop.load = ->
elements = filesystem.getDirectoryItems "#{path}/elements"
+
for i = 1, #elements
-- only attempt to load lua files
unless elements[i]\sub(-4) == ".lua"
@@ -22,6 +28,10 @@ pop.load = ->
-- load into pop.elements table
name = elements[i]\sub 1, -5
pop.elements[name] = require "#{path}/elements/#{name}"
+
+ if pop.elements[name].load
+ pop.elements[name].load pop
+
print "element loaded: \"#{name}\""
-- create pop.element() wrapper if possible
@@ -36,27 +46,47 @@ pop.load = ->
-- works just like above, except no wrappers
skins = filesystem.getDirectoryItems "#{path}/skins"
+
for i = 1, #skins
unless skins[i]\sub(-4) == ".lua"
continue
+
name = skins[i]\sub 1, -5
pop.skins[name] = require "#{path}/skins/#{name}"
+
print "skin loaded: \"#{name}\""
+ -- load extensions by just running them via require
+ 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}"
+
+ print "extension loaded: \"#{name}\""
+
-- main window (called screen because there will be a window element class)
pop.screen = pop.create("element", false)\setSize(graphics.getWidth!, graphics.getHeight!)
print "created \"pop.screen\""
--- creates an element with specified parent (parent can be false)
+-- creates an element with specified parent (parent can be false or non-existent)
pop.create = (element, parent=pop.screen, ...) ->
- element = pop.elements[element](pop, parent, ...)
-
- if parent
+ if inheritsFromElement parent
+ element = pop.elements[element](parent, ...)
insert parent.child, element
+ elseif parent == false
+ element = pop.elements[element](false, ...)
+ else
+ element = pop.elements[element](pop.screen, parent, ...)
+ insert pop.screen.child, element
return element
pop.update = (dt, element=pop.screen) ->
+ --pop.screen\update dt
unless element.excludeUpdate
if element.update
element\update dt
@@ -64,19 +94,56 @@ pop.update = (dt, element=pop.screen) ->
pop.update dt, element.child[i]
pop.draw = (element=pop.screen) ->
+ --pop.screen\draw!
unless element.excludeDraw
if element.draw
element\draw!
for i = 1, #element.child
pop.draw element.child[i]
-pop.mousepressed = (x, y, button, element=pop.screen) ->
- print "mousepressed", x, y, button, element
- return false --TODO event handlers return if they have handled the event!
+pop.mousemoved = (x, y, dx, dy) =>
+ if pop.focused and pop.focused.mousemoved
+ return pop.focused\mousemoved x, y, dx, dy
-pop.mousereleased = (x, y, button, element=pop.screen) ->
- print "mousereleased", x, y, button, element
- return false --TODO event handlers return if they have handled the event!
+ return false
+
+pop.mousepressed = (x, y, button, element) ->
+ unless element
+ print "mousepressed", x, y, button
+ element = pop.screen
+
+ handled = false
+
+ if (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h)
+ if element.mousepressed
+ handled = element\mousepressed x - element.x, y - element.y, button
+ if handled
+ pop.focused = element
+ pop.events[button] = element
+ else
+ for i = 1, #element.child
+ handled = pop.mousepressed x, y, button, element.child[i]
+ if handled
+ break
+
+ return handled
+
+pop.mousereleased = (x, y, button) ->
+ print "mousereleased", x, y, button
+
+ clickedHandled = false
+ mousereleasedHandled = false
+
+ if element = pop.events[button]
+ if element.clicked and (x >= element.x) and (x <= element.x + element.w) and (y >= element.y) and (y <= element.y + element.h)
+ if clickedHandled = element\clicked x - element.x, y - element.y, button
+ pop.events[button] = nil
+
+ if element.mousereleased
+ if mousereleasedHandled = element\mousereleased x - element.x, y - element.y, button
+ pop.events[button] = nil
+
+ return clickedHandled, mousereleasedHandled
pop.keypressed = (key) ->
print "keypressed", key
@@ -90,6 +157,8 @@ pop.textinput = (text) ->
print "textinput", text
return false --TODO event handlers return if they have handled the event!
+--TODO rewrite skin system to not rely on knowing internals of elements,
+-- instead call functions like setColor and setBackground
-- 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
diff --git a/src/pop/skins/default.moon b/src/pop/skins/default.moon
index cb1aa5a..197849e 100644
--- a/src/pop/skins/default.moon
+++ b/src/pop/skins/default.moon
@@ -1,3 +1,6 @@
+-- Note that the "default" name is a bit of a misnomer, as this does not
+-- specify the defaults used in Pop.Box elements (they define their own)
+
import graphics from love
return {
diff --git a/src/pop/util.moon b/src/pop/util.moon
new file mode 100644
index 0000000..b985e13
--- /dev/null
+++ b/src/pop/util.moon
@@ -0,0 +1,17 @@
+inheritsFromElement = (object) ->
+ if object and object.__class
+ cls = object.__class
+
+ if cls.__name == "element"
+ return true
+
+ while cls.__parent
+ cls = cls.__parent
+ if cls.__name == "element"
+ return true
+
+ return false
+
+return {
+ :inheritsFromElement
+}