implemented the rest of the state callbacks
redid layout luadoc + emmylua annotations everywhere grid layout started
This commit is contained in:
parent
ea7d0c2da0
commit
5309f35dfc
@ -80,8 +80,17 @@ function atlases:unassignAll()
|
|||||||
self.atlases[2].taken_area = 0
|
self.atlases[2].taken_area = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
function atlases.onscreenchange(newW, newH)
|
function atlases:onscreenchange(newW, newH)
|
||||||
|
for i, e in ipairs(self.atlases[1].users) do
|
||||||
|
e:reassignCanvas()
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, e in ipairs(self.atlases[2].users) do
|
||||||
|
e:reassignCanvas()
|
||||||
|
end
|
||||||
|
|
||||||
|
self.atlases[1] = atlas.new(newW, newH)
|
||||||
|
self.atlases[2] = atlas.new(newW, newH)
|
||||||
end
|
end
|
||||||
|
|
||||||
function atlas.new(w, h)
|
function atlas.new(w, h)
|
||||||
|
|||||||
@ -11,9 +11,8 @@ element.__index = element
|
|||||||
|
|
||||||
local type,pcall = type,pcall
|
local type,pcall = type,pcall
|
||||||
setmetatable(element, {
|
setmetatable(element, {
|
||||||
__call = function(cls, ...)
|
__call = function(cls, func, param, w, h, flags)
|
||||||
local self
|
local self
|
||||||
local func, param, w, h = ...
|
|
||||||
|
|
||||||
--Make the object inherit class
|
--Make the object inherit class
|
||||||
self = setmetatable({}, element)
|
self = setmetatable({}, element)
|
||||||
@ -22,6 +21,7 @@ setmetatable(element, {
|
|||||||
self:new(param,nil, w, h)
|
self:new(param,nil, w, h)
|
||||||
self:createProxies()
|
self:createProxies()
|
||||||
|
|
||||||
|
---@type Element
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
})
|
})
|
||||||
@ -32,6 +32,9 @@ function element:new(param, immediate, w, h)
|
|||||||
self.parameters = {}
|
self.parameters = {}
|
||||||
self.baseParams = param
|
self.baseParams = param
|
||||||
|
|
||||||
|
--Internal state callbacks
|
||||||
|
self.callbacks = {}
|
||||||
|
|
||||||
--Internal settings
|
--Internal settings
|
||||||
self.settings = {
|
self.settings = {
|
||||||
isSetup = false,
|
isSetup = false,
|
||||||
@ -73,6 +76,18 @@ function element:new(param, immediate, w, h)
|
|||||||
self.view = self.baseView
|
self.view = self.baseView
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function element:reassignCanvas()
|
||||||
|
self.settings.failedCanvas = false
|
||||||
|
self.settings.hasCanvas = false
|
||||||
|
|
||||||
|
self.canvas = nil
|
||||||
|
self.quad = nil
|
||||||
|
self.interQuad = nil
|
||||||
|
self.deferResize = nil
|
||||||
|
|
||||||
|
self.context:bubbleUpdate()
|
||||||
|
end
|
||||||
|
|
||||||
function element:sizeChange(i, v)
|
function element:sizeChange(i, v)
|
||||||
local increase = self.baseView[i] < v
|
local increase = self.baseView[i] < v
|
||||||
|
|
||||||
@ -90,6 +105,12 @@ function element:sizeChange(i, v)
|
|||||||
else
|
else
|
||||||
self.deferResize = { increased = increase }
|
self.deferResize = { increased = increase }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if self.callbacks.onSizeChange then
|
||||||
|
for i, cb in ipairs(self.callbacks.onSizeChange) do
|
||||||
|
cb(self.view.w, self.view.h)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function element:posChange(i, v)
|
function element:posChange(i, v)
|
||||||
@ -99,22 +120,37 @@ function element:posChange(i, v)
|
|||||||
if not self.deferRepos then
|
if not self.deferRepos then
|
||||||
self.deferRepos = true
|
self.deferRepos = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if self.callbacks.onPosChange then
|
||||||
|
for i, cb in ipairs(self.callbacks.onPosChange) do
|
||||||
|
cb(self.view.x, self.view.y)
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function element:onUpdate()
|
function element:onUpdate()
|
||||||
|
if self.callbacks.onUpdate then
|
||||||
end
|
for i, cb in ipairs(self.callbacks.onUpdate) do
|
||||||
|
cb()
|
||||||
function element:onDraw()
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function element:onLoad()
|
function element:onLoad()
|
||||||
|
if self.callbacks.onLoad then
|
||||||
|
for i, cb in ipairs(self.callbacks.onLoad) do
|
||||||
|
cb()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function element:onDestroy()
|
function element:onDestroy()
|
||||||
|
if self.callbacks.onDestroy then
|
||||||
|
for i, cb in ipairs(self.callbacks.onDestroy) do
|
||||||
|
cb()
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function element:createProxies()
|
function element:createProxies()
|
||||||
@ -204,6 +240,7 @@ function element:setup()
|
|||||||
self.context:unset()
|
self.context:unset()
|
||||||
|
|
||||||
self.settings.isSetup = true
|
self.settings.isSetup = true
|
||||||
|
self:onLoad()
|
||||||
end
|
end
|
||||||
|
|
||||||
local setColor, rectangle, setFont, printf = love.graphics.setColor, love.graphics.rectangle, love.graphics.setFont, love.graphics.printf
|
local setColor, rectangle, setFont, printf = love.graphics.setColor, love.graphics.rectangle, love.graphics.setFont, love.graphics.printf
|
||||||
@ -294,6 +331,7 @@ function element:externalUpdate()
|
|||||||
end
|
end
|
||||||
|
|
||||||
if self.settings.pendingUpdate then
|
if self.settings.pendingUpdate then
|
||||||
|
self:onUpdate()
|
||||||
self.settings.needsRendering = true
|
self.settings.needsRendering = true
|
||||||
self.settings.pendingUpdate = false
|
self.settings.pendingUpdate = false
|
||||||
end
|
end
|
||||||
@ -321,8 +359,8 @@ end
|
|||||||
|
|
||||||
local insert = table.insert
|
local insert = table.insert
|
||||||
|
|
||||||
--External functions
|
---External functions
|
||||||
--Acts as the entrypoint for beginning rendering
|
---Acts as the entrypoint for beginning rendering
|
||||||
---@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)
|
||||||
@ -355,10 +393,12 @@ function element:getSize()
|
|||||||
return self.view.w, self.view.h
|
return self.view.w, self.view.h
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Destroys this element
|
||||||
function element:destroy()
|
function element:destroy()
|
||||||
self.settings.remove = true
|
self.settings.remove = true
|
||||||
self.settings.firstDraw = true
|
self.settings.firstDraw = true
|
||||||
self.settings.isSetup = false
|
self.settings.isSetup = false
|
||||||
|
self:onDestroy()
|
||||||
self.context:destroy()
|
self.context:destroy()
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
@ -174,14 +174,18 @@ function subscription:checkOutside(x, y)
|
|||||||
return not (x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h)
|
return not (x>self.x and x<self.x+self.w and y>self.y and y<self.y+self.h)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---@alias InputMouseClickSubscriptionCallback fun(x:number, y:number, mouseButton:string)
|
||||||
|
---@alias InputMouseClickComplexSubscriptionCallback fun(x:number, y:number, mouseButton:string):fun(x:number, y:number)|nil
|
||||||
|
---@alias InputMouseMoveComplexSubscriptionCallback fun(x:number, y:number, dx:number, dy:number):fun(x:number, y:number)|nil
|
||||||
|
---@alias InputKeyPressSubscriptionCallback fun(key:KeyConstant|string, scancode:Scancode|string)
|
||||||
|
---@alias InputTextInputSubscriptionCallback fun(text:string)
|
||||||
|
|
||||||
input.subscribe = subscription.create
|
input.subscribe = subscription.create
|
||||||
---@param eventType string
|
---@overload fun(self:any, eventType: "'mousepressed'"|"'mousereleased'"|"'mousepressed_outside'"|"'mousereleased_outside'", callback: InputMouseClickSubscriptionCallback, cbOff: boolean, x: number|nil, y:number|nil, w: number|nil, h:number|nil)
|
||||||
---@param callback function
|
---@overload fun(self:any, eventType: "'clicked'", callback: InputMouseClickComplexSubscriptionCallback, cbOff: boolean, x:number|nil, y:number|nil, w:number|nil, h:number|nil)
|
||||||
---@param cbOff boolean
|
---@overload fun(self:any, eventType: "'hover'"|"'dragged'", callback: InputMouseMoveComplexSubscriptionCallback, cbOff: boolean, x:number|nil, y:number|nil, w:number|nil, h:number|nil)
|
||||||
---@param x number
|
---@overload fun(self:any, eventType: "'keypressed'"|"'keyreleased'", callback: InputKeyPressSubscriptionCallback, cbOff: boolean)
|
||||||
---@param y number
|
---@overload fun(self:any, eventType: "'textinput'", callback: InputTextInputSubscriptionCallback, cbOff: boolean)
|
||||||
---@param w number
|
|
||||||
---@param h number
|
|
||||||
input.__call = function(self, eventType, callback, cbOff, x, y, w, h)
|
input.__call = function(self, eventType, callback, cbOff, x, y, w, h)
|
||||||
x = x or 0
|
x = x or 0
|
||||||
y = y or 0
|
y = y or 0
|
||||||
|
|||||||
@ -4,12 +4,17 @@ local atlas = require(path..'.core.atlas')
|
|||||||
local helium = require(path..'.dummy')
|
local helium = require(path..'.dummy')
|
||||||
local input = require(path..'.core.input')
|
local input = require(path..'.core.input')
|
||||||
|
|
||||||
|
---@class scene
|
||||||
local scene = {
|
local scene = {
|
||||||
activeScene = nil
|
activeScene = nil
|
||||||
}
|
}
|
||||||
scene.__index = scene
|
scene.__index = scene
|
||||||
|
|
||||||
|
---comment
|
||||||
|
---@param cached boolean @whether to enable caching on this scene
|
||||||
|
---@return scene
|
||||||
function scene.new(cached)
|
function scene.new(cached)
|
||||||
|
---@type scene
|
||||||
local self = {
|
local self = {
|
||||||
atlas = cached and atlas.create() or nil,
|
atlas = cached and atlas.create() or nil,
|
||||||
cached = cached or false,
|
cached = cached or false,
|
||||||
@ -34,30 +39,32 @@ function scene.bench()
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Activates this scene
|
||||||
function scene:activate()
|
function scene:activate()
|
||||||
scene.activeScene = self
|
scene.activeScene = self
|
||||||
end
|
end
|
||||||
|
|
||||||
--Keeps the scene in memory with potentially the atlas
|
---Keeps the scene in memory with potentially the atlas
|
||||||
function scene:deactivate()
|
function scene:deactivate()
|
||||||
scene.activeScene = nil
|
scene.activeScene = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Recreates this scene
|
||||||
function scene:reload()
|
function scene:reload()
|
||||||
self.atlas = self.cached and atlas.create() or nil
|
self.atlas = self.cached and atlas.create() or nil
|
||||||
self.ioSubscriptions = {}
|
self.ioSubscriptions = {}
|
||||||
self.buffer = {}
|
self.buffer = {}
|
||||||
end
|
end
|
||||||
|
|
||||||
--Nukes the scene and it's elements and atlases and subscriptions from memory
|
---Nukes the scene and it's elements and atlases and subscriptions from memory
|
||||||
--To achieve same state as after creation, use reload
|
---To achieve same state as after creation, use reload
|
||||||
function scene:unload()
|
function scene:unload()
|
||||||
self.atlas = nil
|
self.atlas = nil
|
||||||
self.buffer = nil
|
self.buffer = nil
|
||||||
self.ioSubscriptions = nil
|
self.ioSubscriptions = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Draws this scene and it's elements
|
||||||
function scene:draw()
|
function scene:draw()
|
||||||
helium.stack.newFrame()
|
helium.stack.newFrame()
|
||||||
if not helium.benchNum then
|
if not helium.benchNum then
|
||||||
@ -73,6 +80,8 @@ function scene:draw()
|
|||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Updates this scene and it's elements
|
||||||
|
---@param dt number
|
||||||
function scene:update(dt)
|
function scene:update(dt)
|
||||||
for i = 1, #self.buffer do
|
for i = 1, #self.buffer do
|
||||||
if self.buffer[i]:externalUpdate(i) then
|
if self.buffer[i]:externalUpdate(i) then
|
||||||
|
|||||||
@ -1 +1,16 @@
|
|||||||
--Allows to expose a function to outside the element simply
|
--Allows to expose a function to outside the element simply
|
||||||
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onDestroy"))
|
||||||
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---Creates a callback on the 'name' field for the current element
|
||||||
|
---@param name string
|
||||||
|
---@param callback function
|
||||||
|
return function (name, callback)
|
||||||
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
if context.element[name] then
|
||||||
|
error('callback with name '..name..' would interfere with internal fields')
|
||||||
|
end
|
||||||
|
|
||||||
|
context.element[name] = callback
|
||||||
|
end
|
||||||
@ -3,6 +3,10 @@ local context = require(path.. ".core.stack")
|
|||||||
|
|
||||||
local c = {}
|
local c = {}
|
||||||
|
|
||||||
|
---Creates a context, that will be available to ALL nodes below this element
|
||||||
|
---@param name string the name of this context to be referenced later
|
||||||
|
---@param base table the table to be used as the base of this new context
|
||||||
|
---@return table
|
||||||
function c.use(name, base)
|
function c.use(name, base)
|
||||||
base = base or {}
|
base = base or {}
|
||||||
local fakeBase = {}
|
local fakeBase = {}
|
||||||
@ -39,6 +43,9 @@ function c.use(name, base)
|
|||||||
return ctx
|
return ctx
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Gets the context with 'name', if it was initialized
|
||||||
|
---@param name string
|
||||||
|
---@return table|nil
|
||||||
function c.get(name)
|
function c.get(name)
|
||||||
local activeContext = context.getContext()
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1,14 @@
|
|||||||
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onDestroy"))
|
||||||
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---Sets a callback on a destructionevent for the current element (can have multiple)
|
||||||
|
---@param callback function
|
||||||
|
return function (callback)
|
||||||
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
if not activeContext.element.callbacks['onDestroy'] then
|
||||||
|
activeContext.element.callbacks.onDestroy = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
activeContext.element.callbacks.onDestroy[#activeContext.element.callbacks.onDestroy+1] = callback
|
||||||
|
end
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onLoad"))
|
||||||
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---Sets a callback on a load event for the current element (can have multiple)
|
||||||
|
---@param callback function
|
||||||
|
return function (callback)
|
||||||
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
if not activeContext.element.callbacks['onLoad'] then
|
||||||
|
activeContext.element.callbacks.onLoad = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
activeContext.element.callbacks.onLoad[#activeContext.element.callbacks.onLoad+1] = callback
|
||||||
|
end
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onPosChange"))
|
||||||
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---@alias PosChangeCallback fun(x: number, y:number)
|
||||||
|
|
||||||
|
---Sets a callback on a position change event for the current element (can have multiple)
|
||||||
|
---@param callback PosChangeCallback
|
||||||
|
return function (callback)
|
||||||
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
if not activeContext.element.callbacks['onPosChange'] then
|
||||||
|
activeContext.element.callbacks.onPosChange = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
activeContext.element.callbacks.onPosChange[#activeContext.element.callbacks.onPosChange+1] = callback
|
||||||
|
end
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onSizeChange"))
|
||||||
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---@alias SizeChangeCallback fun(w: number, h:number)
|
||||||
|
|
||||||
|
---Sets a callback on a size change event for the current element (can have multiple)
|
||||||
|
---@param callback SizeChangeCallback
|
||||||
|
return function (callback)
|
||||||
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
if not activeContext.element.callbacks['onSizeChange'] then
|
||||||
|
activeContext.element.callbacks.onSizeChange = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
activeContext.element.callbacks.onSizeChange[#activeContext.element.callbacks.onSizeChange+1] = callback
|
||||||
|
end
|
||||||
@ -0,0 +1,15 @@
|
|||||||
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.onUpdate"))
|
||||||
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---Sets a callback on any updatefor the current element (can have multiple)
|
||||||
|
---Use this to get logic outside of rendering function
|
||||||
|
---@param callback function
|
||||||
|
return function (callback)
|
||||||
|
local activeContext = context.getContext()
|
||||||
|
|
||||||
|
if not activeContext.element.callbacks['onUpdate'] then
|
||||||
|
activeContext.element.callbacks.onUpdate = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
activeContext.element.callbacks.onUpdate[activeContext.element.callbacks.onUpdate+1] = callback
|
||||||
|
end
|
||||||
@ -1,6 +1,10 @@
|
|||||||
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.state"))
|
local path = string.sub(..., 1, string.len(...) - string.len(".hooks.state"))
|
||||||
local context = require(path.. ".core.stack")
|
local context = require(path.. ".core.stack")
|
||||||
|
|
||||||
|
---Creates a new 'state' object that will update the current element whenever a field is changed
|
||||||
|
---@generic T : table
|
||||||
|
---@param base T
|
||||||
|
---@return T
|
||||||
return function (base)
|
return function (base)
|
||||||
base = base or {}
|
base = base or {}
|
||||||
local fakeBase = {}
|
local fakeBase = {}
|
||||||
|
|||||||
@ -1,4 +1,17 @@
|
|||||||
return function(x, y, width, height, children, hpad, vpad, alignX)
|
local path = string.sub(..., 1, string.len(...) - string.len(".column"))
|
||||||
|
local layout = require(path..'.init')
|
||||||
|
---@class Column
|
||||||
|
local column = {}
|
||||||
|
column.__index = column
|
||||||
|
|
||||||
|
---@return layout
|
||||||
|
function column.new()
|
||||||
|
local self = setmetatable({}, column)
|
||||||
|
|
||||||
|
return layout(self.draw)
|
||||||
|
end
|
||||||
|
|
||||||
|
function column:draw(x, y, width, height, children, hpad, vpad, alignX)
|
||||||
local carriagePos = 0
|
local carriagePos = 0
|
||||||
if children then
|
if children then
|
||||||
for i, e in ipairs(children) do
|
for i, e in ipairs(children) do
|
||||||
@ -8,3 +21,5 @@ return function(x, y, width, height, children, hpad, vpad, alignX)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return column
|
||||||
277
layout/grid.lua
Normal file
277
layout/grid.lua
Normal file
@ -0,0 +1,277 @@
|
|||||||
|
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')
|
||||||
|
|
||||||
|
---@class GridCell
|
||||||
|
---@field name string @Will find the element with the relevant flag
|
||||||
|
|
||||||
|
---@alias GridRow GridCell[]
|
||||||
|
|
||||||
|
---@class HGridCell
|
||||||
|
---@field width number @Determines how wide this column will be
|
||||||
|
|
||||||
|
---@class WGridCell
|
||||||
|
---@field height string @Determines how high this column will be
|
||||||
|
|
||||||
|
---@alias HGridRow number[]|number|nil @Width of the row in cells
|
||||||
|
|
||||||
|
---@alias WGridCol number[]|number|nil @Width of the row in cells
|
||||||
|
|
||||||
|
---@alias GridLayout GridRow[]
|
||||||
|
|
||||||
|
---@class GridConfig
|
||||||
|
---@field layout GridLayout|nil @preconfigured layout table
|
||||||
|
---@field rows HGridRow|number|nil @set these instead of layout if you just want a regularly spaced 'table'
|
||||||
|
---@field columns WGridCol|number|nil @set these instead of layout if you just want a regularly spaced 'table' leave empty to flow in as many elements as you have
|
||||||
|
---@field verticalStretchMode "'stretch'"|"'normal'"
|
||||||
|
---@field horizontalStretchMode "'stretch'"|"'normal'"
|
||||||
|
---@field horizontalAlignMode "'left'"|"'center'"|"'right'"
|
||||||
|
---@field verticalAlignMode "'top'"|"'center'"|"'bottom'"
|
||||||
|
---@field rowSpacing number @size in pixels to space the rows
|
||||||
|
---@field colSpacing number @size in pixels to space the columns
|
||||||
|
---@field rowSizeMode "'relative'"|"'absolute'"
|
||||||
|
---@field colSizeMode "'relative'"|"'absolute'"
|
||||||
|
|
||||||
|
---@type GridConfig
|
||||||
|
local preconfiguredGrid = {
|
||||||
|
colSpacing = 3,
|
||||||
|
rowSpacing = 3,
|
||||||
|
verticalStretchMode = 'normal',
|
||||||
|
horizontalStretchMode = 'normal',
|
||||||
|
verticalAlignMode = 'center',
|
||||||
|
horizontalAlignMode = 'center',
|
||||||
|
--rows = {1, 1, 1, 1},
|
||||||
|
columns = {1, 1, 1, 1},
|
||||||
|
--[[layout = {
|
||||||
|
{'header', 'header', 'header'},
|
||||||
|
{'sidebar','content','content'},
|
||||||
|
{'sidebar','content','content'},
|
||||||
|
}]]
|
||||||
|
}
|
||||||
|
|
||||||
|
---@class grid
|
||||||
|
---@field gridLayout GridConfig
|
||||||
|
local grid = {}
|
||||||
|
grid.__index = grid
|
||||||
|
|
||||||
|
---@param gridLayout GridConfig
|
||||||
|
---@return layout
|
||||||
|
function grid.new(gridLayout)
|
||||||
|
local gridLayout = gridLayout or preconfiguredGrid
|
||||||
|
local self = setmetatable({
|
||||||
|
gridLayout = gridLayout
|
||||||
|
}, grid)
|
||||||
|
|
||||||
|
return layout(self, self.draw)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function alignLeft(x, wroot, wchild)
|
||||||
|
return x
|
||||||
|
end
|
||||||
|
|
||||||
|
local function alignCenter(x, wroot, wchild)
|
||||||
|
return x+(wroot/2-wchild/2)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function alignRight(x, wroot, wchild)
|
||||||
|
return x+(wroot-wchild)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
local function alignHandlerX(mode, x, wr, wc)
|
||||||
|
if mode == 'center' then
|
||||||
|
return alignCenter(x, wr, wc)
|
||||||
|
elseif mode == 'right' then
|
||||||
|
return alignRight(x, wr, wc)
|
||||||
|
else
|
||||||
|
return alignLeft(x)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function alignHandlerY(mode, y, hr, hc)
|
||||||
|
if mode == 'center' then
|
||||||
|
return alignCenter(y, hr, hc)
|
||||||
|
elseif mode == 'bottom' then
|
||||||
|
return alignRight(y, hr, hc)
|
||||||
|
else
|
||||||
|
return alignLeft(y)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function grid:draw(xRoot, yRoot, width, height, children, hpad, vpad)
|
||||||
|
-- Either of these means no named layout
|
||||||
|
local fullyAutoLayout = false
|
||||||
|
local autoCols = false
|
||||||
|
local autoRows = false
|
||||||
|
local equalRows = false
|
||||||
|
local equalCols = false
|
||||||
|
|
||||||
|
local vertValueToPixels = 0
|
||||||
|
local horValueToPixels = 0
|
||||||
|
|
||||||
|
local finalLayout = {}
|
||||||
|
|
||||||
|
if self.gridLayout.columns then
|
||||||
|
if not self.gridLayout.rows then
|
||||||
|
autoRows = true
|
||||||
|
else
|
||||||
|
if type(self.gridLayout.rows)=="table" then
|
||||||
|
local total = 0
|
||||||
|
|
||||||
|
for i, col in ipairs(self.gridLayout.rows) do
|
||||||
|
total = total + col
|
||||||
|
end
|
||||||
|
|
||||||
|
vertValueToPixels = (height-(self.gridLayout.rowSpacing*total))/total
|
||||||
|
else
|
||||||
|
vertValueToPixels = (height-(self.gridLayout.rowSpacing*self.gridLayout.rows))/self.gridLayout.rows
|
||||||
|
equalRows = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if type(self.gridLayout.columns)=="table" then
|
||||||
|
local total = 0
|
||||||
|
|
||||||
|
for i, col in ipairs(self.gridLayout.columns) do
|
||||||
|
total = total + col
|
||||||
|
end
|
||||||
|
|
||||||
|
horValueToPixels = width/total
|
||||||
|
else
|
||||||
|
horValueToPixels = width/self.gridLayout.columns
|
||||||
|
equalCols = true
|
||||||
|
end
|
||||||
|
else
|
||||||
|
if not self.gridLayout.rows then
|
||||||
|
fullyAutoLayout = true
|
||||||
|
autoRows = true
|
||||||
|
else
|
||||||
|
autoCols = true
|
||||||
|
if type(self.gridLayout.rows)=="table" then
|
||||||
|
local total = 0
|
||||||
|
|
||||||
|
for i, col in ipairs(self.gridLayout.rows) do
|
||||||
|
total = total + col
|
||||||
|
end
|
||||||
|
|
||||||
|
vertValueToPixels = (height-(self.gridLayout.rowSpacing*total))/total
|
||||||
|
else
|
||||||
|
vertValueToPixels = (height-(self.gridLayout.rowSpacing*self.gridLayout.rows))/self.gridLayout.rows
|
||||||
|
equalRows = true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
print(horValueToPixels, width)
|
||||||
|
|
||||||
|
if (not autoRows) and (not autoCols) then
|
||||||
|
|
||||||
|
elseif fullyAutoLayout then--one element per width, vertically down
|
||||||
|
local carriagePos = 0
|
||||||
|
if children then
|
||||||
|
for i, e in ipairs(children) do
|
||||||
|
local w, h = e:getSize()
|
||||||
|
|
||||||
|
if self.gridLayout.horizontalStretchMode =='stretch' then
|
||||||
|
w = width
|
||||||
|
e:draw(xRoot, yRoot+carriagePos, w)
|
||||||
|
else
|
||||||
|
local x = alignHandlerX(self.gridLayout.horizontalAlignMode, xRoot, width, w)
|
||||||
|
e:draw(x, yRoot+carriagePos)
|
||||||
|
end
|
||||||
|
|
||||||
|
carriagePos = carriagePos + self.gridLayout.rowSpacing + h
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif autoCols then--one element per width, rows spaced
|
||||||
|
local carriagePos = 0
|
||||||
|
local row = 1
|
||||||
|
local lastRowSize = 1
|
||||||
|
|
||||||
|
if children then
|
||||||
|
for i, e in ipairs(children) do
|
||||||
|
local w, h = e:getSize()
|
||||||
|
local rowSize
|
||||||
|
local x, y
|
||||||
|
|
||||||
|
if equalRows then
|
||||||
|
rowSize = 1 * vertValueToPixels
|
||||||
|
else
|
||||||
|
rowSize = (self.gridLayout.rows[row] or lastRowSize)*vertValueToPixels
|
||||||
|
end
|
||||||
|
|
||||||
|
rowSize = math.max(h, rowSize)
|
||||||
|
|
||||||
|
if self.gridLayout.horizontalStretchMode =='stretch' then
|
||||||
|
w = width
|
||||||
|
else
|
||||||
|
x = alignHandlerX(self.gridLayout.horizontalAlignMode, xRoot, width, w)
|
||||||
|
end
|
||||||
|
|
||||||
|
if self.gridLayout.verticalStretchMode =='stretch' then
|
||||||
|
h = rowSize
|
||||||
|
else
|
||||||
|
y = alignHandlerY(self.gridLayout.verticalAlignMode, carriagePos, rowSize, h)
|
||||||
|
end
|
||||||
|
|
||||||
|
e:draw(x, y, w, h)
|
||||||
|
|
||||||
|
carriagePos = carriagePos + self.gridLayout.rowSpacing + rowSize
|
||||||
|
row = row + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif autoRows then--flow the elements freely vertically, space columns according to layout
|
||||||
|
local carriagePos = 0
|
||||||
|
local row = 1
|
||||||
|
local colDrawStart = 0
|
||||||
|
|
||||||
|
local currentRowMax = 1
|
||||||
|
|
||||||
|
local currentCol = 1
|
||||||
|
|
||||||
|
local rowWidth
|
||||||
|
|
||||||
|
if equalCols then
|
||||||
|
rowWidth = self.gridLayout.columns
|
||||||
|
else
|
||||||
|
rowWidth = #self.gridLayout.columns
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
if children then
|
||||||
|
for i, e in ipairs(children) do
|
||||||
|
local w, h = e:getSize()
|
||||||
|
local colSize
|
||||||
|
local x, y
|
||||||
|
|
||||||
|
if equalCols then
|
||||||
|
colSize = 1 * horValueToPixels
|
||||||
|
else
|
||||||
|
colSize = self.gridLayout.columns[currentCol] * horValueToPixels
|
||||||
|
end
|
||||||
|
|
||||||
|
currentRowMax = math.max(currentRowMax, h)
|
||||||
|
|
||||||
|
if self.gridLayout.horizontalStretchMode =='stretch' then
|
||||||
|
w = colSize
|
||||||
|
else
|
||||||
|
x = alignHandlerX(self.gridLayout.horizontalAlignMode, colDrawStart, colSize, w)
|
||||||
|
end
|
||||||
|
|
||||||
|
e:draw(x, yRoot+carriagePos, w, h)
|
||||||
|
|
||||||
|
colDrawStart = colDrawStart + colSize + self.gridLayout.colSpacing
|
||||||
|
|
||||||
|
if currentCol == rowWidth then
|
||||||
|
carriagePos = carriagePos + self.gridLayout.rowSpacing + currentRowMax
|
||||||
|
currentCol = 0
|
||||||
|
currentRowMax = 0
|
||||||
|
colDrawStart = 0
|
||||||
|
end
|
||||||
|
currentCol = currentCol + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return grid
|
||||||
@ -1,92 +1,109 @@
|
|||||||
local path = string.sub(..., 1, string.len(...) - string.len(".layout"))
|
local path = string.sub(..., 1, string.len(...) - string.len(".layout.init"))
|
||||||
|
|
||||||
|
---@class layout
|
||||||
|
---@field protected vars table
|
||||||
|
---@field protected type function
|
||||||
local layout = {}
|
local layout = {}
|
||||||
local layouts = {}
|
local layouts = {}
|
||||||
layouts.column = require(path..'.layout.column')
|
|
||||||
layouts.row = require(path..'.layout.row')
|
|
||||||
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')
|
||||||
|
|
||||||
--Start prep phase
|
--Start prep phase
|
||||||
function layout.type(type)
|
function layout.type(binder, callback)
|
||||||
local curStack = stack.getContext()
|
local curStack = stack.getContext()
|
||||||
curStack:startDeferingChildren()
|
curStack:startDeferingChildren()
|
||||||
|
|
||||||
local self = {
|
local self = {
|
||||||
vars = {
|
vars = {
|
||||||
type = type or 'column',
|
|
||||||
offLeft = 0,
|
offLeft = 0,
|
||||||
offTop = 0,
|
offTop = 0,
|
||||||
width = 1,
|
width = 1,
|
||||||
hpad = 3,
|
hpad = 3,
|
||||||
vpad = 3,
|
vpad = 3,
|
||||||
height = 1,
|
height = 1,
|
||||||
alignX = 'left', --options: left, center, right
|
|
||||||
alignY = 'top', --options: top, center, bottom
|
|
||||||
--flowDir = 'rtl', --options: rtl/ttb
|
|
||||||
},
|
},
|
||||||
stack = curStack
|
stack = curStack,
|
||||||
|
binder = binder,
|
||||||
|
callback = callback,
|
||||||
}
|
}
|
||||||
|
|
||||||
return setmetatable(self, layout)
|
return setmetatable(self, layout)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Aligns the container vertically
|
||||||
|
---@param pos 'left'|'center'|'right'
|
||||||
function layout:alignVert(pos)
|
function layout:alignVert(pos)
|
||||||
self.vars.alignY = pos
|
self.vars.alignY = pos
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Aligns the container horizontally
|
||||||
|
---@param pos 'top'|'center'|'bottom'
|
||||||
function layout:alignHoriz(pos)
|
function layout:alignHoriz(pos)
|
||||||
self.vars.alignX = pos
|
self.vars.alignX = pos
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
--Sets up the box of the layout
|
---Sets up the width of the box of the layout
|
||||||
|
---@param w number width in pixels or absolute 0-1
|
||||||
function layout:width(w)
|
function layout:width(w)
|
||||||
self.vars.width = w
|
self.vars.width = w
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
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)
|
function layout:height(h)
|
||||||
self.vars.height = h
|
self.vars.height = h
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
---Offset from the left
|
||||||
|
---@param x number offset in pixels or absolute 0-1
|
||||||
function layout:left(x)
|
function layout:left(x)
|
||||||
self.vars.offLeft = x
|
self.vars.offLeft = x
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Offset from the right
|
||||||
|
---@param x number offset in pixels or absolute 0-1
|
||||||
function layout:right(x)
|
function layout:right(x)
|
||||||
self.vars.offRight = x
|
self.vars.offRight = x
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Offset from the top
|
||||||
|
---@param y number offset in pixels or absolute 0-1
|
||||||
function layout:top(y)
|
function layout:top(y)
|
||||||
self.vars.offTop = y
|
self.vars.offTop = y
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Offset from the bottom
|
||||||
|
---@param y number offset in pixels or absolute 0-1
|
||||||
function layout:bottom(y)
|
function layout:bottom(y)
|
||||||
self.vars.offBot = y
|
self.vars.offBot = y
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Padding for the elements vertically
|
||||||
|
---@param px number offset in pixels
|
||||||
function layout:vPadding(px)
|
function layout:vPadding(px)
|
||||||
self.vars.vpad = px
|
self.vars.vpad = px
|
||||||
|
|
||||||
return self
|
return self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---Padding for the elements horizontally
|
||||||
|
---@param px number offset in pixels
|
||||||
function layout:hPadding(px)
|
function layout:hPadding(px)
|
||||||
self.vars.hpad = px
|
self.vars.hpad = px
|
||||||
|
|
||||||
@ -97,6 +114,8 @@ end
|
|||||||
--top + bottom = height ignored
|
--top + bottom = height ignored
|
||||||
--top px + bottom relative works
|
--top px + bottom relative works
|
||||||
--left relative + bottom px works
|
--left relative + bottom px works
|
||||||
|
|
||||||
|
|
||||||
function layout:draw()
|
function layout:draw()
|
||||||
local stack = self.stack
|
local stack = self.stack
|
||||||
local children = stack:stopDeferingChildren()
|
local children = stack:stopDeferingChildren()
|
||||||
@ -127,19 +146,9 @@ function layout:draw()
|
|||||||
x = 0
|
x = 0
|
||||||
end
|
end
|
||||||
|
|
||||||
layouts[self.vars.type](
|
self.callback(self.binder, x, y, width, height, children, self.vars.hpad, self.vars.vpad)
|
||||||
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, callback, binder) return layout.type(callback, binder) end })
|
||||||
return layout
|
return layout
|
||||||
@ -1,4 +1,18 @@
|
|||||||
return function(x, y, width, height, children, hpad, vpad, alignX)
|
local path = string.sub(..., 1, string.len(...) - string.len(".row"))
|
||||||
|
local layout = require(path..'.init')
|
||||||
|
|
||||||
|
---@class Row
|
||||||
|
local row = {}
|
||||||
|
row.__index = row
|
||||||
|
|
||||||
|
---@return layout
|
||||||
|
function row.new()
|
||||||
|
local self = setmetatable({}, row)
|
||||||
|
|
||||||
|
return layout(self.draw)
|
||||||
|
end
|
||||||
|
|
||||||
|
function row:draw(x, y, width, height, children, hpad, vpad, alignX)
|
||||||
local carriagePos = 0
|
local carriagePos = 0
|
||||||
if children then
|
if children then
|
||||||
for i, e in ipairs(children) do
|
for i, e in ipairs(children) do
|
||||||
@ -8,3 +22,5 @@ return function(x, y, width, height, children, hpad, vpad, alignX)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
return row
|
||||||
@ -2,6 +2,20 @@ local path = string.sub(..., 1, string.len(...) - string.len(".shell.button"))
|
|||||||
local state = require(path.. ".hooks.state")
|
local state = require(path.. ".hooks.state")
|
||||||
local input = require(path.. ".core.input")
|
local input = require(path.. ".core.input")
|
||||||
|
|
||||||
|
---@class buttonState
|
||||||
|
---@param down boolean @indicates whether this element is currently being pressed
|
||||||
|
---@param over boolean @indicates whether the mouse is over this button
|
||||||
|
|
||||||
|
---Creates a simple button wrapper, sets up all the callbacks and state for you
|
||||||
|
---@param onClick function|nil
|
||||||
|
---@param onRelease function|nil
|
||||||
|
---@param onEnter function|nil
|
||||||
|
---@param onExit function|nil
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param w number|nil
|
||||||
|
---@param h number|nil
|
||||||
|
---@return buttonState
|
||||||
return function(onClick, onRelease, onEnter, onExit, x, y, w, h)
|
return function(onClick, onRelease, onEnter, onExit, x, y, w, h)
|
||||||
local button = state {
|
local button = state {
|
||||||
down = false,
|
down = false,
|
||||||
|
|||||||
@ -2,6 +2,22 @@ local path = string.sub(..., 1, string.len(...) - string.len(".shell.button"))
|
|||||||
local state = require(path.. ".hooks.state")
|
local state = require(path.. ".hooks.state")
|
||||||
local input = require(path.. ".core.input")
|
local input = require(path.. ".core.input")
|
||||||
|
|
||||||
|
---@class checkboxState
|
||||||
|
---@param down boolean @indicates whether this element is currently held down
|
||||||
|
---@param toggled boolean @current state of the checkbox
|
||||||
|
---@param over boolean @indicates whether the mouse is over this field
|
||||||
|
|
||||||
|
---A wrapper for state and subscriptions for a checkbox
|
||||||
|
---@param onClick function|nil
|
||||||
|
---@param onRelease function|nil
|
||||||
|
---@param onEnter function|nil
|
||||||
|
---@param onExit function|nil
|
||||||
|
---@param startOn boolean|nil
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param w number|nil
|
||||||
|
---@param h number|nil
|
||||||
|
---@return checkboxState
|
||||||
return function(onClick, onRelease, onEnter, onExit, startOn, x, y, w, h)
|
return function(onClick, onRelease, onEnter, onExit, startOn, x, y, w, h)
|
||||||
local checkbox = state {
|
local checkbox = state {
|
||||||
down = false,
|
down = false,
|
||||||
|
|||||||
@ -3,6 +3,22 @@ local state = require(path.. ".hooks.state")
|
|||||||
local input = require(path.. ".core.input")
|
local input = require(path.. ".core.input")
|
||||||
local utf8 = require("utf8")
|
local utf8 = require("utf8")
|
||||||
|
|
||||||
|
---@class textState
|
||||||
|
---@param focused boolean @indicates whether this element is currently focused
|
||||||
|
---@param text string @current state of the input string
|
||||||
|
---@param over boolean @indicates whether the mouse is over this field
|
||||||
|
|
||||||
|
---Textinput element wrapper
|
||||||
|
---@param onChange function|nil
|
||||||
|
---@param onFinish function|nil
|
||||||
|
---@param startStr function|nil
|
||||||
|
---@param onEnter function|nil
|
||||||
|
---@param onExit function|nil
|
||||||
|
---@param x number|nil
|
||||||
|
---@param y number|nil
|
||||||
|
---@param w number|nil
|
||||||
|
---@param h number|nil
|
||||||
|
---@return textState
|
||||||
return function(onChange, onFinish, startStr, onEnter, onExit, x, y, w, h)
|
return function(onChange, onFinish, startStr, onEnter, onExit, x, y, w, h)
|
||||||
local textState = state {
|
local textState = state {
|
||||||
focused = false,
|
focused = false,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user