mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
[lua] Add events handling with Sprite.events & App.events
Added a new Events object with :on() and :off() methods to start or stop listening to a specific event respectively. This also allows to add several callbacks for the same event. Replaced the temporal Site.onChange & Sprite.onChange implementations. Related to several issues (enable more possibilities for): #138, #1403, #1949, #2965, #2980
This commit is contained in:
parent
0249275f8c
commit
cd342f5630
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit f58ece8248e2ebf6124b28ec49044be913f57292
|
||||
Subproject commit c9bb18ba92915f0d9735da25360e4725f767b447
|
@ -163,6 +163,7 @@ if(ENABLE_SCRIPTING)
|
||||
script/color_space_class.cpp
|
||||
script/dialog_class.cpp
|
||||
script/engine.cpp
|
||||
script/events_class.cpp
|
||||
script/frame_class.cpp
|
||||
script/frames_class.cpp
|
||||
script/image_class.cpp
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
@ -28,10 +28,11 @@ ActiveSiteHandler::~ActiveSiteHandler()
|
||||
void ActiveSiteHandler::addDoc(Doc* doc)
|
||||
{
|
||||
Data data;
|
||||
if (doc::Layer* layer = doc->sprite()->root()->firstLayer())
|
||||
data.layer = layer->id();
|
||||
else
|
||||
data.layer = doc::NullId;
|
||||
data.layer = doc::NullId;
|
||||
if (doc->sprite()) { // The sprite can be nullptr in some tests
|
||||
if (doc::Layer* layer = doc->sprite()->root()->firstLayer())
|
||||
data.layer = layer->id();
|
||||
}
|
||||
data.frame = 0;
|
||||
m_data.insert(std::make_pair(doc, data));
|
||||
doc->add_observer(this);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -81,7 +81,7 @@ Doc* Context::activeDocument() const
|
||||
|
||||
void Context::setActiveDocument(Doc* document)
|
||||
{
|
||||
onSetActiveDocument(document);
|
||||
onSetActiveDocument(document, true);
|
||||
}
|
||||
|
||||
void Context::setActiveLayer(doc::Layer* layer)
|
||||
@ -206,15 +206,19 @@ void Context::onAddDocument(Doc* doc)
|
||||
|
||||
if (m_activeSiteHandler)
|
||||
m_activeSiteHandler->addDoc(doc);
|
||||
|
||||
notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
void Context::onRemoveDocument(Doc* doc)
|
||||
{
|
||||
if (doc == m_lastSelectedDoc)
|
||||
m_lastSelectedDoc = nullptr;
|
||||
|
||||
if (m_activeSiteHandler)
|
||||
m_activeSiteHandler->removeDoc(doc);
|
||||
|
||||
if (doc == m_lastSelectedDoc) {
|
||||
m_lastSelectedDoc = nullptr;
|
||||
notifyActiveSiteChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void Context::onGetActiveSite(Site* site) const
|
||||
@ -224,24 +228,33 @@ void Context::onGetActiveSite(Site* site) const
|
||||
activeSiteHandler()->getActiveSiteForDoc(doc, site);
|
||||
}
|
||||
|
||||
void Context::onSetActiveDocument(Doc* doc)
|
||||
void Context::onSetActiveDocument(Doc* doc, bool notify)
|
||||
{
|
||||
m_lastSelectedDoc = doc;
|
||||
if (notify)
|
||||
notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
void Context::onSetActiveLayer(doc::Layer* layer)
|
||||
{
|
||||
Doc* newDoc = (layer ? static_cast<Doc*>(layer->sprite()->document()): nullptr);
|
||||
if (!newDoc)
|
||||
return;
|
||||
|
||||
activeSiteHandler()->setActiveLayerInDoc(newDoc, layer);
|
||||
|
||||
if (newDoc != m_lastSelectedDoc)
|
||||
setActiveDocument(newDoc);
|
||||
if (newDoc)
|
||||
activeSiteHandler()->setActiveLayerInDoc(newDoc, layer);
|
||||
else
|
||||
notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
void Context::onSetActiveFrame(const doc::frame_t frame)
|
||||
{
|
||||
if (m_lastSelectedDoc)
|
||||
activeSiteHandler()->setActiveFrameInDoc(m_lastSelectedDoc, frame);
|
||||
|
||||
notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
void Context::onSetRange(const DocRange& range)
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -110,7 +110,7 @@ namespace app {
|
||||
void onRemoveDocument(Doc* doc) override;
|
||||
|
||||
virtual void onGetActiveSite(Site* site) const;
|
||||
virtual void onSetActiveDocument(Doc* doc);
|
||||
virtual void onSetActiveDocument(Doc* doc, bool notify);
|
||||
virtual void onSetActiveLayer(doc::Layer* layer);
|
||||
virtual void onSetActiveFrame(const doc::frame_t frame);
|
||||
virtual void onSetRange(const DocRange& range);
|
||||
@ -122,10 +122,12 @@ namespace app {
|
||||
private:
|
||||
ActiveSiteHandler* activeSiteHandler() const;
|
||||
|
||||
// This must be defined before m_docs because ActiveSiteHandler
|
||||
// will be an observer of all documents.
|
||||
mutable std::unique_ptr<ActiveSiteHandler> m_activeSiteHandler;
|
||||
mutable Docs m_docs;
|
||||
ContextFlags m_flags; // Last updated flags.
|
||||
Doc* m_lastSelectedDoc;
|
||||
mutable std::unique_ptr<ActiveSiteHandler> m_activeSiteHandler;
|
||||
mutable std::unique_ptr<Preferences> m_preferences;
|
||||
|
||||
DISABLE_COPYING(Context);
|
||||
|
@ -70,6 +70,14 @@ Doc::Doc(Sprite* sprite)
|
||||
Doc::~Doc()
|
||||
{
|
||||
DOC_TRACE("DOC: Deleting", this);
|
||||
|
||||
try {
|
||||
notify_observers<Doc*>(&DocObserver::onDestroy, this);
|
||||
}
|
||||
catch (...) {
|
||||
LOG(ERROR, "DOC: Exception on DocObserver::onDestroy()\n");
|
||||
}
|
||||
|
||||
removeFromContext();
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@ namespace app {
|
||||
public:
|
||||
virtual ~DocObserver() { }
|
||||
|
||||
virtual void onDestroy(Doc* doc) { }
|
||||
virtual void onFileNameChanged(Doc* doc) { }
|
||||
|
||||
// General update. If an observer receives this event, it's because
|
||||
@ -80,8 +81,6 @@ namespace app {
|
||||
// Slices
|
||||
virtual void onSliceNameChange(DocEvent& ev) { }
|
||||
|
||||
// Called to destroy the observable. (Here you could call "delete this".)
|
||||
virtual void dispose() { }
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021 Igara Studio S.A.
|
||||
// Copyright (C) 2015-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -19,12 +20,12 @@ namespace app {
|
||||
class DocUndoObserver {
|
||||
public:
|
||||
virtual ~DocUndoObserver() { }
|
||||
virtual void onAddUndoState(DocUndo* history) = 0;
|
||||
virtual void onAddUndoState(DocUndo* history) { }
|
||||
virtual void onDeleteUndoState(DocUndo* history,
|
||||
undo::UndoState* state) = 0;
|
||||
virtual void onCurrentUndoStateChange(DocUndo* history) = 0;
|
||||
virtual void onClearRedo(DocUndo* history) = 0;
|
||||
virtual void onTotalUndoSizeChange(DocUndo* history) = 0;
|
||||
undo::UndoState* state) { }
|
||||
virtual void onCurrentUndoStateChange(DocUndo* history) { }
|
||||
virtual void onClearRedo(DocUndo* history) { }
|
||||
virtual void onTotalUndoSizeChange(DocUndo* history) { }
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -456,6 +456,12 @@ int App_useTool(lua_State* L)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int App_get_events(lua_State* L)
|
||||
{
|
||||
push_app_events(L);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int App_get_activeSprite(lua_State* L)
|
||||
{
|
||||
app::Context* ctx = App::instance()->context();
|
||||
@ -736,6 +742,7 @@ const Property App_properties[] = {
|
||||
{ "range", App_get_range, nullptr },
|
||||
{ "isUIAvailable", App_get_isUIAvailable, nullptr },
|
||||
{ "defaultPalette", App_get_defaultPalette, App_set_defaultPalette },
|
||||
{ "events", App_get_events, nullptr },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -155,6 +155,7 @@ void register_color_space_class(lua_State* L);
|
||||
#ifdef ENABLE_UI
|
||||
void register_dialog_class(lua_State* L);
|
||||
#endif
|
||||
void register_events_class(lua_State* L);
|
||||
void register_frame_class(lua_State* L);
|
||||
void register_frames_class(lua_State* L);
|
||||
void register_image_class(lua_State* L);
|
||||
@ -387,6 +388,7 @@ Engine::Engine()
|
||||
#ifdef ENABLE_UI
|
||||
register_dialog_class(L);
|
||||
#endif
|
||||
register_events_class(L);
|
||||
register_frame_class(L);
|
||||
register_frames_class(L);
|
||||
register_image_class(L);
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -121,6 +121,7 @@ namespace app {
|
||||
EngineDelegate* m_oldDelegate;
|
||||
};
|
||||
|
||||
void push_app_events(lua_State* L);
|
||||
int push_image_iterator_function(lua_State* L, const doc::Image* image, int extraArgIndex);
|
||||
void push_brush(lua_State* L, const doc::BrushRef& brush);
|
||||
void push_cel_image(lua_State* L, doc::Cel* cel);
|
||||
@ -136,6 +137,7 @@ namespace app {
|
||||
void push_palette(lua_State* L, doc::Palette* palette);
|
||||
void push_plugin(lua_State* L, Extension* ext);
|
||||
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);
|
||||
void push_sprite_frames(lua_State* L, doc::Sprite* sprite);
|
||||
void push_sprite_frames(lua_State* L, doc::Sprite* sprite, const std::vector<doc::frame_t>& frames);
|
||||
|
343
src/app/script/events_class.cpp
Normal file
343
src/app/script/events_class.cpp
Normal file
@ -0,0 +1,343 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2021 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/app.h"
|
||||
#include "app/context.h"
|
||||
#include "app/context_observer.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_undo.h"
|
||||
#include "app/doc_undo_observer.h"
|
||||
#include "app/script/docobj.h"
|
||||
#include "app/script/engine.h"
|
||||
#include "app/script/luacpp.h"
|
||||
#include "doc/document.h"
|
||||
#include "doc/sprite.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
using namespace doc;
|
||||
|
||||
namespace {
|
||||
|
||||
using EventListener = int;
|
||||
|
||||
class AppEvents;
|
||||
class SpriteEvents;
|
||||
static std::unique_ptr<AppEvents> g_appEvents;
|
||||
static std::map<doc::ObjectId, std::unique_ptr<SpriteEvents>> g_spriteEvents;
|
||||
|
||||
class Events {
|
||||
public:
|
||||
using EventType = int;
|
||||
|
||||
Events() { }
|
||||
virtual ~Events() { }
|
||||
Events(const Events&) = delete;
|
||||
Events& operator=(const Events&) = delete;
|
||||
|
||||
virtual EventType eventType(const char* eventName) const = 0;
|
||||
|
||||
bool hasListener(EventListener callbackRef) const {
|
||||
for (auto& listeners : m_listeners) {
|
||||
for (EventListener listener : listeners) {
|
||||
if (listener == callbackRef)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void add(EventType eventType, EventListener callbackRef) {
|
||||
if (eventType >= m_listeners.size())
|
||||
m_listeners.resize(eventType+1);
|
||||
|
||||
auto& listeners = m_listeners[eventType];
|
||||
listeners.push_back(callbackRef);
|
||||
if (listeners.size() == 1)
|
||||
onAddFirstListener(eventType);
|
||||
}
|
||||
|
||||
void remove(EventListener callbackRef) {
|
||||
for (int i=0; i<int(m_listeners.size()); ++i) {
|
||||
EventListeners& listeners = m_listeners[i];
|
||||
auto it = listeners.begin();
|
||||
auto end = listeners.end();
|
||||
bool removed = false;
|
||||
for (; it != end; ) {
|
||||
if (*it == callbackRef) {
|
||||
removed = true;
|
||||
it = listeners.erase(it);
|
||||
end = listeners.end();
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
if (removed && listeners.empty())
|
||||
onRemoveLastListener(i);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
void call(EventType eventType) {
|
||||
if (eventType >= m_listeners.size())
|
||||
return;
|
||||
|
||||
script::Engine* engine = App::instance()->scriptEngine();
|
||||
lua_State* L = engine->luaState();
|
||||
|
||||
try {
|
||||
for (EventListener callbackRef : m_listeners[eventType]) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, callbackRef);
|
||||
if (lua_pcall(L, 0, 0, 0)) {
|
||||
if (const char* s = lua_tostring(L, -1))
|
||||
engine->consolePrint(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& ex) {
|
||||
engine->consolePrint(ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void onAddFirstListener(EventType eventType) = 0;
|
||||
virtual void onRemoveLastListener(EventType eventType) = 0;
|
||||
|
||||
using EventListeners = std::vector<EventListener>;
|
||||
std::vector<EventListeners> m_listeners;
|
||||
};
|
||||
|
||||
class AppEvents : public Events
|
||||
, private ContextObserver {
|
||||
public:
|
||||
enum : EventType { Unknown = -1, SiteChange };
|
||||
|
||||
AppEvents() {
|
||||
}
|
||||
|
||||
EventType eventType(const char* eventName) const {
|
||||
if (std::strcmp(eventName, "sitechange") == 0)
|
||||
return SiteChange;
|
||||
else
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
void onAddFirstListener(EventType eventType) override {
|
||||
switch (eventType) {
|
||||
case SiteChange: {
|
||||
App::instance()->context()->add_observer(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onRemoveLastListener(EventType eventType) override {
|
||||
switch (eventType) {
|
||||
case SiteChange: {
|
||||
App::instance()->context()->remove_observer(this);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ContextObserver impl
|
||||
void onActiveSiteChange(const Site& site) override { call(SiteChange); }
|
||||
};
|
||||
|
||||
class SpriteEvents : public Events
|
||||
, public DocUndoObserver
|
||||
, public DocObserver {
|
||||
public:
|
||||
enum : EventType { Unknown = -1, Change };
|
||||
|
||||
SpriteEvents(const Sprite* sprite)
|
||||
: m_spriteId(sprite->id()) {
|
||||
doc()->add_observer(this);
|
||||
}
|
||||
|
||||
~SpriteEvents() {
|
||||
if (m_observingUndo) {
|
||||
doc()->undoHistory()->remove_observer(this);
|
||||
m_observingUndo = false;
|
||||
}
|
||||
doc()->remove_observer(this);
|
||||
}
|
||||
|
||||
EventType eventType(const char* eventName) const {
|
||||
if (std::strcmp(eventName, "change") == 0)
|
||||
return Change;
|
||||
else
|
||||
return Unknown;
|
||||
}
|
||||
|
||||
// DocObserver impl
|
||||
void onDestroy(Doc* doc) override {
|
||||
auto it = g_spriteEvents.find(m_spriteId);
|
||||
ASSERT(it != g_spriteEvents.end());
|
||||
if (it != g_spriteEvents.end())
|
||||
g_spriteEvents.erase(it);
|
||||
}
|
||||
|
||||
// DocUndoObserver impl
|
||||
void onAddUndoState(DocUndo* history) override { call(Change); }
|
||||
void onCurrentUndoStateChange(DocUndo* history) override { call(Change); }
|
||||
|
||||
private:
|
||||
|
||||
void onAddFirstListener(EventType eventType) override {
|
||||
switch (eventType) {
|
||||
case Change:
|
||||
ASSERT(!m_observingUndo);
|
||||
doc()->undoHistory()->add_observer(this);
|
||||
m_observingUndo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void onRemoveLastListener(EventType eventType) override {
|
||||
switch (eventType) {
|
||||
case Change: {
|
||||
if (m_observingUndo) {
|
||||
doc()->undoHistory()->remove_observer(this);
|
||||
m_observingUndo = false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Doc* doc() {
|
||||
Sprite* sprite = doc::get<Sprite>(m_spriteId);
|
||||
if (sprite)
|
||||
return static_cast<Doc*>(sprite->document());
|
||||
else
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ObjectId m_spriteId;
|
||||
bool m_observingUndo = false;
|
||||
};
|
||||
|
||||
int Events_on(lua_State* L)
|
||||
{
|
||||
auto evs = get_ptr<Events>(L, 1);
|
||||
const char* eventName = lua_tostring(L, 2);
|
||||
if (!eventName)
|
||||
return 0;
|
||||
|
||||
const int type = evs->eventType(eventName);
|
||||
if (type < 0)
|
||||
return luaL_error(L, "invalid event name to listen");
|
||||
|
||||
if (!lua_isfunction(L, 3))
|
||||
return luaL_error(L, "second argument must be a function");
|
||||
|
||||
// Copy the callback function to add it to the global registry
|
||||
lua_pushvalue(L, 3);
|
||||
int callbackRef = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
evs->add(type, callbackRef);
|
||||
|
||||
// Return the callback ref (this is an EventListener easier to use
|
||||
// in Events_off())
|
||||
lua_pushinteger(L, callbackRef);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Events_off(lua_State* L)
|
||||
{
|
||||
auto evs = get_ptr<Events>(L, 1);
|
||||
int callbackRef = LUA_REFNIL;
|
||||
|
||||
// Remove by listener value
|
||||
if (lua_isinteger(L, 2)) {
|
||||
callbackRef = lua_tointeger(L, 2);
|
||||
}
|
||||
// Remove by function reference
|
||||
else if (lua_isfunction(L, 2)) {
|
||||
lua_pushnil(L);
|
||||
while (lua_next(L, LUA_REGISTRYINDEX) != 0) {
|
||||
if (lua_isnumber(L, -2) &&
|
||||
lua_isfunction(L, -1)) {
|
||||
int i = lua_tointeger(L, -2);
|
||||
if (// Compare value=function in 2nd argument
|
||||
lua_compare(L, -1, 2, LUA_OPEQ) &&
|
||||
// Check that this Events contain this reference
|
||||
evs->hasListener(i)) {
|
||||
callbackRef = i;
|
||||
lua_pop(L, 2); // Pop value and key
|
||||
break;
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1); // Pop the value, leave the key for next lua_next()
|
||||
}
|
||||
}
|
||||
else {
|
||||
return luaL_error(L, "first argument must be a function or a EventListener");
|
||||
}
|
||||
|
||||
if (callbackRef != LUA_REFNIL) {
|
||||
evs->remove(callbackRef);
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, callbackRef);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// We don't need a __gc (to call ~Events()), because Events instances
|
||||
// will be deleted when the Sprite is deleted or on App Exit
|
||||
const luaL_Reg Events_methods[] = {
|
||||
{ "on", Events_on },
|
||||
{ "off", Events_off },
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
DEF_MTNAME(Events);
|
||||
|
||||
void register_events_class(lua_State* L)
|
||||
{
|
||||
REG_CLASS(L, Events);
|
||||
}
|
||||
|
||||
void push_app_events(lua_State* L)
|
||||
{
|
||||
if (!g_appEvents) {
|
||||
App::instance()->Exit.connect([]{ g_appEvents.reset(); });
|
||||
g_appEvents.reset(new AppEvents);
|
||||
}
|
||||
push_ptr<Events>(L, g_appEvents.get());
|
||||
}
|
||||
|
||||
void push_sprite_events(lua_State* L, Sprite* sprite)
|
||||
{
|
||||
ASSERT(sprite);
|
||||
|
||||
SpriteEvents* spriteEvents;
|
||||
|
||||
auto it = g_spriteEvents.find(sprite->id());
|
||||
if (it != g_spriteEvents.end())
|
||||
spriteEvents = it->second.get();
|
||||
else {
|
||||
spriteEvents = new SpriteEvents(sprite);
|
||||
g_spriteEvents[sprite->id()].reset(spriteEvents);
|
||||
}
|
||||
|
||||
push_ptr<Events>(L, spriteEvents);
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
@ -26,47 +26,6 @@ namespace script {
|
||||
|
||||
namespace {
|
||||
|
||||
class ScriptSiteObserver : public ContextObserver {
|
||||
public:
|
||||
static ScriptSiteObserver* instance() {
|
||||
static ScriptSiteObserver instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
void onActiveSiteChange(const Site& site) {
|
||||
if (m_onchangeRef == 0)
|
||||
return;
|
||||
|
||||
script::Engine* engine = App::instance()->scriptEngine();
|
||||
lua_State* L = engine->luaState();
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_onchangeRef);
|
||||
if (lua_pcall(L, 0, 0, 0)) {
|
||||
if (const char* s = lua_tostring(L, -1)) {
|
||||
Console().printf("Error: %s", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int callbackRef() {
|
||||
return m_onchangeRef;
|
||||
}
|
||||
|
||||
void setCallbackRef(int onchangeRef) {
|
||||
m_onchangeRef = onchangeRef;
|
||||
}
|
||||
|
||||
private:
|
||||
ScriptSiteObserver()
|
||||
: ContextObserver(),
|
||||
m_onchangeRef(0)
|
||||
{ }
|
||||
|
||||
~ScriptSiteObserver() { }
|
||||
|
||||
int m_onchangeRef;
|
||||
};
|
||||
|
||||
int Site_get_sprite(lua_State* L)
|
||||
{
|
||||
auto site = get_obj<Site>(L, 1);
|
||||
@ -124,33 +83,6 @@ int Site_get_image(lua_State* L)
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Site_get_onChange(lua_State* L)
|
||||
{
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, ScriptSiteObserver::instance()->callbackRef());
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Site_set_onChange(lua_State* L)
|
||||
{
|
||||
auto site = get_obj<Site>(L, 1);
|
||||
auto obs = ScriptSiteObserver::instance();
|
||||
|
||||
if (lua_isfunction(L, 2)) {
|
||||
int onchangeRef = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
if (!obs->callbackRef()) {
|
||||
App::instance()->context()->add_observer(obs);
|
||||
}
|
||||
obs->setCallbackRef(onchangeRef);
|
||||
}
|
||||
else if (obs->callbackRef()) {
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, obs->callbackRef());
|
||||
obs->setCallbackRef(0);
|
||||
App::instance()->context()->remove_observer(obs);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const luaL_Reg Site_methods[] = {
|
||||
{ nullptr, nullptr }
|
||||
};
|
||||
@ -162,7 +94,6 @@ const Property Site_properties[] = {
|
||||
{ "frame", Site_get_frame, nullptr },
|
||||
{ "frameNumber", Site_get_frameNumber, nullptr },
|
||||
{ "image", Site_get_image, nullptr },
|
||||
{ "onChange", Site_get_onChange, Site_set_onChange },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -59,41 +59,6 @@ namespace app {
|
||||
namespace script {
|
||||
|
||||
namespace {
|
||||
// in lua, observers become `Sprite.onChange`
|
||||
class ScriptDocObserver : public DocUndoObserver {
|
||||
public:
|
||||
ScriptDocObserver(int callbackRef)
|
||||
: DocUndoObserver(),
|
||||
m_callbackRef(callbackRef)
|
||||
{ }
|
||||
|
||||
void onAddUndoState(DocUndo* history) { callback(); }
|
||||
void onDeleteUndoState(DocUndo* history, undo::UndoState* state) { }
|
||||
void onCurrentUndoStateChange(DocUndo* history) { callback(); }
|
||||
void onClearRedo(DocUndo* history) { }
|
||||
void onTotalUndoSizeChange(DocUndo* history) { }
|
||||
|
||||
int callbackRef() { return m_callbackRef; }
|
||||
void setCallbackRef(int callbackRef) { m_callbackRef = callbackRef; }
|
||||
|
||||
private:
|
||||
void callback() {
|
||||
script::Engine* engine = App::instance()->scriptEngine();
|
||||
lua_State* L = engine->luaState();
|
||||
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, m_callbackRef);
|
||||
if (lua_pcall(L, 0, 0, 0)) {
|
||||
if (const char* s = lua_tostring(L, -1)) {
|
||||
Console().printf("Error: %s", s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int m_callbackRef;
|
||||
};
|
||||
|
||||
// used to maintain one-to-one relation between sprites and observers
|
||||
std::map<doc::ObjectId, ScriptDocObserver*> script_observers;
|
||||
|
||||
int Sprite_new(lua_State* L)
|
||||
{
|
||||
@ -627,47 +592,13 @@ int Sprite_deleteSlice(lua_State* L)
|
||||
}
|
||||
}
|
||||
|
||||
int Sprite_get_onChange(lua_State* L)
|
||||
int Sprite_get_events(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
ScriptDocObserver* obs = script_observers[sprite->id()];
|
||||
|
||||
if (obs) {
|
||||
lua_rawgeti(L, LUA_REGISTRYINDEX, obs->callbackRef());
|
||||
}
|
||||
else {
|
||||
lua_pushnil(L);
|
||||
}
|
||||
push_sprite_events(L, sprite);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Sprite_set_onChange(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
auto doc = static_cast<Doc*>(sprite->document());
|
||||
ScriptDocObserver* obs = script_observers[sprite->id()];
|
||||
|
||||
if (lua_isfunction(L, 2)) {
|
||||
int callbackRef = luaL_ref(L, LUA_REGISTRYINDEX);
|
||||
if (obs) {
|
||||
obs->setCallbackRef(callbackRef);
|
||||
}
|
||||
else {
|
||||
obs = new ScriptDocObserver(callbackRef);
|
||||
doc->undoHistory()->add_observer(obs);
|
||||
script_observers[sprite->id()] = obs;
|
||||
}
|
||||
}
|
||||
else if (obs) {
|
||||
doc->undoHistory()->remove_observer(obs);
|
||||
script_observers.erase(sprite->id());
|
||||
luaL_unref(L, LUA_REGISTRYINDEX, obs->callbackRef());
|
||||
delete obs;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Sprite_get_filename(lua_State* L)
|
||||
{
|
||||
auto sprite = get_docobj<Sprite>(L, 1);
|
||||
@ -922,7 +853,7 @@ const Property Sprite_properties[] = {
|
||||
{ "bounds", Sprite_get_bounds, nullptr },
|
||||
{ "gridBounds", Sprite_get_gridBounds, Sprite_set_gridBounds },
|
||||
{ "pixelRatio", Sprite_get_pixelRatio, Sprite_set_pixelRatio },
|
||||
{ "onChange", Sprite_get_onChange, Sprite_set_onChange },
|
||||
{ "events", Sprite_get_events, nullptr },
|
||||
{ nullptr, nullptr, nullptr }
|
||||
};
|
||||
|
||||
|
@ -134,10 +134,10 @@ void UIContext::setActiveView(DocView* docView)
|
||||
notifyActiveSiteChanged();
|
||||
}
|
||||
|
||||
void UIContext::onSetActiveDocument(Doc* document)
|
||||
void UIContext::onSetActiveDocument(Doc* document, bool notify)
|
||||
{
|
||||
bool notify = (lastSelectedDoc() != document);
|
||||
app::Context::onSetActiveDocument(document);
|
||||
notify = (notify && lastSelectedDoc() != document);
|
||||
app::Context::onSetActiveDocument(document, false);
|
||||
|
||||
DocView* docView = getFirstDocView(document);
|
||||
if (docView) { // The view can be null if we are in --batch mode
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2021 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -53,7 +53,7 @@ namespace app {
|
||||
void onAddDocument(Doc* doc) override;
|
||||
void onRemoveDocument(Doc* doc) override;
|
||||
void onGetActiveSite(Site* site) const override;
|
||||
void onSetActiveDocument(Doc* doc) override;
|
||||
void onSetActiveDocument(Doc* doc, bool notify) override;
|
||||
void onSetActiveLayer(doc::Layer* layer) override;
|
||||
void onSetActiveFrame(const doc::frame_t frame) override;
|
||||
void onSetRange(const DocRange& range) override;
|
||||
|
Loading…
x
Reference in New Issue
Block a user