implement drawing in local coordinates
these damn trees are still WIP but I'm confident I'm doing everything right since I've recently embraced Stoicism
This commit is contained in:
parent
460e8b78b7
commit
987ce25474
@ -1,37 +1,51 @@
|
||||
local Element = require "lib.simple_ui.core.element"
|
||||
|
||||
--- Объект, который отвечает за работу с элементами интерфейса одного экрана
|
||||
--- @class UIBuilder
|
||||
--- @field private _cache UIElement[]
|
||||
--- @field elementTree UIElement
|
||||
--- @field private shadowTree UIElement
|
||||
local builder = {}
|
||||
builder.__index = builder
|
||||
|
||||
--- @return UIBuilder
|
||||
local function new(from)
|
||||
from._cache = {}
|
||||
|
||||
from.shadowTree = Element:new {}
|
||||
setmetatable(from, builder)
|
||||
return from
|
||||
end
|
||||
|
||||
--- @param element? UIElement
|
||||
-- --- @param element? UIElement
|
||||
-- --- @private
|
||||
-- function builder:_get(element)
|
||||
-- if not element then return nil end
|
||||
-- local key = builder:_makeKey(element)
|
||||
-- if not key then return element end
|
||||
|
||||
-- local cached = self._cache[key]
|
||||
-- if cached then return cached end
|
||||
|
||||
-- self._cache[key] = element
|
||||
-- return element
|
||||
-- end
|
||||
|
||||
--- @param newNode UIElement?
|
||||
--- @param oldNode UIElement?
|
||||
--- @private
|
||||
function builder:_get(element)
|
||||
if not element then return nil end
|
||||
|
||||
local key = builder:_makeKey(element)
|
||||
if not key then return element end
|
||||
|
||||
local cached = self._cache[key]
|
||||
if cached then return cached end
|
||||
|
||||
self._cache[key] = element
|
||||
return element
|
||||
function builder:didChange(newNode, oldNode)
|
||||
if not oldNode or not newNode then
|
||||
return true
|
||||
end
|
||||
if oldNode.type ~= newNode.type then return true end
|
||||
if oldNode.key ~= newNode.key then return true end
|
||||
return false
|
||||
end
|
||||
|
||||
--- @param element UIElement
|
||||
--- @private
|
||||
function builder:_makeKey(element)
|
||||
if not element.key then return nil end
|
||||
--if not element.key then return nil end
|
||||
if type(element.key) == "string" then return element.key end
|
||||
element.key = element.type .. "<" .. tostring(element.key) .. ">"
|
||||
return element.key
|
||||
@ -39,18 +53,22 @@ end
|
||||
|
||||
--- @private
|
||||
function builder:build_step(cur)
|
||||
if not cur then return end
|
||||
if cur.build then
|
||||
cur.child = self:_get(cur:build())
|
||||
cur.child.parent = cur
|
||||
local orphan = cur:build()
|
||||
local child = cur.child
|
||||
child = orphan
|
||||
if not child then return end
|
||||
|
||||
child.parent = cur
|
||||
cur.child = child
|
||||
|
||||
self:build_step(cur.child)
|
||||
elseif cur.children then
|
||||
for _, child in ipairs(cur.children) do
|
||||
self:build_step(self:_get(child))
|
||||
child.parent = cur
|
||||
|
||||
self:build_step(child)
|
||||
end
|
||||
else
|
||||
cur.child = self:_get(cur.child)
|
||||
self:build_step(cur.child)
|
||||
end
|
||||
end
|
||||
|
||||
@ -58,7 +76,7 @@ end
|
||||
---
|
||||
--- Благодаря этому можно каждый раз создавать новые элементы в верстке, а получать старые :)
|
||||
function builder:build()
|
||||
local root = self:_get(self.elementTree)
|
||||
local root = self.elementTree
|
||||
self:build_step(root)
|
||||
end
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ local Vec3 = require "lib.utils.vec3"
|
||||
--- @field constraints Constraints
|
||||
--- @field offset Vec3 Положение левого верхнего угла элемента в экранных координатах {x, y}. Устанавливается родительским элементом.
|
||||
--- @field size Vec3 Размеры элемента в экранных координатах {x, y}
|
||||
--- @field build? fun(self): UIElement?
|
||||
--- @field build? fun(self, ctx: UIElement): UIElement
|
||||
local element = {}
|
||||
element.__index = element
|
||||
element.type = "Element"
|
||||
@ -23,7 +23,11 @@ function element:layout() end
|
||||
|
||||
function element:update(dt) end
|
||||
|
||||
function element:draw() end
|
||||
function element:draw()
|
||||
if self.type == "SizedBox" then
|
||||
print(self.offset)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param values {[string]: any}
|
||||
--- @return UIElement
|
||||
|
||||
@ -13,28 +13,30 @@ function element:update(dt)
|
||||
end
|
||||
|
||||
function element:draw()
|
||||
love.graphics.push("transform")
|
||||
love.graphics.translate(self.offset.x, self.offset.y)
|
||||
for _, child in ipairs(self.children) do
|
||||
child:draw()
|
||||
end
|
||||
|
||||
|
||||
--- @TODO: сделать дебажный метод для отрисовки границ
|
||||
love.graphics.setColor(1, 0, 0)
|
||||
love.graphics.line(self.offset.x, self.offset.y, self.offset.x + self.size.x, self.offset.y)
|
||||
love.graphics.line(self.offset.x, self.offset.y, self.offset.x, self.offset.y + self.size.y)
|
||||
love.graphics.line(self.offset.x + self.size.x, self.offset.y, self.offset.x + self.size.x,
|
||||
self.offset.y + self.size.y)
|
||||
love.graphics.line(self.offset.x, self.offset.y + self.size.y, self.offset.x + self.size.x,
|
||||
self.offset.y + self.size.y)
|
||||
love.graphics.line(0, 0, 0 + self.size.x, 0)
|
||||
love.graphics.line(0, 0, 0, self.offset.y + 0)
|
||||
love.graphics.line(0 + self.size.x, self.offset.y, 0 + self.size.x,
|
||||
0 + self.size.y)
|
||||
love.graphics.line(0, 0 + self.size.y, 0 + self.size.x,
|
||||
0 + self.size.y)
|
||||
love.graphics.setColor(1, 1, 1)
|
||||
|
||||
love.graphics.pop()
|
||||
end
|
||||
|
||||
--- @generic T : MultiChildElement
|
||||
--- @param values {children: UIElement[]?, [string]: any}
|
||||
--- @return T
|
||||
function element:new(values)
|
||||
for _, child in ipairs(values.children or {}) do
|
||||
child.parent = values
|
||||
end
|
||||
return Element.new(self, values)
|
||||
end
|
||||
|
||||
|
||||
@ -1,15 +1,24 @@
|
||||
local Element = require "lib.simple_ui.core.element"
|
||||
local Constraints = require "lib.simple_ui.core.constraints"
|
||||
|
||||
--- @class SingleChildElement : UIElement
|
||||
--- @field child? UIElement
|
||||
local element = setmetatable({}, require "lib.simple_ui.core.element")
|
||||
element.__index = element
|
||||
|
||||
--- дефолтное поведение -- просто возвращать своего ребенка
|
||||
function element:build()
|
||||
return self.child
|
||||
end
|
||||
|
||||
function element:layout()
|
||||
--- передать ребенку ограничения
|
||||
--- получить назад размеры
|
||||
--- разместить ребенка
|
||||
if not self.child then return end
|
||||
self.child.constraints = self.constraints
|
||||
self.child.constraints = Constraints(self.constraints)
|
||||
self.child:layout()
|
||||
self.child.offset = self.offset:copy()
|
||||
self.child.offset = Vec3 {}
|
||||
end
|
||||
|
||||
function element:update(dt)
|
||||
@ -17,7 +26,10 @@ function element:update(dt)
|
||||
end
|
||||
|
||||
function element:draw()
|
||||
love.graphics.push("transform")
|
||||
love.graphics.translate(self.offset.x, self.offset.y)
|
||||
if self.child then self.child:draw() end
|
||||
love.graphics.pop()
|
||||
end
|
||||
|
||||
--- @generic T : SingleChildElement
|
||||
@ -25,7 +37,6 @@ end
|
||||
--- @param values {child: UIElement?, [string]: any}
|
||||
--- @return T
|
||||
function element:new(values)
|
||||
if values.child then values.child.parent = values end
|
||||
return Element.new(self, values)
|
||||
end
|
||||
|
||||
|
||||
@ -12,7 +12,6 @@ function element:layout()
|
||||
if not self.child then return end
|
||||
self.child.constraints = Constraints(self.constraints)
|
||||
self.child:layout()
|
||||
self.child.offset = self.offset:copy()
|
||||
end
|
||||
|
||||
function element:draw()
|
||||
|
||||
@ -15,10 +15,7 @@ function element:layout()
|
||||
self.size = Vec3 { screenW, screenH }
|
||||
|
||||
if not self.child then return end
|
||||
self.child.constraints = Constraints {
|
||||
maxWidth = screenW,
|
||||
maxHeight = screenH,
|
||||
}
|
||||
self.child.constraints = Constraints(self.constraints)
|
||||
self.child:layout()
|
||||
self.child.offset = Vec3 {}
|
||||
end
|
||||
|
||||
@ -17,7 +17,7 @@ function element:layout()
|
||||
maxHeight = self.height,
|
||||
}
|
||||
self.child:layout()
|
||||
self.child.offset = self.offset:copy()
|
||||
self.child.offset = Vec3 {}
|
||||
end
|
||||
|
||||
--- @return SizedBox
|
||||
|
||||
@ -3,25 +3,30 @@ local Placeholder = require "lib.simple_ui.elements.placeholder"
|
||||
local Padding = require "lib.simple_ui.elements.padding"
|
||||
local Builder = require "lib.simple_ui.core.builder"
|
||||
local Flex = require "lib.simple_ui.elements.flex"
|
||||
local Center = require "lib.simple_ui.elements.center"
|
||||
local SizedBox = require "lib.simple_ui.elements.sized_box"
|
||||
local SingleChildElement = require "lib.simple_ui.core.single_child_element"
|
||||
|
||||
|
||||
local MyWidget = setmetatable({}, SingleChildElement)
|
||||
MyWidget.__index = MyWidget
|
||||
MyWidget.type = "MyWidget"
|
||||
|
||||
local Canary = setmetatable({}, SingleChildElement)
|
||||
Canary.__index = Canary
|
||||
Canary.type = "Canary"
|
||||
|
||||
local reported = false
|
||||
function Canary:build()
|
||||
if not reported then
|
||||
self:traverseUp(function(element)
|
||||
print(element.type)
|
||||
return true
|
||||
end)
|
||||
reported = true
|
||||
end
|
||||
-- self.i = self.i and self.i + 1 or 0
|
||||
-- print(self.i)
|
||||
-- if not reported then
|
||||
-- self:traverseUp(function(element)
|
||||
-- print(element.type)
|
||||
-- return true
|
||||
-- end)
|
||||
-- reported = true
|
||||
-- end
|
||||
|
||||
return Placeholder:new {}
|
||||
end
|
||||
@ -37,7 +42,6 @@ function MyWidget:build()
|
||||
Padding:new {
|
||||
top = 8,
|
||||
child = Flex:new {
|
||||
key = "inner_flex",
|
||||
mainAxisAlignment = "start",
|
||||
mainAxisSize = "min",
|
||||
children = {
|
||||
@ -52,9 +56,12 @@ function MyWidget:build()
|
||||
child = Placeholder:new {}
|
||||
},
|
||||
SizedBox:new {
|
||||
key = "mybox",
|
||||
width = 100,
|
||||
height = 100,
|
||||
child = Canary:new {}
|
||||
child = Canary:new {
|
||||
i = 10
|
||||
}
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -81,6 +88,35 @@ function MyWidget:build()
|
||||
}
|
||||
end
|
||||
|
||||
--
|
||||
-- --- comment
|
||||
-- --- @return Flex
|
||||
-- function MyWidget:build()
|
||||
-- return Flex:new {
|
||||
-- mainAxisAlignment = "start",
|
||||
-- mainAxisSize = "min",
|
||||
-- children = {
|
||||
-- SizedBox:new {
|
||||
-- width = 100,
|
||||
-- height = 100,
|
||||
-- child = Placeholder:new {}
|
||||
-- },
|
||||
-- SizedBox:new {
|
||||
-- width = 150,
|
||||
-- height = 200,
|
||||
-- child = Placeholder:new {}
|
||||
-- },
|
||||
-- SizedBox:new {
|
||||
-- width = 100,
|
||||
-- height = 100,
|
||||
-- child = Canary:new {
|
||||
-- i = 10
|
||||
-- }
|
||||
-- },
|
||||
-- },
|
||||
-- }
|
||||
-- end
|
||||
|
||||
return Builder {
|
||||
elementTree = ScreenArea:new {
|
||||
child = MyWidget:new {}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user