diff --git a/core/atlas.lua b/core/atlas.lua index 15199d4..11784c5 100644 --- a/core/atlas.lua +++ b/core/atlas.lua @@ -29,14 +29,14 @@ function atlas.assign(element) local elH = element.view.h local canvas, quad = createdAtlas:assignElement(element) if not canvas and createdAtlas.ideal_area < createdAtlas.taken_area*4 then - print('refragmenting ;3') + --print('refragmenting ;3') createdAtlas:refragment() canvas, quad = createdAtlas:assignElement(element) if not canvas then - print('ran out of space') + --print('ran out of space') end else - print('wont refragment', createdAtlas.ideal_area, createdAtlas.taken_area) + --print('wont refragment', createdAtlas.ideal_area, createdAtlas.taken_area) end return canvas, quad end @@ -206,6 +206,7 @@ end function atlas:unassignElement(element) local user = self.users[element] 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 end diff --git a/core/element.lua b/core/element.lua index 58ef410..9f9fa99 100644 --- a/core/element.lua +++ b/core/element.lua @@ -19,7 +19,7 @@ setmetatable(element, { self.parentFunc = func self:new(param,nil, w, h) - self:createProxies(param) + self:createProxies() return self end @@ -29,7 +29,6 @@ setmetatable(element, { --The new function that should be used for element creation function element:new(param, immediate, w, h) self.parameters = {} - self.baseParams = param --Internal settings @@ -57,28 +56,48 @@ function element:new(param, immediate, w, h) self.renderBench = { } - self.baseView = { x = 0, y = 0, w = w or 10, h = h or 10, - minW = w or 10, - minH = h or 10, + minW = w or 0, + minH = h or 0, } - - + self.size = setmetatable({}, {__index = function(t, index) + return self.baseView[index] + end,}) + self.view = self.baseView - 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 -function element:posChange() +function element:posChange(i, v) + self.baseView[i] = v + --defer resize + if not self.deferRepos then + self.deferRepos = true + end end function element:onUpdate() @@ -104,14 +123,12 @@ function element:createProxies() end, __newindex = function(t, index, val) if self.baseView[index] ~= val then - self.baseView[index] = val if index=='w' or index=='h' then - self:sizeChange() + self:sizeChange(index, val) else - self:posChange() + self:posChange(index, val) end self.context:bubbleUpdate() - self:updateInputCtx() end end }) @@ -142,14 +159,22 @@ function element.setBench(time) selfRenderTime = time 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 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 sizeCoef = selfTime/selfRenderTime + local sizeCoef = avg/selfRenderTime return (areaCoef+childCoef+sizeCoef)>coefficient end @@ -163,6 +188,7 @@ function element:createCanvas() if not self.canvas then self.settings.failedCanvas = true + self.settings.hasCanvas = false return end self.settings.hasCanvas = true @@ -180,27 +206,6 @@ function element:setCalculatedSize(w, h) self.view.h = math.max(self.view.minH, self.view.h) 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 --Immediate mode code(don't call directly) 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() if ctx then local self = setmetatable({}, element) - self = element:new(param, true) + self = self:new(param, true) else error("Can't render immediate outside of persistent") end @@ -218,7 +223,7 @@ end --Called once dimensions are validated function element:setup() 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.settings.isSetup = true @@ -302,20 +307,12 @@ function element:externalRender() end function element:externalUpdate() + self.context:zIndex() 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 - sum = sum + e - end - - avg = sum/#self.renderBench - - if self:calculateCanvasCoeficient(avg) then - love.graphics.push() - love.graphics.origin() + if self:calculateCanvasCoeficient() then self:createCanvas() - love.graphics.pop() + self.settings.pendingUpdate = true else self.settings.failedCanvas = true @@ -327,6 +324,27 @@ function element:externalUpdate() self.settings.pendingUpdate = false 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 end @@ -337,21 +355,20 @@ local insert = table.insert ---@param x number ---@param y number function element:draw(x, y, w, h) - if not self.view.lock then - if x then self.view.x = x end - if y then self.view.y = y end - if w then self.view.w = self.view.minW<=w and w or self.view.minW end - if h then self.view.h = self.view.minH<=h and h or self.view.minH end - end + 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 - if context.getContext() then - if context.getContext().childRender(self) then + local cx = context.getContext() + if cx then + if cx:childRender(self) then self:externalUpdate() self:externalRender() end elseif not self.settings.inserted then self.settings.inserted = true - insert(helium.elementBuffer, self) + insert(helium.elementInsertionQueue, self) end if self.settings.firstDraw then @@ -360,6 +377,10 @@ function element:draw(x, y, w, h) end end +function element:getSize() + return self.view.w, self.view.h +end + function element:destroy() self.settings.remove = true self.settings.firstDraw = true diff --git a/core/events.lua b/core/events.lua index 550c431..221687c 100644 --- a/core/events.lua +++ b/core/events.lua @@ -9,45 +9,31 @@ function eventClass.new() return setmetatable(self, eventClass) end -function eventClass:sub(name, func) - local sub = { func = func } - - self:provideQueue(name) - self.eventSubs[name][func] = func - - return sub +function eventClass:newQueue(name) + self.eventSubs[name] = {} end -function eventClass:push(name, event) - if self.eventSubs[name] then - table.insert(self.eventSubs[name].queue, event) - end +--Data is the individualized table to pass to each subscriber (or middleware) +function eventClass:sub(name, func, data) + self.eventSubs[name][func] = {func = func, data = data} + + return func end -function eventClass:flush(name) - +function eventClass:unsub(name, func) + self.eventSubs[name][func] = nil end -function eventClass:flushAll() - for i, subs in pairs(self.eventSubs) do - local assembledEvent = {} - - for i, e in ipairs(subs.queue) do - +function eventClass:push(name, evntData) + local pushData = evntData + for i, e in pairs(self.eventSubs[name]) do + if self.eventSubs[name].beforeEach then + pushData = self.eventSubs[name].beforeEach(e.data, evntData) or evntData end + + e.func(pushData) 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 \ No newline at end of file diff --git a/core/input.lua b/core/input.lua index f94df45..f105ba9 100644 --- a/core/input.lua +++ b/core/input.lua @@ -1,47 +1,29 @@ 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 = {}, subscriptions = {}, activeEvents = {} } input.__index = input -local windowMachine = {} -windowMachine.__index = windowMachine -local windowCurrent = false - ---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 +local function sortFunc(t1, t2) + if t1.stack.temporalZ.z == t2.stack.temporalZ.z then + print('same Z ???',t1.stack.temporalZ.z, t2.stack.temporalZ.z) end -end - -windowMachine.get = function() - if windowCurrent then - return windowMachine[windowCurrent] + if t1 == t2 then + print(tostring(t1), tostring(t2)) + return false end -end + return t1.stack.temporalZ.z > t2.stack.temporalZ.z +end -local activeWindow -local windowStack = {} - -function input.unload() - windowStack = {} - activeWindow = nil - windowMachine = {} - input.subscriptions = {} +function input.sortZ() + for i, subs in pairs(input.subscriptions) do + table.sort(subs, sortFunc) + print(#subs) + end end local dummyfunc = function() end @@ -49,11 +31,10 @@ local dummyfunc = function() end local subscription = {} subscription.__index = subscription ----@class inputContext -local context = {} -context.__index = context - -local activeContext +function input.unload() + input.subscriptions = {} + input.activeEvents = {} +end --[[Event types ###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 y number ---@param w number @@ -156,70 +63,45 @@ end ---@param doff boolean ---@return subscription function subscription.create(x, y, w, h, eventType, callback, doff) - local sub - if activeContext then - local wratio,hratio,xratio,yratio - 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 + local stack = stack.getContext() + local xn, yn = stack:normalizePos(x, y) + local wn, hn = stack:normalizeSize(w, h) - if h<=1 and h~=0 then - hratio = h - h = activeContext.elem.view.h * h - end + local sub = setmetatable({ + x = xn, + y = yn, + w = wn, + h = hn, + origX = x, + origY = y, + origW = w, + origH = h, + eventType = eventType, + active = true, + stack = stack, + callback = callback + },subscription) - sub = setmetatable({ - x = activeContext.absX + x, - y = activeContext.absY + y, - w = w, - h = h, - wratio = wratio, - hratio = hratio, - xratio = xratio, - yratio = yratio, - ix = x, - iy = y, - eventType = eventType, - active = true, - callback = callback - },subscription) + sub.onSizeChange = stack:onSizeChange(function() + sub.w, sub.h = sub.stack:normalizeSize(sub.origW, sub.origH) + end) + + sub.onPosChange = stack:onPosChange(function() + sub.x, sub.y = sub.stack:normalizePos(sub.origX, sub.origY) + print(sub.y, sub.stack.absY) + end) - if doff == false then - 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) + if doff == false then + sub:off() end if not input.subscriptions[eventType] then input.subscriptions[eventType] = {} end - input.subscriptions[eventType][#input.subscriptions[eventType]+1] = sub + table.insert(input.subscriptions[eventType],1,sub) + + input.sortZ() return sub end @@ -232,40 +114,9 @@ function subscription:on() self.active = true end -function subscription:suspend() - self.destroyStat = true - self.preActive = self.active - 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 +function subscription:remove() + --input.subscriptions[self.eventType][self] = nil + input.sortZ() end 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) 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 setmetatable(input, input) function input.eventHandlers.mousereleased(x, y, btn) 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 for index, sub in ipairs(input.subscriptions.clicked) do if sub.currentEvent then @@ -388,44 +177,40 @@ function input.eventHandlers.mousereleased(x, y, btn) 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 end - - function input.eventHandlers.mousepressed(x, y, btn) 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 for index, sub in ipairs(input.subscriptions.clicked) do 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.currentEvent = true - captured = true + return true end end @@ -433,16 +218,33 @@ 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) - - if sub.active and sub:checkInside(x, y) and input.checkSub(sub,hit) then -- succ and sub:check + if sub.active and sub:checkInside(x, y) then sub.currentEvent = true - captured = true + return true 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 end @@ -450,7 +252,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 then -- ==true then + if sub.active then sub:emit( btn) captured = true end @@ -485,13 +287,13 @@ function input.eventHandlers.mousemoved(x, y, dx, dy) if sub.active and not sub.currentEvent and succ then sub.cleanUp = sub:emit(x, y, dx, dy) or dummyfunc sub.currentEvent = true - captured = true + return true elseif sub.currentEvent and not succ then sub.currentEvent = false - captured = true if sub.cleanUp then sub.cleanUp(x, y) end + return true end end end @@ -505,8 +307,8 @@ function input.eventHandlers.mousemoved(x, y, dx, dy) else sub:emit(x, y, dx, dy) end - --sub.currentEvent = true -- checked in the condition so must be true - captured = true + + return true end end diff --git a/core/stack.lua b/core/stack.lua index 74d2178..de96347 100644 --- a/core/stack.lua +++ b/core/stack.lua @@ -2,12 +2,14 @@ local path = string.sub(..., 1, string.len(...) - string.len(".core.stack")) local helium = require(path .. ".dummy") +local event = require(path..'.core.events') ---@class context local context = {} context.__index = context local activeContext +local currentTemporalZ = 0 ---@param elem element function context.new(elem) @@ -15,11 +17,15 @@ function context.new(elem) view = elem.view, element = elem, childrenContexts = {}, - inputContext = helium.input.newContext(elem), childRenderTime = 0, deferChildren = false, + events = event.new(), capturedChilds = {}, - }, context) + temporalZ = {z = nil}, + }, context) + + ctx.events:newQueue('resize') + ctx.events:newQueue('poschange') return ctx end @@ -28,7 +34,7 @@ function context:bubbleUpdate() self.element.settings.pendingUpdate = 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() end end @@ -56,14 +62,9 @@ function context:set() activeContext = self end - - self.inputContext:set() end function context:unset() - self.inputContext:unset() - self.inputContext:afterLoad() - if self.parentCtx then activeContext = self.parentCtx else @@ -71,8 +72,9 @@ function context:unset() end end -function context:unsuspend() - self.inputContext:unsuspend() +function context:zIndex() + currentTemporalZ = currentTemporalZ+1 + self.temporalZ.z = currentTemporalZ end function context:startSelfRender() @@ -97,12 +99,6 @@ function context:destroy() end end -function context:suspend() - self.inputContext:set() - self.inputContext:suspend() - self.inputContext:unset() -end - function context:getChildrenCount() return #self.childrenContexts end @@ -124,12 +120,103 @@ end function context:stopDeferingChildren() self.deferChildren = false + return self.capturedChilds 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 context.getContext() return activeContext end +function context.newFrame() + currentTemporalZ = 0 +end + +function context.unload() + activeContext = nil +end + return context \ No newline at end of file diff --git a/init.lua b/init.lua index a47caae..6f8b2cc 100644 --- a/init.lua +++ b/init.lua @@ -10,8 +10,10 @@ helium.utils = require(path..".utils") helium.element = require(path..".core.element") helium.input = require(path..".core.input") helium.loader = require(path..".loader") +helium.stack = require(path..".core.stack") helium.atlas = require(path..".core.atlas") helium.elementBuffer = {} +helium.elementInsertionQueue = {} helium.__index = helium setmetatable(helium, {__call = function(s, chunk) @@ -61,15 +63,17 @@ function helium.draw() for i, e in ipairs(helium.elementBuffer) do e:externalRender() end + + for i, e in ipairs(helium.elementInsertionQueue) do + table.insert(helium.elementBuffer, e) + end + helium.elementInsertionQueue = {} end function helium.update(dt) - if helium.conf.HOTSWAP then - helium.loader.update(dt) - end - for i = #helium.elementBuffer, 1, -1 do - if helium.elementBuffer[i]:externalUpdate() then + for i = 1, #helium.elementBuffer do + if helium.elementBuffer[i]:externalUpdate(i) then table.remove(helium.elementBuffer,i) end end @@ -110,6 +114,7 @@ if helium.conf.AUTO_RUN then -- Main loop time. return function() -- Process events. + helium.stack.newFrame() if love.event then love.event.pump() 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 love.graphics.origin() love.graphics.clear(love.graphics.getBackgroundColor()) - - if love.draw then love.draw() end - + st = love.timer.getTime() helium.draw() heliumTime=heliumTime+love.timer.getTime()-st + if love.draw then love.draw() end + + love.graphics.present() end diff --git a/layout/column.lua b/layout/column.lua new file mode 100644 index 0000000..53a6711 --- /dev/null +++ b/layout/column.lua @@ -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 \ No newline at end of file diff --git a/layout/flow.lua b/layout/flow.lua new file mode 100644 index 0000000..e69de29 diff --git a/layout/init.lua b/layout/init.lua index dffbf61..9c89ad8 100644 --- a/layout/init.lua +++ b/layout/init.lua @@ -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 layouts = {} +layouts.column = require(path..'.layout.column') layout.__index = layout -local element = require(path..'core.element') -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) +local element = require(path..'.core.element') +local stack = require(path..'.core.stack') --Start prep phase function layout.type(type) + local curStack = stack.getContext() + curStack:startDeferingChildren() + local self = { vars = { - type = type or 'flow', + type = type or 'column', offLeft = 0, offTop = 0, width = 1, + hpad = 3, + vpad = 3, height = 1, alignX = 'left', --options: left, center, right alignY = 'top', --options: top, center, bottom - flowDir = 'rtl' --options: rtl/ttb - } + --flowDir = 'rtl', --options: rtl/ttb + }, + stack = curStack } - - return setmetatable(self, layout) end function layout:alignVert(pos) self.vars.alignY = pos + + return self end function layout:alignHoriz(pos) self.vars.alignX = pos + + return self end +--Sets up the box of the layout function layout:width(w) self.vars.width = w + + return self end function layout:height(h) self.vars.height = h + + return self end function layout:left(x) - self.vars.offTop = x + self.vars.offLeft = x + + return self end function layout:right(x) self.vars.offRight = x + + return self end function layout:top(y) self.vars.offTop = y + + return self end function layout:bottom(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 - -setmetatable(layout, {__call = function(s, type) return layout.type(type) end } +setmetatable(layout, {__call = function(s, type) return layout.type(type) end }) return layout