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