implement core ui widget logic
This commit is contained in:
parent
7753413385
commit
44a71d98ae
44
lib/ui/element.lua
Normal file
44
lib/ui/element.lua
Normal file
@ -0,0 +1,44 @@
|
||||
require "lib.utils.vec3"
|
||||
--- Stateful UI element
|
||||
--- @class Element
|
||||
--- @field state table
|
||||
--- @field update fun(self: Element, dt: number) called each logical frame, alters self.state
|
||||
--- @field draw fun(self: Element) uses self.state to draw, should not alter anything
|
||||
--- @field origin Vec3
|
||||
--- @field size Vec3
|
||||
--- @field parent Element | nil
|
||||
--- @field child Element | nil
|
||||
local Element = {}
|
||||
Element.__index = Element
|
||||
|
||||
Element.state = {}
|
||||
function Element:update(dt)
|
||||
local parent = self.parent
|
||||
if not parent then return end
|
||||
|
||||
self.origin = self.origin or parent.origin
|
||||
self.size = self.size or parent.size
|
||||
end
|
||||
|
||||
function Element:draw() end
|
||||
|
||||
--- Recursive depth-first traversal.
|
||||
--- If `visit` returns false, traversal is stopped early.
|
||||
--- @param visit fun(el: Element): boolean|nil
|
||||
--- @return boolean
|
||||
function Element:traverse(visit)
|
||||
local cont = visit(self)
|
||||
if not cont then return false end
|
||||
if not self.child then return false end
|
||||
self.child.parent = self
|
||||
return not not self.child:traverse(visit)
|
||||
end
|
||||
|
||||
--- template constructor
|
||||
--- @param data {state: table, update: fun(dt: number), draw: fun(), [any]: any}
|
||||
--- @return Element
|
||||
function Element.new(data)
|
||||
return setmetatable(data, Element)
|
||||
end
|
||||
|
||||
return Element
|
||||
72
lib/ui/widgets.lua
Normal file
72
lib/ui/widgets.lua
Normal file
@ -0,0 +1,72 @@
|
||||
require "lib.utils.vec3"
|
||||
local baseElement = require "lib.ui.element"
|
||||
|
||||
local W = {}
|
||||
--- @alias Alignment "topLeft" | "topCenter" | "topRight" | "centerLeft" | "center" | "centerRight" | "bottomLeft" | "bottomCenter" | "bottomRight"
|
||||
--- @type {[Alignment]: Vec3}
|
||||
local alignments = {
|
||||
topLeft = Vec3 { 0, 0 },
|
||||
topCenter = Vec3 { 0.5, 0 },
|
||||
topRight = Vec3 { 1, 0 },
|
||||
centerLeft = Vec3 { 0, 0.5 },
|
||||
center = Vec3 { 0.5, 0.5 },
|
||||
centerRight = Vec3 { 1, 0.5 },
|
||||
bottomLeft = Vec3 { 0, 1 },
|
||||
bottomCenter = Vec3 { 0.5, 1 },
|
||||
bottomRight = Vec3 { 1, 1 }
|
||||
}
|
||||
|
||||
|
||||
--- @class UIRoot : Element
|
||||
local Root = {}
|
||||
setmetatable(Root, { __index = baseElement })
|
||||
|
||||
function Root.new(data)
|
||||
return setmetatable(data, { __index = Root })
|
||||
end
|
||||
|
||||
function Root:update(dt)
|
||||
self.size = Vec3 { love.graphics.getWidth(), love.graphics.getHeight() }
|
||||
end
|
||||
|
||||
W.Root = Root.new
|
||||
--------------------------------------------------
|
||||
|
||||
--- @class Align : Element
|
||||
--- @field alignment Alignment
|
||||
local Align = {}
|
||||
setmetatable(Align, { __index = baseElement })
|
||||
|
||||
function Align.new(data)
|
||||
data.alignment = data.alignment or "center"
|
||||
return setmetatable(data, { __index = Align })
|
||||
end
|
||||
|
||||
function Align:update(dt)
|
||||
local parent = self.parent --[[@as Element]]
|
||||
local shift = alignments[self.alignment]
|
||||
self.origin = Vec3 { parent.size.x * shift.x, parent.size.y * shift.y }
|
||||
end
|
||||
|
||||
W.Align = Align.new
|
||||
--------------------------------------------------
|
||||
|
||||
--- @class Rectangle : Element
|
||||
--- @field color number[]
|
||||
local Rectangle = {}
|
||||
setmetatable(Rectangle, { __index = baseElement })
|
||||
|
||||
function Rectangle.new(data)
|
||||
return setmetatable(data, { __index = Rectangle })
|
||||
end
|
||||
|
||||
function Rectangle:draw()
|
||||
love.graphics.setColor(self.color or { 1, 1, 1 })
|
||||
love.graphics.rectangle("fill", self.origin.x - self.size.x / 2, self.origin.y - self.size.y / 2, self.size.x,
|
||||
self.size.y)
|
||||
end
|
||||
|
||||
W.Rectangle = Rectangle.new
|
||||
---------------------------------------------------
|
||||
|
||||
return W
|
||||
27
main.lua
27
main.lua
@ -1,10 +1,10 @@
|
||||
-- CameraLoader = require 'lib/camera'
|
||||
|
||||
local ui = require "lib.ui.widgets"
|
||||
local character = require "lib/character/character"
|
||||
require "lib/tree"
|
||||
|
||||
|
||||
|
||||
function love.conf(t)
|
||||
t.console = true
|
||||
end
|
||||
@ -24,6 +24,16 @@ function love.load()
|
||||
end
|
||||
end
|
||||
|
||||
Widgets = ui.Root {
|
||||
child = ui.Align {
|
||||
alignment = "bottomCenter",
|
||||
child = ui.Rectangle {
|
||||
size = Vec3 { 100, 100 },
|
||||
color = { 0, 0, 1 }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- PlayerFaction.characters = { Hero1, Hero2 }
|
||||
love.window.setMode(1080, 720, { resizable = true, msaa = 4, vsync = true })
|
||||
end
|
||||
@ -35,6 +45,11 @@ function love.update(dt)
|
||||
Tree.panning:update(dt)
|
||||
Tree.level:update(dt)
|
||||
Tree.controls:cache()
|
||||
|
||||
Widgets:traverse(function(el)
|
||||
el:update(dt)
|
||||
return true
|
||||
end)
|
||||
local t2 = love.timer.getTime()
|
||||
lt = string.format("%.3f", (t2 - t1) * 1000)
|
||||
end
|
||||
@ -81,11 +96,17 @@ function love.draw()
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
end
|
||||
|
||||
|
||||
|
||||
Tree.level:draw()
|
||||
Tree.level.camera:detach()
|
||||
|
||||
Widgets:traverse(
|
||||
function(el)
|
||||
el:draw()
|
||||
return true
|
||||
end
|
||||
)
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
|
||||
local stats = "fps: " .. love.timer.getFPS() .. " lt: " .. lt .. " dt: " .. dt
|
||||
love.graphics.print(stats, 10, 10)
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user