mirror of
https://github.com/Insality/druid.git
synced 2025-06-27 18:37:44 +02:00
488 lines
18 KiB
Lua
488 lines
18 KiB
Lua
return function()
|
|
describe("Layout Component", function()
|
|
local mock_time
|
|
local mock_input
|
|
local druid_system
|
|
|
|
local druid
|
|
local context
|
|
|
|
before(function()
|
|
mock_time = require("deftest.mock.time")
|
|
mock_input = require("test.helper.mock_input")
|
|
druid_system = require("druid.druid")
|
|
|
|
mock_time.mock()
|
|
mock_time.set(0)
|
|
|
|
context = vmath.vector3()
|
|
druid = druid_system.new(context)
|
|
end)
|
|
|
|
after(function()
|
|
mock_time.unmock()
|
|
druid:final()
|
|
druid = nil
|
|
end)
|
|
|
|
|
|
it("Should initialize layout with default settings", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
assert(layout ~= nil)
|
|
assert(layout.node == layout_node)
|
|
assert(layout.type == "horizontal")
|
|
assert(layout.is_dirty == true)
|
|
assert(layout.entities ~= nil)
|
|
assert(#layout.entities == 0)
|
|
assert(layout.is_resize_width == false)
|
|
assert(layout.is_resize_height == false)
|
|
assert(layout.is_justify == false)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
end)
|
|
|
|
it("Should add and remove nodes", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(20, 20, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(20, 20, 0))
|
|
|
|
layout:add(child1)
|
|
assert(#layout.entities == 1)
|
|
assert(layout.entities[1] == child1)
|
|
|
|
layout:add(child2)
|
|
assert(#layout.entities == 2)
|
|
assert(layout.entities[2] == child2)
|
|
|
|
layout:remove(child1)
|
|
assert(#layout.entities == 1)
|
|
assert(layout.entities[1] == child2)
|
|
|
|
layout:clear_layout()
|
|
assert(#layout.entities == 0)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
end)
|
|
|
|
it("Should set node index", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(20, 20, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(20, 20, 0))
|
|
local child3 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(20, 20, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
layout:add(child3)
|
|
|
|
assert(layout.entities[1] == child1)
|
|
assert(layout.entities[2] == child2)
|
|
assert(layout.entities[3] == child3)
|
|
|
|
layout:set_node_index(child3, 1)
|
|
|
|
assert(layout.entities[1] == child3)
|
|
assert(layout.entities[2] == child1)
|
|
assert(layout.entities[3] == child2)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
gui.delete_node(child3)
|
|
end)
|
|
|
|
it("Should set layout type", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
assert(layout.type == "horizontal")
|
|
|
|
layout:set_type("vertical")
|
|
assert(layout.type == "vertical")
|
|
assert(layout.is_dirty == true)
|
|
|
|
layout:set_type("horizontal_wrap")
|
|
assert(layout.type == "horizontal_wrap")
|
|
assert(layout.is_dirty == true)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
end)
|
|
|
|
it("Should set margin", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
local initial_margin_x = layout.margin.x
|
|
local initial_margin_y = layout.margin.y
|
|
|
|
layout:set_margin(10, 20)
|
|
|
|
assert(layout.margin.x == 10)
|
|
assert(layout.margin.y == 20)
|
|
assert(layout.is_dirty == true)
|
|
|
|
-- Test partial update
|
|
layout:set_margin(15)
|
|
assert(layout.margin.x == 15)
|
|
assert(layout.margin.y == 20)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
end)
|
|
|
|
it("Should set padding", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
layout:set_padding(5, 10, 15, 20)
|
|
|
|
assert(layout.padding.x == 5) -- left
|
|
assert(layout.padding.y == 10) -- top
|
|
assert(layout.padding.z == 15) -- right
|
|
assert(layout.padding.w == 20) -- bottom
|
|
assert(layout.is_dirty == true)
|
|
|
|
-- Test partial update
|
|
layout:set_padding(25)
|
|
assert(layout.padding.x == 25)
|
|
assert(layout.padding.y == 10)
|
|
assert(layout.padding.z == 15)
|
|
assert(layout.padding.w == 20)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
end)
|
|
|
|
it("Should set justify", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
assert(layout.is_justify == false)
|
|
|
|
layout:set_justify(true)
|
|
assert(layout.is_justify == true)
|
|
assert(layout.is_dirty == true)
|
|
|
|
layout:set_justify(false)
|
|
assert(layout.is_justify == false)
|
|
assert(layout.is_dirty == true)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
end)
|
|
|
|
it("Should set hug content", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
assert(layout.is_resize_width == false)
|
|
assert(layout.is_resize_height == false)
|
|
|
|
layout:set_hug_content(true, false)
|
|
assert(layout.is_resize_width == true)
|
|
assert(layout.is_resize_height == false)
|
|
assert(layout.is_dirty == true)
|
|
|
|
layout:set_hug_content(false, true)
|
|
assert(layout.is_resize_width == false)
|
|
assert(layout.is_resize_height == true)
|
|
assert(layout.is_dirty == true)
|
|
|
|
layout:set_hug_content(true, true)
|
|
assert(layout.is_resize_width == true)
|
|
assert(layout.is_resize_height == true)
|
|
assert(layout.is_dirty == true)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
end)
|
|
|
|
it("Should fire on_size_changed event", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
|
|
local on_size_changed_calls = 0
|
|
local last_size = nil
|
|
|
|
layout.on_size_changed:subscribe(function(new_size)
|
|
on_size_changed_calls = on_size_changed_calls + 1
|
|
last_size = new_size
|
|
end)
|
|
|
|
-- Set to hug content
|
|
layout:set_hug_content(true, true)
|
|
|
|
-- Add some nodes
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(30, 20, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(30, 20, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
|
|
-- Force refresh to trigger the event
|
|
layout:refresh_layout()
|
|
|
|
assert(on_size_changed_calls >= 1)
|
|
assert(last_size ~= nil)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
end)
|
|
|
|
it("Should handle horizontal layout correctly", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(300, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("horizontal")
|
|
layout:set_margin(10, 0)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child3 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
layout:add(child3)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Check positions - in horizontal layout, nodes should be arranged left to right
|
|
local pos1 = gui.get_position(child1)
|
|
local pos2 = gui.get_position(child2)
|
|
local pos3 = gui.get_position(child3)
|
|
|
|
assert(pos2.x > pos1.x)
|
|
assert(pos3.x > pos2.x)
|
|
|
|
-- Y positions should be approximately the same
|
|
assert(math.abs(pos1.y - pos2.y) < 0.001)
|
|
assert(math.abs(pos2.y - pos3.y) < 0.001)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
gui.delete_node(child3)
|
|
end)
|
|
|
|
it("Should handle vertical layout correctly", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 300, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("vertical")
|
|
layout:set_margin(0, 10)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child3 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
layout:add(child3)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Check positions - in vertical layout, nodes should be arranged top to bottom
|
|
local pos1 = gui.get_position(child1)
|
|
local pos2 = gui.get_position(child2)
|
|
local pos3 = gui.get_position(child3)
|
|
|
|
assert(pos2.y < pos1.y)
|
|
assert(pos3.y < pos2.y)
|
|
|
|
-- X positions should be approximately the same
|
|
assert(math.abs(pos1.x - pos2.x) < 0.001)
|
|
assert(math.abs(pos2.x - pos3.x) < 0.001)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
gui.delete_node(child3)
|
|
end)
|
|
|
|
it("Should handle horizontal_wrap layout correctly", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(120, 200, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("horizontal_wrap")
|
|
layout:set_margin(10, 10)
|
|
|
|
-- Create nodes that will need to wrap
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child3 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child4 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
layout:add(child3)
|
|
layout:add(child4)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Check positions - in horizontal_wrap layout, nodes should wrap to new line
|
|
local pos1 = gui.get_position(child1)
|
|
local pos2 = gui.get_position(child2)
|
|
local pos3 = gui.get_position(child3)
|
|
local pos4 = gui.get_position(child4)
|
|
|
|
-- First two nodes should be on the same row
|
|
assert(math.abs(pos1.y - pos2.y) < 0.001)
|
|
|
|
-- child3 should be on a new row
|
|
assert(pos3.y < pos1.y)
|
|
|
|
-- child3 and child4 should be on the same row
|
|
assert(math.abs(pos3.y - pos4.y) < 0.001)
|
|
|
|
-- X position should flow left to right on each row
|
|
assert(pos2.x > pos1.x)
|
|
assert(pos4.x > pos3.x)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
gui.delete_node(child3)
|
|
gui.delete_node(child4)
|
|
end)
|
|
|
|
it("Should correctly calculate size with content hugging", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(100, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("vertical")
|
|
layout:set_hug_content(true, true)
|
|
layout:set_margin(0, 10)
|
|
layout:set_padding(5, 5, 5, 5)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(60, 30, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(70, 30, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Size should be adjusted to fit content plus padding
|
|
local size = gui.get_size(layout_node)
|
|
|
|
-- Expected width: width of widest child (70) + left and right padding (5+5)
|
|
assert(math.abs(size.x - 80) < 1)
|
|
|
|
-- Expected height: sum of child heights (30+30) + margin (10) + top and bottom padding (5+5)
|
|
assert(math.abs(size.y - 80) < 1)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
end)
|
|
|
|
it("Should justify content horizontally", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(300, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("horizontal")
|
|
layout:set_justify(true)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Check positions - in justified horizontal layout, nodes should be spaced far apart
|
|
local pos1 = gui.get_position(child1)
|
|
local pos2 = gui.get_position(child2)
|
|
|
|
-- Get the layout size and calculate expected positions
|
|
local size = gui.get_size(layout_node)
|
|
|
|
-- In justified layout, the distance between nodes should be larger than with normal layout
|
|
assert((pos2.x - pos1.x) > 100)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
end)
|
|
|
|
it("Should handle disabled nodes", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(300, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("horizontal")
|
|
layout:set_margin(10, 0)
|
|
|
|
local child1 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child2 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
local child3 = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
|
|
layout:add(child1)
|
|
layout:add(child2)
|
|
layout:add(child3)
|
|
|
|
-- Disable the middle node
|
|
gui.set_enabled(child2, false)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Check positions - the disabled node should be ignored in the layout
|
|
local pos1 = gui.get_position(child1)
|
|
local pos3 = gui.get_position(child3)
|
|
|
|
-- child3 should be positioned right after child1 (as if child2 doesn't exist)
|
|
local node_width = gui.get_size(child1).x
|
|
local expected_gap = node_width + layout.margin.x
|
|
|
|
-- The distance should be approximately the width of a node plus margin
|
|
assert(math.abs((pos3.x - pos1.x) - expected_gap) < 1)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(child1)
|
|
gui.delete_node(child2)
|
|
gui.delete_node(child3)
|
|
end)
|
|
|
|
it("Should handle text nodes correctly", function()
|
|
local layout_node = gui.new_box_node(vmath.vector3(50, 50, 0), vmath.vector3(300, 100, 0))
|
|
local layout = druid:new_layout(layout_node)
|
|
layout:set_type("horizontal")
|
|
layout:set_margin(10, 0)
|
|
|
|
local text_node = gui.new_text_node(vmath.vector3(0, 0, 0), "Hello World")
|
|
gui.set_font(text_node, "druid_text_bold")
|
|
|
|
local box_node = gui.new_box_node(vmath.vector3(0, 0, 0), vmath.vector3(50, 30, 0))
|
|
|
|
layout:add(text_node)
|
|
layout:add(box_node)
|
|
|
|
layout:refresh_layout()
|
|
|
|
-- Check positions - the text node should be positioned based on its text size
|
|
local pos_text = gui.get_position(text_node)
|
|
local pos_box = gui.get_position(box_node)
|
|
|
|
assert(pos_box.x > pos_text.x)
|
|
|
|
druid:remove(layout)
|
|
gui.delete_node(layout_node)
|
|
gui.delete_node(text_node)
|
|
gui.delete_node(box_node)
|
|
end)
|
|
end)
|
|
end
|