mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-30 04:20:23 +00:00
[lua] Add undo information when we modify user data properties
This commit is contained in:
parent
e2024c6edd
commit
645605305f
@ -1,5 +1,5 @@
|
||||
# Aseprite
|
||||
# Copyright (C) 2018-2022 Igara Studio S.A.
|
||||
# Copyright (C) 2018-2023 Igara Studio S.A.
|
||||
# Copyright (C) 2001-2018 David Capello
|
||||
|
||||
# Generate a ui::Widget for each widget in a XML file
|
||||
@ -495,6 +495,7 @@ 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
|
||||
@ -528,6 +529,8 @@ add_library(app-lib
|
||||
cmd/set_total_frames.cpp
|
||||
cmd/set_transparent_color.cpp
|
||||
cmd/set_user_data.cpp
|
||||
cmd/set_user_data_properties.cpp
|
||||
cmd/set_user_data_property.cpp
|
||||
cmd/shift_masked_cel.cpp
|
||||
cmd/trim_cel.cpp
|
||||
cmd/unlink_cel.cpp
|
||||
|
53
src/app/cmd/remove_user_data_property.cpp
Normal file
53
src/app/cmd/remove_user_data_property.cpp
Normal file
@ -0,0 +1,53 @@
|
||||
// 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
|
46
src/app/cmd/remove_user_data_property.h
Normal file
46
src/app/cmd/remove_user_data_property.h
Normal file
@ -0,0 +1,46 @@
|
||||
// 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
|
48
src/app/cmd/set_user_data_properties.cpp
Normal file
48
src/app/cmd/set_user_data_properties.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
// 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/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 {
|
||||
namespace cmd {
|
||||
|
||||
SetUserDataProperties::SetUserDataProperties(
|
||||
doc::WithUserData* obj,
|
||||
const std::string& group,
|
||||
doc::UserData::Properties&& newProperties)
|
||||
: m_objId(obj->id())
|
||||
, m_group(group)
|
||||
, m_oldProperties(obj->userData().properties(group))
|
||||
, m_newProperties(std::move(newProperties))
|
||||
{
|
||||
}
|
||||
|
||||
void SetUserDataProperties::onExecute()
|
||||
{
|
||||
auto obj = doc::get<doc::WithUserData>(m_objId);
|
||||
obj->userData().properties(m_group) = m_newProperties;
|
||||
obj->incrementVersion();
|
||||
}
|
||||
|
||||
void SetUserDataProperties::onUndo()
|
||||
{
|
||||
auto obj = doc::get<doc::WithUserData>(m_objId);
|
||||
obj->userData().properties(m_group) = m_oldProperties;
|
||||
obj->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
46
src/app/cmd/set_user_data_properties.h
Normal file
46
src/app/cmd/set_user_data_properties.h
Normal file
@ -0,0 +1,46 @@
|
||||
// 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_SET_USER_DATA_PROPERTIES_H_INCLUDED
|
||||
#define APP_CMD_SET_USER_DATA_PROPERTIES_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 SetUserDataProperties : public Cmd {
|
||||
public:
|
||||
SetUserDataProperties(
|
||||
doc::WithUserData* obj,
|
||||
const std::string& group,
|
||||
doc::UserData::Properties&& newProperties);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this); // TODO + properties size
|
||||
}
|
||||
|
||||
private:
|
||||
doc::ObjectId m_objId;
|
||||
std::string m_group;
|
||||
doc::UserData::Properties m_oldProperties;
|
||||
doc::UserData::Properties m_newProperties;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
50
src/app/cmd/set_user_data_property.cpp
Normal file
50
src/app/cmd/set_user_data_property.cpp
Normal file
@ -0,0 +1,50 @@
|
||||
// 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/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 {
|
||||
namespace cmd {
|
||||
|
||||
SetUserDataProperty::SetUserDataProperty(
|
||||
doc::WithUserData* obj,
|
||||
const std::string& group,
|
||||
const std::string& field,
|
||||
doc::UserData::Variant&& newValue)
|
||||
: m_objId(obj->id())
|
||||
, m_group(group)
|
||||
, m_field(field)
|
||||
, m_oldValue(obj->userData().properties(group)[m_field])
|
||||
, m_newValue(std::move(newValue))
|
||||
{
|
||||
}
|
||||
|
||||
void SetUserDataProperty::onExecute()
|
||||
{
|
||||
auto obj = doc::get<doc::WithUserData>(m_objId);
|
||||
obj->userData().properties(m_group)[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;
|
||||
obj->incrementVersion();
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
48
src/app/cmd/set_user_data_property.h
Normal file
48
src/app/cmd/set_user_data_property.h
Normal file
@ -0,0 +1,48 @@
|
||||
// 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_SET_USER_DATA_PROPERTY_H_INCLUDED
|
||||
#define APP_CMD_SET_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 SetUserDataProperty : public Cmd {
|
||||
public:
|
||||
SetUserDataProperty(
|
||||
doc::WithUserData* obj,
|
||||
const std::string& group,
|
||||
const std::string& field,
|
||||
doc::UserData::Variant&& newValue);
|
||||
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this); // TODO + variant size
|
||||
}
|
||||
|
||||
private:
|
||||
doc::ObjectId m_objId;
|
||||
std::string m_group;
|
||||
std::string m_field;
|
||||
doc::UserData::Variant m_oldValue;
|
||||
doc::UserData::Variant m_newValue;
|
||||
};
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
||||
#endif
|
@ -8,10 +8,14 @@
|
||||
#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"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
#include "app/script/values.h"
|
||||
#include "app/tx.h"
|
||||
#include "doc/with_user_data.h"
|
||||
|
||||
#include <cstring>
|
||||
@ -81,21 +85,45 @@ int Properties_newindex(lua_State* L)
|
||||
|
||||
auto& properties = obj->userData().properties(propObj->extID);
|
||||
|
||||
// TODO add undo information
|
||||
switch (lua_type(L, 3)) {
|
||||
|
||||
case LUA_TNONE:
|
||||
case LUA_TNIL: {
|
||||
// Just erase the property
|
||||
// If we assign nil to a property, we just remove the property.
|
||||
|
||||
auto it = properties.find(field);
|
||||
if (it != properties.end())
|
||||
properties.erase(it);
|
||||
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:
|
||||
properties[field] = get_value_from_lua<doc::UserData::Variant>(L, 3);
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -112,12 +140,23 @@ int Properties_call(lua_State* L)
|
||||
// object.property("extension", { ...})
|
||||
//
|
||||
if (lua_istable(L, 3)) {
|
||||
auto newProperties = get_value_from_lua<doc::UserData::Properties>(L, 3);
|
||||
|
||||
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(extID);
|
||||
properties = get_value_from_lua<doc::UserData::Properties>(L, 3);
|
||||
// TODO add Object::sprite() member function
|
||||
//if (obj->sprite()) {
|
||||
if (App::instance()->context()->activeDocument()) {
|
||||
Tx tx;
|
||||
tx(new cmd::SetUserDataProperties(obj, extID, std::move(newProperties)));
|
||||
tx.commit();
|
||||
}
|
||||
else {
|
||||
auto& properties = obj->userData().properties(extID);
|
||||
properties = std::move(newProperties);
|
||||
}
|
||||
}
|
||||
|
||||
push_new<Properties>(L, propObj->id, extID);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#pragma once
|
||||
|
||||
#include "app/cmd/set_user_data.h"
|
||||
#include "app/cmd/set_user_data_properties.h"
|
||||
#include "app/color.h"
|
||||
#include "app/color_utils.h"
|
||||
#include "app/script/luacpp.h"
|
||||
@ -59,15 +60,6 @@ int UserData_get_properties(lua_State* L) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int UserData_set_properties(lua_State* L) {
|
||||
auto obj = get_docobj<T>(L, 1);
|
||||
auto& properties = get_WithUserData<T>(obj)->userData().properties();
|
||||
// TODO add undo information
|
||||
properties = get_value_from_lua<doc::UserData::Properties>(L, 2);
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int UserData_set_text(lua_State* L) {
|
||||
auto obj = get_docobj<T>(L, 1);
|
||||
@ -106,6 +98,25 @@ int UserData_set_color(lua_State* L) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
int UserData_set_properties(lua_State* L) {
|
||||
auto obj = get_docobj<T>(L, 1);
|
||||
auto wud = get_WithUserData<T>(obj);
|
||||
auto newProperties = get_value_from_lua<doc::UserData::Properties>(L, 2);
|
||||
if (obj->sprite()) {
|
||||
Tx tx;
|
||||
tx(new cmd::SetUserDataProperties(wud,
|
||||
std::string(),
|
||||
std::move(newProperties)));
|
||||
tx.commit();
|
||||
}
|
||||
else {
|
||||
auto& properties = wud->userData().properties();
|
||||
properties = std::move(newProperties);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
||||
|
||||
|
@ -78,6 +78,12 @@ do
|
||||
spr.properties = { a=1000 }
|
||||
assert(spr.properties.a == 1000)
|
||||
assert(#spr.properties == 1)
|
||||
app.undo() -- Test undo/redo
|
||||
assert(spr.properties.a == false)
|
||||
assert(#spr.properties == 6)
|
||||
app.redo()
|
||||
assert(spr.properties.a == 1000)
|
||||
assert(#spr.properties == 1)
|
||||
|
||||
-- Extension properties
|
||||
spr.properties.a = 10
|
||||
@ -88,6 +94,10 @@ do
|
||||
spr.properties("ext1", { a=30, b=35 })
|
||||
assert(spr.properties("ext1").a == 30)
|
||||
assert(spr.properties("ext1").b == 35)
|
||||
app.undo() -- Test undo/redo
|
||||
assert(spr.properties("ext1").a == 20)
|
||||
assert(spr.properties("ext1").b == nil)
|
||||
app.redo()
|
||||
|
||||
local ext1 = spr.properties("ext1")
|
||||
ext1.a = 40
|
||||
@ -98,4 +108,24 @@ do
|
||||
spr.properties("", { a=50, b=60 }) -- Empty extension is the user properties
|
||||
assert(spr.properties.a == 50)
|
||||
assert(spr.properties.b == 60)
|
||||
|
||||
-- Test undo/redo setting/removing one property at a time
|
||||
spr.properties.a = "hi"
|
||||
assert(spr.properties.a == "hi")
|
||||
assert(spr.properties.b == 60)
|
||||
app.undo()
|
||||
assert(spr.properties.a == 50)
|
||||
assert(spr.properties.b == 60)
|
||||
app.redo()
|
||||
assert(spr.properties.a == "hi")
|
||||
assert(spr.properties.b == 60)
|
||||
assert(#spr.properties == 2)
|
||||
spr.properties.a = nil
|
||||
assert(#spr.properties == 1)
|
||||
assert(spr.properties.a == nil)
|
||||
assert(spr.properties.b == 60)
|
||||
app.undo()
|
||||
assert(#spr.properties == 2)
|
||||
assert(spr.properties.a == "hi")
|
||||
assert(spr.properties.b == 60)
|
||||
end
|
||||
|
Loading…
x
Reference in New Issue
Block a user