[lua] Add access to user data properties in Sprite object (aseprite/api#88)

This is a basic implementation where we can only access basic
properties (not maps or vectors yet).
This commit is contained in:
David Capello 2022-12-30 14:51:43 -03:00
parent c7864c9fac
commit 76a398b162
8 changed files with 208 additions and 4 deletions

View File

@ -191,6 +191,7 @@ if(ENABLE_SCRIPTING)
script/plugin_class.cpp
script/point_class.cpp
script/preferences_object.cpp
script/properties_class.cpp
script/range_class.cpp
script/rectangle_class.cpp
script/security.cpp

View File

@ -176,6 +176,7 @@ void register_palette_class(lua_State* L);
void register_palettes_class(lua_State* L);
void register_plugin_class(lua_State* L);
void register_point_class(lua_State* L);
void register_properties_class(lua_State* L);
void register_range_class(lua_State* L);
void register_rect_class(lua_State* L);
void register_selection_class(lua_State* L);
@ -454,6 +455,7 @@ Engine::Engine()
register_palettes_class(L);
register_plugin_class(L);
register_point_class(L);
register_properties_class(L);
register_range_class(L);
register_rect_class(L);
register_selection_class(L);

View File

@ -150,6 +150,7 @@ namespace app {
void push_layers(lua_State* L, const doc::ObjectIds& layers);
void push_palette(lua_State* L, doc::Palette* palette);
void push_plugin(lua_State* L, Extension* ext);
void push_properties(lua_State* L, doc::WithUserData* userData);
void push_sprite_cel(lua_State* L, doc::Cel* cel);
void push_sprite_events(lua_State* L, doc::Sprite* sprite);
void push_sprite_frame(lua_State* L, doc::Sprite* sprite, doc::frame_t frame);
@ -166,7 +167,6 @@ namespace app {
void push_tileset_image(lua_State* L, doc::Tileset* tileset, doc::Image* image);
void push_tilesets(lua_State* L, doc::Tilesets* tilesets);
void push_tool(lua_State* L, app::tools::Tool* tool);
void push_userdata(lua_State* L, doc::WithUserData* userData);
void push_version(lua_State* L, const base::Version& ver);
gfx::Point convert_args_into_point(lua_State* L, int index);

View File

@ -0,0 +1,128 @@
// Aseprite
// Copyright (C) 2022 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "app/script/docobj.h"
#include "app/script/engine.h"
#include "app/script/luacpp.h"
#include "app/script/values.h"
#include "doc/with_user_data.h"
#include <cstring>
namespace app {
namespace script {
namespace {
struct Properties {
doc::ObjectId id = 0;
Properties(doc::ObjectId id) : id(id) { }
};
int Properties_index(lua_State* L)
{
auto propObj = get_obj<Properties>(L, 1);
const char* field = lua_tostring(L, 2);
if (!field)
return luaL_error(L, "field in 'properties.field' must be a string");
auto obj = static_cast<doc::WithUserData*>(get_object(propObj->id));
if (!obj)
return luaL_error(L, "the object with these properties was destroyed");
auto& properties = obj->userData().properties();
auto it = properties.find(field);
if (it != properties.end()) {
push_value_to_lua(L, (*it).second);
}
else {
lua_pushnil(L);
}
return 1;
}
int Properties_newindex(lua_State* L)
{
auto propObj = get_obj<Properties>(L, 1);
const char* field = lua_tostring(L, 2);
if (!field)
return luaL_error(L, "field in 'properties.field' must be a string");
auto obj = static_cast<doc::WithUserData*>(get_object(propObj->id));
if (!obj)
return luaL_error(L, "the object with these properties was destroyed");
auto& properties = obj->userData().properties();
// TODO add undo information
switch (lua_type(L, 3)) {
case LUA_TNONE:
case LUA_TNIL:
default: {
// Just erase the property
auto it = properties.find(field);
if (it != properties.end())
properties.erase(it);
break;
}
case LUA_TBOOLEAN:
properties[field] = (lua_toboolean(L, 3) ? true: false);
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] = 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;
}
const luaL_Reg Properties_methods[] = {
{ "__index", Properties_index },
{ "__newindex", Properties_newindex },
{ nullptr, nullptr }
};
} // anonymous namespace
DEF_MTNAME(Properties);
void register_properties_class(lua_State* L)
{
REG_CLASS(L, Properties);
}
void push_properties(lua_State* L, doc::WithUserData* userData)
{
push_obj<Properties>(L, userData->id());
}
} // namespace script
} // namespace app

View File

@ -882,6 +882,7 @@ const Property Sprite_properties[] = {
{ "gridBounds", Sprite_get_gridBounds, Sprite_set_gridBounds },
{ "color", UserData_get_color<Sprite>, UserData_set_color<Sprite> },
{ "data", UserData_get_text<Sprite>, UserData_set_text<Sprite> },
{ "properties", UserData_get_properties<Sprite>, nullptr },
{ "pixelRatio", Sprite_get_pixelRatio, Sprite_set_pixelRatio },
{ "events", Sprite_get_events, nullptr },
{ nullptr, nullptr, nullptr }

View File

@ -51,6 +51,13 @@ int UserData_get_color(lua_State* L) {
return 1;
}
template<typename T>
int UserData_get_properties(lua_State* L) {
auto obj = get_docobj<T>(L, 1);
push_properties(L, get_WithUserData<T>(obj));
return 1;
}
template<typename T>
int UserData_set_text(lua_State* L) {
auto obj = get_docobj<T>(L, 1);

View File

@ -16,6 +16,7 @@
#include "doc/remap.h"
#include <any>
#include <variant>
namespace app {
namespace script {
@ -39,9 +40,21 @@ bool get_value_from_lua(lua_State* L, int index) {
// int
template<>
void push_value_to_lua(lua_State* L, const int& value) {
lua_pushinteger(L, value);
}
void push_value_to_lua(lua_State* L, const int8_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const int16_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const int32_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const int64_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const uint8_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const uint16_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const uint32_t& value) { lua_pushinteger(L, value); }
template<>
void push_value_to_lua(lua_State* L, const uint64_t& value) { lua_pushinteger(L, value); }
template<>
int get_value_from_lua(lua_State* L, int index) {
@ -61,6 +74,14 @@ double get_value_from_lua(lua_State* L, int index) {
return lua_tonumber(L, index);
}
// ----------------------------------------------------------------------
// fixed
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Fixed& value) {
lua_pushnumber(L, fixmath::fixtof(value.value));
}
// ----------------------------------------------------------------------
// std::string
@ -193,10 +214,12 @@ app::tools::InkType get_value_from_lua(lua_State* L, int index) {
// ----------------------------------------------------------------------
// doc::tile_t
#if 0 // doc::tile_t matches uint32_t, and we have the uint32_t version already defined
template<>
void push_value_to_lua(lua_State* L, const doc::tile_t& value) {
lua_pushinteger(L, value);
}
#endif
template<>
doc::tile_t get_value_from_lua(lua_State* L, int index) {
@ -251,5 +274,23 @@ FOR_ENUM(filters::HueSaturationFilter::Mode)
FOR_ENUM(filters::TiledMode)
FOR_ENUM(render::OnionskinPosition)
// ----------------------------------------------------------------------
// UserData::Properties / VariantStruct
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Properties& value) {
// TODO convert a Properties map into a Lua table
}
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Vector& value) {
// TODO convert a Vector into a Lua table
}
template<>
void push_value_to_lua(lua_State* L, const doc::UserData::Variant& value) {
std::visit([L](auto&& v){ push_value_to_lua(L, v); }, value);
}
} // namespace script
} // namespace app

View File

@ -0,0 +1,24 @@
-- Copyright (C) 2022 Igara Studio S.A.
--
-- This file is released under the terms of the MIT license.
-- Read LICENSE.txt for more information.
do
local spr = Sprite(1, 1)
spr.properties.a = true
spr.properties.b = 1
spr.properties.c = "hi"
spr.properties.d = 2.3
assert(spr.properties.a == true)
assert(spr.properties.b == 1)
assert(spr.properties.c == "hi")
-- TODO we don't have too much precision saving fixed points in properties
assert(math.abs(spr.properties.d - 2.3) < 0.00001)
spr.properties.a = false
assert(spr.properties.a == false)
spr.properties.b = nil
assert(spr.properties.b == nil)
end