bug fixes and new conf values

This commit is contained in:
Elmārs Āboliņš 2021-07-03 21:13:03 +03:00
parent 91e3441201
commit 81b24eee59
25 changed files with 459 additions and 199 deletions

View File

@ -1,6 +1,10 @@
![alt text](https://i.imgur.com/ZQBQfsa.png "Helium")
# Helium
![Helium main menu demo](https://j.gifs.com/nRrKmp.gif)
*a main menu demo made with helium, in action, find [it here](https://github.com/qeffects/main-menu-example)*
## Basic overview:
Helium is practically more like a UI framework than a fully fledged UI library.
The idea is to build custom and build simple.
@ -119,4 +123,6 @@ Or continue on to the State and Input guide: [Here](./docs/State-Input-Guide.md)
If you are using gamestates, scene guide will be of interest: [Here](./docs/core/Scenes.md)
For a more general overview of the whole library: [Module index](./docs/Modules-Index.md)
Also check out the helium configuration values: [Config](./docs/Configuration.md)
There's also a main menu example project available here: [Project](https://github.com/qeffects/main-menu-example)

View File

@ -1,4 +1,15 @@
---@class __HELIUM_CONFIG
---@field DEBUG boolean
---@field LOAD_HOOKS boolean
---@field LOAD_LAYOUT boolean
---@field LOAD_SHELL boolean
---@field MANUAL_CACHING boolean
---@type __HELIUM_CONFIG
return {
AUTO_RUN = true, --Replaces the default love.run
DEBUG = true, --Reserved for later
LOAD_HOOKS = false, --Loads the hooks module in to helium table
LOAD_LAYOUT = false, --Loads the layout module in to helium table
LOAD_SHELL = false, --Loads the shell mocule in to helium table
MANUAL_CACHING = false, --Whether or not to perform automatic caching
}

View File

@ -3,7 +3,8 @@
Copyright (c) 2021 Elmārs Āboliņš
https://github.com/qeffects/helium
----------------------------------------------------]]
local path = string.sub(..., 1, string.len(...) - string.len(".core.atlas"))
local helium = require(path..'.dummy')
local atlas = {}
atlas.__index = atlas
---@class atlases
@ -39,6 +40,7 @@ end
function atlases:assign(element)
local avg, sum, canvasID = 0, 0, element.context:getCanvasIndex(true) or 1
if not helium.conf.MANUAL_CACHING then
for i, e in ipairs(element.renderBench) do
sum = sum + e
end
@ -54,6 +56,7 @@ function atlases:assign(element)
if not ((areaCoef+speedCoef)>coefficient) then
return
end
end
local elW = element.view.w
local elH = element.view.h

View File

@ -125,7 +125,6 @@ function element:posChange(i, v)
self.deferRepos = true
end
if self.callbacks.onPosChange then
for i, cb in ipairs(self.callbacks.onPosChange) do
cb(self.view.x, self.view.y)
@ -258,13 +257,13 @@ local setColor, rectangle, setFont, printf = love.graphics.setColor, love.graphi
local calcT
function element:internalRender()
if self.settings.testRenderPasses > 0 and selfRenderTime then
if self.settings.testRenderPasses > 0 and selfRenderTime and not helium.conf.MANUAL_CACHING then
calcT = love.timer.getTime()
end
self.renderer()
if self.settings.testRenderPasses > 0 and selfRenderTime then
if self.settings.testRenderPasses > 0 and selfRenderTime and not helium.conf.MANUAL_CACHING then
self.settings.testRenderPasses = self.settings.testRenderPasses-1
local selfTime = love.timer.getTime()-calcT
table.insert(self.renderBench, selfTime)
@ -334,10 +333,12 @@ end
function element:externalUpdate()
self.context:set()
self.context:zIndex()
if not self.settings.failedCanvas
if ((not self.settings.failedCanvas
and self.settings.testRenderPasses == 0
and not self.settings.hasCanvas
and scene.activeScene.cached then
and scene.activeScene.cached
and not helium.conf.MANUAL_CACHING)
or self.settings.forcedCanvas)
and not self.settings.hasCanvas then
self:createCanvas()

17
core/init.lua Normal file
View File

@ -0,0 +1,17 @@
local path = ...
---@class __HELIUM_CORE
---@field atlas any
---@field element any
---@field events any
---@field input any
---@field scene any
---@field stack any
return {
atlas = require(path..'.atlas'),
element = require(path..'.element'),
events = require(path..'.events'),
input = require(path..'.input'),
scene = require(path..'.scene'),
stack = require(path..'.stack'),
}

View File

@ -172,11 +172,11 @@ function subscription:emit(...)
end
function subscription:checkInside(x, y)
return x>self.stack.absX and x<self.stack.absX+self.w and y>self.stack.absY and y<self.stack.absY+self.h
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)
return not (x>self.stack.absX and x<self.stack.absX+self.w and y>self.stack.absY and y<self.stack.absY+self.h)
return not (x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h)
end
---@alias InputMouseClickSubscriptionCallback fun(x:number, y:number, mouseButton:string)

View File

@ -98,8 +98,8 @@ end
function scene:drawAtlases(x, y)
if self.atlas then
local aw = self.atlas.atlases[1].canvas:getWidth()
love.graphics.draw(self.atlas.atlases[1].canvas, x, y, 0, 0.5, 0.5)
love.graphics.draw(self.atlas.atlases[2].canvas, x+aw/2, y, 0, 0.5, 0.5)
love.graphics.draw(self.atlas.atlases[1].canvas, x, y)
love.graphics.draw(self.atlas.atlases[2].canvas, x+aw, y)
end
end

View File

@ -8,13 +8,16 @@ local helium = require(path .. ".dummy")
local event = require(path..'.core.events')
---@class context
local context = {}
---@field element Element
local context = {
type = 'context'
}
context.__index = context
local activeContext
local currentTemporalZ = 0
---@param elem element
---@param elem Element
function context.new(elem)
local ctx = setmetatable({
capturedChilds = {},
@ -203,9 +206,21 @@ end
--To be used by the element
function context:sizeChanged()
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('resize')
end
local posPropogator = function(elem)
elem:posChanged()
end
function context:posChanged()
if self.parentCtx then
self.absX = self.parentCtx.absX + self.view.x
@ -215,6 +230,8 @@ function context:posChanged()
self.absY = self.view.y
end
self:doOnEveryChild(posPropogator)
self.events:push('poschange')
end
@ -235,10 +252,16 @@ function context:offPosChange(callback)
self.events:unsub('poschange', callback)
end
function context:onEveryChild(func)
func(self.element)
function context:doOnEveryChild(func)
for i, e in ipairs(self.childrenContexts) do
self.childrenContexts:onEveryChild(func)
e:onEveryChild(func)
end
end
function context:onEveryChild(func)
func(self)
for i, e in ipairs(self.childrenContexts) do
e:onEveryChild(func)
end
end

85
docs/Configuration.md Normal file
View File

@ -0,0 +1,85 @@
## Configuration
Helium offers some configuration values, exposed to you, the user with HELIUM_CONFIG table.
To start configuring, create a global table before the first helium require like this:
```lua
HELIUM_CONFIG = {
LOAD_SHELL = true
}
local helium = require('helium')
```
If the configuration isn't working, you're probably not defining the `HELIUM_CONFIG` table early enough
After the first require it's safe to remove `HELIUM_CONFIG` as the values will be copied to an internal table.
## The current configuration values
the default value is indicated with () around em:
options: `other / (default)`
### LOAD_SHELL
options: `true / (false)`
This is an optional config that starts off by default, but it will load all of the ./shell/ modules in to the helium table
so you can use it like this later:
```lua
local helium = require('helium')
--
helium.shell.button()
```
The table structure mirrors the folders exactly, so, instead of
```lua
local checkbox = require('helium.shell.checkbox')
```
You can do
```lua
local helium = require('helium')
--
helium.shell.checkbox()
```
### LOAD_LAYOUT
options: `true / (false)`
This one is extremely similar to LOAD_SHELL, the result is exactly the same, except it loads the ./layout/ folder, and it's also off by default
so you can do
```lua
local helium = require('helium')
--
helium.layout.container.new()
```
### LOAD_HOOKS
options: `true / (false)`
This one is similar to LOAD_LAYOUT and LOAD_SHELL, the result is the same, except it loads the modules in ./hook/ folder, and it's also off by default
so you can do
```lua
local helium = require('helium')
--
helium.hooks.state({blah = false})
```
### MANUAL_CACHING
options: `true / (false)`
Manual caching can be enabled if you want manual control over which elements are atlassed, use together with the `setCaching()` hook
Make sure to enable caching for the scenes you intend to use your element class for.

View File

@ -14,7 +14,7 @@ Element is the class for every ui element, in practice it's glue between the cod
Input allows to create callbacks for helium internal input events
[Find more here](./core/State-Input-Guide.md)
[Find more here](./State-Input-Guide.md)
[and here](./core/Input-events.md)

View File

@ -245,3 +245,23 @@ function(param, view)
end
end
```
### /hooks/setCaching.lua
Makes this element get cached, you'll need to set MANUAL_CACHING in config for this to work
Keep in mind that it won't make a magical performance benefit to an element that is being re-rendered by changes in children components, it's own state, resizing or position changes.
`setCaching()`
Usage:
```lua
local setCaching = require('helium.hooks.setCaching')
function(param, view)
setCaching()
return function()
end
end
```

View File

@ -1,5 +1,5 @@
--Allows to expose a function to outside the element simply
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onDestroy"))
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.callback"))
local context = require(path.. ".core.stack")
---Creates a callback on the 'name' field for the current element

28
hooks/init.lua Normal file
View File

@ -0,0 +1,28 @@
local path = ...
---@class __HELIUM_HOOKS
---@field callback any
---@field context any
---@field onDestroy any
---@field onLoad any
---@field onPosChange any
---@field onSizeChange any
---@field onUpdate any
---@field setMinSize any
---@field setPos any
---@field setSize any
---@field state any
return {
callback = require(path..'.callback'),
context = require(path..'.context'),
onDestroy = require(path..'.onDestroy'),
onLoad = require(path..'.onLoad'),
onPosChange = require(path..'.onPosChange'),
onSizeChange = require(path..'.onSizeChange'),
onUpdate = require(path..'.onUpdate'),
setMinSize = require(path..'.setMinSize'),
setCaching = require(path..'.setCaching'),
setPos = require(path..'.setPos'),
setSize = require(path..'.setSize'),
state = require(path..'.state'),
}

15
hooks/setCaching.lua Normal file
View File

@ -0,0 +1,15 @@
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.setCaching"))
---@type context
local context = require(path.. ".core.stack")
local helium = require(path.. ".dummy")
return function ()
local activeContext = context.getContext()
if not helium.conf.MANUAL_CACHING then
error('use setCaching only with manual caching enabled, check your configs')
end
activeContext.element:createCanvas()
activeContext.element.settings.forcedCanvas = true
activeContext.element.settings.pendingUpdate = true
end

View File

@ -11,7 +11,8 @@ return function (base)
local activeContext = context.getContext()
return setmetatable({},{
__index = function(t, index)
return fakeBase[index] or base[index]
local f = fakeBase[index] ~= nil and fakeBase[index] or base[index]
return f
end,
__newindex = function(t, index, val)
if fakeBase[index] ~= val then

View File

@ -4,10 +4,20 @@
https://github.com/qeffects/helium
----------------------------------------------------]]
local path = ...
local helium = require(path..'.dummy')
---@class __HELIUM
---@field private scene any
---@field private element any
---@field private atlas any
---@field private stack any
---@field private input any
local helium = require(path..'.dummy')
helium.__index = helium
---@type __HELIUM_CONFIG
local defaultConf = require(path..".conf")
helium.conf = {}
if HELIUM_CONFIG then
for i, e in pairs(defaultConf) do
helium.conf[i] = HELIUM_CONFIG[i] or e
@ -16,12 +26,28 @@ else
helium.conf = defaultConf
end
if helium.conf.LOAD_HOOKS then
---@type __HELIUM_HOOKS
helium.hooks = require(path..'.hooks')
end
if helium.conf.LOAD_SHELL then
---@type __HELIUM_SHELL
helium.shell = require(path..'.shell')
end
if helium.conf.LOAD_LAYOUT then
---@type __HELIUM_LAYOUT
helium.layout = require(path..'.layout')
end
helium.core = require(path..'.core')
helium.scene = require(path..".core.scene")
helium.element = require(path..".core.element")
helium.input = require(path..".core.input")
helium.stack = require(path..".core.stack")
helium.atlas = require(path..".core.atlas")
helium.__index = helium
function helium.setBench(time)
helium.benchNum = time
@ -39,6 +65,4 @@ setmetatable(helium, {__call = function(s, chunk)
end,})
end})
--Typescript
helium.helium = helium
return helium

View File

@ -1,5 +1,5 @@
local path = string.sub(..., 1, string.len(...) - string.len(".column"))
local layout = require(path..'.init')
local layout = require(path..'.layout')
---@class Column
local column = {}
column.__index = column

View File

@ -1,5 +1,5 @@
local path = string.sub(..., 1, string.len(...) - string.len(".container"))
local layout = require(path..'.init')
local layout = require(path..'.layout')
---@class Container
local container = {}

View File

@ -1,7 +1,7 @@
local column = require "helium.layout.column"
--my copy of the cssssss grids
local path = string.sub(..., 1, string.len(...) - string.len(".grid"))
local layout = require(path..'.init')
local layout = require(path..'.layout')
---@class GridCell
---@field name string @Will find the element with the relevant flag
@ -350,7 +350,7 @@ function grid:draw(xRoot, yRoot, width, height, children)
y = alignHandlerY(self.gridLayout.verticalAlignMode, carriagePos, rowSize, h)
end
e:draw(x, y, w, h)
e:draw(x, y + yRoot, w, h)
carriagePos = carriagePos + self.gridLayout.rowSpacing + rowSize
row = row + 1

View File

@ -1,156 +1,15 @@
local path = string.sub(..., 1, string.len(...) - string.len(".layout.init"))
local path = ...
---@class layout
---@field protected vars table
---@field protected type function
local layout = {}
local layouts = {}
layout.__index = layout
local element = require(path..'.core.element')
local stack = require(path..'.core.stack')
--Start prep phase
function layout.type(binder, callback)
local curStack = stack.getContext()
curStack:startDeferingChildren()
local self = {
vars = {
offLeft = 0,
offTop = 0,
width = 1,
hpad = 3,
vpad = 3,
height = 1,
},
stack = curStack,
binder = binder,
callback = callback,
---@class __HELIUM_LAYOUT
---@field column any
---@field container any
---@field grid any
---@field layout any
---@field row any
return {
column = require(path..'.column'),
container = require(path..'.container'),
grid = require(path..'.grid'),
layout = require(path..'.layout'),
row = require(path..'.row'),
}
return setmetatable(self, layout)
end
---Aligns the container vertically
---@param pos 'left'|'center'|'right'
function layout:alignVert(pos)
self.vars.alignY = pos
return self
end
---Aligns the container horizontally
---@param pos 'top'|'center'|'bottom'
function layout:alignHoriz(pos)
self.vars.alignX = pos
return self
end
---Sets up the width of the box of the layout
---@param w number width in pixels or absolute 0-1
function layout:width(w)
self.vars.width = w
return self
end
---Sets up the height of the box of the layout
---@param h number width in pixels or absolute 0-1
function layout:height(h)
self.vars.height = h
return self
end
---Offset from the left
---@param x number offset in pixels or absolute 0-1
function layout:left(x)
self.vars.offLeft = x
return self
end
---Offset from the right
---@param x number offset in pixels or absolute 0-1
function layout:right(x)
self.vars.offRight = x
return self
end
---Offset from the top
---@param y number offset in pixels or absolute 0-1
function layout:top(y)
self.vars.offTop = y
return self
end
---Offset from the bottom
---@param y number offset in pixels or absolute 0-1
function layout:bottom(y)
self.vars.offBot = y
return self
end
---Padding for the elements vertically
---@param px number offset in pixels
function layout:vPadding(px)
self.vars.vpad = px
return self
end
---Padding for the elements horizontally
---@param px number offset in pixels
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
y = self.vars.offTop
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
x = self.vars.offLeft
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
self.callback(self.binder, x, y, width, height, children, self.vars.hpad, self.vars.vpad)
end
setmetatable(layout, {__call = function(s, binder, callback) return layout.type(binder, callback) end })
return layout

154
layout/layout.lua Normal file
View File

@ -0,0 +1,154 @@
local path = string.sub(..., 1, string.len(...) - string.len(".layout.layout"))
---@class layout
---@field protected vars table
---@field protected type function
local layout = {}
local layouts = {}
layout.__index = layout
local element = require(path..'.core.element')
local stack = require(path..'.core.stack')
--Start prep phase
function layout.type(binder, callback)
local curStack = stack.getContext()
curStack:startDeferingChildren()
local self = {
vars = {
width = 1,
hpad = 3,
vpad = 3,
height = 1,
},
stack = curStack,
binder = binder,
callback = callback,
}
return setmetatable(self, layout)
end
---Aligns the container vertically
---@param pos 'left'|'center'|'right'
function layout:alignVert(pos)
self.vars.alignY = pos
return self
end
---Aligns the container horizontally
---@param pos 'top'|'center'|'bottom'
function layout:alignHoriz(pos)
self.vars.alignX = pos
return self
end
---Sets up the width of the box of the layout
---@param w number width in pixels or absolute 0-1
function layout:width(w)
self.vars.width = w
return self
end
---Sets up the height of the box of the layout
---@param h number width in pixels or absolute 0-1
function layout:height(h)
self.vars.height = h
return self
end
---Offset from the left
---@param x number offset in pixels or absolute 0-1
function layout:left(x)
self.vars.offLeft = x
return self
end
---Offset from the right
---@param x number offset in pixels or absolute 0-1
function layout:right(x)
self.vars.offRight = x
return self
end
---Offset from the top
---@param y number offset in pixels or absolute 0-1
function layout:top(y)
self.vars.offTop = y
return self
end
---Offset from the bottom
---@param y number offset in pixels or absolute 0-1
function layout:bottom(y)
self.vars.offBot = y
return self
end
---Padding for the elements vertically
---@param px number offset in pixels
function layout:vPadding(px)
self.vars.vpad = px
return self
end
---Padding for the elements horizontally
---@param px number offset in pixels
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
y = stack:normY(self.vars.offTop)
elseif self.vars.offBot then
y = stack:normY(self.vars.offBot)
height = math.min(stack:normY(self.vars.height), maxH-y)
y = maxH - height - y
else
y = stack:normY(self.vars.offTop or 0)
height = math.min(stack:normY(self.vars.height), maxH-y)
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
x = stack:normX(self.vars.offLeft)
elseif self.vars.offRight then
x = stack:normX(self.vars.offRight)
width = math.min(stack:normX(self.vars.width), maxW-x)
x = maxW - width -h
else
x = stack:normX(self.vars.offLeft or 0)
width = math.min(stack:normX(self.vars.width), maxW-x)
end
self.callback(self.binder, x, y, width, height, children, self.vars.hpad, self.vars.vpad)
end
setmetatable(layout, {__call = function(s, binder, callback) return layout.type(binder, callback) end })
return layout

View File

@ -1,5 +1,5 @@
local path = string.sub(..., 1, string.len(...) - string.len(".row"))
local layout = require(path..'.init')
local layout = require(path..'.layout')
---@class Row
local row = {}

View File

@ -1,4 +1,4 @@
local path = string.sub(..., 1, string.len(...) - string.len(".shell.button"))
local path = string.sub(..., 1, string.len(...) - string.len(".shell.checkbox"))
local state = require(path.. ".hooks.state")
local input = require(path.. ".core.input")

13
shell/init.lua Normal file
View File

@ -0,0 +1,13 @@
local path = ...
---@class __HELIUM_SHELL
---@field button any
---@field checkbox any
---@field input any
---@field slider any
return {
button = require(path..'.button'),
checkbox = require(path..'.checkbox'),
input = require(path..'.input'),
slider = require(path..'.slider'),
}

View File

@ -47,14 +47,14 @@ return function(onChange, onFinish, startStr, onEnter, onExit, x, y, w, h)
onFinish(textState.text)
end
end
end)
end, false)
textInput = input('textinput', function(text)
textState.text = textState.text .. text
if onChange then
onChange(textState.text)
end
end)
end, false)
input('mousepressed', function()
textState.focused = true