Skip to content

Commit dc51a73

Browse files
committed
Fixed a bug in reactive not calling the observers
Fixed a layout issue (not updating properly) added the flow layout
1 parent 7375c33 commit dc51a73

4 files changed

Lines changed: 239 additions & 4 deletions

File tree

layouts/flow.lua

Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
local flow = {}
2+
3+
--- Calculates positions for all children in a flow layout
4+
--- @param instance table The layout instance
5+
--- - container: the container to layout
6+
--- - options: layout options
7+
--- - direction: "horizontal" or "vertical" (default: "horizontal")
8+
--- - spacing: gap between elements (default: 0)
9+
--- - padding: padding around the flow (default: 0)
10+
--- - align: "start", "center", or "end" (default: "start")
11+
function flow.calculate(instance)
12+
local container = instance.container
13+
local options = instance.options or {}
14+
15+
local children = container.get("children")
16+
local containerWidth = container.get("width")
17+
local containerHeight = container.get("height")
18+
19+
local direction = options.direction or "horizontal"
20+
local spacing = options.spacing or 0
21+
local padding = options.padding or 0
22+
local align = options.align or "start"
23+
24+
local childCount = #children
25+
if childCount == 0 then
26+
instance._positions = {}
27+
return
28+
end
29+
30+
local positions = {}
31+
32+
if direction == "horizontal" then
33+
local rows = {}
34+
local currentRow = {}
35+
local currentX = padding + 1
36+
local currentY = padding + 1
37+
local maxHeightInRow = 0
38+
39+
for i, child in ipairs(children) do
40+
local childWidth = child.get("width")
41+
local childHeight = child.get("height")
42+
43+
if currentX + childWidth - 1 > containerWidth - padding and currentX > padding + 1 then
44+
45+
table.insert(rows, {
46+
children = currentRow,
47+
y = currentY,
48+
height = maxHeightInRow
49+
})
50+
51+
currentRow = {}
52+
currentX = padding + 1
53+
currentY = currentY + maxHeightInRow + spacing
54+
maxHeightInRow = 0
55+
end
56+
57+
table.insert(currentRow, {
58+
child = child,
59+
width = childWidth,
60+
height = childHeight
61+
})
62+
63+
currentX = currentX + childWidth + spacing
64+
maxHeightInRow = math.max(maxHeightInRow, childHeight)
65+
end
66+
67+
if #currentRow > 0 then
68+
table.insert(rows, {
69+
children = currentRow,
70+
y = currentY,
71+
height = maxHeightInRow
72+
})
73+
end
74+
75+
for _, row in ipairs(rows) do
76+
local rowWidth = 0
77+
for j, item in ipairs(row.children) do
78+
rowWidth = rowWidth + item.width
79+
if j < #row.children then
80+
rowWidth = rowWidth + spacing
81+
end
82+
end
83+
84+
local startX = padding + 1
85+
if align == "center" then
86+
startX = padding + 1 + math.floor((containerWidth - 2 * padding - rowWidth) / 2)
87+
elseif align == "end" then
88+
startX = containerWidth - padding - rowWidth + 1
89+
end
90+
91+
local x = startX
92+
for _, item in ipairs(row.children) do
93+
local y = row.y
94+
if align == "center" then
95+
y = row.y + math.floor((row.height - item.height) / 2)
96+
elseif align == "end" then
97+
y = row.y + row.height - item.height
98+
end
99+
100+
positions[item.child] = {
101+
x = x,
102+
y = y,
103+
width = item.width,
104+
height = item.height
105+
}
106+
107+
x = x + item.width + spacing
108+
end
109+
end
110+
111+
else
112+
local columns = {}
113+
local currentColumn = {}
114+
local currentX = padding + 1
115+
local currentY = padding + 1
116+
local maxWidthInColumn = 0
117+
118+
for i, child in ipairs(children) do
119+
local childWidth = child.get("width")
120+
local childHeight = child.get("height")
121+
122+
if currentY + childHeight - 1 > containerHeight - padding and currentY > padding + 1 then
123+
table.insert(columns, {
124+
children = currentColumn,
125+
x = currentX,
126+
width = maxWidthInColumn
127+
})
128+
129+
currentColumn = {}
130+
currentY = padding + 1
131+
currentX = currentX + maxWidthInColumn + spacing
132+
maxWidthInColumn = 0
133+
end
134+
135+
table.insert(currentColumn, {
136+
child = child,
137+
width = childWidth,
138+
height = childHeight
139+
})
140+
141+
currentY = currentY + childHeight + spacing
142+
maxWidthInColumn = math.max(maxWidthInColumn, childWidth)
143+
end
144+
145+
if #currentColumn > 0 then
146+
table.insert(columns, {
147+
children = currentColumn,
148+
x = currentX,
149+
width = maxWidthInColumn
150+
})
151+
end
152+
153+
for _, column in ipairs(columns) do
154+
local columnHeight = 0
155+
for j, item in ipairs(column.children) do
156+
columnHeight = columnHeight + item.height
157+
if j < #column.children then
158+
columnHeight = columnHeight + spacing
159+
end
160+
end
161+
162+
local startY = padding + 1
163+
if align == "center" then
164+
startY = padding + 1 + math.floor((containerHeight - 2 * padding - columnHeight) / 2)
165+
elseif align == "end" then
166+
startY = containerHeight - padding - columnHeight + 1
167+
end
168+
169+
local y = startY
170+
for _, item in ipairs(column.children) do
171+
local x = column.x
172+
if align == "center" then
173+
x = column.x + math.floor((column.width - item.width) / 2)
174+
elseif align == "end" then
175+
x = column.x + column.width - item.width
176+
end
177+
178+
positions[item.child] = {
179+
x = x,
180+
y = y,
181+
width = item.width,
182+
height = item.height
183+
}
184+
185+
y = y + item.height + spacing
186+
end
187+
end
188+
end
189+
190+
instance._positions = positions
191+
end
192+
193+
return flow

layouts/grid.lua

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ function grid.calculate(instance)
2222
local columns = options.columns
2323

2424
local childCount = #children
25+
if childCount == 0 then
26+
instance._positions = {}
27+
return
28+
end
29+
2530
if not rows and not columns then
2631
columns = math.ceil(math.sqrt(childCount))
2732
rows = math.ceil(childCount / columns)
@@ -31,11 +36,21 @@ function grid.calculate(instance)
3136
rows = math.ceil(childCount / columns)
3237
end
3338

39+
if columns <= 0 then columns = 1 end
40+
if rows <= 0 then rows = 1 end
41+
3442
local availableWidth = containerWidth - (2 * padding) - ((columns - 1) * spacing)
3543
local availableHeight = containerHeight - (2 * padding) - ((rows - 1) * spacing)
44+
45+
if availableWidth < 1 then availableWidth = 1 end
46+
if availableHeight < 1 then availableHeight = 1 end
47+
3648
local cellWidth = math.floor(availableWidth / columns)
3749
local cellHeight = math.floor(availableHeight / rows)
3850

51+
if cellWidth < 1 then cellWidth = 1 end
52+
if cellHeight < 1 then cellHeight = 1 end
53+
3954
local positions = {}
4055

4156
for i, child in ipairs(children) do

src/elements/Container.lua

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -103,10 +103,12 @@ function Container:init(props, basalt)
103103
self:observe("width", function()
104104
self.set("childrenSorted", false)
105105
self.set("childrenEventsSorted", false)
106+
self:updateRender()
106107
end)
107108
self:observe("height", function()
108109
self.set("childrenSorted", false)
109110
self.set("childrenEventsSorted", false)
111+
self:updateRender()
110112
end)
111113
end
112114

@@ -203,11 +205,12 @@ end
203205
--- @shortDescription Updates child element ordering
204206
--- @return Container self For method chaining
205207
function Container:sortChildren()
206-
self.set("visibleChildren", sortAndFilterChildren(self, self._values.children))
207208
self.set("childrenSorted", true)
208209
if self._layoutInstance then
209210
self:updateLayout()
210211
end
212+
213+
self.set("visibleChildren", sortAndFilterChildren(self, self._values.children))
211214
return self
212215
end
213216

@@ -546,7 +549,7 @@ end
546549
--- @protected
547550
function Container:multiBlit(x, y, width, height, text, fg, bg)
548551
local w, h = self.get("width"), self.get("height")
549-
552+
550553
width = x < 1 and math.min(width + x - 1, w) or math.min(width, math.max(0, w - x + 1))
551554
height = y < 1 and math.min(height + y - 1, h) or math.min(height, math.max(0, h - y + 1))
552555

src/plugins/reactive.lua

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,17 @@ local observerCache = setmetatable({}, {
191191
end
192192
})
193193

194+
local valueCache = setmetatable({}, {
195+
__mode = "k",
196+
__index = function(t, k)
197+
t[k] = {}
198+
return t[k]
199+
end
200+
})
201+
194202
local function setupObservers(element, expr, propertyName)
195203
local deps = analyzeDependencies(expr)
196-
204+
197205
if observerCache[element][propertyName] then
198206
for _, observer in ipairs(observerCache[element][propertyName]) do
199207
observer.target:removeObserver(observer.property, observer.callback)
@@ -229,7 +237,20 @@ local function setupObservers(element, expr, propertyName)
229237
target = target,
230238
property = isState and "states" or prop,
231239
callback = function()
232-
element:updateRender()
240+
local oldValue = valueCache[element][propertyName]
241+
local newValue = element.get(propertyName)
242+
243+
if oldValue ~= newValue then
244+
valueCache[element][propertyName] = newValue
245+
246+
if element._observers and element._observers[propertyName] then
247+
for _, obs in ipairs(element._observers[propertyName]) do
248+
obs()
249+
end
250+
end
251+
252+
element:updateRender()
253+
end
233254
end
234255
}
235256
target:observe(observer.property, observer.callback)
@@ -281,6 +302,8 @@ PropertySystem.addSetterHook(function(element, propertyName, value, config)
281302
end
282303
return config.default
283304
end
305+
306+
valueCache[element][propertyName] = result
284307
return result
285308
end
286309
end
@@ -304,6 +327,7 @@ BaseElement.hooks = {
304327
end
305328
end
306329
observerCache[self] = nil
330+
valueCache[self] = nil
307331
functionCache[self] = nil
308332
end
309333
end

0 commit comments

Comments
 (0)