Add nullptr_t to doc::UserData::Variant to fix bug when undoing an previously unexistent property

This commit is contained in:
David Capello 2023-01-06 14:36:51 -03:00
parent 8f09728105
commit d590d3f39a
10 changed files with 64 additions and 155 deletions

View File

@ -495,7 +495,6 @@ add_library(app-lib
cmd/remove_tag.cpp
cmd/remove_tile.cpp
cmd/remove_tileset.cpp
cmd/remove_user_data_property.cpp
cmd/replace_image.cpp
cmd/replace_tileset.cpp
cmd/reselect_mask.cpp

View File

@ -1,53 +0,0 @@
// Aseprite
// Copyright (C) 2023 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/cmd/remove_user_data_property.h"
#include "app/cmd.h"
#include "app/cmd/with_document.h"
#include "doc/object_id.h"
#include "doc/user_data.h"
#include "doc/with_user_data.h"
namespace app {
namespace cmd {
RemoveUserDataProperty::RemoveUserDataProperty(
doc::WithUserData* obj,
const std::string& group,
const std::string& field)
: m_objId(obj->id())
, m_group(group)
, m_field(field)
, m_oldVariant(obj->userData().properties(m_group)[m_field])
{
}
void RemoveUserDataProperty::onExecute()
{
auto obj = doc::get<doc::WithUserData>(m_objId);
auto& properties = obj->userData().properties(m_group);
auto it = properties.find(m_field);
ASSERT(it != properties.end());
if (it != properties.end()) {
properties.erase(it);
obj->incrementVersion();
}
}
void RemoveUserDataProperty::onUndo()
{
auto obj = doc::get<doc::WithUserData>(m_objId);
obj->userData().properties(m_group)[m_field] = m_oldVariant;
obj->incrementVersion();
}
} // namespace cmd
} // namespace app

View File

@ -1,46 +0,0 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_CMD_REMOVE_USER_DATA_PROPERTY_H_INCLUDED
#define APP_CMD_REMOVE_USER_DATA_PROPERTY_H_INCLUDED
#pragma once
#include "app/cmd.h"
#include "doc/object_id.h"
#include "doc/user_data.h"
namespace doc {
class WithUserData;
}
namespace app {
namespace cmd {
class RemoveUserDataProperty : public Cmd {
public:
RemoveUserDataProperty(
doc::WithUserData* obj,
const std::string& group,
const std::string& field);
protected:
void onExecute() override;
void onUndo() override;
size_t onMemSize() const override {
return sizeof(*this); // TODO + variants size
}
private:
doc::ObjectId m_objId;
std::string m_group;
std::string m_field;
doc::UserData::Variant m_oldVariant;
};
} // namespace cmd
} // namespace app
#endif

View File

@ -10,10 +10,6 @@
#include "app/cmd/set_user_data_properties.h"
#include "app/cmd.h"
#include "app/cmd/with_document.h"
#include "doc/object_id.h"
#include "doc/user_data.h"
#include "doc/with_user_data.h"
namespace app {

View File

@ -10,10 +10,6 @@
#include "app/cmd/set_user_data_property.h"
#include "app/cmd.h"
#include "app/cmd/with_document.h"
#include "doc/object_id.h"
#include "doc/user_data.h"
#include "doc/with_user_data.h"
namespace app {
@ -35,14 +31,34 @@ SetUserDataProperty::SetUserDataProperty(
void SetUserDataProperty::onExecute()
{
auto obj = doc::get<doc::WithUserData>(m_objId);
obj->userData().properties(m_group)[m_field] = m_newValue;
auto& properties = obj->userData().properties(m_group);
if (m_newValue.type() == USER_DATA_PROPERTY_TYPE_NULLPTR) {
auto it = properties.find(m_field);
if (it != properties.end())
properties.erase(it);
}
else {
properties[m_field] = m_newValue;
}
obj->incrementVersion();
}
void SetUserDataProperty::onUndo()
{
auto obj = doc::get<doc::WithUserData>(m_objId);
obj->userData().properties(m_group)[m_field] = m_oldValue;
auto& properties = obj->userData().properties(m_group);
if (m_oldValue.type() == USER_DATA_PROPERTY_TYPE_NULLPTR) {
auto it = properties.find(m_field);
if (it != properties.end())
properties.erase(it);
}
else {
properties[m_field] = m_oldValue;
}
obj->incrementVersion();
}

View File

@ -8,7 +8,6 @@
#include "config.h"
#endif
#include "app/cmd/remove_user_data_property.h"
#include "app/cmd/set_user_data_properties.h"
#include "app/cmd/set_user_data_property.h"
#include "app/script/docobj.h"
@ -84,46 +83,18 @@ int Properties_newindex(lua_State* L)
return luaL_error(L, "the object with these properties was destroyed");
auto& properties = obj->userData().properties(propObj->extID);
auto newValue = get_value_from_lua<doc::UserData::Variant>(L, 3);
switch (lua_type(L, 3)) {
case LUA_TNONE:
case LUA_TNIL: {
// If we assign nil to a property, we just remove the property.
auto it = properties.find(field);
if (it != properties.end()) {
// TODO add Object::sprite() member function, and fix "Tx" object
// to use the sprite of this object instead of the activeDocument()
//if (obj->sprite()) {
if (App::instance()->context()->activeDocument()) {
Tx tx;
tx(new cmd::RemoveUserDataProperty(obj, propObj->extID, field));
tx.commit();
}
else {
properties.erase(it);
}
}
break;
}
default: {
auto newValue = get_value_from_lua<doc::UserData::Variant>(L, 3);
// TODO add Object::sprite() member function
//if (obj->sprite()) {
if (App::instance()->context()->activeDocument()) {
Tx tx;
tx(new cmd::SetUserDataProperty(obj, propObj->extID, field,
std::move(newValue)));
tx.commit();
}
else {
properties[field] = std::move(newValue);
}
break;
}
// TODO add Object::sprite() member function
//if (obj->sprite()) {
if (App::instance()->context()->activeDocument()) {
Tx tx;
tx(new cmd::SetUserDataProperty(obj, propObj->extID, field,
std::move(newValue)));
tx.commit();
}
else {
properties[field] = std::move(newValue);
}
return 0;
}

View File

@ -23,6 +23,15 @@ namespace script {
// TODO this is similar to app::Param<> specializations::fromLua() specializations
// ----------------------------------------------------------------------
// nullptr_t
template<>
void push_value_to_lua(lua_State* L, const nullptr_t&) {
TRACEARGS("push_value_to_lua nullptr_t");
lua_pushnil(L);
}
// ----------------------------------------------------------------------
// bool
@ -305,6 +314,9 @@ 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_NULLPTR:
push_value_to_lua<nullptr_t>(L, nullptr);
break;
case USER_DATA_PROPERTY_TYPE_BOOL:
push_value_to_lua(L, *std::get_if<bool>(&value));
break;
@ -374,7 +386,7 @@ doc::UserData::Variant get_value_from_lua(lua_State* L, int index)
case LUA_TNONE:
case LUA_TNIL:
// TODO should we add nullptr_t in Variant?
v = nullptr;
break;
case LUA_TBOOLEAN:

View File

@ -1,5 +1,5 @@
// Aseprite Document IO Library
// Copyright (c) 2018-2022 Igara Studio S.A.
// Copyright (c) 2018-2023 Igara Studio S.A.
// Copyright (c) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -1270,6 +1270,11 @@ void AsepriteDecoder::readPropertiesMaps(doc::UserData::PropertiesMaps& properti
const doc::UserData::Variant AsepriteDecoder::readPropertyValue(uint16_t type)
{
switch (type) {
case USER_DATA_PROPERTY_TYPE_NULLPTR: {
// This shouldn't exist in a .aseprite file
ASSERT(false);
return nullptr;
}
case USER_DATA_PROPERTY_TYPE_BOOL: {
bool value = read8();
return value;

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2022 Igara Studio S.A.
// Copyright (c) 2022-2023 Igara Studio S.A.
// Copyright (c) 2001-2015 David Capello
//
// This file is released under the terms of the MIT license.
@ -15,12 +15,14 @@
#include "gfx/size.h"
#include "gfx/rect.h"
#include <cstddef>
#include <map>
#include <stdexcept>
#include <string>
#include <variant>
#include <vector>
#define USER_DATA_PROPERTY_TYPE_NULLPTR 0x0000
#define USER_DATA_PROPERTY_TYPE_BOOL 0x0001
#define USER_DATA_PROPERTY_TYPE_INT8 0x0002
#define USER_DATA_PROPERTY_TYPE_UINT8 0x0003
@ -55,7 +57,8 @@ namespace doc {
using Vector = std::vector<Variant>;
using Properties = std::map<std::string, Variant>;
using PropertiesMaps = std::map<std::string, Properties>;
using VariantBase = std::variant<bool,
using VariantBase = std::variant<std::nullptr_t,
bool,
int8_t, uint8_t,
int16_t, uint16_t,
int32_t, uint32_t,
@ -87,7 +90,7 @@ namespace doc {
}
const uint16_t type() const {
return index() + 1;
return index();
}
};

View File

@ -33,6 +33,12 @@ do
spr.properties.b = nil
assert(spr.properties.b == nil)
assert(#spr.properties == 3)
-- We set an unexistent property now, undo it, the property shouldn't exist
spr.properties.b = 5
assert(spr.properties.b == 5)
assert(#spr.properties == 4)
app.undo()
assert(#spr.properties == 3)
spr.properties.v = { 10, 20, 30 }
assert(#spr.properties.v == 3)