[lua] Add support to set/get tables (arrays/maps) into user properties

This commit is contained in:
David Capello 2023-01-02 19:51:07 -03:00
parent 11dbb22efa
commit 9138592e98
3 changed files with 197 additions and 38 deletions

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2022 Igara Studio S.A.
// Copyright (C) 2022-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -79,8 +79,7 @@ int Properties_newindex(lua_State* L)
switch (lua_type(L, 3)) {
case LUA_TNONE:
case LUA_TNIL:
default: {
case LUA_TNIL: {
// Just erase the property
auto it = properties.find(field);
if (it != properties.end())
@ -88,32 +87,9 @@ int Properties_newindex(lua_State* L)
break;
}
case LUA_TBOOLEAN:
properties[field] = (lua_toboolean(L, 3) ? true: false);
default:
properties[field] = get_value_from_lua<doc::UserData::Variant>(L, 3);
break;
case LUA_TNUMBER:
if (lua_isinteger(L, 3))
properties[field] = lua_tointeger(L, 3);
else {
properties[field] = doc::UserData::Fixed{
fixmath::ftofix(lua_tonumber(L, 3))
};
}
break;
case LUA_TSTRING:
properties[field] = std::string(lua_tostring(L, 3));
break;
case LUA_TTABLE:
// TODO convert a full table into properties recursively
break;
case LUA_TUSERDATA:
// TODO convert table-like objects (Size, Point, Rectangle, etc.)
break;
}
return 1;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -275,20 +275,21 @@ FOR_ENUM(filters::TiledMode)
FOR_ENUM(render::OnionskinPosition)
// ----------------------------------------------------------------------
// UserData::Properties / VariantStruct
// UserData::Properties / Variant
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Properties& value) {
// TODO convert a Properties map into a Lua table
}
void push_value_to_lua(lua_State* L, const doc::UserData::Properties& value);
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Vector& value);
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Vector& value) {
// TODO convert a Vector into a Lua table
}
doc::UserData::Properties get_value_from_lua(lua_State* L, int index);
template<>
doc::UserData::Vector get_value_from_lua(lua_State* L, int index);
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Variant& value) {
void push_value_to_lua(lua_State* L, const doc::UserData::Variant& value)
{
#if 1 // We are targetting macOS 10.9, so we don't have the std::visit() available
switch (value.type()) {
case USER_DATA_PROPERTY_TYPE_BOOL:
@ -345,5 +346,147 @@ void push_value_to_lua(lua_State* L, const doc::UserData::Variant& value) {
#endif
}
template<>
doc::UserData::Variant get_value_from_lua(lua_State* L, int index)
{
doc::UserData::Variant v;
switch (lua_type(L, index)) {
case LUA_TNONE:
case LUA_TNIL:
// TODO should we add nullptr_t in Variant?
break;
case LUA_TBOOLEAN:
v = (lua_toboolean(L, index) ? true: false);
break;
case LUA_TNUMBER:
if (lua_isinteger(L, index))
v = lua_tointeger(L, index);
else {
v = doc::UserData::Fixed{
fixmath::ftofix(lua_tonumber(L, index))
};
}
break;
case LUA_TSTRING:
v = std::string(lua_tostring(L, index));
break;
case LUA_TTABLE: {
int i = 0;
bool isArray = true;
if (index < 0)
--index;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
if (lua_isinteger(L, -2)) {
// TODO we should check that all values are of the same type
// to create the vector
if (++i != lua_tointeger(L, -2)) {
isArray = false;
lua_pop(L, 2); // Pop value and key
break;
}
}
else {
isArray = false;
lua_pop(L, 2);
break;
}
lua_pop(L, 1); // Pop the value, leave the key for lua_next()
}
if (index < 0)
++index;
if (isArray) {
v = get_value_from_lua<doc::UserData::Vector>(L, index);
}
else {
v = get_value_from_lua<doc::UserData::Properties>(L, index);
}
break;
}
case LUA_TUSERDATA: {
if (auto rect = may_get_obj<gfx::Rect>(L, index)) {
v = *rect;
}
else if (auto pt = may_get_obj<gfx::Point>(L, index)) {
v = *pt;
}
else if (auto sz = may_get_obj<gfx::Size>(L, index)) {
v = *sz;
}
break;
}
}
return v;
}
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Properties& value)
{
lua_newtable(L);
for (const auto& kv : value) {
push_value_to_lua(L, kv.second);
lua_setfield(L, -2, kv.first.c_str());
}
}
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Vector& value)
{
int i = 0;
lua_newtable(L);
for (const auto& kv : value) {
push_value_to_lua(L, kv);
lua_seti(L, -2, ++i);
}
}
template<>
doc::UserData::Properties get_value_from_lua(lua_State* L, int index)
{
doc::UserData::Properties m;
if (index < 0)
--index;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
if (auto k = lua_tostring(L, -2))
m[k] = get_value_from_lua<doc::UserData::Variant>(L, -1);
lua_pop(L, 1);
}
return m;
}
template<>
doc::UserData::Vector get_value_from_lua(lua_State* L, int index)
{
doc::UserData::Vector v;
lua_len(L, index);
int len = lua_tointeger(L, -1);
lua_pop(L, 1);
if (len > 0)
v.reserve(len);
if (index < 0)
--index;
lua_pushnil(L);
while (lua_next(L, index) != 0) {
// TODO we should check that all variants are of the same type
v.push_back(get_value_from_lua<doc::UserData::Variant>(L, -1));
lua_pop(L, 1);
}
return v;
}
} // namespace script
} // namespace app

View File

@ -1,4 +1,4 @@
-- Copyright (C) 2022 Igara Studio S.A.
-- Copyright (C) 2022-2023 Igara Studio S.A.
--
-- This file is released under the terms of the MIT license.
-- Read LICENSE.txt for more information.
@ -32,4 +32,44 @@ do
spr.properties.b = nil
assert(spr.properties.b == nil)
assert(#spr.properties == 3)
spr.properties.v = { 10, 20, 30 }
assert(#spr.properties.v == 3)
assert(spr.properties.v[1] == 10)
assert(spr.properties.v[2] == 20)
assert(spr.properties.v[3] == 30)
spr.properties.u = spr.properties.v -- Copy a property
assert(#spr.properties.u == 3)
assert(spr.properties.u[1] == 10)
assert(spr.properties.u[2] == 20)
assert(spr.properties.u[3] == 30)
spr.properties.m = { a=10,
b="bye",
c={ "a", "b", "c" },
d={ a=1, b="d", c={ x=2, y=0.5, z=0 } },
e=Point(32, 20),
f=Size(40, 80),
g=Rectangle(2, 4, 6, 8) }
local m = spr.properties.m
assert(m.a == 10)
assert(m.b == "bye")
assert(m.c[1] == "a")
assert(m.c[2] == "b")
assert(m.c[3] == "c")
assert(m.d.a == 1)
assert(m.d.b == "d")
assert(m.d.c.x == 2)
assert(m.d.c.y == 0.5)
assert(m.d.c.z == 0)
assert(m.e.x == 32)
assert(m.e.y == 20)
assert(m.f.width == 40)
assert(m.f.height == 80)
assert(m.g.x == 2)
assert(m.g.y == 4)
assert(m.g.width == 6)
assert(m.g.height == 8)
end