diff --git a/elements/element.lua b/elements/element.lua index b54d458..bdb15b5 100644 --- a/elements/element.lua +++ b/elements/element.lua @@ -159,6 +159,13 @@ do getPadding = function(self) return self.data.padding end, + indexOf = function(self, element) + for i = 1, #self.child do + if self.child[i] == element then + return i + end + end + end, delete = function(self) for i = #self.child, 1, -1 do self.child[i]:delete() diff --git a/elements/element.moon b/elements/element.moon index d5e5404..839ecef 100644 --- a/elements/element.moon +++ b/elements/element.moon @@ -206,6 +206,11 @@ class element getPadding: => return @data.padding + indexOf: (element) => + for i = 1, #@child + if @child[i] == element + return i + --- Deletes references to this element and then deletes it. delete: => for i=#@child, 1, -1 diff --git a/elements/window.lua b/elements/window.lua index e52137e..d16b342 100644 --- a/elements/window.lua +++ b/elements/window.lua @@ -1,19 +1,195 @@ -local element = require(tostring((...):sub(1, -7)) .. "/element") +local pop +local mouse +mouse = love.mouse +local path = (...):sub(1, -7) +local element = require(tostring(path) .. "/element") +local box = require(tostring(path) .. "/box") +local text = require(tostring(path) .. "/text") local window do local _class_0 local _parent_0 = element - local _base_0 = { } + local _base_0 = { + load = function(pop_lib) + pop = pop_lib + end, + align = function(self, ...) + if not (self.data.align) then + return self + end + _class_0.__parent.__base.align(self, ...) + self.header:align() + self.title:align() + self.window_area:align() + self.window_area:move(nil, self.header:getHeight()) + return self + end, + setSize = function(self, w, h) + local x = 0 + local y = 0 + if w then + local _exp_0 = self.data.horizontal + if "center" == _exp_0 then + x = x - ((w - self.data.w) / 2) + elseif "right" == _exp_0 then + x = x - (w - self.data.w) + end + self.header:setWidth(w) + self.window_area:setWidth(w) + self.data.w = w + self.data.x = self.data.x + x + self.title:align() + end + if h then + local _exp_0 = self.data.vertical + if "center" == _exp_0 then + y = y - ((h - self.data.h) / 2) + elseif "right" == _exp_0 then + y = y - (h - self.data.h) + end + self.window_area:setHeight(h - self.header:getHeight()) + self.window_area:move(nil, self.header:getHeight()) + self.data.h = h + self.data.y = self.data.y + y + end + self.header:move(x, y) + self.window_area:move(x, y) + return self + end, + setWidth = function(self, w) + return self:setSize(w) + end, + setHeight = function(self, h) + return self:setSize(nil, h) + end + } _base_0.__index = _base_0 setmetatable(_base_0, _parent_0.__base) _class_0 = setmetatable({ - __init = function(self, parent, data) + __init = function(self, parent, data, title) if data == nil then data = { } end + if title == nil then + title = "Window" + end self.parent, self.data = parent, data _class_0.__parent.__init(self, self.parent, self.data) self.data.type = "window" + if not (self.data.w > 0) then + self.data.w = 100 + end + if not (self.data.h > 0) then + self.data.h = 80 + end + if not (self.data.containMethod) then + self.data.containMethod = "mouse" + end + self.header = pop.box(self, self.data.titleBackground or { + 25, + 180, + 230, + 255 + }) + self.title = pop.text(self.header, { + horizontal = "center" + }, title, self.data.titleColor or { + 255, + 255, + 255, + 255 + }) + self.window_area = pop.box(self, self.data.windowBackground or { + 200, + 200, + 210, + 255 + }) + local height = self.title:getHeight() + self.header:setSize(self.data.w, height) + self.window_area:setSize(self.data.w, self.data.h - height) + self.window_area:move(nil, height) + self.window_area.mousepressed = function(x, y, button) + if button == pop.constants.left_mouse then + local grandparent = self.parent.parent + table.insert(grandparent.child, table.remove(grandparent.child, grandparent:indexOf(self.parent))) + end + return nil + end + self.window_area.clicked = function() + return nil + end + local selected = false + local mx = 0 + local my = 0 + self.header.mousemoved = function(self, x, y, dx, dy) + if selected then + self.parent:move(dx, dy) + local grandparent = self.parent.parent + local _exp_0 = self.parent.data.containMethod + if "title" == _exp_0 then + if self.data.x < grandparent.data.x then + self.parent:move(grandparent.data.x - self.data.x) + end + if self.data.y < grandparent.data.y then + self.parent:move(nil, grandparent.data.y - self.data.y) + end + if self.data.x + self.data.w > grandparent.data.x + grandparent.data.w then + self.parent:move(grandparent.data.x + grandparent.data.w - (self.data.x + self.data.w)) + end + if self.data.y + self.data.h > grandparent.data.y + grandparent.data.h then + self.parent:move(nil, grandparent.data.y + grandparent.data.h - (self.data.y + self.data.h)) + end + elseif "body" == _exp_0 then + if self.data.x < grandparent.data.x then + self.parent:move(grandparent.data.x - self.data.x) + end + if self.data.y < grandparent.data.y then + self.parent:move(nil, grandparent.data.y - self.data.y) + end + if self.parent.data.x + self.parent.data.w > grandparent.data.x + grandparent.data.w then + self.parent:move(grandparent.data.x + grandparent.data.w - (self.parent.data.x + self.parent.data.w)) + end + if self.parent.data.y + self.parent.data.h > grandparent.data.y + grandparent.data.h then + self.parent:move(nil, grandparent.data.y + grandparent.data.h - (self.parent.data.y + self.parent.data.h)) + end + elseif "mouse" == _exp_0 then + if mouse.getX() < grandparent.data.x then + self.parent:setPosition(grandparent.data.x + self.data.w - mx) + end + if mouse.getY() < grandparent.data.y then + self.parent:setPosition(nil, grandparent.data.y + self.parent.data.h - my) + end + if mouse.getX() > grandparent.data.x + grandparent.data.w then + self.parent:setPosition(grandparent.data.x + grandparent.data.w + self.data.w - mx) + end + if mouse.getY() > grandparent.data.y + grandparent.data.h then + self.parent:setPosition(nil, grandparent.data.y + grandparent.data.h + self.parent.data.h - my) + end + end + return true + end + return false + end + self.header.mousepressed = function(self, x, y, button) + if button == pop.constants.left_mouse then + local grandparent = self.parent.parent + table.insert(grandparent.child, table.remove(grandparent.child, grandparent:indexOf(self.parent))) + selected = true + mx = x + my = y + return true + end + return false + end + self.header.mousereleased = function(self, x, y, button) + if button == pop.constants.left_mouse then + selected = false + return true + end + return false + end + return self:align() end, __base = _base_0, __name = "window", diff --git a/elements/window.moon b/elements/window.moon index d76e91d..665ad35 100644 --- a/elements/window.moon +++ b/elements/window.moon @@ -3,17 +3,158 @@ --- @classmod window --- @copyright Paul Liverman III (2016) --- @license The MIT License (MIT) +--- @todo Implement missing features. -element = require "#{(...)\sub 1, -7}/element" +local pop + +import mouse from love + +path = (...)\sub 1, -7 +element = require "#{path}/element" +box = require "#{path}/box" +text = require "#{path}/text" + +-- images would go here class window extends element + load: (pop_lib) -> + pop = pop_lib + --- Constructor expects nothing, or a data table describing it. - new: (@parent, @data={}) => + --- @todo document containMethod values + new: (@parent, @data={}, title="Window") => super @parent, @data @data.type = "window" + @data.w = 100 unless @data.w > 0 + @data.h = 80 unless @data.h > 0 + @data.containMethod = "mouse" unless @data.containMethod - --- @todo if data, do stuff about it + @header = pop.box @, @data.titleBackground or {25, 180, 230, 255} + @title = pop.text @header, {horizontal: "center"}, title, @data.titleColor or {255, 255, 255, 255} + @window_area = pop.box @, @data.windowBackground or {200, 200, 210, 255} + -- closeButton, minimizeButton, etc - --setSize: => - --do more stuff! + height = @title\getHeight! + @header\setSize @data.w, height + @window_area\setSize @data.w, @data.h - height + @window_area\move nil, height + + -- window area steals mouse events to prevent propagation to elements under it + @window_area.mousepressed = (x, y, button) -> + -- attempted to also make them pull to foreground, but it doesn't work + if button == pop.constants.left_mouse + grandparent = @parent.parent + table.insert grandparent.child, table.remove(grandparent.child, grandparent\indexOf @parent) + return nil + @window_area.clicked = -> + return nil + + selected = false + mx = 0 + my = 0 + + @header.mousemoved = (x, y, dx, dy) => + if selected + @parent\move dx, dy + -- do not leave area of grandparent (based on containMethod) + grandparent = @parent.parent + switch @parent.data.containMethod + when "title" -- the window title can't leave + @parent\move(grandparent.data.x - @data.x) if @data.x < grandparent.data.x + @parent\move(nil, grandparent.data.y - @data.y) if @data.y < grandparent.data.y + @parent\move(grandparent.data.x + grandparent.data.w - (@data.x + @data.w)) if @data.x + @data.w > grandparent.data.x + grandparent.data.w + @parent\move(nil, grandparent.data.y + grandparent.data.h - (@data.y + @data.h)) if @data.y + @data.h > grandparent.data.y + grandparent.data.h + when "body" -- the entire window can't leave + @parent\move(grandparent.data.x - @data.x) if @data.x < grandparent.data.x + @parent\move(nil, grandparent.data.y - @data.y) if @data.y < grandparent.data.y + @parent\move(grandparent.data.x + grandparent.data.w - (@parent.data.x + @parent.data.w)) if @parent.data.x + @parent.data.w > grandparent.data.x + grandparent.data.w + @parent\move(nil, grandparent.data.y + grandparent.data.h - (@parent.data.y + @parent.data.h)) if @parent.data.y + @parent.data.h > grandparent.data.y + grandparent.data.h + when "mouse" -- wherever the mouse has clicked can't leave + @parent\setPosition(grandparent.data.x + @data.w - mx) if mouse.getX! < grandparent.data.x + @parent\setPosition(nil, grandparent.data.y + @parent.data.h - my) if mouse.getY! < grandparent.data.y + @parent\setPosition(grandparent.data.x + grandparent.data.w + @data.w - mx) if mouse.getX! > grandparent.data.x + grandparent.data.w + @parent\setPosition(nil, grandparent.data.y + grandparent.data.h + @parent.data.h - my) if mouse.getY! > grandparent.data.y + grandparent.data.h + return true + return false + + @header.mousepressed = (x, y, button) => + if button == pop.constants.left_mouse + grandparent = @parent.parent + table.insert grandparent.child, table.remove(grandparent.child, grandparent\indexOf @parent) + selected = true + mx = x + my = y + return true + return false + + @header.mousereleased = (x, y, button) => + if button == pop.constants.left_mouse + selected = false + --pop.focused = false -- we have to manually clear our focus + return true + return false + + -- unsure if needed or how needed + --@setSize @data.w, @data.h -- or 100, 80 + @align! + + align: (...) => + unless @data.align return @ + super ... + + -- don't know if this is needed or why + --for i = 1, #@child + -- @child[i]\align! + @header\align! + @title\align! + @window_area\align! + + @window_area\move nil, @header\getHeight! + + return @ + + setSize: (w, h) => + x = 0 + y = 0 + + if w + switch @data.horizontal + when "center" + x -= (w - @data.w) / 2 + when "right" + x -= w - @data.w + + -- close button stuff + + @header\setWidth w + @window_area\setWidth w + @data.w = w + @data.x += x + + @title\align! + + -- close button stuff 2 ? + + if h + switch @data.vertical + when "center" + y -= (h - @data.h) / 2 + when "right" + y -= h - @data.h + + @window_area\setHeight h - @header\getHeight! + @window_area\move nil, @header\getHeight! + @data.h = h + @data.y += y + + @header\move x, y + @window_area\move x, y + + return @ + + setWidth: (w) => + return @setSize w + + setHeight: (h) => + return @setSize nil, h diff --git a/extensions/utility.lua b/extensions/utility.lua index 4a3506f..7018e60 100644 --- a/extensions/utility.lua +++ b/extensions/utility.lua @@ -5,7 +5,7 @@ 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 path = sub(..., 1, len(...) - len("/extensions/utility")) local element = require(tostring(path) .. "/elements/element") element.__base.fill = function(self) self.data.x = self.parent.data.x + self.data.padding diff --git a/extensions/utility.moon b/extensions/utility.moon index 566e279..12cf1ee 100644 --- a/extensions/utility.moon +++ b/extensions/utility.moon @@ -5,11 +5,12 @@ import graphics from love import sub, len from string -path = sub ..., 1, len(...) - len "/extensions/streamlined_get_set" +path = sub ..., 1, len(...) - len "/extensions/utility" element = require "#{path}/elements/element" --box = require "#{path}/elements/box" --text = require "#{path}/elements/text" +--- @todo make this built-in as maximize for window elements element.__base.fill = => @data.x = @parent.data.x + @data.padding @data.y = @parent.data.y + @data.padding diff --git a/init.lua b/init.lua index 4ece8b3..6131954 100644 --- a/init.lua +++ b/init.lua @@ -134,7 +134,7 @@ pop.load = function(load_path) local name = extensions[i]:sub(1, -5) log("Requiring \"" .. tostring(name) .. "\" from \"" .. tostring(load_path) .. "/extensions/" .. tostring(name) .. "\"") pop.extensions[name] = require(tostring(load_path) .. "/extensions/" .. tostring(name)) - if pop.extensions[name].load then + if type(pop.extensions[name]) == "table" and pop.extensions[name].load then pop.extensions[name].load(pop) end log("Extension loaded: \"" .. tostring(name) .. "\"") @@ -226,10 +226,10 @@ pop.mousemoved = function(x, y, dx, dy, element) end pop.mousepressed = function(x, y, button, element) if not (element) then - if button == "wd" then + if button == pop.constants.mouse_wheel_down then pop.wheelmoved(0, -1) return true - elseif button == "wu" then + elseif button == pop.constants.mouse_wheel_up then pop.wheelmoved(0, 1) return true end @@ -276,6 +276,22 @@ pop.mousereleased = function(x, y, button, element) end else log("mousereleased", x, y, button) + do + element = pop.focused + if element then + if element.data.draw and (x >= element.data.x) and (x <= element.data.x + element.data.w) and (y >= element.data.y) and (y <= element.data.y + element.data.h) then + if element.clicked then + clickedHandled = element:clicked(x - element.data.x, y - element.data.y, button) + end + end + if element.mousereleased then + mousereleasedHandled = element:mousereleased(x - element.data.x, y - element.data.y, button) + end + if clickedHandled ~= false or mousereleasedHandled ~= false then + return clickedHandled, mousereleasedHandled + end + end + end pop.mousereleased(x, y, button, pop.screen) end return clickedHandled, mousereleasedHandled diff --git a/init.moon b/init.moon index a4341b3..df96402 100644 --- a/init.moon +++ b/init.moon @@ -158,7 +158,7 @@ pop.load = (load_path=path) -> pop.extensions[name] = require "#{load_path}/extensions/#{name}" -- call the extension's load function if it exists - if pop.extensions[name].load + if type(pop.extensions[name]) == "table" and pop.extensions[name].load pop.extensions[name].load pop log "Extension loaded: \"#{name}\"" @@ -299,10 +299,10 @@ pop.mousepressed = (x, y, button, element) -> -- start at the screen, print that we received an event unless element -- take pre 0.10.0 wheel movement and pass it along - if button == "wd" + if button == pop.constants.mouse_wheel_down pop.wheelmoved 0, -1 return true - elseif button == "wu" + elseif button == pop.constants.mouse_wheel_up pop.wheelmoved 0, 1 return true @@ -373,9 +373,20 @@ pop.mousereleased = (x, y, button, element) -> --element.parent\focusChild element --table.insert element.parent, element.parent\removeChild(element), - -- else, default to pop.screen to begin! (and print that we received an event) + -- else, check for focused element, and then default to pop.screen to begin! (and print that we received an event) else log "mousereleased", x, y, button + + if element = pop.focused + if element.data.draw and (x >= element.data.x) and (x <= element.data.x + element.data.w) and (y >= element.data.y) and (y <= element.data.y + element.data.h) + if element.clicked + clickedHandled = element\clicked x - element.data.x, y - element.data.y, button + -- a focused element needs to know when it has been released no matter what! + if element.mousereleased + mousereleasedHandled = element\mousereleased x - element.data.x, y - element.data.y, button + if clickedHandled != false or mousereleasedHandled != false + return clickedHandled, mousereleasedHandled + pop.mousereleased x, y, button, pop.screen return clickedHandled, mousereleasedHandled diff --git a/main.lua b/main.lua index 04505b2..03cfa4c 100644 --- a/main.lua +++ b/main.lua @@ -2,6 +2,13 @@ local pop = require("") local debug = false love.load = function() pop.text("Hello World!"):align("center", "center") + pop.window({ + windowBackground = { + 200, + 200, + 200 + } + }, "Testing Window"):move(20, 20):setSize(200, 100):align("right", "top") local centerBox = pop.box({ w = 200, h = 200 @@ -129,12 +136,27 @@ love.load = function() } }):align("right", "bottom") end +love.update = function(dt) + return pop.update(dt) +end love.draw = function() pop.draw() if debug then return pop.debugDraw() end end +love.mousemoved = function(x, y, dx, dy) + return pop.mousemoved(x, y, dx, dy) +end +love.mousepressed = function(x, y, button) + return pop.mousepressed(x, y, button) +end +love.mousereleased = function(x, y, button) + return pop.mousereleased(x, y, button) +end +love.wheelmoved = function(x, y) + return pop.wheelmoved(x, y) +end love.keypressed = function(key) if key == "escape" then return love.event.quit() @@ -142,3 +164,9 @@ love.keypressed = function(key) debug = not debug end end +love.keyreleased = function(key) + return pop.keyreleased(key) +end +love.textinput = function(text) + return pop.textinput(text) +end diff --git a/main.moon b/main.moon index b6b37f6..eaa0ca4 100644 --- a/main.moon +++ b/main.moon @@ -7,6 +7,7 @@ debug = false love.load = -> pop.text("Hello World!")\align "center", "center" + pop.window({windowBackground: {200, 200, 200}}, "Testing Window")\move(20, 20)\setSize(200, 100)\align "right", "top" -- alignment testing centerBox = pop.box({w: 200, h: 200}, {255, 255, 0, 120})\align "center", "center" @@ -28,18 +29,37 @@ love.load = -> pop.text(centerBox, {padding: 5, color: {0, 0, 255, 100}}, "Align me!")\align "right", "top" pop.window(centerBox, {padding: 5, titleColor: {0, 0, 0, 150}, titleBackground: {0, 0, 255, 100}, windowBackground: {200, 200, 255, 100}})\align "right", "bottom" ---- @todo finish writing callbacks! +love.update = (dt) -> + pop.update dt love.draw = -> pop.draw! pop.debugDraw! if debug +love.mousemoved = (x, y, dx, dy) -> + pop.mousemoved x, y, dx, dy + +love.mousepressed = (x, y, button) -> + pop.mousepressed x, y, button + +love.mousereleased = (x, y, button) -> + pop.mousereleased x, y, button + +love.wheelmoved = (x, y) -> + pop.wheelmoved x, y + love.keypressed = (key) -> if key == "escape" love.event.quit! elseif key == "d" debug = not debug +love.keyreleased = (key) -> + pop.keyreleased key + +love.textinput = (text) -> + pop.textinput text + -- NOTE TEMPORARY