implement MultiChildElement.build

This commit is contained in:
PeaAshMeter 2026-06-01 01:18:02 +03:00
parent 6c8dc6a52b
commit 0e8181baf3
4 changed files with 56 additions and 104 deletions

View File

@ -2,34 +2,17 @@ local Element = require "lib.simple_ui.core.element"
--- Объект, который отвечает за работу с элементами интерфейса одного экрана --- Объект, который отвечает за работу с элементами интерфейса одного экрана
--- @class UIBuilder --- @class UIBuilder
--- @field private _cache UIElement[] --- @field builder fun(): UIElement
--- @field elementTree UIElement --- @field private elementTree UIElement
--- @field private shadowTree UIElement
local builder = {} local builder = {}
builder.__index = builder builder.__index = builder
--- @return UIBuilder --- @return UIBuilder
local function new(from) local function new(from)
from._cache = {}
from.shadowTree = Element:new {}
setmetatable(from, builder) setmetatable(from, builder)
return from return from
end end
-- --- @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 newNode UIElement?
--- @param oldNode UIElement? --- @param oldNode UIElement?
--- @private --- @private
@ -44,37 +27,30 @@ end
--- @param element UIElement --- @param element UIElement
--- @private --- @private
function builder:_makeKey(element) 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 return element.type .. "<" .. tostring(element.key) .. ">"
element.key = element.type .. "<" .. tostring(element.key) .. ">"
return element.key
end end
--- @param cur UIElement --- @param cur UIElement
--- @private --- @private
function builder:build_step(cur) function builder:build_step(cur)
-- Самоприсваивания должны вырезаться компилятором, я в это верю
cur = cur --[[@as SingleChildElement | MultiChildElement]] cur = cur --[[@as SingleChildElement | MultiChildElement]]
if cur.build then local buildRes = cur:build()
if not buildRes then return end
if buildRes.type then
cur = cur --[[@as SingleChildElement]] cur = cur --[[@as SingleChildElement]]
local child = cur:build() cur._child_ = buildRes
if not child then return end buildRes._parent_ = cur
-- local oldChild = cur._child_
-- if oldChild then
-- for key, value in pairs(oldChild) do
-- if (not Element[key]) then child[key] = value end
-- end
-- end
cur._child_ = child
child._parent_ = cur
self:build_step(cur._child_) self:build_step(cur._child_)
elseif cur.children then else
cur = cur --[[@as MultiChildElement]] cur = cur --[[@as MultiChildElement]]
for _, child in ipairs(cur.children) do cur._children_ = buildRes
for _, child in ipairs(cur._children_) do
child._parent_ = cur child._parent_ = cur
self:build_step(child) self:build_step(child)
end end
@ -85,8 +61,8 @@ end
--- ---
--- Благодаря этому можно каждый раз создавать новые элементы в верстке, а получать старые :) --- Благодаря этому можно каждый раз создавать новые элементы в верстке, а получать старые :)
function builder:build() function builder:build()
local root = self.elementTree self.elementTree = self:builder()
self:build_step(root) self:build_step(self.elementTree)
end end
function builder:layout() function builder:layout()

View File

@ -2,12 +2,18 @@ local Element = require "lib.simple_ui.core.element"
--- @class MultiChildElement : UIElement --- @class MultiChildElement : UIElement
--- @field children UIElement[] --- @field children UIElement[]
--- @field _children_ UIElement[]
local element = setmetatable({}, require "lib.simple_ui.core.element") local element = setmetatable({}, require "lib.simple_ui.core.element")
element.__index = element element.__index = element
element.children = {} element._children_ = {}
--- @return UIElement[]
function element:build()
return self.children
end
function element:update(dt) function element:update(dt)
for _, child in ipairs(self.children) do for _, child in ipairs(self._children_) do
child:update(dt) child:update(dt)
end end
end end
@ -15,19 +21,19 @@ end
function element:draw() function element:draw()
love.graphics.push("transform") love.graphics.push("transform")
love.graphics.translate(self._offset_.x, self._offset_.y) love.graphics.translate(self._offset_.x, self._offset_.y)
for _, child in ipairs(self.children) do for _, child in ipairs(self._children_) do
child:draw() child:draw()
end end
--- @TODO: сделать дебажный метод для отрисовки границ --- @TODO: сделать дебажный метод для отрисовки границ
love.graphics.setColor(1, 0, 0) love.graphics.setColor(1, 0, 0)
love.graphics.line(0, 0, 0 + self._size_.x, 0) love.graphics.line(0, 0, self._size_.x, 0)
love.graphics.line(0, 0, 0, self._offset_.y + 0) love.graphics.line(0, 0, 0, self._size_.y)
love.graphics.line(0 + self._size_.x, self._offset_.y, 0 + self._size_.x, love.graphics.line(self._size_.x, 0, self._size_.x,
0 + self._size_.y) self._size_.y)
love.graphics.line(0, 0 + self._size_.y, 0 + self._size_.x, love.graphics.line(0, self._size_.y, self._size_.x,
0 + self._size_.y) self._size_.y)
love.graphics.setColor(1, 1, 1) love.graphics.setColor(1, 1, 1)
love.graphics.pop() love.graphics.pop()

View File

@ -16,7 +16,7 @@ function element:layout()
local mainAxisSize = 0 local mainAxisSize = 0
local crossAxisSize = 0 local crossAxisSize = 0
if self.direction == "horizontal" then if self.direction == "horizontal" then
for _, child in ipairs(self.children) do for _, child in ipairs(self._children_) do
child._constraints_ = Constraints { maxHeight = self._constraints_.maxHeight } child._constraints_ = Constraints { maxHeight = self._constraints_.maxHeight }
child:layout() child:layout()
if child._size_.y > crossAxisSize then crossAxisSize = child._size_.y end if child._size_.y > crossAxisSize then crossAxisSize = child._size_.y end
@ -30,7 +30,7 @@ function element:layout()
start = self._constraints_.maxWidth - mainAxisSize start = self._constraints_.maxWidth - mainAxisSize
end end
local shift = 0 local shift = 0
for _, child in ipairs(self.children) do for _, child in ipairs(self._children_) do
child._offset_ = Vec3 { start + shift, 0 } child._offset_ = Vec3 { start + shift, 0 }
shift = shift + child._size_.x shift = shift + child._size_.x
end end
@ -41,7 +41,7 @@ function element:layout()
self._size_ = Vec3 { mainAxisSize, crossAxisSize } self._size_ = Vec3 { mainAxisSize, crossAxisSize }
end end
else else
for _, child in ipairs(self.children) do for _, child in ipairs(self._children_) do
child._constraints_ = Constraints { maxWidth = self._constraints_.maxWidth } child._constraints_ = Constraints { maxWidth = self._constraints_.maxWidth }
child:layout() child:layout()
if child._size_.x > crossAxisSize then crossAxisSize = child._size_.x end if child._size_.x > crossAxisSize then crossAxisSize = child._size_.x end
@ -56,7 +56,7 @@ function element:layout()
start = self._constraints_.maxHeight - mainAxisSize start = self._constraints_.maxHeight - mainAxisSize
end end
local shift = 0 local shift = 0
for _, child in ipairs(self.children) do for _, child in ipairs(self._children_) do
child._offset_ = Vec3 { 0, start + shift } child._offset_ = Vec3 { 0, start + shift }
shift = shift + child._size_.y shift = shift + child._size_.y
end end

View File

@ -18,15 +18,15 @@ Canary.type = "Canary"
local reported = false local reported = false
function Canary:build() function Canary:build()
self.i = self.i and self.i + 1 or 0 -- self.i = self.i and self.i + 1 or 0
print(self.i) -- print(self.i)
-- if not reported then if not reported then
-- self:traverseUp(function(element) self:traverseUp(function(element)
-- print(element.type) print(element.type)
-- return true return true
-- end) end)
-- reported = true reported = true
-- end end
return Placeholder:new {} return Placeholder:new {}
end end
@ -35,7 +35,7 @@ end
--- @return Flex --- @return Flex
function MyWidget:build() function MyWidget:build()
return Flex:new { return Flex:new {
key = "my_flex", key = "test",
direction = "vertical", direction = "vertical",
mainAxisSize = "max", mainAxisSize = "max",
children = { children = {
@ -68,6 +68,7 @@ function MyWidget:build()
}, },
Flex:new { Flex:new {
key = "inner_flex2", key = "inner_flex2",
mainAxisAlignment = "end", mainAxisAlignment = "end",
children = { children = {
SizedBox:new { SizedBox:new {
@ -81,53 +82,22 @@ function MyWidget:build()
height = 100, height = 100,
child = Placeholder:new {} child = Placeholder:new {}
}, },
SizedBox:new {
width = 100,
height = 100,
child = Placeholder:new {}
},
}, },
}, },
math.floor(love.timer.getTime()) % 2 == 0 and self.child or nil
} }
} }
end 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 { return Builder {
elementTree = ScreenArea:new { builder = function()
child = MyWidget:new { return ScreenArea:new {
child = SizedBox:new { child = MyWidget:new {}
width = 100,
height = 100,
child = Canary:new {
i = 100
}
}
} }
} end
} }