Refactor + prevent memory leaks

This commit is contained in:
qfx 2020-02-11 20:07:18 +02:00
parent 31317ad926
commit 5d707925c6
4 changed files with 123 additions and 102 deletions

View File

@ -11,7 +11,7 @@ local activeContext
---@param elem element
function context.new(elem)
local ctx = setmetatable({view = elem.view, element = elem}, context)
local ctx = setmetatable({view = elem.view, element = elem, childrenContexts={}}, context)
return ctx
end
@ -27,7 +27,10 @@ end
function context:set()
if activeContext then
self.parentCtx = activeContext
if not self.parentCtx then
self.parentCtx = activeContext
activeContext.childrenContexts[#activeContext.childrenContexts] = self
end
self.absX = self.parentCtx.absX + self.view.x
self.absY = self.parentCtx.absY + self.view.y
@ -49,10 +52,18 @@ function context:unset()
end
end
function context:destroy()
self.elem:undraw()
for i=1,#self.childrenContexts do
self.childrenContexts[i]:destroy()
end
end
---@class element
local element = {}
element.__index = element
local type,pcall = type,pcall
setmetatable(element,{
__call = function(cls, ...)
local self
@ -60,7 +71,6 @@ setmetatable(element,{
if type(func)=='function' then
self = setmetatable({}, element)
self.parentFunc = func
self.classless = true
end
if loader then
@ -79,11 +89,6 @@ setmetatable(element,{
--Dummy functions
function element:renderer() print('no renderer') end
function element:updater() end
function element:constructor() end
--Control functions
--The new function that should be used for element creation
function element:new()
@ -92,7 +97,7 @@ function element:new()
self.parameters = {}
--The element canvas
self.canvas = nil
--self.canvas = nil
--Internal settings
self.settings = {
@ -129,19 +134,17 @@ function element:new()
self.inputContext = helium.input.newContext(self)
self.context = context.new(self)
if self.classless then
self.classlessFuncs = {}
self.classlessFuncs = {}
--Allows manipulation of the arbitrary state
self.classlessFuncs.getState = function ()
return self.state
end
--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
--Allows manipulation of rendering width height, relative X and relative Y
self.classlessFuncs.getView = function ()
self.settings.restrictView = true
return self.view
end
end
@ -173,6 +176,7 @@ function element:reLoader(newFunc)
self.inputContext:unset()
end
local newCanvas,newQuad = love.graphics.newCanvas,love.graphics.newQuad
--Called once dimensions are validated
function element:setup()
self.state = setmetatable({},{
@ -203,21 +207,17 @@ function element:setup()
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)
self.canvas = newCanvas(self.view.w*1.25, self.view.h*1.25)
self.quad = newQuad(0, 0, self.view.w, self.view.h, 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.inputContext:set()
self.renderer = self.parentFunc(self.parameters,self.state,self.view)
self.inputContext:unset()
self.settings.isSetup = true
end
local setColor,rectangle,setFont,printf = love.graphics.setColor,love.graphics.rectangle,love.graphics.setFont,love.graphics.printf
function element:classlessRender()
self.inputContext:set()
@ -225,38 +225,38 @@ function element:classlessRender()
local status, err = pcall(self.renderer)
if not status 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: "..err,0,0,self.view.w)
setColor(1,0,0)
rectangle('line',0,0,self.view.w,self.view.h)
setColor(1,1,1)
printf("Error: "..err,0,0,self.view.w)
end
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)
setColor(1,0,0)
rectangle('line',0,0,self.view.w,self.view.h)
setColor(1,1,1)
printf("Error: "..self.renderer,0,0,self.view.w)
end
self.inputContext:unset()
self.inputContext:unset()
end
local getCanvas,setCanvas,clear = love.graphics.getCanvas,love.graphics.setCanvas,love.graphics.clear
function element:renderWrapper()
local cnvs = love.graphics.getCanvas()
love.graphics.setCanvas({self.canvas, stencil = true})
local cnvs = getCanvas()
setCanvas({self.canvas, stencil = true})
love.graphics.clear()
clear()
if self.classless and self.parameters then
if self.parameters then
self:classlessRender()
self.settings.pendingUpdate = false
else
self:renderer()
end
love.graphics.setCanvas(cnvs)
setCanvas(cnvs)
end
local draw = love.graphics.draw
function element:externalRender()
self.context:set()
@ -265,8 +265,8 @@ function element:externalRender()
self.settings.needsRendering = false
end
love.graphics.setColor(1,1,1)
love.graphics.draw(self.canvas, self.quad, self.view.x, self.view.y)
setColor(1,1,1)
draw(self.canvas, self.quad, self.view.x, self.view.y)
self.context:unset()
end
@ -282,6 +282,7 @@ function element:externalUpdate()
end
end
local insert = table.insert
--External functions
--Acts as the entrypoint for beginning rendering
---@param params any
@ -291,10 +292,14 @@ end
---@param h number
function element:draw(params, x, y, w, 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
--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 x then self.view.x = x end
if y then self.view.y = y end
if w then self.view.w = w end
if h then self.view.h = h end
end
if params then
@ -316,13 +321,15 @@ function element:draw(params, x, y, w, h)
self:externalRender()
elseif not self.settings.inserted then
self.settings.inserted = true
table.insert(helium.elementBuffer, self)
insert(helium.elementBuffer, self)
end
end
function element:undraw()
self.settings.remove = true
self.settings.isSetup = false
self.inputContext:set()
self.inputContext:destroy()
end
return element

View File

@ -36,14 +36,17 @@ local activeContext
]]
function input.newContext(element)
local ctx = setmetatable({elem = element, subs = {}}, context)
local ctx = setmetatable({elem = element, subs = {}, childContexts={}}, context)
return ctx
end
function context:set()
if activeContext then
self.parentCtx = activeContext
if not self.parentCtx then
activeContext.childContexts[#activeContext.childContexts+1] = self
self.parentCtx = activeContext
end
self.absX = self.parentCtx.absX + self.elem.view.x
self.absY = self.parentCtx.absY + self.elem.view.y
activeContext = self
@ -72,6 +75,9 @@ function context:destroy()
for i, e in ipairs(self.subs) do
e:destroy()
end
for i, e in ipairs(self.childContexts) do
e:destroy()
end
end
---@param x number
@ -149,33 +155,23 @@ function subscription:emit(...)
end
function subscription:checkInside(x, y)
if x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h then
return true
else
return false
end
return x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h
end
function subscription:checkOutside(x, y)
if x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h then
return false
else
return true
end
return not (x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h)
end
input.subscribe = function(...)
return subscription.create(...)
end
input.subscribe = subscription.create
function input.eventHandlers.mousereleased(x, y, btn)
local captured = false
if input.subscriptions.mousereleased then
for index, sub in ipairs(input.subscriptions.mousereleased) do
local succ = sub:checkInside(x, y)
--local succ = sub:checkInside(x, y)
if succ and sub.active then
if sub.active and sub:checkInside(x, y) then -- succ and sub:check
sub:emit(x, y, btn)
captured = true
end
@ -184,9 +180,9 @@ function input.eventHandlers.mousereleased(x, y, btn)
end
if input.subscriptions.mousereleased_outside then
for index, sub in ipairs(input.subscriptions.mousereleased_outside) do
local succ = sub:checkOutside(x, y)
--local succ = sub:checkOutside(x, y)
if succ and sub.active then
if sub.active and sub:checkOutside(x, y) then -- succ and sub.active then
sub:emit(x, y, btn)
captured = true
end
@ -227,9 +223,9 @@ function input.eventHandlers.mousepressed(x, y, btn)
local captured = false
if input.subscriptions.mousepressed then
for index, sub in ipairs(input.subscriptions.mousepressed) do
local succ = sub:checkInside(x, y)
--local succ = sub:checkInside(x, y)
if succ and sub.active then
if sub.active and sub:checkInside(x, y) then -- succ and sub:check
sub:emit(x, y, btn)
captured = true
end
@ -238,9 +234,9 @@ function input.eventHandlers.mousepressed(x, y, btn)
end
if input.subscriptions.mousepressed_outside then
for index, sub in ipairs(input.subscriptions.mousepressed_outside) do
local succ = sub:checkOutside(x, y)
--local succ = sub:checkOutside(x, y)
if succ and sub.active then
if sub.active and sub:checkOutside(x, y) then -- succ and sub.active then
sub:emit(x, y, btn)
captured = true
end
@ -262,9 +258,9 @@ function input.eventHandlers.mousepressed(x, y, btn)
if input.subscriptions.dragged then
for index, sub in ipairs(input.subscriptions.dragged) do
local succ = sub:checkInside(x, y)
--local succ = sub:checkInside(x, y)
if succ and sub.active then
if sub.active and sub:checkInside(x, y) then -- succ and sub:check
sub.currentEvent = true
captured = true
end
@ -279,7 +275,7 @@ function input.eventHandlers.keypressed(btn)
local captured = false
if input.subscriptions.keypressed then
for index, sub in ipairs(input.subscriptions.keypressed) do
if sub.active ==true then
if sub.active then -- ==true then
sub:emit( btn)
captured = true
end
@ -311,11 +307,11 @@ function input.eventHandlers.mousemoved(x, y, dx, dy)
for index, sub in ipairs(input.subscriptions.hover) do
local succ = sub:checkInside(x, y)
if succ and sub.active and not sub.currentEvent then
if sub.active and not sub.currentEvent and succ then
sub.cleanUp = sub:emit(x, y)
sub.currentEvent = true
captured = true
elseif sub.currentEvent and not sub:checkInside(x, y) then
elseif sub.currentEvent and not succ then
sub.currentEvent = false
captured = true
if sub.cleanUp then
@ -334,7 +330,7 @@ function input.eventHandlers.mousemoved(x, y, dx, dy)
else
sub:emit(x, y, dx, dy)
end
sub.currentEvent = true
--sub.currentEvent = true -- checked in the condition so must be true
captured = true
end

View File

@ -5,23 +5,40 @@ local elements = {}
local debugLoader = {}
--Return level: 1--string; 2--chunk; 3--return value; default: element factory
local function loader(path)
local succ = true
--File string
local fileContents, err = love.filesystem.read(path)
local lastLoaded, status, func, succ, ret
if fileContents==nil then
print('Error loading ',path,':',tostring(err),', will continue watching!')
succ = false
end
if fileContents then
lastLoaded = love.filesystem.getInfo(path).modtime
status, func = pcall(loadstring,fileContents)
if not status then
print('Error compiling ',path,':',tostring(err),', will continue watching!')
else
succ, ret = pcall(func,path)
end
local t, lastLoaded
if succ then
t = love.filesystem.getInfo(path)
lastLoaded = t['modtime']
end
--Chunk
local status, err
if succ then
status, err = pcall(loadstring,fileContents)
end
if status==false or status==nil then
print('Error compiling ',path,':',tostring(err),', will continue watching!')
succ = false
end
--Return values
local ret
if succ then
succ, ret = pcall(err,path)
if not succ then
print('Error calling ',path,':',tostring(ret))
end
else
print('Error loading ',path,':',tostring(err),', will continue watching!')
end
return fileContents, err, ret, lastLoaded
@ -60,6 +77,7 @@ function debugLoader.update(dt)
--If last save time differs then start reload sequence
local _, _, ret, lastLoaded = loader(elem[4])
local setfuncs = {}
local reloader = function(setFunc)

View File

@ -4,25 +4,25 @@ function utils.ArrayRemove(t, fnKeep)
local j, n = 1, #t;
for i=1,n do
if (fnKeep(t, i, j)) then
if fnKeep(t, i, j) then
-- Move i's kept value to j's position, if it's not already there.
if (i ~= j) then
if i ~= j then
t[j] = t[i];
t[i] = nil;
--t[i] = nil;
end
j = j + 1; -- Increment position of where we'll place the next kept value.
else
t[i] = nil;
end
end --else
t[i] = nil; -- in both if cases you nil it sooooo
--end
end
return t;
end
function utils.tableMerge(t, bt)
for i, e in pairs(t) do
if e ~= bt[i] then
bt[i] = e
for k, v in pairs(t) do
if v ~= bt[k] then
bt[k] = v
end
end
end