mirror of
https://github.com/TangentFoxy/Pop.Box.git
synced 2024-12-15 12:44:20 +00:00
pretty much ready (hopefully)
This commit is contained in:
parent
145533195d
commit
1ba7314243
35
README.md
35
README.md
@ -6,32 +6,27 @@ Pop.Box attempts to make a GUI system for use in the [LÖVE][2] engine that is
|
||||
easy to use, requiring as little code as possible to get working, but also
|
||||
extensible, allowing for complex interfaces to be built in it.
|
||||
|
||||
I've never written a GUI library before..so we'll see how that goes.
|
||||
## Features
|
||||
|
||||
- Quickly set up and align GUI elements.
|
||||
- Fully customizable alignment / styling.
|
||||
- Supports moving/resizing things in ways that take the alignment headache away,
|
||||
without trying to do too much and become bloated.
|
||||
- Supports custom elements/skins, make your own and move them into the
|
||||
appropriate directories for them to be automatically loaded.
|
||||
|
||||
## Usage
|
||||
|
||||
```lua
|
||||
local pop = require "pop"
|
||||
-- define love callbacks here (update, draw, textinput, mouse/key events)
|
||||
-- define LÖVE callbacks here (update, draw, textinput, mouse/key events)
|
||||
local box = pop.box()
|
||||
```
|
||||
|
||||
## Using
|
||||
|
||||
Elements store position, size, and child elements. When moved, an element's
|
||||
children also move. Elements have simple methods for adjusting their position
|
||||
and size.
|
||||
|
||||
`pop.window` - An element representing the game window. It will not auto-resize.
|
||||
|
||||
Any element (and its children) with `excludeMovement == true` will not be moved
|
||||
except when its `move()` or `setPosition()` are called.
|
||||
|
||||
Children render on top of their parents. (Rendering starts at `pop.window` and
|
||||
loops down.) Any element (and its children) with `excludeRendering == true` will
|
||||
not be rendered.
|
||||
|
||||
See [Elements.md][3] for the standard methods each element has, and what
|
||||
elements are available.
|
||||
Docs: [pop Module][3], [Elements][4], [Skins][5]
|
||||
|
||||
[1]: https://en.wikipedia.org/wiki/Cola_(programming_language)
|
||||
[2]: https://love2d.org/
|
||||
[3]: ./Elements.md
|
||||
[3]: ./docs/Pop.md
|
||||
[4]: ./docs/Elements.md
|
||||
[5]: ./docs/Skins.md
|
||||
|
@ -1,30 +1,41 @@
|
||||
# Elements
|
||||
|
||||
Elements are the core of Pop.Box.
|
||||
|
||||
- Elements are arranged hierarchically.
|
||||
- When an element is moved, its child elements move with it.
|
||||
- When an element is resized, it resizes in a way that makes sense based on its
|
||||
alignment.
|
||||
- Elements are drawn from the top down (meaning child elements will always draw
|
||||
on top of their parents.)
|
||||
|
||||
The alignment stuff is much easier explained by experimenting or running the
|
||||
demo, please check it out!
|
||||
|
||||
All elements have the following standard methods:
|
||||
|
||||
- `move(x, y)` - Moves from current position by `x`/`y`.
|
||||
- `move(x, y)` - Moves the element by `x`/`y`.
|
||||
- `setPosition(x, y)` - Sets the `x`/`y` position based on current alignment.
|
||||
- `getPosition()` - Returns `x` and `y` position based on current alignment.
|
||||
- `setSize(x, y)` - Sets the witdh/height of the element. Will stretch based on
|
||||
alignment.
|
||||
alignment (run the demo to see an example).
|
||||
- `getSize()` - Returns width and height of the element.
|
||||
- `align(alignment)` - Sets alignment based on the parent's position and size.
|
||||
`alignment` is a string specifying how to align: `top-left`, `top-center`,
|
||||
`top-right`, `left-center`, `center`, `right-center`, `bottom-left`,
|
||||
`bottom-center`, `bottom-right`
|
||||
- `alignTo(element, alignment)` - Sets alignment based on an element's position
|
||||
and size. Same `alignment`'s as `align()`.
|
||||
- `setAlignment(alignment)` - Sets alignment *value* to this, but does not move
|
||||
the element.
|
||||
- `align(horizontal, vertical)` - Sets alignment based on the parent's position
|
||||
and size. Valid `horizontal` strings: `left`, `center`, `right`. Valid
|
||||
`vertical` strings: `top`, `center`, `bottom`.
|
||||
- `alignTo(element, horizontal, vertical)` - Sets alignment based on an
|
||||
element's position and size. Same `horizontal`/`vertical` strings as `align()`
|
||||
- `setAlignment(horizontal, vertical)` - Sets alignment *values* to this, but
|
||||
does not move the element. Same `horizontal`/`vertical` strings as `align()`
|
||||
- `setSkin(skin)` - Sets the skin (see [Skins.md][1]) used for this element.
|
||||
|
||||
**Note**! Calls to `align()`, `alignTo()`, and `setAlignment()` change what
|
||||
positions will be returned, and how positioning and resizing will work.
|
||||
positions will be returned, and how positioning and resizing will work. Run the
|
||||
demo to see how these affect things.
|
||||
|
||||
## Box Element
|
||||
|
||||
Box is the simplest element, a rectangular area that can be styled or used for
|
||||
alignment.
|
||||
Box is the simplest element, a rectangular area.
|
||||
|
||||
`pop.box(parent, skin)`
|
||||
If `parent` not specified, uses `pop.window` (the top level element).
|
||||
@ -32,16 +43,17 @@ If `skin` is not specified, uses `pop.currentSkin` (see [Skins.md][1]).
|
||||
|
||||
TODO Make it possible to just specify skin?
|
||||
|
||||
## Text Element
|
||||
## Text Element (NOT DEFINED YET!)
|
||||
|
||||
Text is used to draw text. Its styling is based on its skin, see [Skins.md][1]
|
||||
for information on how to set that up.
|
||||
Text is used to draw text.
|
||||
|
||||
`pop.text(parent, text, skin)`
|
||||
If `parent` not specified, uses `pop.window` (the top level element).
|
||||
If `skin` is not specified, uses `pop.currentSkin` (see [Skins.md][1]).
|
||||
|
||||
TODO Make it possible to just specify text, or just text and skin?
|
||||
TODO Make it possible to use setting size on text to actually calculate what
|
||||
font size will make that work?
|
||||
|
||||
# Excluding Movement/Rendering
|
||||
|
47
docs/Pop.md
Normal file
47
docs/Pop.md
Normal file
@ -0,0 +1,47 @@
|
||||
# `pop` Module
|
||||
|
||||
`pop` is the name of the Pop.Box module. You are expected to require it and then
|
||||
define the following callbacks in LÖVE's callbacks: `pop.update(dt)`,
|
||||
`pop.draw()`, `pop.textinput(text)`, `pop.mousepressed(button, x, y)`,
|
||||
`pop.mousereleased(button, x, y)`, `pop.keypressed(key)`, `pop.keyreleased(key)`
|
||||
|
||||
Once that has been done (or at the very least, `pop` has been required and a
|
||||
callback is set up for `pop.draw`), you can start creating [Elements][1] and
|
||||
drawing them.
|
||||
|
||||
Also look into [Skins][2], which control how elements are rendered.
|
||||
|
||||
## `pop` Values / Methods
|
||||
|
||||
- `pop.window` is the top level element. It essentially represents the game
|
||||
window.
|
||||
- `pop.currentSkin` holds a string specifying the currently in-use skin.
|
||||
Basically, it's a shortcut so you don't have to specify a skin with every
|
||||
call to construct an element.
|
||||
- `pop.create(element, parent, ...)` is how elements are actually created,
|
||||
`element` is a string naming the desired element. There are wrappers around
|
||||
any element that doesn't conflict with a key in the `pop` module so that you
|
||||
can call `pop.element(parent, ...)` instead.
|
||||
- `pop.load()` loads elements and skins, and sets up `pop.window`. This is used
|
||||
internally, and will probably lead to issues if you use it (namely, destroying
|
||||
Pop.Box's access to any existing GUI).
|
||||
|
||||
## `pop` Callbacks
|
||||
|
||||
- `pop.update(dt)` is used so that any element can have a frame-by-frame update
|
||||
attached to it.
|
||||
- `pop.draw()` is used to draw everything.
|
||||
- `pop.textinput(text)` is used to grab text input for any focused element that
|
||||
can accept it.
|
||||
- `pop.mousepressed(button, x, y)` is used to detect and handle when an element
|
||||
is clicked on. (Not actually used yet.)
|
||||
|
||||
- `pop.mousereleased(button, x, y)` is not used yet, but probably will be used
|
||||
in the future.
|
||||
- `pop.keypressed(key)` is not used yet, but probably will be used in the
|
||||
future.
|
||||
- `pop.keyreleased(key)` is also not used yet, but probably will be used in the
|
||||
future.
|
||||
|
||||
[1]: ./Elements.md
|
||||
[2]: ./Skins.md
|
@ -11,6 +11,8 @@ require it.
|
||||
- `draw(element)` - If defined, will be used to render an element instead of the
|
||||
standard drawing method.
|
||||
|
||||
**Note**: Supported Drawables: Canvas, Image, Video
|
||||
|
||||
TODO various text style infos!
|
||||
|
||||
[1]: https://love2d.org/wiki/Drawable
|
@ -1,11 +1,39 @@
|
||||
local lg = love.graphics
|
||||
|
||||
local path = string.sub(..., 1, string.len(...) - string.len("/elements/box"))
|
||||
local class = require(path .. "/lib/middleclass")
|
||||
local element = require(path .. "/elements/element")
|
||||
|
||||
local box = class("pop.box", element) --TODO follow middleclass standards!?@@R/
|
||||
local box = class("pop.box", element)
|
||||
|
||||
function box:initialize(pop, parent, skin)
|
||||
element.initialize(self, pop, parent, skin)
|
||||
end
|
||||
|
||||
function box:draw() --TODO these ifs are probably wrong
|
||||
if type(self.skin.background) == "table" then
|
||||
lg.setColor(self.skin.background)
|
||||
lg.rectangle("fill", self.x, self.y, self.w, self.h)
|
||||
else
|
||||
lg.setColor(255, 255, 255, 255)
|
||||
local w, h = self.skin.background:getDimensions()
|
||||
-- scale!
|
||||
w = self.w/w
|
||||
h = self.h/h
|
||||
lg.draw(self.skin.background, self.x, self.y, 0, w, h)
|
||||
end
|
||||
|
||||
if type(self.skin.foreground) == "table" then
|
||||
lg.setColor(self.skin.foreground)
|
||||
lg.rectangle("fill", self.x, self.y, self.w, self.h)
|
||||
else
|
||||
lg.setColor(255, 255, 255, 255)
|
||||
local w, h = self.skin.foreground:getDimensions()
|
||||
-- scale!
|
||||
w = self.w/w
|
||||
h = self.h/h
|
||||
lg.draw(self.skin.foreground, self.x, self.y, 0, w, h)
|
||||
end
|
||||
end
|
||||
|
||||
return box
|
||||
|
@ -1,7 +1,7 @@
|
||||
local path = string.sub(..., 1, string.len(...) - string.len("/elements/element"))
|
||||
local class = require(path .. "/lib/middleclass")
|
||||
|
||||
local element = class("pop.element") --TODO follow middleclass standards!?@@R/
|
||||
local element = class("pop.element")
|
||||
|
||||
function element:initialize(pop, parent, skin)
|
||||
self.parent = parent
|
||||
@ -13,152 +13,109 @@ function element:initialize(pop, parent, skin)
|
||||
self.h = 10
|
||||
|
||||
self.skin = pop.skins[skin] or pop.skins[pop.currentSkin]
|
||||
self.alignment = "top-left"
|
||||
|
||||
self.horizontal = "left"
|
||||
self.vertical = "top"
|
||||
end
|
||||
|
||||
function element:move(x, y)
|
||||
self.x = self.x + x
|
||||
self.y = self.y + y
|
||||
|
||||
for i=1,#element.child do
|
||||
if not element.child[i].excludeMovement then
|
||||
element.child[i]:move(x - oldX, y - oldY)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function element:setPosition(x, y)
|
||||
if self.alignment == "top-left" then
|
||||
local oldX = self.x
|
||||
local oldY = self.y
|
||||
|
||||
if self.horizontal == "left" then
|
||||
self.x = x
|
||||
self.y = y
|
||||
elseif self.alignment == "top-center" then
|
||||
elseif self.horizontal == "center" then
|
||||
self.x = x - self.w/2
|
||||
self.y = y
|
||||
elseif self.alignment == "top-right" then
|
||||
elseif self.horizontal == "right" then
|
||||
self.x = x - self.w
|
||||
end
|
||||
|
||||
if self.vertical == "top" then
|
||||
self.y = y
|
||||
elseif self.alignment == "left-center" then
|
||||
self.x = x
|
||||
elseif self.vertical == "center" then
|
||||
self.y = y - self.h/2
|
||||
elseif self.alignment == "center" then
|
||||
self.x = x - self.w/2
|
||||
self.y = y - self.h/2
|
||||
elseif self.alignment == "right-center" then
|
||||
self.x = x - self.w
|
||||
self.y = y - self.h/2
|
||||
elseif self.alignment == "bottom-left" then
|
||||
self.x = x
|
||||
self.y = y - self.h
|
||||
elseif self.alignment == "bottom-center" then
|
||||
self.x = x - self.w/2
|
||||
self.y = y
|
||||
elseif self.alignment == "bottom-right" then
|
||||
self.x = x - self.w
|
||||
elseif self.vertical == "bottom" then
|
||||
self.y = y - self.h
|
||||
end
|
||||
|
||||
for i=1,#element.child do
|
||||
if not element.child[i].excludeMovement then
|
||||
element.child[i]:move(x - oldX, y - oldY)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function element:getPosition()
|
||||
if self.alignment == "top-left" then
|
||||
return self.x, self.y
|
||||
elseif self.alignment == "top-center" then
|
||||
return self.x + self.w/2, self.y
|
||||
elseif self.alignment == "top-right" then
|
||||
return self.x + self.w, self.y
|
||||
elseif self.alignment == "left-center" then
|
||||
return self.x, self.y + self.h/2
|
||||
elseif self.alignment == "center" then
|
||||
return self.x + self.w/2, self.y + self.h/2
|
||||
elseif self.alignment == "right-center" then
|
||||
return self.x + self.w, self.y + self.h/2
|
||||
elseif self.alignment == "bottom-left" then
|
||||
return self.x, self.y + self.h
|
||||
elseif self.alignment == "bottom-center" then
|
||||
return self.x + self.w/2, self.y
|
||||
elseif self.alignment == "bottom-right" then
|
||||
return self.x + self.w, self.y + self.h
|
||||
local resultX = self.x
|
||||
local resultY = self.y
|
||||
|
||||
if self.horizontal == "center" then
|
||||
resultX = resultX + self.w/2
|
||||
elseif self.horizontal == "right" then
|
||||
resultX = resultX + self.w
|
||||
end
|
||||
|
||||
if self.vertical == "center" then
|
||||
resultY = resultY + self.h/2
|
||||
elseif self.vertical == "bottom" then
|
||||
resultY = resultY + self.h
|
||||
end
|
||||
|
||||
return resultX, resultY
|
||||
end
|
||||
|
||||
function element:setSize(w, h)
|
||||
if self.alignment == "top-left" then
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "top-center" then
|
||||
-- x minus half difference to expand horizontally
|
||||
if self.horizontal == "center" then
|
||||
self.x = self.x - (w - self.w)/2
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "top-right" then
|
||||
-- x minus difference to expand left
|
||||
elseif self.horizontal == "right" then
|
||||
self.x = self.x - (w - self.w)
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "left-center" then
|
||||
self.y = self.y - (h - self.h)/2
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "center" then
|
||||
self.x = self.x - (w - self.w)/2
|
||||
self.y = self.y - (h - self.h)/2
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "right-center" then
|
||||
self.x = self.x - (w - self.w)
|
||||
self.y = self.y - (h - self.h)/2
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "bottom-left" then
|
||||
self.y = self.y - (h - self.h)
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "bottom-center" then
|
||||
self.x = self.x - (w - self.w)/2
|
||||
self.y = self.y - (h - self.h)
|
||||
self.w = w
|
||||
self.h = h
|
||||
elseif self.alignment == "bottom-right" then
|
||||
self.x = self.x - (w - self.w)
|
||||
self.y = self.y - (h - self.h)
|
||||
self.w = w
|
||||
self.h = h
|
||||
end
|
||||
|
||||
if self.vertical == "center" then
|
||||
self.y = self.y - (h - self.h)/2
|
||||
elseif self.vertical == "bottom" then
|
||||
self.y = self.y - (h - self.h)
|
||||
end
|
||||
|
||||
self.w = w
|
||||
self.h = h
|
||||
end
|
||||
|
||||
function element:getSize()
|
||||
return self.w, self.h
|
||||
end
|
||||
|
||||
function element:align(alignment)
|
||||
self.alignment = alignment
|
||||
function element:align(horizontal, vertical)
|
||||
self:setAlignment(horizontal, vertical)
|
||||
|
||||
if self.alignment == "top-left" then
|
||||
self.x = self.parent.x
|
||||
self.y = self.parent.y
|
||||
elseif self.alignment == "top-center" then
|
||||
-- parent's x plus half of difference in width to center
|
||||
self.x = self.parent.x + (self.parent.w - self.w)/2
|
||||
self.y = self.parent.y
|
||||
elseif self.alignment == "top-right" then
|
||||
-- parent's x plus difference in width to align right
|
||||
self.x = self.parent.x + (self.parent.w - self.w)
|
||||
self.y = self.parent.y
|
||||
elseif self.alignment == "left-center" then
|
||||
self.x = self.parent.x
|
||||
self.y = self.parent.y + (self.parent.h - self.h)/2
|
||||
elseif self.alignment == "center" then
|
||||
self.x = self.parent.x + (self.parent.w - self.w)/2
|
||||
self.y = self.parent.y + (self.parent.h - self.h)/2
|
||||
elseif self.alignment == "right-center" then
|
||||
self.x = self.parent.x + (self.parent.w - self.w)
|
||||
self.y = self.parent.y + (self.parent.h - self.h)/2
|
||||
elseif self.alignment == "bottom-left" then
|
||||
self.x = self.parent.x
|
||||
self.y = self.parent.y + (self.parent.h - self.h)
|
||||
elseif self.alignment == "bottom-center" then
|
||||
self.x = self.parent.x + (self.parent.w - self.w)/2
|
||||
self.y = self.parent.y + (self.parent.h - self.h)
|
||||
elseif self.alignment == "bottom-right" then
|
||||
self.x = self.parent.x + (self.parent.w - self.w)
|
||||
self.y = self.parent.y + (self.parent.h - self.h)
|
||||
|
||||
if self.horizontal == "center" then
|
||||
self.x = self.x + (self.parent.w - self.w)/2
|
||||
elseif self.horizontal == "right" then
|
||||
self.x = self.x + (self.parent.w - self.w)
|
||||
end
|
||||
|
||||
if self.vertical == "center" then
|
||||
self.y = self.y + (self.parent.h - self.h)/2
|
||||
elseif self.vertical == "bottom" then
|
||||
self.y = self.y + (self.parent.h - self.h)
|
||||
end
|
||||
end
|
||||
|
||||
function element:alignTo(element, alignment)
|
||||
function element:alignTo(element, horizontal, vertical)
|
||||
local realParent = self.parent
|
||||
self.parent = element
|
||||
|
||||
@ -167,8 +124,13 @@ function element:alignTo(element, alignment)
|
||||
self.parent = realParent
|
||||
end
|
||||
|
||||
function element:setAlignment(alignment)
|
||||
self.alignment = alignment
|
||||
function element:setAlignment(horizontal, vertical)
|
||||
if horizontal then
|
||||
self.horizontal = horizontal
|
||||
end
|
||||
if vertical then
|
||||
self.vertical = vertical
|
||||
end
|
||||
end
|
||||
|
||||
function element:setSkin(skin)
|
||||
|
19
pop/init.lua
19
pop/init.lua
@ -8,12 +8,13 @@ pop.elementClasses = {}
|
||||
pop.window = false --top level element, defined in pop.load()
|
||||
pop.skins = {}
|
||||
pop.currentSkin = "clear"
|
||||
--TODO we need a "focused" element for textinput or whatever
|
||||
|
||||
function pop.load()
|
||||
-- load element classes
|
||||
local elementList = lf.getDirectoryItems(path .. "/elements")
|
||||
|
||||
for i=0, #elementList do
|
||||
for i=1, #elementList do
|
||||
local name = elementList[i]:sub(1, -5)
|
||||
pop.elementClasses[name] = require(path .. "/elements/" .. name)
|
||||
|
||||
@ -26,14 +27,14 @@ function pop.load()
|
||||
-- load skins
|
||||
local skinList = lf.getDirectoryItems(path .. "/skins")
|
||||
|
||||
for i=0, #skinList do
|
||||
for i=1, #skinList do
|
||||
local name = skinList[i]:sub(1, -5)
|
||||
pop.skins[name] = require(path .. "/skins/" .. name)
|
||||
pop.skins[name].name = name
|
||||
end
|
||||
|
||||
-- set top element
|
||||
pop.window = pop.create("box"):setSize(lg.getWidth(), lg.getHeight())
|
||||
pop.window = pop.create("element"):setSize(lg.getWidth(), lg.getHeight())
|
||||
end
|
||||
|
||||
function pop.create(elementType, parent, ...)
|
||||
@ -56,9 +57,8 @@ function pop.update(dt, element)
|
||||
element:update(dt)
|
||||
end
|
||||
|
||||
--TODO redo this loop
|
||||
for _, childElement in pairs(element.child) do
|
||||
pop.update(dt, childElement)
|
||||
for i=1,#element.child do
|
||||
pop.update(dt, element.child[i])
|
||||
end
|
||||
end
|
||||
|
||||
@ -67,15 +67,16 @@ function pop.draw(element)
|
||||
element = pop.window
|
||||
end
|
||||
|
||||
if not element.excludeRendering then
|
||||
if element.skin.draw and element.skin.draw(element) then
|
||||
-- do nothing...
|
||||
elseif element.draw then
|
||||
element:draw()
|
||||
end
|
||||
|
||||
--TODO redo this loop
|
||||
for _, childElement in pairs(element.child) do
|
||||
pop.draw(childElement)
|
||||
for i=1,#element.child do
|
||||
pop.draw(element.child[i])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user