diff --git a/conf.lua b/conf.lua new file mode 100644 index 0000000..7657ccd --- /dev/null +++ b/conf.lua @@ -0,0 +1,5 @@ +return { + HOTSWAP = true, + AUTO_RUN = true, + DEBUG = true, +} \ No newline at end of file diff --git a/core/element.lua b/core/element.lua index 25fed2b..756ac31 100755 --- a/core/element.lua +++ b/core/element.lua @@ -1,4 +1,5 @@ --[[ Element superclass ]] +--[[ Love is currently a hard dependency, although not in many places ]] local path = string.sub(..., 1, string.len(...) - string.len(".core.element")) local helium = require(path .. ".dummy") @@ -58,11 +59,8 @@ setmetatable(element,{ local func, loader = ... if type(func)=='function' then self = setmetatable({}, element) - self.renderer = ... - self.classless = true - elseif type(cls)=='table' then - self = setmetatable({}, cls) - self.classless = false + self.parentFunc = func + self.classless = true end if loader then @@ -105,32 +103,79 @@ function element:new() inserted = false } - self.view = { + self.baseState = {} + + self.baseView = { x = 0, y = 0, w = 10, h = 10, } + self.view = setmetatable({},{ + __index = function(t, index) + return self.baseView[index] + end, + __newindex = function(t, index, val) + if self.baseView[index] ~= val then + self.baseView[index] = val + self.context:bubbleUpdate() + self:updateInputCtx() + end + end + }) + + --Context makes sure element internals don't have to worry about absolute coordinates + self.inputContext = helium.input.newContext(self) + self.context = context.new(self) + if self.classless then - self.classlessState = {} - self.classlessData = {} + self.classlessFuncs = {} + + --Allows manipulation of the arbitrary state + self.classlessFuncs.getState = function () + return self.state + end + + --Allows manipulation of rendering width height, relative X and relative Y + self.classlessFuncs.getView = function () + self.settings.restrictView = true + return self.view + end end end +function element:updateInputCtx() + self.inputContext:update() + if self.settings.canvasW then + if self.settings.canvasW < self.view.w or self.settings.canvasH < self.view.h then + self.settings.canvasW = self.view.w*1.25 + self.settings.canvasH = self.view.h*1.25 + + self.canvas = love.graphics.newCanvas(self.view.w*1.25, self.view.h*1.25) + end + + self.quad = love.graphics.newQuad(0, 0, self.view.w, self.view.h, self.settings.canvasW, self.settings.canvasH) + end +end + + --Hotswapping code function element:reLoader(newFunc) - self.renderer = newFunc + self.inputContext:set() + self.inputContext:destroy() + + self.parentFunc = newFunc + self.renderer = self.parentFunc(self.parameters,self.state,self.view) self.context:bubbleUpdate() + + self.inputContext:unset() end --Called once dimensions are validated function element:setup() - self.state = - setmetatable( - {}, - { + self.state = setmetatable({},{ __index = function(t, index) return self.baseState[index] end, @@ -140,15 +185,11 @@ function element:setup() self.context:bubbleUpdate() end end - } - ) + }) - self.parameters = - setmetatable( - {}, - { + self.parameters = setmetatable({},{ __index = function(t, index) - return self.baseParams[index] or nil + return self.baseParams[index] end, __newindex = function(t, index, val) if self.baseParams[index] ~= val then @@ -156,68 +197,32 @@ function element:setup() self.context:bubbleUpdate() end end - } - ) + }) - self.canvas = love.graphics.newCanvas(self.view.w, self.view.h) - self.quad = love.graphics.newQuad(0, 0, self.view.w, self.view.h, self.view.w, self.view.h) + self.settings.canvasW = self.view.w*1.25 + self.settings.canvasH = self.view.h*1.25 - --Context makes sure element internals don't have to worry about absolute coordinates - self.inputContext = helium.input.newContext(self) - self.context = context.new(self) + self.canvas = love.graphics.newCanvas(self.view.w*1.25, self.view.h*1.25) + + self.quad = love.graphics.newQuad(0, 0, self.view.w, self.view.h, self.view.w*1.25, self.view.h*1.25) + + if self.classless then + self.inputContext:set() + + self.renderer = self.parentFunc(self.parameters,self.state,self.view) + + self.inputContext:unset() + end self.settings.isSetup = true - - --Classless rendering - if self.classless then - self.classlessData.loadEffect = function (func) - if self.classlessData.loaded and self.classlessData.loadCaptured then - return self.classlessData.loadCaptured - elseif not self.classlessData.loaded then - self.classlessData.loadCaptured = func() - self.classlessData.loaded = true - if self.classlessData.loadCaptured then - return self.classlessData.loadCaptured - end - end - end - ---@param initial any - ---@return any - ---@return function - self.classlessData.useState = function (initial) - self.settings.indice = self.settings.indice + 1 - local indice = self.settings.indice - - if self.classlessState[indice] and self.classlessState[indice].state then - return self.classlessState[indice].state, self.classlessState[indice].setState - else - self.classlessState[indice] = {} - self.classlessState[indice].state = initial - - self.classlessState[indice].setState = function(set) - self.classlessState[indice].state = set - self.context:bubbleUpdate() - end - - self.classlessState[indice].getState = function() - return self.classlessState[indice].state - end - return self.classlessState[indice].state, self.classlessState[indice].setState, self.classlessState[indice].getState - end - end - end end function element:classlessRender() - self.inputContext:set() - self.settings.indice = 0 - if type(self.renderer)=='function' then - local denv = getfenv(self.renderer) - denv['useState'] = self.classlessData.useState - denv['loadEffect'] = self.classlessData.loadEffect - local status,err = pcall(self.renderer,self.parameters, self.view.w, self.view.h) + self.inputContext:set() + if type(self.renderer)=='function' then + local status, err = pcall(self.renderer) if not status then love.graphics.setColor(1,0,0) @@ -226,15 +231,14 @@ function element:classlessRender() love.graphics.printf("Error: "..err,0,0,self.view.w) end - denv['useState'] = nil - denv['loadEffect'] = nil elseif type(self.renderer)=='string' then - love.graphics.setColor(1,0,0) - love.graphics.rectangle('line',0,0,self.view.w,self.view.h) - love.graphics.setColor(1,1,1) - love.graphics.printf("Error: "..self.renderer,0,0,self.view.w) + love.graphics.setColor(1,0,0) + love.graphics.rectangle('line',0,0,self.view.w,self.view.h) + love.graphics.setColor(1,1,1) + love.graphics.printf("Error: "..self.renderer,0,0,self.view.w) end self.inputContext:unset() + end function element:renderWrapper() @@ -286,10 +290,12 @@ end ---@param w number ---@param h number function element:draw(params, x, y, w, h) - self.view.x = x or self.view.x - self.view.y = y or self.view.y - self.view.w = w or self.view.w - self.view.h = h or self.view.h + if not self.view.lock then + self.view.x = x or self.view.x + self.view.y = y or self.view.y + self.view.w = w or self.view.w + self.view.h = h or self.view.h + end if params then if type(params)=='table' and self.baseParams then diff --git a/core/input.lua b/core/input.lua index 9d4b234..204ff9b 100755 --- a/core/input.lua +++ b/core/input.lua @@ -36,7 +36,7 @@ local activeContext ]] function input.newContext(element) - local ctx = setmetatable({view = element.view, subs = {}}, context) + local ctx = setmetatable({elem = element, subs = {}}, context) return ctx end @@ -44,16 +44,22 @@ end function context:set() if activeContext then self.parentCtx = activeContext - self.absX = self.parentCtx.absX + self.view.x - self.absY = self.parentCtx.absY + self.view.y + self.absX = self.parentCtx.absX + self.elem.view.x + self.absY = self.parentCtx.absY + self.elem.view.y activeContext = self else - self.absX = self.view.x - self.absY = self.view.y + self.absX = self.elem.view.x + self.absY = self.elem.view.y activeContext = self end end +function context:update() + for i, sub in ipairs(self.subs) do + sub:contextUpdate(self.absX,self.absY) + end +end + function context:unset() if self.parentCtx then activeContext = self.parentCtx @@ -64,7 +70,7 @@ end function context:destroy() for i, e in ipairs(self.subs) do - self.subs:destroy() + e:destroy() end end @@ -84,10 +90,14 @@ function subscription.create(x, y, w, h, eventType, callback, doff) y = activeContext.absY + y, w = w, h = h, + ix = x, + iy = y, eventType = eventType, active = doff or true, callback = callback },subscription) + + activeContext.subs[#activeContext.subs+1] = sub else sub = setmetatable({ x = x, @@ -118,10 +128,15 @@ function subscription:on() end function subscription:destroy() - self.destroy = true + self.destroyStat = true self.active = false end +function subscription:contextUpdate(absX, absY) + self.x = absX + self.ix + self.y = absY + self.iy +end + function subscription:update(x, y, w, h) self.x = x or self.x self.y = y or self.y @@ -189,12 +204,25 @@ function input.eventHandlers.mousereleased(x, y, btn) end end end + end + + if input.subscriptions.dragged then + for index, sub in ipairs(input.subscriptions.dragged) do + if sub.currentEvent then + sub.currentEvent = false + captured = true + if sub.cleanUp then + sub.cleanUp(x, y) + end + end + end end return captured end + function input.eventHandlers.mousepressed(x, y, btn) local captured = false if input.subscriptions.mousepressed then @@ -231,6 +259,18 @@ function input.eventHandlers.mousepressed(x, y, btn) end end + + if input.subscriptions.dragged then + for index, sub in ipairs(input.subscriptions.dragged) do + local succ = sub:checkInside(x, y) + + if succ and sub.active then + sub.currentEvent = true + captured = true + end + + end + end return captured end @@ -264,7 +304,7 @@ function input.eventHandlers.keyreleased(btn) return captured end -function input.eventHandlers.mousemoved(x, y) +function input.eventHandlers.mousemoved(x, y, dx, dy) local captured = false if input.subscriptions.hover then @@ -285,6 +325,22 @@ function input.eventHandlers.mousemoved(x, y) end end + + if input.subscriptions.dragged then + for index, sub in ipairs(input.subscriptions.dragged) do + if sub.active and sub.currentEvent then + if not sub.cleanUp then + sub.cleanUp = sub:emit(x, y, dx, dy) + else + sub:emit(x, y, dx, dy) + end + sub.currentEvent = true + captured = true + end + + end + end + return captured end diff --git a/init.lua b/init.lua index 4c637b1..5237a66 100755 --- a/init.lua +++ b/init.lua @@ -5,10 +5,11 @@ ----------------------------------------------------]] local path = ... local helium = require(path..".dummy") +helium.conf = require(path..".conf") helium.utils = require(path..".utils") helium.element = require(path..".core.element") helium.input = require(path..".core.input") -helium.debugLoader = require(path..".debugLoader") +helium.loader = require(path..".loader") helium.elementBuffer = {} function helium.render() @@ -17,7 +18,11 @@ function helium.render() end end -function helium.update() +function helium.update(dt) + if helium.conf.HOTSWAP then + helium.loader.update(dt) + end + local remove = false for i, e in ipairs(helium.elementBuffer) do @@ -60,61 +65,61 @@ end end end ]] -function love.run() +if helium.conf.AUTO_RUN then + function love.run() - if love.math then - love.math.setRandomSeed(os.time()) - end + if love.math then + love.math.setRandomSeed(os.time()) + end - if love.load then love.load(arg) end + if love.load then love.load(arg) end - -- We don't want the first frame's dt to include time taken by love.load. - if love.timer then love.timer.step() end + -- We don't want the first frame's dt to include time taken by love.load. + if love.timer then love.timer.step() end - local dt = 0 + local dt = 0 - -- Main loop time. - while true do - -- Process events. - if love.event then - love.event.pump() - for name, a,b,c,d,e,f in love.event.poll() do - if name == "quit" then - if not love.quit or not love.quit() then - return a + -- Main loop time. + while true do + -- Process events. + if love.event then + love.event.pump() + for name, a,b,c,d,e,f in love.event.poll() do + if name == "quit" then + if not love.quit or not love.quit() then + return a + end + end + + if not(helium.input.eventHandlers[name]) or not(helium.input.eventHandlers[name](a, b, c, d, e, f)) then + love.handlers[name](a, b, c, d, e, f) end end - - if not(helium.input.eventHandlers[name]) or not(helium.input.eventHandlers[name](a, b, c, d, e, f)) then - love.handlers[name](a, b, c, d, e, f) - end end + + + -- Update dt, as we'll be passing it to update + if love.timer then + love.timer.step() + dt = love.timer.getDelta() + end + + -- Call update and draw + if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled + helium.update(dt) + + if love.graphics and love.graphics.isActive() then + love.graphics.clear(love.graphics.getBackgroundColor()) + love.graphics.origin() + + if love.draw then love.draw() end + helium.render() + love.graphics.present() + end + + if love.timer then love.timer.sleep(0.00001) end end - - - -- Update dt, as we'll be passing it to update - if love.timer then - love.timer.step() - dt = love.timer.getDelta() - end - - -- Call update and draw - if love.update then love.update(dt) end -- will pass 0 if love.timer is disabled - helium.update(dt) - helium.debugLoader.update(dt) - - if love.graphics and love.graphics.isActive() then - love.graphics.clear(love.graphics.getBackgroundColor()) - love.graphics.origin() - if love.draw then love.draw() end - helium.render() - love.graphics.present() - end - - - if love.timer then love.timer.sleep(0.00001) end end - end return helium \ No newline at end of file diff --git a/debugLoader.lua b/loader.lua similarity index 96% rename from debugLoader.lua rename to loader.lua index b6e8f61..f1518cc 100644 --- a/debugLoader.lua +++ b/loader.lua @@ -1,5 +1,5 @@ -local path = string.sub(..., 1, string.len(...) - string.len(".debugLoader")) +local path = string.sub(..., 1, string.len(...) - string.len(".loader")) local helium = require(path..'.dummy') local elements = {} local debugLoader = {}