diff --git a/lib/simple_ui/core/builder.lua b/lib/simple_ui/core/builder.lua index d3878e4..5ec8c6e 100644 --- a/lib/simple_ui/core/builder.lua +++ b/lib/simple_ui/core/builder.lua @@ -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 diff --git a/lib/simple_ui/core/element.lua b/lib/simple_ui/core/element.lua index 721704d..d0cb78f 100644 --- a/lib/simple_ui/core/element.lua +++ b/lib/simple_ui/core/element.lua @@ -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 diff --git a/lib/simple_ui/core/multi_child_element.lua b/lib/simple_ui/core/multi_child_element.lua index f3b1810..2bf383c 100644 --- a/lib/simple_ui/core/multi_child_element.lua +++ b/lib/simple_ui/core/multi_child_element.lua @@ -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 diff --git a/lib/simple_ui/core/single_child_element.lua b/lib/simple_ui/core/single_child_element.lua index 1f2086c..88c3094 100644 --- a/lib/simple_ui/core/single_child_element.lua +++ b/lib/simple_ui/core/single_child_element.lua @@ -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 diff --git a/lib/simple_ui/elements/placeholder.lua b/lib/simple_ui/elements/placeholder.lua index 93e53d5..10d0a9a 100644 --- a/lib/simple_ui/elements/placeholder.lua +++ b/lib/simple_ui/elements/placeholder.lua @@ -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() diff --git a/lib/simple_ui/elements/screen_area.lua b/lib/simple_ui/elements/screen_area.lua index 6f5885c..7cb8253 100644 --- a/lib/simple_ui/elements/screen_area.lua +++ b/lib/simple_ui/elements/screen_area.lua @@ -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 diff --git a/lib/simple_ui/elements/sized_box.lua b/lib/simple_ui/elements/sized_box.lua index 7d5f5d1..0b5194e 100644 --- a/lib/simple_ui/elements/sized_box.lua +++ b/lib/simple_ui/elements/sized_box.lua @@ -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 diff --git a/lib/simple_ui/level/test.lua b/lib/simple_ui/level/test.lua index d78e8d0..c567e92 100644 --- a/lib/simple_ui/level/test.lua +++ b/lib/simple_ui/level/test.lua @@ -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 {}