mirror of
https://github.com/centau/vide.git
synced 2026-08-20 14:41:37 +00:00
Add aggregate initialization
This commit is contained in:
parent
be15f69522
commit
2aebaf5abb
9 changed files with 139 additions and 36 deletions
|
|
@ -28,15 +28,17 @@ Creates a new UI element, applying any given properties.
|
||||||
|
|
||||||
- **Property setting rules**
|
- **Property setting rules**
|
||||||
|
|
||||||
- If a table value is another table, that nested table is processed so that
|
|
||||||
any properties inside that table are also applied to the instance just
|
|
||||||
like the outer table.
|
|
||||||
- If a table index is a string:
|
- If a table index is a string:
|
||||||
|
- If its value is a table then it will attempt to perform aggregate
|
||||||
|
initialization.
|
||||||
- If its value is a function then it will either bind that property to
|
- If its value is a function then it will either bind that property to
|
||||||
the function or connect it if the property type is a `RBXScriptSignal`.
|
the function or connect it if the property type is a `RBXScriptSignal`.
|
||||||
- If the value is not a function then the property will be set to that
|
- If the value is not a function then the property will be set to that
|
||||||
value.
|
value.
|
||||||
- If a table index is a number:
|
- If a table index is a number:
|
||||||
|
- If its value is a table then that table will be recursively
|
||||||
|
- processed just like the outer table.
|
||||||
- If its value is a function then it will parent and bind any instances
|
- If its value is a function then it will parent and bind any instances
|
||||||
returned by that function as children.
|
returned by that function as children.
|
||||||
- If its value is an instance then it will be parented to the instance.
|
- If its value is an instance then it will be parented to the instance.
|
||||||
|
|
|
||||||
|
|
@ -54,3 +54,17 @@ create "TextButton" {
|
||||||
end
|
end
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can also use a form of aggregate initialization to create datatypes instead
|
||||||
|
of explicitly typing out the class name and constructor.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
create "Frame" {
|
||||||
|
AnchorPoint = { 0.5, 1 },
|
||||||
|
UDim2 = { 0.5, 0, 0.5, 0}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
When a property is assigned a table, Vide will inspect the type of the property
|
||||||
|
being assigned to, and call that type's default `new()` constructor with the
|
||||||
|
values from the unpacked table.
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,12 @@
|
||||||
local typeof = typeof
|
local typeof = typeof
|
||||||
|
local Vector2 = Vector2
|
||||||
|
local UDim2 = UDim2
|
||||||
|
|
||||||
if not game then
|
if not game then
|
||||||
script = require "test/relative-string"
|
script = require "test/relative-string"
|
||||||
typeof = require "test/mock".typeof
|
typeof = require "test/mock".typeof
|
||||||
|
Vector2 = require "test/mock".Vector2
|
||||||
|
UDim2 = require "test/mock".UDim2
|
||||||
end
|
end
|
||||||
|
|
||||||
local flags = require(script.Parent.flags)
|
local flags = require(script.Parent.flags)
|
||||||
|
|
@ -33,6 +37,19 @@ setmetatable(nested_debug_cache :: any, {
|
||||||
|
|
||||||
local nested_stack = {} :: { {} | number }
|
local nested_stack = {} :: { {} | number }
|
||||||
|
|
||||||
|
-- todo
|
||||||
|
local classes = {
|
||||||
|
Vector2 = Vector2,
|
||||||
|
UDim2 = UDim2,
|
||||||
|
UDim = UDim2,
|
||||||
|
Rect = Rect,
|
||||||
|
Color3 = Color3
|
||||||
|
}
|
||||||
|
|
||||||
|
local function get_class(v: unknown): { new: (...any) -> () }
|
||||||
|
return classes[typeof(v)]
|
||||||
|
end
|
||||||
|
|
||||||
local function process(instance: Instance, properties: { [unknown]: unknown })
|
local function process(instance: Instance, properties: { [unknown]: unknown })
|
||||||
local strict = flags.strict
|
local strict = flags.strict
|
||||||
|
|
||||||
|
|
@ -43,14 +60,7 @@ local function process(instance: Instance, properties: { [unknown]: unknown })
|
||||||
|
|
||||||
repeat
|
repeat
|
||||||
for property, value in properties do
|
for property, value in properties do
|
||||||
if type(value) == "table" then
|
if type(property) == "string" then
|
||||||
if is_action(value) then
|
|
||||||
table.insert(action_buffers[(value :: any).priority], (value :: any).callback :: () -> ())
|
|
||||||
else
|
|
||||||
table.insert(nested_stack, depth + 1)
|
|
||||||
table.insert(nested_stack, value :: {})
|
|
||||||
end
|
|
||||||
elseif type(property) == "string" then
|
|
||||||
if strict then
|
if strict then
|
||||||
if nested_debug_cache[depth][property] then
|
if nested_debug_cache[depth][property] then
|
||||||
throw(`duplicate property {property} at depth {depth}`)
|
throw(`duplicate property {property} at depth {depth}`)
|
||||||
|
|
@ -58,7 +68,13 @@ local function process(instance: Instance, properties: { [unknown]: unknown })
|
||||||
nested_debug_cache[depth][property] = true
|
nested_debug_cache[depth][property] = true
|
||||||
end
|
end
|
||||||
|
|
||||||
if type(value) == "function" then
|
if type(value) == "table" then
|
||||||
|
local class = get_class((instance :: any)[property])
|
||||||
|
if class == nil then
|
||||||
|
throw(`cannot aggregate construct type {typeof(value)} for property {property}`)
|
||||||
|
end
|
||||||
|
(instance :: any)[property] = class.new(unpack(value :: {}))
|
||||||
|
elseif type(value) == "function" then
|
||||||
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
if typeof((instance :: any)[property]) == "RBXScriptSignal" then
|
||||||
event_buffer[property] = value :: () -> ()
|
event_buffer[property] = value :: () -> ()
|
||||||
else
|
else
|
||||||
|
|
@ -70,6 +86,13 @@ local function process(instance: Instance, properties: { [unknown]: unknown })
|
||||||
elseif type(property) == "number" then
|
elseif type(property) == "number" then
|
||||||
if type(value) == "function" then
|
if type(value) == "function" then
|
||||||
bind.children(instance, value :: () -> { Instance })
|
bind.children(instance, value :: () -> { Instance })
|
||||||
|
elseif type(value) == "table" then
|
||||||
|
if is_action(value) then
|
||||||
|
table.insert(action_buffers[(value :: any).priority], (value :: any).callback :: () -> ())
|
||||||
|
else
|
||||||
|
table.insert(nested_stack, depth + 1)
|
||||||
|
table.insert(nested_stack, value :: {})
|
||||||
|
end
|
||||||
else
|
else
|
||||||
(value :: Instance).Parent = instance
|
(value :: Instance).Parent = instance
|
||||||
end
|
end
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,16 @@
|
||||||
local Enum = Enum
|
local Enum = Enum
|
||||||
local Color3 = Color3
|
local Color3 = Color3
|
||||||
local Vector3 = Vector3
|
|
||||||
|
|
||||||
if not game then
|
if not game then
|
||||||
local mock = require "test/mock"
|
local mock = require "test/mock"
|
||||||
Enum = mock.Enum
|
Enum = mock.Enum
|
||||||
Color3 = mock.Color3
|
Color3 = mock.Color3
|
||||||
Vector3 = mock.Vector3
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Part = {
|
Part = {
|
||||||
Material = Enum.Material.SmoothPlastic,
|
Material = Enum.Material.SmoothPlastic,
|
||||||
Size = Vector3.new(1, 1, 1),
|
--Size = Vector3.new(1, 1, 1),
|
||||||
Anchored = true
|
Anchored = true
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
local function get_path(s)
|
local function get_path(s)
|
||||||
if string.sub(s, #s - 4, #s) == ".luau" then
|
if string.sub(s, #s - 4, #s) == ".luau" then
|
||||||
s = string.sub(s, 1, #s - 5)
|
s = string.sub(s, 1, #s - 5)
|
||||||
print("test, ", s)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
return string.split(s, string.match(s, "%w+/") and "/" or ".")
|
return string.split(s, string.match(s, "%w+/") and "/" or ".")
|
||||||
|
|
|
||||||
|
|
@ -325,4 +325,41 @@ BENCH("cleanup gc removal", function()
|
||||||
vide.step(0) -- cleanup from previous benchmark
|
vide.step(0) -- cleanup from previous benchmark
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
|
||||||
|
do
|
||||||
|
-- the purpose of the two following benchmarks is to measure the overhead of
|
||||||
|
-- aggregate construction
|
||||||
|
BENCH("set explicit mock vector2", function()
|
||||||
|
local create = vide.create
|
||||||
|
local apply = require "src/apply"
|
||||||
|
local Vector2 = require "test/mock".Vector2
|
||||||
|
|
||||||
|
local label = create "TextLabel" {
|
||||||
|
AnchorPoint = Vector2.new(1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, START(N) do
|
||||||
|
apply(label, {
|
||||||
|
AnchorPoint = Vector2.new(i, i)
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
BENCH("set aggregate mock vector2", function()
|
||||||
|
local create = vide.create
|
||||||
|
local apply = require "src/apply"
|
||||||
|
local Vector2 = require "test/mock".Vector2
|
||||||
|
|
||||||
|
local label = create "TextLabel" {
|
||||||
|
AnchorPoint = Vector2.new(1, 1)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i = 1, START(N) do
|
||||||
|
apply(label, {
|
||||||
|
AnchorPoint = { i, i }
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|
|
||||||
|
|
@ -217,9 +217,28 @@ local Instance = {} :: any do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local Color3 = {} :: any do
|
local function table_to_proxy(t: any)
|
||||||
|
local proxy = newproxy(true)
|
||||||
|
local mt = getmetatable(proxy)
|
||||||
|
|
||||||
|
for i, v in getmetatable(t) do
|
||||||
|
mt[i] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:__index(i)
|
||||||
|
return t[i]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mt:__newindex(i, v)
|
||||||
|
t[i] = v
|
||||||
|
end
|
||||||
|
|
||||||
|
return proxy
|
||||||
|
end
|
||||||
|
|
||||||
|
local Color3 = { __type = "Color3" } :: any do
|
||||||
function Color3.new(r, g, b)
|
function Color3.new(r, g, b)
|
||||||
return setmetatable({ r = r, g = g, b = b}, Color3)
|
return table_to_proxy(setmetatable({ r = r, g = g, b = b }, Color3))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Color3.__eq(a, b)
|
function Color3.__eq(a, b)
|
||||||
|
|
@ -227,19 +246,9 @@ local Color3 = {} :: any do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local Vector3 = {} :: any do
|
local Vector2 = { __type = "Vector2" } :: any do
|
||||||
function Vector3.new(x, y, z)
|
|
||||||
return setmetatable({ x = x, y = y, z = z}, Vector3)
|
|
||||||
end
|
|
||||||
|
|
||||||
function Vector3.__eq(a, b)
|
|
||||||
return a.x == b.x and a.y == b.y and a.z == b.z
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local Vector2 = {} :: any do
|
|
||||||
function Vector2.new(x, y)
|
function Vector2.new(x, y)
|
||||||
return setmetatable({ x = x, y = y }, Vector2)
|
return table_to_proxy(setmetatable({ x = x, y = y }, Vector2))
|
||||||
end
|
end
|
||||||
|
|
||||||
function Vector2.__eq(a, b)
|
function Vector2.__eq(a, b)
|
||||||
|
|
@ -247,9 +256,13 @@ local Vector2 = {} :: any do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local UDim2 = {} :: any do
|
local UDim2 = { __type = "UDim2" } :: any do
|
||||||
|
function UDim2.new(sx, ox, sy, oy)
|
||||||
|
return table_to_proxy(setmetatable({ x = { scale = sx, offset = ox }, y = { scale = sy, offset = oy } }, UDim2))
|
||||||
|
end
|
||||||
|
|
||||||
function UDim2.fromScale(x, y)
|
function UDim2.fromScale(x, y)
|
||||||
return setmetatable({ x = { scale = x, offset = 0 }, y = { scale = y, offset = 0 } }, UDim2)
|
return table_to_proxy(setmetatable({ x = { scale = x, offset = 0 }, y = { scale = y, offset = 0 } }, UDim2))
|
||||||
end
|
end
|
||||||
|
|
||||||
function UDim2.__eq(a, b)
|
function UDim2.__eq(a, b)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ local TEST, CASE, CHECK, FINISH = testkit.test()
|
||||||
|
|
||||||
local mock = require "test/mock"
|
local mock = require "test/mock"
|
||||||
local Instance, Signal = mock.Instance, mock.Signal
|
local Instance, Signal = mock.Instance, mock.Signal
|
||||||
|
local Vector2, UDim2 = mock.Vector2, mock.UDim2
|
||||||
|
|
||||||
local vide = require "src/init"
|
local vide = require "src/init"
|
||||||
|
|
||||||
|
|
@ -598,15 +599,32 @@ TEST("create()", function()
|
||||||
do CASE "set nested properties"
|
do CASE "set nested properties"
|
||||||
local text = create "TextLabel" {
|
local text = create "TextLabel" {
|
||||||
{ Name = "Label" },
|
{ Name = "Label" },
|
||||||
Group = { Text = "test" }
|
{ Text = "test" }
|
||||||
}
|
}
|
||||||
CHECK(text.Name == "Label")
|
CHECK(text.Name == "Label")
|
||||||
CHECK(text.Text == "test")
|
CHECK(text.Text == "test")
|
||||||
end
|
end
|
||||||
|
|
||||||
|
do CASE "aggregate construction"
|
||||||
|
local template = create "TextLabel" {
|
||||||
|
AnchorPoint = Vector2.new(),
|
||||||
|
Position = UDim2.new()
|
||||||
|
}
|
||||||
|
|
||||||
|
print("template", template.AnchorPoint)
|
||||||
|
|
||||||
|
local text = create(template) {
|
||||||
|
AnchorPoint = { 1, 2 },
|
||||||
|
Position = { 3, 4 }
|
||||||
|
}
|
||||||
|
|
||||||
|
CHECK(text.AnchorPoint == Vector2.new(1, 2))
|
||||||
|
CHECK(text.Position == UDim2.new(3, 4))
|
||||||
|
end
|
||||||
|
|
||||||
do CASE "nested precedence"
|
do CASE "nested precedence"
|
||||||
local text = create "TextLabel" {
|
local text = create "TextLabel" {
|
||||||
Group = {
|
{
|
||||||
Text = "1",
|
Text = "1",
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -634,7 +652,7 @@ TEST("create()", function()
|
||||||
create "TextLabel" { Name = "E" }
|
create "TextLabel" { Name = "E" }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Children = {
|
{
|
||||||
create "TextLabel" { Name = "F" } :: any,
|
create "TextLabel" { Name = "F" } :: any,
|
||||||
{ create "TextLabel" { Name = "G" } }
|
{ create "TextLabel" { Name = "G" } }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
todo.md
1
todo.md
|
|
@ -5,7 +5,6 @@
|
||||||
- better error reporting and stack traces in strict mode
|
- better error reporting and stack traces in strict mode
|
||||||
- auto-enable of strict mode depending on compiler optimizaton level
|
- auto-enable of strict mode depending on compiler optimizaton level
|
||||||
- check smoothness of spring at high frequency updates
|
- check smoothness of spring at high frequency updates
|
||||||
- aggregate initialization of properties
|
|
||||||
- implement from solid
|
- implement from solid
|
||||||
- [x] onCleanup > `cleanup()`
|
- [x] onCleanup > `cleanup()`
|
||||||
- [x] Index > `indexes()`
|
- [x] Index > `indexes()`
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue