From 486f3072747dc716e1a7dc637c2d759d4ffea04c Mon Sep 17 00:00:00 2001 From: Paul Liverman III Date: Tue, 17 Nov 2015 19:27:06 -0800 Subject: [PATCH] init crap --- README.md | 24 +++++ main.lua | 18 ++++ pop/elements/box.lua | 24 +++++ pop/elements/element.lua | 105 ++++++++++++++++++++++ pop/init.lua | 63 +++++++++++++ pop/lib/middleclass.lua | 182 ++++++++++++++++++++++++++++++++++++++ pop/skins/blacknwhite.lua | 10 +++ pop/skins/clear.lua | 10 +++ 8 files changed, 436 insertions(+) create mode 100644 README.md create mode 100644 main.lua create mode 100644 pop/elements/box.lua create mode 100644 pop/elements/element.lua create mode 100644 pop/init.lua create mode 100644 pop/lib/middleclass.lua create mode 100644 pop/skins/blacknwhite.lua create mode 100644 pop/skins/clear.lua diff --git a/README.md b/README.md new file mode 100644 index 0000000..9850a5d --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Pop.Box + +*Do not mix with [Cola][1].* + +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 + +```lua +local pop = require "pop" +-- define love callbacks here +local box = pop.box() +``` + +* `box` is a box for containing things. +* `text` is a class for handling text. +* Nothing else! Is alpha, just started. + +[1]: https://en.wikipedia.org/wiki/Cola_(programming_language) +[2]: https://love2d.org/ diff --git a/main.lua b/main.lua new file mode 100644 index 0000000..4d2fd85 --- /dev/null +++ b/main.lua @@ -0,0 +1,18 @@ +local pop = require "pop" + +function love.load() + pop.box() -- returns the box element + -- or pop.create("box") (this is what is actually called when you call pop.box()) +end + +function love.draw() + pop.draw() +end + +function love.mousepressed(button, x, y) + pop.mousepressed(button, x, y) +end + +function love.mousereleased(button, x, y) + pop.mousereleased(button, x, y) +end diff --git a/pop/elements/box.lua b/pop/elements/box.lua new file mode 100644 index 0000000..2de59f8 --- /dev/null +++ b/pop/elements/box.lua @@ -0,0 +1,24 @@ +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) + +function box:initialize(pop, parent) + element.initialize(self, pop, parent) + self.sizeControl = "specified" + self.outerWidth = 300 + self.outerHeight = 250 + self.innerWidth = self.outerWidth - self.style.borderSize + self.innerHeight = self.outerHeight - self.style.borderSize +end + +function box:update(pop) + -- +end + +function box:draw() + --TODO find a way for relative x/y to be passed here, because else, we won't have proper coords for drawing +end + +return box diff --git a/pop/elements/element.lua b/pop/elements/element.lua new file mode 100644 index 0000000..09b0fb9 --- /dev/null +++ b/pop/elements/element.lua @@ -0,0 +1,105 @@ +local path = string.sub(..., 1, string.len(...) - string.len(".elements.element")) +local class = require(path .. ".lib.middleclass") + +local element = class("pop.element") --NOTE are periods allowed in middleclass class names? + +--TODO setting widths and heights need to call update() +--TODO setters and getters for just width/height, aliases for outerWidth/outerHeight + +function element:initialize(pop, parent) + self.x = 0 + self.y = 0 + self.alignPoint = 1 -- 1 to 9, how aligned relative to x/y, see docs + + self.sizeControl = "fromInner" -- fromInner, fromOuter, specified + self.outerWidth = 0 + self.outerHeight = 0 + self.innerWidth = 0 + self.innerHeight = 0 + + self.style = pop.style + self.visible = true + + self.parent = parent + self.child = {} + + parent.child[self] = self -- add ourselves to the parent's children +end + +function element:getAlignPoint() + return self.alignPoint +end +function element:setAlignPoint(point) + self.alignPoint = point +end + +function element:getOuterWidth() + return self.outerWidth +end +function element:setOuterWidth(width) + assert(width > 0, "width must be above 0") + self.outerWidth = width +end + +function element:getOuterHeight() + return self.outerHeight() +end +function element:setOuterHeight(height) + assert(height > 0, "height must be above 0") + self.outerHeight = height +end + +function element:getInnerWidth() + return self.innerWidth +end +function element:setInnerWidth(width) + assert(width > 0, "width must be above 0") + self.innerWidth = width +end + +function element:getInnerHeight() + return self.innerHeight +end +function element:setInnerHeight(height) + assert(height > 0, "height must be above 0") + self.innerHeight = height +end + +--[[ TODO determine how to write these better (consistency motherfucker) +function element:getStyle() + return self.style.name +end +function element:setStyle(style) + self.style = style +end +]] + +function element:getVisible() + return self.visible +end +function element:setVisible(bool) + self.visible = bool +end + +function element:getParent() + return self.parent +end +function element:setParent(parent) + self.parent.child[self] = nil + self.parent = parent + self.parent.child[self] = self +end + +--TODO figure out how getting and setting children might work? or no?? + +function element:update() + --TODO a proper error message + print("update() not deifnenfei") +end + +function element:draw() + --TODO figure out how to get class name + print("Attempting to use element, or did not overwrite element's :draw() method.") +end + +return element diff --git a/pop/init.lua b/pop/init.lua new file mode 100644 index 0000000..0b2274d --- /dev/null +++ b/pop/init.lua @@ -0,0 +1,63 @@ +local pop = {} +local path = ... -- this only works as long as the require() does't specify init.lua..which it shouldn't + +-- elements are local +local box = require(path .. ".elements.box") +local text = require(path .. ".elements.text") + +-- style defines how elements are drawn +pop.style = require(path .. ".skins.clear") + +-- everything has one parent element (initialized at the end) +pop.parentElement = false + +function pop.create(elementType, parent, ...) + if not parent then + parent = pop.parentElement + end + + local newElement + + if elementType == "box" then + newElement = box(pop, parent, ...) + elseif elementType == "text" then + newElement = text(pop, parent, ...) + else + error("Invalid element type: " .. elementType) + end + + return newElement +end + +-- pretty wrappers to call pop.element() instead of pop.create("element") +pop.box = function() return pop.create("box", ...) end +pop.text = function() return pop.create("text", ...) end + +function pop.draw(element) + if not element then + element = pop.parentElement + end + + for _, childElements in pairs(element.child) do + --pop.draw(childElements, element.x, element.y) + pop.draw(childElements) + end + + element:draw() +end + +-- TODO decide if we should track mouse movement + +function pop.mousepressed(button, x, y) + -- +end + +function pop.mousereleased(button, x, y) + -- +end + +-- initialize the top element +pop.parentElement = box(pop, nil) -- nil because it has no parent +--pop.parentElement:setVisible(false) -- uneeded since its clear... + +return pop diff --git a/pop/lib/middleclass.lua b/pop/lib/middleclass.lua new file mode 100644 index 0000000..ba8b24b --- /dev/null +++ b/pop/lib/middleclass.lua @@ -0,0 +1,182 @@ +local middleclass = { + _VERSION = 'middleclass v3.1.0', + _DESCRIPTION = 'Object Orientation for Lua', + _URL = 'https://github.com/kikito/middleclass', + _LICENSE = [[ + MIT LICENSE + + Copyright (c) 2011 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. + ]] +} + +local function _setClassDictionariesMetatables(aClass) + local dict = aClass.__instanceDict + dict.__index = dict + + local super = aClass.super + if super then + local superStatic = super.static + setmetatable(dict, super.__instanceDict) + setmetatable(aClass.static, { __index = function(_,k) return rawget(dict,k) or superStatic[k] end }) + else + setmetatable(aClass.static, { __index = function(_,k) return dict[k] end }) + end +end + +local function _setClassMetatable(aClass) + setmetatable(aClass, { + __tostring = function() return "class " .. aClass.name end, + __index = aClass.static, + __newindex = aClass.__instanceDict, + __call = function(self, ...) return self:new(...) end + }) +end + +local function _createClass(name, super) + local aClass = { name = name, super = super, static = {}, __mixins = {}, __instanceDict={} } + aClass.subclasses = setmetatable({}, {__mode = "k"}) + + _setClassDictionariesMetatables(aClass) + _setClassMetatable(aClass) + + return aClass +end + +local function _createLookupMetamethod(aClass, name) + return function(...) + local method = aClass.super[name] + assert( type(method)=='function', tostring(aClass) .. " doesn't implement metamethod '" .. name .. "'" ) + return method(...) + end +end + +local function _setClassMetamethods(aClass) + for _,m in ipairs(aClass.__metamethods) do + aClass[m]= _createLookupMetamethod(aClass, m) + end +end + +local function _setDefaultInitializeMethod(aClass, super) + aClass.initialize = function(instance, ...) + return super.initialize(instance, ...) + end +end + +local function _includeMixin(aClass, mixin) + assert(type(mixin)=='table', "mixin must be a table") + for name,method in pairs(mixin) do + if name ~= "included" and name ~= "static" then aClass[name] = method end + end + if mixin.static then + for name,method in pairs(mixin.static) do + aClass.static[name] = method + end + end + if type(mixin.included)=="function" then mixin:included(aClass) end + aClass.__mixins[mixin] = true +end + +local Object = _createClass("Object", nil) + +Object.static.__metamethods = { '__add', '__band', '__bor', '__bxor', '__bnot', '__call', '__concat', + '__div', '__eq', '__ipairs', '__idiv', '__le', '__len', '__lt', '__mod', + '__mul', '__pairs', '__pow', '__shl', '__shr', '__sub', '__tostring', '__unm' } + +function Object.static:allocate() + assert(type(self) == 'table', "Make sure that you are using 'Class:allocate' instead of 'Class.allocate'") + return setmetatable({ class = self }, self.__instanceDict) +end + +function Object.static:new(...) + local instance = self:allocate() + instance:initialize(...) + return instance +end + +function Object.static:subclass(name) + assert(type(self) == 'table', "Make sure that you are using 'Class:subclass' instead of 'Class.subclass'") + assert(type(name) == "string", "You must provide a name(string) for your class") + + local subclass = _createClass(name, self) + _setClassMetamethods(subclass) + _setDefaultInitializeMethod(subclass, self) + self.subclasses[subclass] = true + self:subclassed(subclass) + + return subclass +end + +function Object.static:subclassed(other) end + +function Object.static:isSubclassOf(other) + return type(other) == 'table' and + type(self) == 'table' and + type(self.super) == 'table' and + ( self.super == other or + type(self.super.isSubclassOf) == 'function' and + self.super:isSubclassOf(other) + ) +end + +function Object.static:include( ... ) + assert(type(self) == 'table', "Make sure you that you are using 'Class:include' instead of 'Class.include'") + for _,mixin in ipairs({...}) do _includeMixin(self, mixin) end + return self +end + +function Object.static:includes(mixin) + return type(mixin) == 'table' and + type(self) == 'table' and + type(self.__mixins) == 'table' and + ( self.__mixins[mixin] or + type(self.super) == 'table' and + type(self.super.includes) == 'function' and + self.super:includes(mixin) + ) +end + +function Object:initialize() end + +function Object:__tostring() return "instance of " .. tostring(self.class) end + +function Object:isInstanceOf(aClass) + return type(self) == 'table' and + type(self.class) == 'table' and + type(aClass) == 'table' and + ( aClass == self.class or + type(aClass.isSubclassOf) == 'function' and + self.class:isSubclassOf(aClass) + ) +end + + + +function middleclass.class(name, super, ...) + super = super or Object + return super:subclass(name, ...) +end + +middleclass.Object = Object + +setmetatable(middleclass, { __call = function(_, ...) return middleclass.class(...) end }) + +return middleclass diff --git a/pop/skins/blacknwhite.lua b/pop/skins/blacknwhite.lua new file mode 100644 index 0000000..007c3cb --- /dev/null +++ b/pop/skins/blacknwhite.lua @@ -0,0 +1,10 @@ +local skin = {} + +skin.style = { + background = {255,255,255,1}, -- color table, image, or false + foreground = {0,0,0,1}, -- color table + borderSize = 2, -- integer (minimum 0) + borderStyle = {255,255,255,1} -- color table, image, or false +} + +return skin diff --git a/pop/skins/clear.lua b/pop/skins/clear.lua new file mode 100644 index 0000000..85691f1 --- /dev/null +++ b/pop/skins/clear.lua @@ -0,0 +1,10 @@ +local skin = {} + +skin.style = { + background = false, -- color table, image, or false + foreground = {0,0,0,1}, -- color table + borderSize = 0, -- integer (minimum 0) + borderStyle = false -- color table, image, or false +} + +return skin