/*
* Copyright (C) 2024 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "AnimationJS.h"
#include
#include
#include
#include "SceneJS.h"
class OnCallJS : public ThreadSafeCallback {
NapiApi::StrongRef jsThis_;
NapiApi::StrongRef ref_;
public:
OnCallJS(const char* name, napi_value jsThis, NapiApi::Function toCall) : ThreadSafeCallback(toCall.GetEnv(), name)
{
jsThis_ = { toCall.GetEnv(), jsThis };
ref_ = { toCall.GetEnv(), toCall };
}
~OnCallJS()
{
jsThis_.Reset();
ref_.Reset();
Release();
}
void Finalize(napi_env env)
{
jsThis_.Reset();
ref_.Reset();
}
void Invoked(napi_env env)
{
napi_value res;
napi_call_function(env, jsThis_.GetValue(), ref_.GetValue(), 0, nullptr, &res);
}
};
void AnimationJS::Init(napi_env env, napi_value exports)
{
BASE_NS::vector node_props;
SceneResourceImpl::GetPropertyDescs(node_props);
// Try out the helper macros.
// Declare NAPI_API_JS_NAME to simplify the registering.
#define NAPI_API_JS_NAME Animation
DeclareGetSet(bool, "enabled", GetEnabled, SetEnabled);
DeclareGet(float, "duration", GetDuration);
DeclareGet(bool, "running", GetRunning);
DeclareGet(float, "progress", GetProgress);
DeclareMethod("pause", Pause);
DeclareMethod("restart", Restart);
DeclareMethod("seek", Seek, float);
DeclareMethod("start", Start);
DeclareMethod("stop", Stop);
DeclareMethod("finish", Finish);
DeclareMethod("onFinished", OnFinished, NapiApi::Function);
DeclareMethod("onStarted", OnStarted, NapiApi::Function);
DeclareClass();
#undef NAPI_API_JS_NAME
}
AnimationJS::AnimationJS(napi_env e, napi_callback_info i)
: BaseObject(e, i), SceneResourceImpl(SceneResourceImpl::ANIMATION)
{
NapiApi::FunctionContext fromJs(e, i);
NapiApi::Object meJs(e, fromJs.This());
NapiApi::Object scene = fromJs.Arg<0>(); // access to owning scene...
scene_ = { scene };
if (!GetNativeMeta(scene_.GetObject())) {
CORE_LOG_F("INVALID SCENE!");
}
auto* tro = scene.Native();
auto* sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
sceneJS->DisposeHook((uintptr_t)&scene_, meJs);
}
void* AnimationJS::GetInstanceImpl(uint32_t id)
{
if (id == AnimationJS::ID) {
return this;
}
return SceneResourceImpl::GetInstanceImpl(id);
}
void AnimationJS::Finalize(napi_env env)
{
DisposeNative();
BaseObject::Finalize(env);
}
AnimationJS::~AnimationJS()
{
LOG_F("AnimationJS -- ");
DisposeNative();
}
void AnimationJS::DisposeNative()
{
// do nothing for now..
if (!disposed_) {
disposed_ = true;
LOG_F("AnimationJS::DisposeNative");
NapiApi::Object obj = scene_.GetObject();
auto* tro = obj.Native();
SceneJS* sceneJS;
if (tro) {
sceneJS = ((SceneJS*)tro->GetInstanceImpl(SceneJS::ID));
sceneJS->ReleaseDispose((uintptr_t)&scene_);
}
scene_.Reset();
// make sure we release postProc settings
if (auto animation = interface_pointer_cast(GetNativeObject())) {
// reset the native object refs
SetNativeObject(nullptr, false);
SetNativeObject(nullptr, true);
ExecSyncTask([this, anim = BASE_NS::move(animation)]() -> META_NS::IAny::Ptr {
// remove listeners.
if (OnStartedToken_) {
anim->OnStarted()->RemoveHandler(OnStartedToken_);
}
if (OnFinishedToken_) {
anim->OnFinished()->RemoveHandler(OnFinishedToken_);
}
return {};
});
if (OnStartedCB_) {
// does a delayed delete
OnStartedCB_->Release();
OnStartedCB_ = nullptr;
}
if (OnFinishedCB_) {
// does a delayed delete
OnFinishedCB_->Release();
OnFinishedCB_ = nullptr;
}
}
}
}
napi_value AnimationJS::GetEnabled(NapiApi::FunctionContext<>& ctx)
{
bool enabled { false };
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a, &enabled]() {
if (a) {
enabled = a->Enabled()->GetValue();
}
return META_NS::IAny::Ptr {};
});
}
return ctx.GetBoolean(enabled);
}
void AnimationJS::SetEnabled(NapiApi::FunctionContext& ctx)
{
bool enabled = ctx.Arg<0>();
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a, enabled]() {
a->Enabled()->SetValue(enabled);
return META_NS::IAny::Ptr {};
});
}
}
napi_value AnimationJS::GetDuration(NapiApi::FunctionContext<>& ctx)
{
float duration = 0.0;
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a, &duration]() {
if (a) {
duration = a->TotalDuration()->GetValue().ToSecondsFloat();
}
return META_NS::IAny::Ptr {};
});
}
return NapiApi::Value(ctx, duration);
}
napi_value AnimationJS::GetRunning(NapiApi::FunctionContext<>& ctx)
{
bool running { false };
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a, &running]() {
if (a) {
running = a->Running()->GetValue();
}
return META_NS::IAny::Ptr {};
});
}
return ctx.GetBoolean(running);
}
napi_value AnimationJS::GetProgress(NapiApi::FunctionContext<>& ctx)
{
float progress = 0.0;
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a, &progress]() {
if (a) {
progress = a->Progress()->GetValue();
}
return META_NS::IAny::Ptr {};
});
}
return NapiApi::Value(ctx, progress);
}
napi_value AnimationJS::OnFinished(NapiApi::FunctionContext& ctx)
{
auto func = ctx.Arg<0>();
// do we have existing callback?
if (OnFinishedCB_) {
// stop listening ...
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
a->OnFinished()->RemoveHandler(OnFinishedToken_);
OnFinishedToken_ = 0;
return {};
});
}
// ... and release it
OnFinishedCB_->Release();
}
// do we have a new callback?
if (func) {
// create handler...
OnFinishedCB_ = new OnCallJS("OnFinished", ctx.This(), func);
// ... and start listening
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
OnFinishedToken_ = a->OnFinished()->AddHandler(
META_NS::MakeCallback(OnFinishedCB_, &OnCallJS::Trigger));
return {};
});
}
}
return ctx.GetUndefined();
}
napi_value AnimationJS::OnStarted(NapiApi::FunctionContext& ctx)
{
auto func = ctx.Arg<0>();
// do we have existing callback?
if (OnStartedCB_) {
// stop listening ...
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
a->OnStarted()->RemoveHandler(OnStartedToken_);
OnStartedToken_ = 0;
return {};
});
}
// ... and release it
OnStartedCB_->Release();
}
// do we have a new callback?
if (func) {
// create handler...
OnStartedCB_ = new OnCallJS("OnStart", ctx.This(), func);
// ... and start listening
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([this, a]() -> META_NS::IAny::Ptr {
OnStartedToken_ = a->OnStarted()->AddHandler(
META_NS::MakeCallback(OnStartedCB_, &OnCallJS::Trigger));
return {};
});
}
}
return ctx.GetUndefined();
}
napi_value AnimationJS::Pause(NapiApi::FunctionContext<>& ctx)
{
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a]() {
a->Pause();
return META_NS::IAny::Ptr {};
});
}
return ctx.GetUndefined();
}
napi_value AnimationJS::Restart(NapiApi::FunctionContext<>& ctx)
{
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a]() {
a->Restart();
return META_NS::IAny::Ptr {};
});
}
return ctx.GetUndefined();
}
napi_value AnimationJS::Seek(NapiApi::FunctionContext& ctx)
{
float pos = ctx.Arg<0>();
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a, pos]() {
a->Seek(pos);
return META_NS::IAny::Ptr {};
});
}
return ctx.GetUndefined();
}
napi_value AnimationJS::Start(NapiApi::FunctionContext<>& ctx)
{
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a]() {
a->Start();
return META_NS::IAny::Ptr {};
});
}
return ctx.GetUndefined();
}
napi_value AnimationJS::Stop(NapiApi::FunctionContext<>& ctx)
{
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a]() {
a->Stop();
return META_NS::IAny::Ptr {};
});
}
return ctx.GetUndefined();
}
napi_value AnimationJS::Finish(NapiApi::FunctionContext<>& ctx)
{
if (auto a = interface_cast(GetNativeObject())) {
ExecSyncTask([a]() {
a->Finish();
return META_NS::IAny::Ptr {};
});
}
return ctx.GetUndefined();
}