big chungus commit

This commit is contained in:
Elmārs Āboliņš 2020-09-28 00:16:50 +03:00
parent b6e29a3ed7
commit a200960ad1
9 changed files with 422 additions and 452 deletions

View File

@ -29,14 +29,14 @@ function atlas.assign(element)
local elH = element.view.h local elH = element.view.h
local canvas, quad = createdAtlas:assignElement(element) local canvas, quad = createdAtlas:assignElement(element)
if not canvas and createdAtlas.ideal_area < createdAtlas.taken_area*4 then if not canvas and createdAtlas.ideal_area < createdAtlas.taken_area*4 then
print('refragmenting ;3') --print('refragmenting ;3')
createdAtlas:refragment() createdAtlas:refragment()
canvas, quad = createdAtlas:assignElement(element) canvas, quad = createdAtlas:assignElement(element)
if not canvas then if not canvas then
print('ran out of space') --print('ran out of space')
end end
else else
print('wont refragment', createdAtlas.ideal_area, createdAtlas.taken_area) --print('wont refragment', createdAtlas.ideal_area, createdAtlas.taken_area)
end end
return canvas, quad return canvas, quad
end end
@ -206,6 +206,7 @@ end
function atlas:unassignElement(element) function atlas:unassignElement(element)
local user = self.users[element] local user = self.users[element]
self:unMarkTiles(user.x, user.y, user.w, user.h) self:unMarkTiles(user.x, user.y, user.w, user.h)
self.taken_area = self.taken_area - ((user.w*BLOCK_SIZE)*(user.h*BLOCK_SIZE))
self.users[element] = nil self.users[element] = nil
end end

View File

@ -19,7 +19,7 @@ setmetatable(element, {
self.parentFunc = func self.parentFunc = func
self:new(param,nil, w, h) self:new(param,nil, w, h)
self:createProxies(param) self:createProxies()
return self return self
end end
@ -29,7 +29,6 @@ setmetatable(element, {
--The new function that should be used for element creation --The new function that should be used for element creation
function element:new(param, immediate, w, h) function element:new(param, immediate, w, h)
self.parameters = {} self.parameters = {}
self.baseParams = param self.baseParams = param
--Internal settings --Internal settings
@ -57,28 +56,48 @@ function element:new(param, immediate, w, h)
self.renderBench = { self.renderBench = {
} }
self.baseView = { self.baseView = {
x = 0, x = 0,
y = 0, y = 0,
w = w or 10, w = w or 10,
h = h or 10, h = h or 10,
minW = w or 10, minW = w or 0,
minH = h or 10, minH = h or 0,
} }
self.size = setmetatable({}, {__index = function(t, index)
return self.baseView[index]
end,})
self.view = self.baseView self.view = self.baseView
end end
function element:sizeChange() function element:sizeChange(i, v)
local increase = self.baseView[i] < v
if i == 'w' then
self.baseView.w = math.max(self.baseView.minW, v)
else
self.baseView.h = math.max(self.baseView.minH, v)
end
--defer resize
if self.deferResize then
if not self.deferResize.increased then
self.deferResize.increased = increase
end
else
self.deferResize = { increased = increase }
end
end end
function element:posChange() function element:posChange(i, v)
self.baseView[i] = v
--defer resize
if not self.deferRepos then
self.deferRepos = true
end
end end
function element:onUpdate() function element:onUpdate()
@ -104,14 +123,12 @@ function element:createProxies()
end, end,
__newindex = function(t, index, val) __newindex = function(t, index, val)
if self.baseView[index] ~= val then if self.baseView[index] ~= val then
self.baseView[index] = val
if index=='w' or index=='h' then if index=='w' or index=='h' then
self:sizeChange() self:sizeChange(index, val)
else else
self:posChange() self:posChange(index, val)
end end
self.context:bubbleUpdate() self.context:bubbleUpdate()
self:updateInputCtx()
end end
end end
}) })
@ -142,14 +159,22 @@ function element.setBench(time)
selfRenderTime = time selfRenderTime = time
end end
function element:calculateCanvasCoeficient(selfTime) function element:calculateCanvasCoeficient()
local avg, sum = 0, 0
for i, e in ipairs(self.renderBench) do
sum = sum + e
end
avg = sum/#self.renderBench
local sW, sH = love.graphics.getDimensions() local sW, sH = love.graphics.getDimensions()
local areaBelow = helium.atlas.getFreeArea() local areaBelow = helium.atlas.getFreeArea()
local area = self.view.h * self.view.w local area = self.view.h*self.view.w
local areaCoef = (2-(helium.atlas.getRatio())) - (area/(areaBelow/(4+3*helium.atlas.getRatio()))) local areaCoef = (2-(helium.atlas.getRatio()) )-(area/(areaBelow/(4+3*helium.atlas.getRatio())))
local childCoef = self.context:getChildrenCount()/childrenNum local childCoef = self.context:getChildrenCount()/childrenNum
local sizeCoef = selfTime/selfRenderTime local sizeCoef = avg/selfRenderTime
return (areaCoef+childCoef+sizeCoef)>coefficient return (areaCoef+childCoef+sizeCoef)>coefficient
end end
@ -163,6 +188,7 @@ function element:createCanvas()
if not self.canvas then if not self.canvas then
self.settings.failedCanvas = true self.settings.failedCanvas = true
self.settings.hasCanvas = false
return return
end end
self.settings.hasCanvas = true self.settings.hasCanvas = true
@ -180,27 +206,6 @@ function element:setCalculatedSize(w, h)
self.view.h = math.max(self.view.minH, self.view.h) self.view.h = math.max(self.view.minH, self.view.h)
end end
function element:updateInputCtx()
self.context.inputContext:update()
--[[if self.settings.canvasW then
--If canvas too small make a bigger one
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)
--If canvas too big make a smaller one
elseif self.settings.canvasW > self.view.w*1.50 or self.settings.canvasH > self.view.h*1.50 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
local dummy = function() end local dummy = function() end
--Immediate mode code(don't call directly) --Immediate mode code(don't call directly)
function element.immediate(param, func, x, y, w, h) function element.immediate(param, func, x, y, w, h)
@ -209,7 +214,7 @@ function element.immediate(param, func, x, y, w, h)
local ctx = context.getContext() local ctx = context.getContext()
if ctx then if ctx then
local self = setmetatable({}, element) local self = setmetatable({}, element)
self = element:new(param, true) self = self:new(param, true)
else else
error("Can't render immediate outside of persistent") error("Can't render immediate outside of persistent")
end end
@ -218,7 +223,7 @@ end
--Called once dimensions are validated --Called once dimensions are validated
function element:setup() function element:setup()
self.context:set() self.context:set()
self.renderer = self.parentFunc(self.parameters, self.view.w, self.view.h) self.renderer = self.parentFunc(self.parameters, self.size)
self.context:unset() self.context:unset()
self.settings.isSetup = true self.settings.isSetup = true
@ -302,20 +307,12 @@ function element:externalRender()
end end
function element:externalUpdate() function element:externalUpdate()
self.context:zIndex()
if not self.settings.failedCanvas and self.settings.testRenderPasses == 0 and not self.settings.hasCanvas then if not self.settings.failedCanvas and self.settings.testRenderPasses == 0 and not self.settings.hasCanvas then
local avg, sum = 0, 0
for i, e in ipairs(self.renderBench) do if self:calculateCanvasCoeficient() then
sum = sum + e
end
avg = sum/#self.renderBench
if self:calculateCanvasCoeficient(avg) then
love.graphics.push()
love.graphics.origin()
self:createCanvas() self:createCanvas()
love.graphics.pop()
self.settings.pendingUpdate = true self.settings.pendingUpdate = true
else else
self.settings.failedCanvas = true self.settings.failedCanvas = true
@ -327,6 +324,27 @@ function element:externalUpdate()
self.settings.pendingUpdate = false self.settings.pendingUpdate = false
end end
if self.deferResize then
self.context:sizeChanged()
if self.settings.hasCanvas then
helium.atlas.unassign(self)
if self:calculateCanvasCoeficient() then
self:createCanvas()
else
self.settings.hasCanvas = false
self.canvas = nil
self.quad = nil
self.deferResize = nil
end
end
end
if self.deferRepos then
self.context:posChanged()
self.deferRepos = false
end
return self.settings.remove return self.settings.remove
end end
@ -337,21 +355,20 @@ local insert = table.insert
---@param x number ---@param x number
---@param y number ---@param y number
function element:draw(x, y, w, h) function element:draw(x, y, w, h)
if not self.view.lock then if x then self.view.x = x end
if x then self.view.x = x end if y then self.view.y = y end
if y then self.view.y = y end if w then self.view.w = w end
if w then self.view.w = self.view.minW<=w and w or self.view.minW end if h then self.view.h = h end
if h then self.view.h = self.view.minH<=h and h or self.view.minH end
end
if context.getContext() then local cx = context.getContext()
if context.getContext().childRender(self) then if cx then
if cx:childRender(self) then
self:externalUpdate() self:externalUpdate()
self:externalRender() self:externalRender()
end end
elseif not self.settings.inserted then elseif not self.settings.inserted then
self.settings.inserted = true self.settings.inserted = true
insert(helium.elementBuffer, self) insert(helium.elementInsertionQueue, self)
end end
if self.settings.firstDraw then if self.settings.firstDraw then
@ -360,6 +377,10 @@ function element:draw(x, y, w, h)
end end
end end
function element:getSize()
return self.view.w, self.view.h
end
function element:destroy() function element:destroy()
self.settings.remove = true self.settings.remove = true
self.settings.firstDraw = true self.settings.firstDraw = true

View File

@ -9,45 +9,31 @@ function eventClass.new()
return setmetatable(self, eventClass) return setmetatable(self, eventClass)
end end
function eventClass:sub(name, func) function eventClass:newQueue(name)
local sub = { func = func } self.eventSubs[name] = {}
self:provideQueue(name)
self.eventSubs[name][func] = func
return sub
end end
function eventClass:push(name, event) --Data is the individualized table to pass to each subscriber (or middleware)
if self.eventSubs[name] then function eventClass:sub(name, func, data)
table.insert(self.eventSubs[name].queue, event) self.eventSubs[name][func] = {func = func, data = data}
end
return func
end end
function eventClass:flush(name) function eventClass:unsub(name, func)
self.eventSubs[name][func] = nil
end end
function eventClass:flushAll() function eventClass:push(name, evntData)
for i, subs in pairs(self.eventSubs) do local pushData = evntData
local assembledEvent = {} for i, e in pairs(self.eventSubs[name]) do
if self.eventSubs[name].beforeEach then
for i, e in ipairs(subs.queue) do pushData = self.eventSubs[name].beforeEach(e.data, evntData) or evntData
end end
e.func(pushData)
end end
end end
function eventClass:provideQueue(name)
if not self.eventSubs[name] then
self.eventSubs[name] = {
queue = {}
}
end
end
function eventClass:unsub(name, event)
self.eventSubs[name][event] = nil
end
return eventClass return eventClass

View File

@ -1,47 +1,29 @@
local path = string.sub(..., 1, string.len(...) - string.len(".core.input")) local path = string.sub(..., 1, string.len(...) - string.len(".core.input"))
local helium = require(path .. ".dummy") local stack = require(path .. ".core.stack")
local input={ local input = {
eventHandlers = {}, eventHandlers = {},
subscriptions = {}, subscriptions = {},
activeEvents = {} activeEvents = {}
} }
input.__index = input input.__index = input
local windowMachine = {} local function sortFunc(t1, t2)
windowMachine.__index = windowMachine if t1.stack.temporalZ.z == t2.stack.temporalZ.z then
local windowCurrent = false print('same Z ???',t1.stack.temporalZ.z, t2.stack.temporalZ.z)
--Go forward
windowMachine.push = function(window)
windowMachine[#windowMachine+1] = window
windowCurrent = #windowMachine
end
--Go a level back
windowMachine.pop = function()
windowMachine[#windowMachine] = nil
if windowCurrent>1 then
windowCurrent = windowCurrent-1
else
windowCurrent = false
end end
end if t1 == t2 then
print(tostring(t1), tostring(t2))
windowMachine.get = function() return false
if windowCurrent then
return windowMachine[windowCurrent]
end end
end return t1.stack.temporalZ.z > t2.stack.temporalZ.z
end
local activeWindow function input.sortZ()
local windowStack = {} for i, subs in pairs(input.subscriptions) do
table.sort(subs, sortFunc)
function input.unload() print(#subs)
windowStack = {} end
activeWindow = nil
windowMachine = {}
input.subscriptions = {}
end end
local dummyfunc = function() end local dummyfunc = function() end
@ -49,11 +31,10 @@ local dummyfunc = function() end
local subscription = {} local subscription = {}
subscription.__index = subscription subscription.__index = subscription
---@class inputContext function input.unload()
local context = {} input.subscriptions = {}
context.__index = context input.activeEvents = {}
end
local activeContext
--[[Event types --[[Event types
###SIMPLE EVENTS### ###SIMPLE EVENTS###
@ -73,80 +54,6 @@ local activeContext
]] ]]
function input.newContext(element)
local ctx = setmetatable({elem = element, subs = {}, childContexts={}}, context)
return ctx
end
function context:set()
self.activeWindow = windowMachine.get()
if activeContext and activeContext~=self then
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
else
self.absX = self.elem.view.x
self.absY = self.elem.view.y
activeContext = self
end
end
function context:update()
if self.parentCtx then
self.absX = self.parentCtx.absX + self.elem.view.x
self.absY = self.parentCtx.absY + self.elem.view.y
else
self.absX = self.elem.view.x
self.absY = self.elem.view.y
end
for i, e in ipairs(self.childContexts) do
e:update()
end
for i, sub in ipairs(self.subs) do
sub:contextUpdate(self.absX,self.absY,self)
end
end
function context:unset()
if self.parentCtx then
activeContext = self.parentCtx
else
activeContext = nil
end
end
function context:afterLoad()
--If i created window, pop it
if self.window then
windowMachine.pop()
end
end
function context:unsuspend()
for i, e in ipairs(self.subs) do
e:unsuspend()
end
for i, e in ipairs(self.childContexts) do
e:unsuspend()
end
end
function context:suspend()
for i, e in ipairs(self.subs) do
e:suspend()
end
for i, e in ipairs(self.childContexts) do
e:suspend()
end
end
---@param x number ---@param x number
---@param y number ---@param y number
---@param w number ---@param w number
@ -156,70 +63,45 @@ end
---@param doff boolean ---@param doff boolean
---@return subscription ---@return subscription
function subscription.create(x, y, w, h, eventType, callback, doff) function subscription.create(x, y, w, h, eventType, callback, doff)
local sub local stack = stack.getContext()
if activeContext then local xn, yn = stack:normalizePos(x, y)
local wratio,hratio,xratio,yratio local wn, hn = stack:normalizeSize(w, h)
if x<=1 and x~=0 then
xratio = x
x = activeContext.elem.view.w * x
end
if y<=1 and y~=0 then
yratio = y
y = activeContext.elem.view.h * y
end
if w<=1 and w~=0 then
wratio = w
w = activeContext.elem.view.w * w
end
if h<=1 and h~=0 then local sub = setmetatable({
hratio = h x = xn,
h = activeContext.elem.view.h * h y = yn,
end w = wn,
h = hn,
origX = x,
origY = y,
origW = w,
origH = h,
eventType = eventType,
active = true,
stack = stack,
callback = callback
},subscription)
sub = setmetatable({ sub.onSizeChange = stack:onSizeChange(function()
x = activeContext.absX + x, sub.w, sub.h = sub.stack:normalizeSize(sub.origW, sub.origH)
y = activeContext.absY + y, end)
w = w,
h = h, sub.onPosChange = stack:onPosChange(function()
wratio = wratio, sub.x, sub.y = sub.stack:normalizePos(sub.origX, sub.origY)
hratio = hratio, print(sub.y, sub.stack.absY)
xratio = xratio, end)
yratio = yratio,
ix = x,
iy = y,
eventType = eventType,
active = true,
callback = callback
},subscription)
if doff == false then if doff == false then
sub:off() sub:off()
end
if activeContext.window or activeContext.activeWindow then
sub.parentWindow = activeContext.window or activeContext.activeWindow
end
activeContext.subs[#activeContext.subs+1] = sub
else
sub = setmetatable({
x = x,
y = y,
w = w,
h = h,
eventType = eventType,
active = doff or true,
callback = callback
},subscription)
end end
if not input.subscriptions[eventType] then if not input.subscriptions[eventType] then
input.subscriptions[eventType] = {} input.subscriptions[eventType] = {}
end end
input.subscriptions[eventType][#input.subscriptions[eventType]+1] = sub table.insert(input.subscriptions[eventType],1,sub)
input.sortZ()
return sub return sub
end end
@ -232,40 +114,9 @@ function subscription:on()
self.active = true self.active = true
end end
function subscription:suspend() function subscription:remove()
self.destroyStat = true --input.subscriptions[self.eventType][self] = nil
self.preActive = self.active input.sortZ()
self.active = false
end
function subscription:unsuspend()
self.active = self.preActive
end
function subscription:contextUpdate(absX, absY,activeContext)
if self.xratio then
self.x = absX + activeContext.elem.view.w * self.xratio
else
self.x = absX + self.ix
end
if self.yratio then
self.y = absY + activeContext.elem.view.h * self.yratio
else
self.y = absY + self.iy
end
if self.hratio then
self.h = activeContext.elem.view.h * self.hratio
end
if self.wratio then
self.w = activeContext.elem.view.w * self.wratio
end
end
function subscription:update(x, y, w, h)
self.x = x or self.x
self.y = y or self.y
self.w = w or self.w
self.h = h or self.h
end end
function subscription:emit(...) function subscription:emit(...)
@ -297,73 +148,11 @@ input.__call = function(self, eventType, callback, cbOff, x, y, w, h)
return subscription.create(x,y,w,h,eventType,callback,cbOff) return subscription.create(x,y,w,h,eventType,callback,cbOff)
end end
--Will block ui clicks from going through it
input.window = function(x,y,w,h)
x = x or 0
y = y or 0
w = w or 1
h = h or 1
activeContext.window = subscription.create(x,y,w,h,'window',nil,false)
windowMachine.push(activeContext.window)
windowStack[#windowStack+1] = activeContext.window
return activeContext.window
end
--Run once per applicable event
--The windows should be basically pre sorted, so if something is a hit on a lower window
--And is on a higher one too, the lower one is discarded
local hits = {}
input.checkWindows = function(x,y)
local hit = false
for i = 1, #windowStack do
if windowStack[i]:checkInside(x,y) and windowStack[i].active then
hit = windowStack[i]
end
end
--Returns latest hit
return hit
end
--Run per sub
function input.checkSub(sub,hit)
if sub.parentWindow and sub.parentWindow == hit then
return true
elseif not hit then
return true
end
return false
end
--Since the introduction of the relative subscriptions, there is more utility in ommiting coordinates by default --Since the introduction of the relative subscriptions, there is more utility in ommiting coordinates by default
setmetatable(input, input) setmetatable(input, input)
function input.eventHandlers.mousereleased(x, y, btn) function input.eventHandlers.mousereleased(x, y, btn)
local captured = false local captured = false
local hit = input.checkWindows(x, y)
if input.subscriptions.mousereleased then
for index, sub in ipairs(input.subscriptions.mousereleased) do
--local succ = sub:checkInside(x, y)
if sub.active and sub:checkInside(x, y) and input.checkSub(sub,hit) then -- succ and sub:check
sub:emit(x, y, btn)
captured = true
end
end
end
if input.subscriptions.mousereleased_outside then
for index, sub in ipairs(input.subscriptions.mousereleased_outside) do
--local succ = sub:checkOutside(x, y)
if sub.active and sub:checkOutside(x, y) and input.checkSub(sub,hit) then -- succ and sub.active then
sub:emit(x, y, btn)
captured = true
end
end
end
if input.subscriptions.clicked then if input.subscriptions.clicked then
for index, sub in ipairs(input.subscriptions.clicked) do for index, sub in ipairs(input.subscriptions.clicked) do
if sub.currentEvent then if sub.currentEvent then
@ -388,44 +177,40 @@ function input.eventHandlers.mousereleased(x, y, btn)
end end
end end
if input.subscriptions.mousereleased then
for index, sub in ipairs(input.subscriptions.mousereleased) do
if sub.active and sub:checkInside(x, y) then
sub:emit(x, y, btn)
captured = true
end
end
end
if input.subscriptions.mousereleased_outside then
for index, sub in ipairs(input.subscriptions.mousereleased_outside) do
if sub.active and sub:checkOutside(x, y) then
sub:emit(x, y, btn)
captured = true
end
end
end
return captured return captured
end end
function input.eventHandlers.mousepressed(x, y, btn) function input.eventHandlers.mousepressed(x, y, btn)
local captured = false local captured = false
local hit = input.checkWindows(x, y)
if input.subscriptions.mousepressed then
for index, sub in ipairs(input.subscriptions.mousepressed) do
--local succ = sub:checkInside(x, y)
if sub.active and sub:checkInside(x, y) and input.checkSub(sub,hit) then -- succ and sub:check
sub:emit(x, y, btn)
captured = true
end
end
end
if input.subscriptions.mousepressed_outside then
for index, sub in ipairs(input.subscriptions.mousepressed_outside) do
--local succ = sub:checkOutside(x, y)
if sub.active and sub:checkOutside(x, y) and input.checkSub(sub,hit) then -- succ and sub.active then
sub:emit(x, y, btn)
captured = true
end
end
end
if input.subscriptions.clicked then if input.subscriptions.clicked then
for index, sub in ipairs(input.subscriptions.clicked) do for index, sub in ipairs(input.subscriptions.clicked) do
local succ = sub:checkInside(x, y) local succ = sub:checkInside(x, y)
if succ and sub.active and input.checkSub(sub,hit) then if succ and sub.active then
sub.cleanUp = sub:emit(x, y, btn) or dummyfunc sub.cleanUp = sub:emit(x, y, btn) or dummyfunc
sub.currentEvent = true sub.currentEvent = true
captured = true return true
end end
end end
@ -433,16 +218,33 @@ function input.eventHandlers.mousepressed(x, y, btn)
if input.subscriptions.dragged then if input.subscriptions.dragged then
for index, sub in ipairs(input.subscriptions.dragged) do for index, sub in ipairs(input.subscriptions.dragged) do
--local succ = sub:checkInside(x, y) if sub.active and sub:checkInside(x, y) then
if sub.active and sub:checkInside(x, y) and input.checkSub(sub,hit) then -- succ and sub:check
sub.currentEvent = true sub.currentEvent = true
captured = true return true
end end
end end
end end
if input.subscriptions.mousepressed then
for index, sub in ipairs(input.subscriptions.mousepressed) do
if sub.active and sub:checkInside(x, y) then
sub:emit(x, y, btn)
return true
end
end
end
if input.subscriptions.mousepressed_outside then
for index, sub in ipairs(input.subscriptions.mousepressed_outside) do
if sub.active and sub:checkOutside(x, y) then
sub:emit(x, y, btn)
return true
end
end
end
return captured return captured
end end
@ -450,7 +252,7 @@ function input.eventHandlers.keypressed(btn)
local captured = false local captured = false
if input.subscriptions.keypressed then if input.subscriptions.keypressed then
for index, sub in ipairs(input.subscriptions.keypressed) do for index, sub in ipairs(input.subscriptions.keypressed) do
if sub.active then -- ==true then if sub.active then
sub:emit( btn) sub:emit( btn)
captured = true captured = true
end end
@ -485,13 +287,13 @@ function input.eventHandlers.mousemoved(x, y, dx, dy)
if sub.active and not sub.currentEvent and succ then if sub.active and not sub.currentEvent and succ then
sub.cleanUp = sub:emit(x, y, dx, dy) or dummyfunc sub.cleanUp = sub:emit(x, y, dx, dy) or dummyfunc
sub.currentEvent = true sub.currentEvent = true
captured = true return true
elseif sub.currentEvent and not succ then elseif sub.currentEvent and not succ then
sub.currentEvent = false sub.currentEvent = false
captured = true
if sub.cleanUp then if sub.cleanUp then
sub.cleanUp(x, y) sub.cleanUp(x, y)
end end
return true
end end
end end
end end
@ -505,8 +307,8 @@ function input.eventHandlers.mousemoved(x, y, dx, dy)
else else
sub:emit(x, y, dx, dy) sub:emit(x, y, dx, dy)
end end
--sub.currentEvent = true -- checked in the condition so must be true
captured = true return true
end end
end end

View File

@ -2,12 +2,14 @@
local path = string.sub(..., 1, string.len(...) - string.len(".core.stack")) local path = string.sub(..., 1, string.len(...) - string.len(".core.stack"))
local helium = require(path .. ".dummy") local helium = require(path .. ".dummy")
local event = require(path..'.core.events')
---@class context ---@class context
local context = {} local context = {}
context.__index = context context.__index = context
local activeContext local activeContext
local currentTemporalZ = 0
---@param elem element ---@param elem element
function context.new(elem) function context.new(elem)
@ -15,11 +17,15 @@ function context.new(elem)
view = elem.view, view = elem.view,
element = elem, element = elem,
childrenContexts = {}, childrenContexts = {},
inputContext = helium.input.newContext(elem),
childRenderTime = 0, childRenderTime = 0,
deferChildren = false, deferChildren = false,
events = event.new(),
capturedChilds = {}, capturedChilds = {},
}, context) temporalZ = {z = nil},
}, context)
ctx.events:newQueue('resize')
ctx.events:newQueue('poschange')
return ctx return ctx
end end
@ -28,7 +34,7 @@ function context:bubbleUpdate()
self.element.settings.pendingUpdate = true self.element.settings.pendingUpdate = true
self.element.settings.needsRendering = true self.element.settings.needsRendering = true
if self.parentCtx and self.parentCtx~=self then if self.parentCtx and self.parentCtx~=self then
self.parentCtx:bubbleUpdate() self.parentCtx:bubbleUpdate()
end end
end end
@ -56,14 +62,9 @@ function context:set()
activeContext = self activeContext = self
end end
self.inputContext:set()
end end
function context:unset() function context:unset()
self.inputContext:unset()
self.inputContext:afterLoad()
if self.parentCtx then if self.parentCtx then
activeContext = self.parentCtx activeContext = self.parentCtx
else else
@ -71,8 +72,9 @@ function context:unset()
end end
end end
function context:unsuspend() function context:zIndex()
self.inputContext:unsuspend() currentTemporalZ = currentTemporalZ+1
self.temporalZ.z = currentTemporalZ
end end
function context:startSelfRender() function context:startSelfRender()
@ -97,12 +99,6 @@ function context:destroy()
end end
end end
function context:suspend()
self.inputContext:set()
self.inputContext:suspend()
self.inputContext:unset()
end
function context:getChildrenCount() function context:getChildrenCount()
return #self.childrenContexts return #self.childrenContexts
end end
@ -124,12 +120,103 @@ end
function context:stopDeferingChildren() function context:stopDeferingChildren()
self.deferChildren = false self.deferChildren = false
return self.capturedChilds return self.capturedChilds
end end
function context:normalizePos(x, y)
local xPX, yPX
if x<=1 and x~=0 then
xPX = self.element.view.w * x
end
if y<=1 and y~=0 then
yPX = self.element.view.h * y
end
return (xPX or x) + self.absX, (yPX or y) + self.absY
end
function context:normY(y)
local yPX
if y<=1 and y~=0 then
yPX = self.element.view.h * y
end
return (yPX or y)
end
function context:normX(x)
local xPX
if x<=1 and x~=0 then
xPX = self.element.view.w * x
end
return (xPX or x)
end
function context:normalizeSize(w, h)
local wPX, hPX
if w<=1 and w~=0 then
wPX = self.element.view.w * w
end
if h<=1 and h~=0 then
hPX = self.element.view.h * h
end
return wPX or w, hPX or h
end
--To be used by the element
function context:sizeChanged()
self.events:push('resize')
end
function context:posChanged()
if self.parentCtx then
self.absX = self.parentCtx.absX + self.view.x
self.absY = self.parentCtx.absY + self.view.y
else
self.absX = self.view.x
self.absY = self.view.y
end
self.events:push('poschange')
end
--Event subscriptions
function context:onSizeChange(callback)
return self.events:sub('resize', callback)
end
function context:onPosChange(callback)
return self.events:sub('poschange', callback)
end
function context:offSizeChange(callback)
self.events:unsub('resize', callback)
end
function context:offPosChange(callback)
self.events:unsub('poschange', callback)
end
--Function meant for external context capture --Function meant for external context capture
function context.getContext() function context.getContext()
return activeContext return activeContext
end end
function context.newFrame()
currentTemporalZ = 0
end
function context.unload()
activeContext = nil
end
return context return context

View File

@ -10,8 +10,10 @@ helium.utils = require(path..".utils")
helium.element = require(path..".core.element") helium.element = require(path..".core.element")
helium.input = require(path..".core.input") helium.input = require(path..".core.input")
helium.loader = require(path..".loader") helium.loader = require(path..".loader")
helium.stack = require(path..".core.stack")
helium.atlas = require(path..".core.atlas") helium.atlas = require(path..".core.atlas")
helium.elementBuffer = {} helium.elementBuffer = {}
helium.elementInsertionQueue = {}
helium.__index = helium helium.__index = helium
setmetatable(helium, {__call = function(s, chunk) setmetatable(helium, {__call = function(s, chunk)
@ -61,15 +63,17 @@ function helium.draw()
for i, e in ipairs(helium.elementBuffer) do for i, e in ipairs(helium.elementBuffer) do
e:externalRender() e:externalRender()
end end
for i, e in ipairs(helium.elementInsertionQueue) do
table.insert(helium.elementBuffer, e)
end
helium.elementInsertionQueue = {}
end end
function helium.update(dt) function helium.update(dt)
if helium.conf.HOTSWAP then
helium.loader.update(dt)
end
for i = #helium.elementBuffer, 1, -1 do for i = 1, #helium.elementBuffer do
if helium.elementBuffer[i]:externalUpdate() then if helium.elementBuffer[i]:externalUpdate(i) then
table.remove(helium.elementBuffer,i) table.remove(helium.elementBuffer,i)
end end
end end
@ -110,6 +114,7 @@ if helium.conf.AUTO_RUN then
-- Main loop time. -- Main loop time.
return function() return function()
-- Process events. -- Process events.
helium.stack.newFrame()
if love.event then if love.event then
love.event.pump() love.event.pump()
for name, a,b,c,d,e,f in love.event.poll() do for name, a,b,c,d,e,f in love.event.poll() do
@ -138,13 +143,14 @@ if helium.conf.AUTO_RUN then
if love.graphics and love.graphics.isActive() then if love.graphics and love.graphics.isActive() then
love.graphics.origin() love.graphics.origin()
love.graphics.clear(love.graphics.getBackgroundColor()) love.graphics.clear(love.graphics.getBackgroundColor())
if love.draw then love.draw() end
st = love.timer.getTime() st = love.timer.getTime()
helium.draw() helium.draw()
heliumTime=heliumTime+love.timer.getTime()-st heliumTime=heliumTime+love.timer.getTime()-st
if love.draw then love.draw() end
love.graphics.present() love.graphics.present()
end end

11
layout/column.lua Normal file
View File

@ -0,0 +1,11 @@
return function(x, y, width, height, children, hpad, vpad, alignX)
local carriagePos = 0
if children then
for i, e in ipairs(children) do
local _, h = e:getSize()
e:draw(x, y+carriagePos+vpad)
carriagePos = carriagePos + h + vpad
end
end
print('finished layout')
end

0
layout/flow.lua Normal file
View File

View File

@ -1,88 +1,144 @@
local path = string.sub(..., 1, string.len(...) - string.len(".core.layout")) local path = string.sub(..., 1, string.len(...) - string.len(".layout"))
local layout = {} local layout = {}
local layouts = {}
layouts.column = require(path..'.layout.column')
layout.__index = layout layout.__index = layout
local element = require(path..'core.element') local element = require(path..'.core.element')
local stack = require(path..'core.stack') local stack = require(path..'.core.stack')
local function layout_new(type, x, y, w, h)
local ctx = element.getContext()
--The output will be in pixel numbers regardless of inputs
if x <= 1 or not x then
x = ctx.view.x * (x or 0)
end
if y <= 1 then
y = ctx.view.y * (y or 0)
end
if w <= 1 then
w = ctx.view.w * (w or 1)
end
if h <= 1 then
h = ctx.view.h * (h or 1)
end
return
end
layout(0,0,1,1)
--Start prep phase --Start prep phase
function layout.type(type) function layout.type(type)
local curStack = stack.getContext()
curStack:startDeferingChildren()
local self = { local self = {
vars = { vars = {
type = type or 'flow', type = type or 'column',
offLeft = 0, offLeft = 0,
offTop = 0, offTop = 0,
width = 1, width = 1,
hpad = 3,
vpad = 3,
height = 1, height = 1,
alignX = 'left', --options: left, center, right alignX = 'left', --options: left, center, right
alignY = 'top', --options: top, center, bottom alignY = 'top', --options: top, center, bottom
flowDir = 'rtl' --options: rtl/ttb --flowDir = 'rtl', --options: rtl/ttb
} },
stack = curStack
} }
return setmetatable(self, layout) return setmetatable(self, layout)
end end
function layout:alignVert(pos) function layout:alignVert(pos)
self.vars.alignY = pos self.vars.alignY = pos
return self
end end
function layout:alignHoriz(pos) function layout:alignHoriz(pos)
self.vars.alignX = pos self.vars.alignX = pos
return self
end end
--Sets up the box of the layout
function layout:width(w) function layout:width(w)
self.vars.width = w self.vars.width = w
return self
end end
function layout:height(h) function layout:height(h)
self.vars.height = h self.vars.height = h
return self
end end
function layout:left(x) function layout:left(x)
self.vars.offTop = x self.vars.offLeft = x
return self
end end
function layout:right(x) function layout:right(x)
self.vars.offRight = x self.vars.offRight = x
return self
end end
function layout:top(y) function layout:top(y)
self.vars.offTop = y self.vars.offTop = y
return self
end end
function layout:bottom(y) function layout:bottom(y)
self.vars.offBot = y self.vars.offBot = y
return self
end
function layout:vPadding(px)
self.vars.vpad = px
return self
end
function layout:hPadding(px)
self.vars.hpad = px
return self
end
--Schemes: left + right = width ignored
--top + bottom = height ignored
--top px + bottom relative works
--left relative + bottom px works
function layout:draw()
local stack = self.stack
local children = stack:stopDeferingChildren()
local height, width, x, y, _, marginV, marginH
local maxW, maxH = stack:normalizeSize(1,1)
if self.vars.offTop and self.vars.offBot then
marginV = stack:normY(self.vars.offTop) + stack:normY(self.vars.offBot)
height = stack:normY(1) - marginV
elseif self.vars.offTop then
y = stack:normY(self.vars.offTop)
height = math.min(stack:normY(self.vars.height), maxH-y)
elseif self.vars.offBot then
y = stack:normY(self.vars.offBot)
height = math.min(stack:normY(self.vars.height), maxH-y)
y = 0
end
if self.vars.offLeft and self.vars.offRight then
marginH = stack:normX(self.vars.offLeft) + stack:normX(self.vars.offRight)
width = stack:normX(1) - marginH
elseif self.vars.offLeft then
x = stack:normX(self.vars.offLeft)
width = math.min(stack:normX(self.vars.width), maxW-x)
elseif self.vars.offRight then
x = stack:normX(self.vars.offRight)
height = math.min(stack:normX(self.vars.width), maxW-x)
x = 0
end
layouts[self.vars.type](
x,
y,
width,
height,
children,
self.vars.hpad,
self.vars.vpad,
self.vars.alignX,
self.vars.alignY
)
end end
setmetatable(layout, {__call = function(s, type) return layout.type(type) end })
setmetatable(layout, {__call = function(s, type) return layout.type(type) end }
return layout return layout