2016-04-07 02:37:13 +08:00
|
|
|
// Aseprite
|
Fix transparent color is possible on opaque sprites (fix #4370)
To reproduce the error before this fix on RGBA/Grayscale Color Mode:
- New 100x100 RGBA/Grayscale opaque sprite (white background).
- Draw something with some gray color in the palette.
- Keep the selected gray color as primary color.
- Configure as secondary color the mask color (#000000 alpha=0).
- Pick 'eraser' tool and erase over the gray color with right click.
- Result: The sprite doesn't look more opaque, which is wrong. Also,
if we export this sprite, the transparent parts will turn black.
A similar problem occurs in Indexed Color Mode, but getting a
transparent color in a Background sprite is inevitable if the color of
a palette entry is transparent or semi-transparent, since the index
must be set as is. This could be fixed in the future at the
render stage, however, this could lead to other perceived
inconsistencies. For now it'll be left as is.
Original issue description:
Downloaded PNG in RGB mode fails to support transparency: erase
uses secondary color and export PNG replaces transparent color
with black
Added tests for 'eraser' in 'Replace Color Mode'
To make the eraser work in 'Replace Color Mode' within the tests,
was implemented the possibility of using the right button in
the creation of the point vector.
During testing with UI available it was observed that the 'bg' color
was copied from the 'fg'. Changed this to be compatible with the way
the default value of 'fg' is assigned when it is not specified.
This last modification resulted in errors during 'tilemap.lua' due to
incompatibility of the type of 'bg' color. This was corrected
considering the color type of 'fg' color.
Furthermore, it was found that the command 'app.range.tiles = { 1 }'
did not finish assigning the tile picks to the activeSite,
then 'assert(1, #app.range.tiles)' was failing. This was fixed too.
2024-08-22 22:07:54 +08:00
|
|
|
// Copyright (C) 2018-2024 Igara Studio S.A.
|
2018-05-07 11:11:50 +08:00
|
|
|
// Copyright (C) 2015-2018 David Capello
|
2016-04-07 02:37:13 +08:00
|
|
|
//
|
2016-08-27 04:02:58 +08:00
|
|
|
// This program is distributed under the terms of
|
|
|
|
// the End-User License Agreement for Aseprite.
|
2016-04-07 02:37:13 +08:00
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2018-05-07 11:11:50 +08:00
|
|
|
#include "app/app.h"
|
2016-04-16 06:59:25 +08:00
|
|
|
#include "app/commands/commands.h"
|
|
|
|
#include "app/commands/params.h"
|
2018-07-07 22:54:44 +08:00
|
|
|
#include "app/context.h"
|
2023-12-13 08:34:30 +08:00
|
|
|
#include "app/context_access.h"
|
2018-07-07 22:54:44 +08:00
|
|
|
#include "app/doc.h"
|
2019-04-18 10:59:59 +08:00
|
|
|
#include "app/doc_access.h"
|
2018-09-08 03:42:58 +08:00
|
|
|
#include "app/i18n/strings.h"
|
2022-02-11 21:19:32 +08:00
|
|
|
#include "app/inline_command_execution.h"
|
2018-10-26 21:52:40 +08:00
|
|
|
#include "app/loop_tag.h"
|
2019-04-25 06:08:25 +08:00
|
|
|
#include "app/modules/palettes.h"
|
2018-09-07 00:02:23 +08:00
|
|
|
#include "app/pref/preferences.h"
|
2018-11-14 09:31:14 +08:00
|
|
|
#include "app/script/api_version.h"
|
2018-11-21 05:12:43 +08:00
|
|
|
#include "app/script/docobj.h"
|
2018-08-20 22:25:08 +08:00
|
|
|
#include "app/script/engine.h"
|
|
|
|
#include "app/script/luacpp.h"
|
2018-09-18 00:14:56 +08:00
|
|
|
#include "app/script/security.h"
|
2018-08-21 01:20:27 +08:00
|
|
|
#include "app/site.h"
|
2019-03-16 04:03:02 +08:00
|
|
|
#include "app/tools/active_tool.h"
|
2021-04-23 00:00:13 +08:00
|
|
|
#include "app/tools/ink.h"
|
2019-03-16 04:03:02 +08:00
|
|
|
#include "app/tools/tool_box.h"
|
|
|
|
#include "app/tools/tool_loop.h"
|
|
|
|
#include "app/tools/tool_loop_manager.h"
|
2018-08-21 01:20:27 +08:00
|
|
|
#include "app/tx.h"
|
2019-04-19 09:23:33 +08:00
|
|
|
#include "app/ui/context_bar.h"
|
2018-09-12 23:17:48 +08:00
|
|
|
#include "app/ui/editor/editor.h"
|
2019-03-16 04:03:02 +08:00
|
|
|
#include "app/ui/editor/tool_loop_impl.h"
|
2023-06-12 00:27:50 +08:00
|
|
|
#include "app/ui/main_window.h"
|
2025-08-13 23:26:03 +08:00
|
|
|
#include "app/ui/status_bar.h"
|
2018-10-26 21:52:40 +08:00
|
|
|
#include "app/ui/timeline/timeline.h"
|
2018-09-12 23:17:48 +08:00
|
|
|
#include "app/ui_context.h"
|
2018-09-18 00:14:56 +08:00
|
|
|
#include "base/fs.h"
|
2019-05-22 01:06:35 +08:00
|
|
|
#include "base/replace_string.h"
|
2019-04-21 11:04:49 +08:00
|
|
|
#include "base/version.h"
|
2018-09-12 23:17:48 +08:00
|
|
|
#include "doc/layer.h"
|
2019-04-18 10:59:59 +08:00
|
|
|
#include "doc/primitives.h"
|
2019-10-02 01:55:08 +08:00
|
|
|
#include "doc/tag.h"
|
2019-04-18 10:59:59 +08:00
|
|
|
#include "render/render.h"
|
2018-09-08 03:42:58 +08:00
|
|
|
#include "ui/alert.h"
|
2023-03-21 05:26:44 +08:00
|
|
|
#include "ui/scale.h"
|
2020-03-16 21:29:58 +08:00
|
|
|
#include "ver/info.h"
|
2016-04-07 02:37:13 +08:00
|
|
|
|
2020-05-20 07:11:47 +08:00
|
|
|
#include <cstring>
|
2016-04-07 02:37:13 +08:00
|
|
|
#include <iostream>
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
namespace app { namespace script {
|
2016-04-07 02:37:13 +08:00
|
|
|
|
2019-04-18 10:59:59 +08:00
|
|
|
int load_sprite_from_file(lua_State* L, const char* filename, const LoadSpriteFromFileParam param)
|
2016-04-16 06:59:25 +08:00
|
|
|
{
|
2019-04-18 10:59:59 +08:00
|
|
|
std::string absFn = base::get_absolute_path(filename);
|
2021-10-08 06:34:40 +08:00
|
|
|
if (!ask_access(L, absFn.c_str(), FileAccessMode::Read, ResourceType::File))
|
2019-04-18 10:59:59 +08:00
|
|
|
return luaL_error(L, "script doesn't have access to open file %s", absFn.c_str());
|
2016-04-16 06:59:25 +08:00
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Doc* oldDoc = ctx->activeDocument();
|
2016-04-16 06:59:25 +08:00
|
|
|
|
2017-12-02 02:10:21 +08:00
|
|
|
Command* openCommand = Commands::instance()->byId(CommandId::OpenFile());
|
2016-04-16 06:59:25 +08:00
|
|
|
Params params;
|
2018-09-18 00:14:56 +08:00
|
|
|
params.set("filename", absFn.c_str());
|
2020-07-29 04:41:01 +08:00
|
|
|
if (param == LoadSpriteFromFileParam::OneFrameAsSprite ||
|
|
|
|
param == LoadSpriteFromFileParam::OneFrameAsImage)
|
2019-04-18 10:59:59 +08:00
|
|
|
params.set("oneframe", "true");
|
2018-08-20 22:25:08 +08:00
|
|
|
ctx->executeCommand(openCommand, params);
|
2016-04-16 06:59:25 +08:00
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
Doc* newDoc = ctx->activeDocument();
|
2019-04-18 10:59:59 +08:00
|
|
|
if (newDoc != oldDoc) {
|
|
|
|
if (param == LoadSpriteFromFileParam::OneFrameAsImage) {
|
|
|
|
doc::Sprite* sprite = newDoc->sprite();
|
|
|
|
|
|
|
|
// Render the first frame of the sprite
|
|
|
|
// TODO add "frame" parameter to render different frames
|
|
|
|
std::unique_ptr<doc::Image> image(doc::Image::create(sprite->spec()));
|
|
|
|
doc::clear_image(image.get(), sprite->transparentColor());
|
|
|
|
render::Render().renderSprite(image.get(), sprite, 0);
|
|
|
|
|
|
|
|
// Restore the old document and active and destroy the recently
|
|
|
|
// loaded sprite.
|
|
|
|
ctx->setActiveDocument(oldDoc);
|
|
|
|
|
|
|
|
try {
|
|
|
|
DocDestroyer destroyer(ctx, newDoc, 500);
|
|
|
|
destroyer.destroyDocument();
|
|
|
|
}
|
|
|
|
catch (const LockedDocException& ex) {
|
|
|
|
// Almost impossible?
|
|
|
|
luaL_error(L, "cannot lock document to close it\n%s", ex.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
push_image(L, image.release());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
push_docobj(L, newDoc->sprite());
|
|
|
|
}
|
|
|
|
}
|
2016-04-16 06:59:25 +08:00
|
|
|
else
|
2018-08-20 22:25:08 +08:00
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
2016-04-16 06:59:25 +08:00
|
|
|
}
|
|
|
|
|
2019-04-18 10:59:59 +08:00
|
|
|
namespace {
|
|
|
|
|
|
|
|
int App_open(lua_State* L)
|
|
|
|
{
|
|
|
|
return load_sprite_from_file(L, luaL_checkstring(L, 1), LoadSpriteFromFileParam::FullAniAsSprite);
|
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
int App_exit(lua_State* L)
|
2016-05-07 03:49:43 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
if (ctx && ctx->isUIAvailable()) {
|
2017-12-02 02:10:21 +08:00
|
|
|
Command* exitCommand = Commands::instance()->byId(CommandId::Exit());
|
2018-08-20 22:25:08 +08:00
|
|
|
ctx->executeCommand(exitCommand);
|
2017-08-12 04:22:28 +08:00
|
|
|
}
|
2018-08-20 22:25:08 +08:00
|
|
|
return 0;
|
2016-05-07 03:49:43 +08:00
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
int App_transaction(lua_State* L)
|
2018-08-21 01:20:27 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
int top = lua_gettop(L);
|
|
|
|
int nresults = 0;
|
2023-01-18 02:39:16 +08:00
|
|
|
int index = 1;
|
|
|
|
std::string label = Tx::kDefaultTransactionName;
|
|
|
|
|
|
|
|
// This can be:
|
|
|
|
//
|
|
|
|
// app.transaction(function)
|
|
|
|
// app.transaction(string, function)
|
|
|
|
//
|
|
|
|
// Where if the string is the first argument, it will be the
|
|
|
|
// transaction name/undo-redo label.
|
|
|
|
|
|
|
|
if (lua_isstring(L, index)) {
|
|
|
|
label = lua_tostring(L, index);
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (lua_isfunction(L, index)) {
|
2023-12-13 08:34:30 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
if (!ctx)
|
|
|
|
return luaL_error(L, "no context");
|
|
|
|
|
2023-12-27 09:29:18 +08:00
|
|
|
try {
|
|
|
|
// We lock the document in the whole transaction because the
|
|
|
|
// RWLock now is re-entrant and we are able to call commands
|
|
|
|
// inside the app.transaction() (creating inner ContextWriters).
|
|
|
|
ContextWriter writer(ctx);
|
|
|
|
Tx tx(writer, label);
|
|
|
|
|
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
if (lua_pcall(L, 0, LUA_MULTRET, 0) == LUA_OK)
|
|
|
|
tx.commit();
|
|
|
|
else
|
|
|
|
return lua_error(L); // pcall already put an error object on the stack
|
|
|
|
nresults = lua_gettop(L) - top;
|
|
|
|
}
|
|
|
|
catch (const LockedDocException& ex) {
|
|
|
|
return luaL_error(L, "cannot lock document for transaction\n%s", ex.what());
|
|
|
|
}
|
2018-08-21 01:20:27 +08:00
|
|
|
}
|
2018-08-20 22:25:08 +08:00
|
|
|
return nresults;
|
2018-08-21 01:20:27 +08:00
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
int App_undo(lua_State* L)
|
2018-08-21 01:34:14 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
if (ctx) {
|
2018-08-21 01:34:14 +08:00
|
|
|
Command* undo = Commands::instance()->byId(CommandId::Undo());
|
2018-08-20 22:25:08 +08:00
|
|
|
ctx->executeCommand(undo);
|
2018-08-21 01:34:14 +08:00
|
|
|
}
|
2018-08-20 22:25:08 +08:00
|
|
|
return 0;
|
2018-08-21 01:34:14 +08:00
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
int App_redo(lua_State* L)
|
2018-08-21 01:34:14 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
if (ctx) {
|
2018-08-21 01:34:14 +08:00
|
|
|
Command* redo = Commands::instance()->byId(CommandId::Redo());
|
2018-08-20 22:25:08 +08:00
|
|
|
ctx->executeCommand(redo);
|
2018-08-21 01:34:14 +08:00
|
|
|
}
|
2018-08-20 22:25:08 +08:00
|
|
|
return 0;
|
2018-08-21 01:34:14 +08:00
|
|
|
}
|
|
|
|
|
2018-09-08 03:42:58 +08:00
|
|
|
int App_alert(lua_State* L)
|
|
|
|
{
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
if (!ctx || !ctx->isUIAvailable())
|
|
|
|
return 0; // No UI to show the alert
|
|
|
|
// app.alert("text...")
|
|
|
|
else if (lua_isstring(L, 1)) {
|
|
|
|
ui::AlertPtr alert(new ui::Alert);
|
|
|
|
alert->addLabel(lua_tostring(L, 1), ui::CENTER);
|
|
|
|
alert->addButton(Strings::general_ok());
|
|
|
|
lua_pushinteger(L, alert->show());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
// app.alert{ ... }
|
|
|
|
else if (lua_istable(L, 1)) {
|
|
|
|
ui::AlertPtr alert(new ui::Alert);
|
|
|
|
|
|
|
|
int type = lua_getfield(L, 1, "title");
|
|
|
|
if (type != LUA_TNIL)
|
|
|
|
alert->setTitle(lua_tostring(L, -1));
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "text");
|
|
|
|
if (type == LUA_TTABLE) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
while (lua_next(L, -2) != 0) {
|
|
|
|
const char* v = luaL_tolstring(L, -1, nullptr);
|
|
|
|
if (v)
|
|
|
|
alert->addLabel(v, ui::LEFT);
|
|
|
|
lua_pop(L, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == LUA_TSTRING) {
|
|
|
|
alert->addLabel(lua_tostring(L, -1), ui::LEFT);
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
int nbuttons = 0;
|
|
|
|
type = lua_getfield(L, 1, "buttons");
|
|
|
|
if (type == LUA_TTABLE) {
|
|
|
|
lua_pushnil(L);
|
|
|
|
while (lua_next(L, -2) != 0) {
|
|
|
|
const char* v = luaL_tolstring(L, -1, nullptr);
|
|
|
|
if (v) {
|
|
|
|
alert->addButton(v);
|
|
|
|
++nbuttons;
|
|
|
|
}
|
|
|
|
lua_pop(L, 2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (type == LUA_TSTRING) {
|
|
|
|
alert->addButton(lua_tostring(L, -1));
|
|
|
|
++nbuttons;
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
if (nbuttons == 0)
|
|
|
|
alert->addButton(Strings::general_ok());
|
|
|
|
|
|
|
|
lua_pushinteger(L, alert->show());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-11-27 21:24:13 +08:00
|
|
|
int App_refresh(lua_State* L)
|
|
|
|
{
|
2020-02-05 23:18:12 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
if (ctx && ctx->isUIAvailable())
|
|
|
|
app_refresh_screen();
|
2018-11-27 21:24:13 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-03-24 00:31:14 +08:00
|
|
|
int App_useTool(lua_State* L)
|
2019-03-16 04:03:02 +08:00
|
|
|
{
|
|
|
|
// First argument must be a table
|
|
|
|
if (!lua_istable(L, 1))
|
2019-03-24 00:31:14 +08:00
|
|
|
return luaL_error(L, "app.useTool() must be called with a table as its first argument");
|
2019-03-16 04:03:02 +08:00
|
|
|
|
|
|
|
auto ctx = App::instance()->context();
|
2019-03-24 03:34:51 +08:00
|
|
|
Site site = ctx->activeSite();
|
|
|
|
|
|
|
|
// Draw in a specific cel, layer, or frame
|
|
|
|
int type = lua_getfield(L, 1, "layer");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
if (auto layer = get_docobj<Layer>(L, -1)) {
|
|
|
|
site.document(static_cast<Doc*>(layer->sprite()->document()));
|
|
|
|
site.sprite(layer->sprite());
|
|
|
|
site.layer(layer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "frame");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
site.frame(get_frame_number_from_arg(L, -1));
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "cel");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
if (auto cel = get_docobj<Cel>(L, -1)) {
|
|
|
|
site.document(static_cast<Doc*>(cel->sprite()->document()));
|
|
|
|
site.sprite(cel->sprite());
|
|
|
|
site.layer(cel->layer());
|
|
|
|
site.frame(cel->frame());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
if (!site.document())
|
2019-03-16 04:03:02 +08:00
|
|
|
return luaL_error(L, "there is no active document to draw with the tool");
|
|
|
|
|
2020-05-20 07:11:47 +08:00
|
|
|
// Options to create the ToolLoop (tool, ink, color, opacity, etc.)
|
|
|
|
ToolLoopParams params;
|
|
|
|
|
2020-05-21 03:34:43 +08:00
|
|
|
// Mouse button
|
|
|
|
params.button = tools::ToolLoop::Left;
|
|
|
|
type = lua_getfield(L, 1, "button");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
// Only supported button at the moment left (default) or right
|
|
|
|
if (lua_tointeger(L, -1) == (int)ui::kButtonRight)
|
|
|
|
params.button = tools::ToolLoop::Right;
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2019-03-16 04:03:02 +08:00
|
|
|
// Select tool by name
|
2020-05-28 23:01:34 +08:00
|
|
|
const int buttonIdx = (params.button == tools::ToolLoop::Left ? 0 : 1);
|
2020-05-21 03:34:43 +08:00
|
|
|
auto activeToolMgr = App::instance()->activeToolManager();
|
|
|
|
params.tool = activeToolMgr->activeTool();
|
2020-05-28 23:01:34 +08:00
|
|
|
params.ink = params.tool->getInk(buttonIdx);
|
|
|
|
params.controller = params.tool->getController(buttonIdx);
|
2019-03-24 03:34:51 +08:00
|
|
|
type = lua_getfield(L, 1, "tool");
|
2019-04-26 03:33:49 +08:00
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
if (auto toolArg = get_tool_from_arg(L, -1)) {
|
2020-05-20 07:11:47 +08:00
|
|
|
params.tool = toolArg;
|
2020-05-28 23:01:34 +08:00
|
|
|
params.ink = params.tool->getInk(buttonIdx);
|
|
|
|
params.controller = params.tool->getController(buttonIdx);
|
2019-04-26 03:33:49 +08:00
|
|
|
}
|
|
|
|
else
|
|
|
|
return luaL_error(L, "invalid tool specified in app.useTool() function");
|
2019-03-16 04:03:02 +08:00
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2020-05-21 03:34:43 +08:00
|
|
|
// Select ink by name
|
|
|
|
type = lua_getfield(L, 1, "ink");
|
|
|
|
if (type != LUA_TNIL)
|
|
|
|
params.inkType = get_value_from_lua<tools::InkType>(L, -1);
|
2020-05-20 07:11:47 +08:00
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2024-09-04 06:25:19 +08:00
|
|
|
// Are we going to modify pixels or tiles?
|
|
|
|
type = lua_getfield(L, 1, "tilemapMode");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
site.tilemapMode(TilemapMode(lua_tointeger(L, -1)));
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
// How the tileset must be modified depending on this tool usage
|
|
|
|
type = lua_getfield(L, 1, "tilesetMode");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
site.tilesetMode(TilesetMode(lua_tointeger(L, -1)));
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2020-05-20 07:11:47 +08:00
|
|
|
// Color
|
2019-03-16 04:03:02 +08:00
|
|
|
type = lua_getfield(L, 1, "color");
|
|
|
|
if (type != LUA_TNIL)
|
2020-05-20 07:11:47 +08:00
|
|
|
params.fg = convert_args_into_color(L, -1);
|
2024-09-04 06:25:19 +08:00
|
|
|
else if (site.tilemapMode() == TilemapMode::Tiles)
|
|
|
|
params.fg = Color::fromTile(Preferences::instance().colorBar.fgTile());
|
|
|
|
else // Default color is the active fgColor
|
2020-05-20 07:11:47 +08:00
|
|
|
params.fg = Preferences::instance().colorBar.fgColor();
|
2019-03-16 04:03:02 +08:00
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2020-05-20 07:11:47 +08:00
|
|
|
type = lua_getfield(L, 1, "bgColor");
|
|
|
|
if (type != LUA_TNIL)
|
|
|
|
params.bg = convert_args_into_color(L, -1);
|
2024-09-04 06:25:19 +08:00
|
|
|
else if (site.tilemapMode() == TilemapMode::Tiles)
|
|
|
|
params.bg = Color::fromTile(Preferences::instance().colorBar.bgTile());
|
2020-05-20 07:11:47 +08:00
|
|
|
else
|
2024-09-04 06:25:19 +08:00
|
|
|
params.bg = Preferences::instance().colorBar.bgColor();
|
2020-05-20 07:11:47 +08:00
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2020-05-21 03:34:43 +08:00
|
|
|
// Adjust ink depending on "inkType" and "color"
|
|
|
|
// (e.g. InkType::SIMPLE depends on the color too, to adjust
|
|
|
|
// eraser/alpha compositing/opaque depending on the color alpha
|
|
|
|
// value).
|
|
|
|
params.ink =
|
|
|
|
activeToolMgr->adjustToolInkDependingOnSelectedInkType(params.ink, params.inkType, params.fg);
|
|
|
|
|
2020-05-20 07:11:47 +08:00
|
|
|
// Brush
|
|
|
|
type = lua_getfield(L, 1, "brush");
|
|
|
|
if (type != LUA_TNIL)
|
|
|
|
params.brush = get_brush_from_arg(L, -1);
|
|
|
|
else {
|
|
|
|
// Default brush is the active brush in the context bar
|
|
|
|
if (App::instance()->isGui() && App::instance()->contextBar()) {
|
|
|
|
params.brush = App::instance()->contextBar()->activeBrush(params.tool, params.ink);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
if (!params.brush) {
|
|
|
|
// In case the brush is nullptr (e.g. there is no UI) we use the
|
|
|
|
// default 1 pixel brush (e.g. to run scripts from CLI).
|
|
|
|
params.brush.reset(new Brush(BrushType::kCircleBrushType, 1, 0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Opacity, tolerance, and others
|
|
|
|
type = lua_getfield(L, 1, "opacity");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
params.opacity = lua_tointeger(L, -1);
|
2022-06-10 05:28:06 +08:00
|
|
|
params.opacity = std::clamp(params.opacity, 0, 255);
|
2020-05-20 07:11:47 +08:00
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "tolerance");
|
|
|
|
if (type != LUA_TNIL) {
|
|
|
|
params.tolerance = lua_tointeger(L, -1);
|
2022-06-10 05:28:06 +08:00
|
|
|
params.tolerance = std::clamp(params.tolerance, 0, 255);
|
2020-05-20 07:11:47 +08:00
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "contiguous");
|
|
|
|
if (type != LUA_TNIL)
|
|
|
|
params.contiguous = lua_toboolean(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "freehandAlgorithm");
|
2019-04-19 09:23:33 +08:00
|
|
|
if (type != LUA_TNIL)
|
2020-05-20 07:11:47 +08:00
|
|
|
params.freehandAlgorithm = get_value_from_lua<tools::FreehandAlgorithm>(L, -1);
|
2019-04-19 09:23:33 +08:00
|
|
|
lua_pop(L, 1);
|
|
|
|
|
2021-04-23 00:00:13 +08:00
|
|
|
if (params.ink->isSelection()) {
|
|
|
|
gen::SelectionMode selectionMode = Preferences::instance().selection.mode();
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "selection");
|
|
|
|
if (type != LUA_TNIL)
|
|
|
|
selectionMode = get_value_from_lua<gen::SelectionMode>(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
switch (selectionMode) {
|
|
|
|
case gen::SelectionMode::REPLACE:
|
|
|
|
params.modifiers = tools::ToolLoopModifiers::kReplaceSelection;
|
|
|
|
break;
|
|
|
|
case gen::SelectionMode::ADD:
|
|
|
|
params.modifiers = tools::ToolLoopModifiers::kAddSelection;
|
|
|
|
break;
|
|
|
|
case gen::SelectionMode::SUBTRACT:
|
|
|
|
params.modifiers = tools::ToolLoopModifiers::kSubtractSelection;
|
|
|
|
break;
|
|
|
|
case gen::SelectionMode::INTERSECT:
|
|
|
|
params.modifiers = tools::ToolLoopModifiers::kIntersectSelection;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-03-16 04:03:02 +08:00
|
|
|
// Do the tool loop
|
|
|
|
type = lua_getfield(L, 1, "points");
|
|
|
|
if (type == LUA_TTABLE) {
|
2022-02-11 21:41:56 +08:00
|
|
|
InlineCommandExecution inlineCmd(ctx);
|
2022-02-11 21:19:32 +08:00
|
|
|
|
2020-05-20 07:11:47 +08:00
|
|
|
std::unique_ptr<tools::ToolLoop> loop(create_tool_loop_for_script(ctx, site, params));
|
2019-03-24 03:34:51 +08:00
|
|
|
if (!loop)
|
|
|
|
return luaL_error(L, "cannot draw in the active site");
|
|
|
|
|
2019-03-16 04:03:02 +08:00
|
|
|
tools::ToolLoopManager manager(loop.get());
|
|
|
|
tools::Pointer lastPointer;
|
|
|
|
bool first = true;
|
|
|
|
|
|
|
|
lua_pushnil(L);
|
Fix transparent color is possible on opaque sprites (fix #4370)
To reproduce the error before this fix on RGBA/Grayscale Color Mode:
- New 100x100 RGBA/Grayscale opaque sprite (white background).
- Draw something with some gray color in the palette.
- Keep the selected gray color as primary color.
- Configure as secondary color the mask color (#000000 alpha=0).
- Pick 'eraser' tool and erase over the gray color with right click.
- Result: The sprite doesn't look more opaque, which is wrong. Also,
if we export this sprite, the transparent parts will turn black.
A similar problem occurs in Indexed Color Mode, but getting a
transparent color in a Background sprite is inevitable if the color of
a palette entry is transparent or semi-transparent, since the index
must be set as is. This could be fixed in the future at the
render stage, however, this could lead to other perceived
inconsistencies. For now it'll be left as is.
Original issue description:
Downloaded PNG in RGB mode fails to support transparency: erase
uses secondary color and export PNG replaces transparent color
with black
Added tests for 'eraser' in 'Replace Color Mode'
To make the eraser work in 'Replace Color Mode' within the tests,
was implemented the possibility of using the right button in
the creation of the point vector.
During testing with UI available it was observed that the 'bg' color
was copied from the 'fg'. Changed this to be compatible with the way
the default value of 'fg' is assigned when it is not specified.
This last modification resulted in errors during 'tilemap.lua' due to
incompatibility of the type of 'bg' color. This was corrected
considering the color type of 'fg' color.
Furthermore, it was found that the command 'app.range.tiles = { 1 }'
did not finish assigning the tile picks to the activeSite,
then 'assert(1, #app.range.tiles)' was failing. This was fixed too.
2024-08-22 22:07:54 +08:00
|
|
|
tools::ToolBox* toolbox = App::instance()->toolBox();
|
|
|
|
const bool isSelectionInk = (params.ink ==
|
|
|
|
toolbox->getInkById(tools::WellKnownInks::Selection));
|
|
|
|
const tools::Pointer::Button button = (!isSelectionInk ?
|
|
|
|
(buttonIdx == 0 ? tools::Pointer::Button::Left :
|
|
|
|
tools::Pointer::Button::Right) :
|
|
|
|
tools::Pointer::Button::Left);
|
2019-03-16 04:03:02 +08:00
|
|
|
while (lua_next(L, -2) != 0) {
|
|
|
|
gfx::Point pt = convert_args_into_point(L, -1);
|
|
|
|
|
2020-04-22 09:27:49 +08:00
|
|
|
tools::Pointer pointer(pt,
|
|
|
|
// TODO configurable params
|
2020-04-24 22:19:35 +08:00
|
|
|
tools::Vec2(0.0f, 0.0f),
|
Fix transparent color is possible on opaque sprites (fix #4370)
To reproduce the error before this fix on RGBA/Grayscale Color Mode:
- New 100x100 RGBA/Grayscale opaque sprite (white background).
- Draw something with some gray color in the palette.
- Keep the selected gray color as primary color.
- Configure as secondary color the mask color (#000000 alpha=0).
- Pick 'eraser' tool and erase over the gray color with right click.
- Result: The sprite doesn't look more opaque, which is wrong. Also,
if we export this sprite, the transparent parts will turn black.
A similar problem occurs in Indexed Color Mode, but getting a
transparent color in a Background sprite is inevitable if the color of
a palette entry is transparent or semi-transparent, since the index
must be set as is. This could be fixed in the future at the
render stage, however, this could lead to other perceived
inconsistencies. For now it'll be left as is.
Original issue description:
Downloaded PNG in RGB mode fails to support transparency: erase
uses secondary color and export PNG replaces transparent color
with black
Added tests for 'eraser' in 'Replace Color Mode'
To make the eraser work in 'Replace Color Mode' within the tests,
was implemented the possibility of using the right button in
the creation of the point vector.
During testing with UI available it was observed that the 'bg' color
was copied from the 'fg'. Changed this to be compatible with the way
the default value of 'fg' is assigned when it is not specified.
This last modification resulted in errors during 'tilemap.lua' due to
incompatibility of the type of 'bg' color. This was corrected
considering the color type of 'fg' color.
Furthermore, it was found that the command 'app.range.tiles = { 1 }'
did not finish assigning the tile picks to the activeSite,
then 'assert(1, #app.range.tiles)' was failing. This was fixed too.
2024-08-22 22:07:54 +08:00
|
|
|
button,
|
2020-04-22 09:27:49 +08:00
|
|
|
tools::Pointer::Type::Unknown,
|
|
|
|
0.0f);
|
2019-03-16 04:03:02 +08:00
|
|
|
if (first) {
|
|
|
|
first = false;
|
|
|
|
manager.prepareLoop(pointer);
|
|
|
|
manager.pressButton(pointer);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
manager.movement(pointer);
|
|
|
|
}
|
|
|
|
lastPointer = pointer;
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
if (!first)
|
|
|
|
manager.releaseButton(lastPointer);
|
|
|
|
|
2020-09-01 22:57:47 +08:00
|
|
|
manager.end();
|
2019-03-16 04:03:02 +08:00
|
|
|
}
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2025-08-13 23:26:03 +08:00
|
|
|
int App_tip(lua_State* L)
|
|
|
|
{
|
|
|
|
const auto* ctx = App::instance()->context();
|
|
|
|
if (!ctx || !ctx->isUIAvailable() || !StatusBar::instance())
|
|
|
|
return 0; // No UI to show the tooltip
|
|
|
|
|
|
|
|
std::string text;
|
|
|
|
double duration = 2.0;
|
|
|
|
|
|
|
|
if (lua_istable(L, 1)) {
|
|
|
|
int type = lua_getfield(L, 1, "text");
|
|
|
|
if (type == LUA_TSTRING)
|
|
|
|
text = lua_tostring(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
|
|
|
|
type = lua_getfield(L, 1, "duration");
|
|
|
|
if (type == LUA_TNUMBER)
|
|
|
|
duration = lua_tonumber(L, -1);
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
if (!lua_isstring(L, 1))
|
|
|
|
return luaL_error(L, "app.tip text parameter must be a string");
|
|
|
|
|
|
|
|
text = lua_tostring(L, 1);
|
|
|
|
|
|
|
|
if (lua_isnumber(L, 2))
|
|
|
|
duration = lua_tonumber(L, 2);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (text.empty())
|
|
|
|
return luaL_error(L, "app.tip text cannot be empty");
|
|
|
|
|
|
|
|
int msecs = std::clamp<int>(duration * 1000.0, 500, 30000);
|
|
|
|
StatusBar::instance()->showTip(msecs, text);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2021-10-08 05:56:39 +08:00
|
|
|
int App_get_events(lua_State* L)
|
|
|
|
{
|
|
|
|
push_app_events(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2022-12-16 20:50:36 +08:00
|
|
|
int App_get_theme(lua_State* L)
|
|
|
|
{
|
|
|
|
push_app_theme(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-03-21 05:26:44 +08:00
|
|
|
int App_get_uiScale(lua_State* L)
|
|
|
|
{
|
|
|
|
lua_pushinteger(L, ui::guiscale());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 06:10:28 +08:00
|
|
|
int App_get_editor(lua_State* L)
|
|
|
|
{
|
|
|
|
auto ctx = UIContext::instance();
|
|
|
|
if (Editor* editor = ctx->activeEditor()) {
|
|
|
|
push_editor(L, editor);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-12-03 08:44:10 +08:00
|
|
|
int App_get_clipboard(lua_State* L)
|
|
|
|
{
|
|
|
|
push_app_clipboard(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_sprite(lua_State* L)
|
2016-04-07 02:37:13 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Doc* doc = ctx->activeDocument();
|
2016-04-07 02:37:13 +08:00
|
|
|
if (doc)
|
2018-11-21 05:12:43 +08:00
|
|
|
push_docobj(L, doc->sprite());
|
2016-04-07 06:05:06 +08:00
|
|
|
else
|
2018-08-20 22:25:08 +08:00
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
2016-04-07 06:05:06 +08:00
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_layer(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
|
|
|
if (site.layer())
|
2018-11-21 05:12:43 +08:00
|
|
|
push_docobj(L, site.layer());
|
2018-09-12 23:17:48 +08:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_frame(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
2018-11-01 04:18:09 +08:00
|
|
|
if (site.sprite())
|
|
|
|
push_sprite_frame(L, site.sprite(), site.frame());
|
2018-09-12 23:17:48 +08:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_cel(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
|
|
|
if (site.cel())
|
|
|
|
push_sprite_cel(L, site.cel());
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_image(lua_State* L)
|
2016-04-07 06:05:06 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
2018-08-31 07:47:11 +08:00
|
|
|
if (site.cel())
|
|
|
|
push_cel_image(L, site.cel());
|
2018-08-21 01:20:27 +08:00
|
|
|
else
|
2018-08-20 22:25:08 +08:00
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
2016-04-07 02:37:13 +08:00
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_tag(lua_State* L)
|
2018-10-26 21:52:40 +08:00
|
|
|
{
|
2019-10-02 01:55:08 +08:00
|
|
|
Tag* tag = nullptr;
|
2018-10-26 21:52:40 +08:00
|
|
|
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
|
|
|
if (site.sprite()) {
|
|
|
|
if (App::instance()->timeline()) {
|
2019-10-02 01:55:08 +08:00
|
|
|
tag = App::instance()->timeline()->getTagByFrame(site.frame(), false);
|
2018-10-26 21:52:40 +08:00
|
|
|
}
|
2024-09-03 10:12:13 +08:00
|
|
|
else {
|
2018-10-26 21:52:40 +08:00
|
|
|
tag = get_animation_tag(site.sprite(), site.frame());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (tag)
|
2018-11-21 05:12:43 +08:00
|
|
|
push_docobj(L, tag);
|
2018-10-26 21:52:40 +08:00
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-09-12 23:55:56 +08:00
|
|
|
int App_get_sprites(lua_State* L)
|
|
|
|
{
|
|
|
|
push_sprites(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-09-07 00:02:23 +08:00
|
|
|
int App_get_fgColor(lua_State* L)
|
|
|
|
{
|
|
|
|
push_obj<app::Color>(L, Preferences::instance().colorBar.fgColor());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int App_set_fgColor(lua_State* L)
|
|
|
|
{
|
2018-09-13 21:53:43 +08:00
|
|
|
Preferences::instance().colorBar.fgColor(convert_args_into_color(L, 2));
|
2018-09-07 00:02:23 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int App_get_bgColor(lua_State* L)
|
|
|
|
{
|
|
|
|
push_obj<app::Color>(L, Preferences::instance().colorBar.bgColor());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int App_set_bgColor(lua_State* L)
|
|
|
|
{
|
2018-09-13 21:53:43 +08:00
|
|
|
Preferences::instance().colorBar.bgColor(convert_args_into_color(L, 2));
|
2018-09-07 00:02:23 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2024-08-27 22:04:56 +08:00
|
|
|
int App_get_fgTile(lua_State* L)
|
|
|
|
{
|
|
|
|
lua_pushinteger(L, Preferences::instance().colorBar.fgTile());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int App_set_fgTile(lua_State* L)
|
|
|
|
{
|
|
|
|
Preferences::instance().colorBar.fgTile(lua_tointeger(L, 2));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int App_get_bgTile(lua_State* L)
|
|
|
|
{
|
|
|
|
lua_pushinteger(L, Preferences::instance().colorBar.bgTile());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
int App_set_bgTile(lua_State* L)
|
|
|
|
{
|
|
|
|
Preferences::instance().colorBar.bgTile(lua_tointeger(L, 2));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
int App_get_site(lua_State* L)
|
2018-08-21 02:15:38 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
|
|
|
push_obj(L, site);
|
|
|
|
return 1;
|
2018-08-21 02:15:38 +08:00
|
|
|
}
|
|
|
|
|
2018-11-23 23:56:30 +08:00
|
|
|
int App_get_range(lua_State* L)
|
|
|
|
{
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
Site site = ctx->activeSite();
|
2019-08-11 01:37:18 +08:00
|
|
|
push_doc_range(L, site);
|
2018-11-23 23:56:30 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-09-08 03:06:32 +08:00
|
|
|
int App_get_isUIAvailable(lua_State* L)
|
|
|
|
{
|
|
|
|
app::Context* ctx = App::instance()->context();
|
|
|
|
lua_pushboolean(L, ctx && ctx->isUIAvailable());
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
int App_get_version(lua_State* L)
|
2016-04-07 02:37:13 +08:00
|
|
|
{
|
2020-03-16 21:29:58 +08:00
|
|
|
std::string ver = get_app_version();
|
2019-05-22 01:06:35 +08:00
|
|
|
base::replace_string(ver, "-x64", ""); // Remove "-x64" suffix
|
|
|
|
push_version(L, base::Version(ver));
|
2018-08-20 22:25:08 +08:00
|
|
|
return 1;
|
2016-04-07 02:37:13 +08:00
|
|
|
}
|
|
|
|
|
2018-11-14 09:31:14 +08:00
|
|
|
int App_get_apiVersion(lua_State* L)
|
|
|
|
{
|
|
|
|
lua_pushinteger(L, API_VERSION);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_tool(lua_State* L)
|
2019-04-19 09:23:33 +08:00
|
|
|
{
|
|
|
|
tools::Tool* tool = App::instance()->activeToolManager()->activeTool();
|
|
|
|
push_tool(L, tool);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_get_brush(lua_State* L)
|
2019-04-19 09:23:33 +08:00
|
|
|
{
|
|
|
|
App* app = App::instance();
|
|
|
|
if (app->isGui()) {
|
|
|
|
doc::BrushRef brush = app->contextBar()->activeBrush();
|
|
|
|
push_brush(L, brush);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
push_brush(L, doc::BrushRef(new doc::Brush()));
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2019-04-25 06:08:25 +08:00
|
|
|
int App_get_defaultPalette(lua_State* L)
|
|
|
|
{
|
|
|
|
const Palette* pal = get_default_palette();
|
|
|
|
if (pal)
|
|
|
|
push_palette(L, new Palette(*pal));
|
|
|
|
else
|
|
|
|
lua_pushnil(L);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-07-19 04:54:57 +08:00
|
|
|
int App_get_window(lua_State* L)
|
2023-06-12 00:27:50 +08:00
|
|
|
{
|
|
|
|
App* app = App::instance();
|
2023-07-19 04:54:57 +08:00
|
|
|
if (app && app->mainWindow()) {
|
|
|
|
push_ptr(L, (ui::Window*)app->mainWindow());
|
|
|
|
}
|
2024-09-03 10:12:13 +08:00
|
|
|
else {
|
2023-07-19 04:54:57 +08:00
|
|
|
lua_pushnil(L);
|
|
|
|
}
|
2023-06-12 00:27:50 +08:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_sprite(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
2023-05-19 00:20:10 +08:00
|
|
|
auto sprite = may_get_docobj<Sprite>(L, 2);
|
2018-09-12 23:17:48 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
2023-05-19 00:20:10 +08:00
|
|
|
doc::Document* doc = (sprite ? sprite->document() : nullptr);
|
2018-09-12 23:17:48 +08:00
|
|
|
ctx->setActiveDocument(static_cast<Doc*>(doc));
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_layer(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
2019-03-19 00:30:17 +08:00
|
|
|
auto layer = get_docobj<Layer>(L, 2);
|
2018-09-12 23:17:48 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
2019-04-14 03:25:21 +08:00
|
|
|
ctx->setActiveLayer(layer);
|
2018-09-12 23:17:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_frame(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
2019-03-19 00:30:17 +08:00
|
|
|
const doc::frame_t frame = get_frame_number_from_arg(L, 2);
|
2018-09-12 23:17:48 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
2019-04-14 03:25:21 +08:00
|
|
|
ctx->setActiveFrame(frame);
|
2018-09-12 23:17:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_cel(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
2019-03-19 00:30:17 +08:00
|
|
|
const auto cel = get_docobj<Cel>(L, 2);
|
2018-09-12 23:17:48 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
2019-04-14 03:25:21 +08:00
|
|
|
ctx->setActiveLayer(cel->layer());
|
|
|
|
ctx->setActiveFrame(cel->frame());
|
2018-09-12 23:17:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_image(lua_State* L)
|
2018-09-12 23:17:48 +08:00
|
|
|
{
|
|
|
|
const auto cel = get_image_cel_from_arg(L, 2);
|
|
|
|
if (!cel)
|
|
|
|
return 0;
|
2019-04-14 03:25:21 +08:00
|
|
|
|
2018-09-12 23:17:48 +08:00
|
|
|
app::Context* ctx = App::instance()->context();
|
2019-04-14 03:25:21 +08:00
|
|
|
ctx->setActiveLayer(cel->layer());
|
|
|
|
ctx->setActiveFrame(cel->frame());
|
2018-09-12 23:17:48 +08:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_tool(lua_State* L)
|
2019-03-23 20:18:59 +08:00
|
|
|
{
|
|
|
|
if (auto tool = get_tool_from_arg(L, 2))
|
|
|
|
App::instance()->activeToolManager()->setSelectedTool(tool);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
int App_set_brush(lua_State* L)
|
2019-04-19 09:23:33 +08:00
|
|
|
{
|
|
|
|
if (auto brush = get_brush_from_arg(L, 2)) {
|
|
|
|
App* app = App::instance();
|
|
|
|
if (app->isGui())
|
|
|
|
app->contextBar()->setActiveBrush(brush);
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2019-04-25 06:08:25 +08:00
|
|
|
int App_set_defaultPalette(lua_State* L)
|
|
|
|
{
|
|
|
|
if (const doc::Palette* pal = get_palette_from_arg(L, 2))
|
|
|
|
set_default_palette(pal);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
const luaL_Reg App_methods[] = {
|
|
|
|
{ "open", App_open },
|
|
|
|
{ "exit", App_exit },
|
|
|
|
{ "transaction", App_transaction },
|
|
|
|
{ "undo", App_undo },
|
|
|
|
{ "redo", App_redo },
|
2018-09-08 03:42:58 +08:00
|
|
|
{ "alert", App_alert },
|
2018-11-27 21:24:13 +08:00
|
|
|
{ "refresh", App_refresh },
|
2019-03-24 00:31:14 +08:00
|
|
|
{ "useTool", App_useTool },
|
2025-08-13 23:26:03 +08:00
|
|
|
{ "tip", App_tip },
|
2018-08-20 22:25:08 +08:00
|
|
|
{ nullptr, nullptr }
|
2016-04-07 02:37:13 +08:00
|
|
|
};
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
const Property App_properties[] = {
|
2023-04-20 00:41:11 +08:00
|
|
|
// Deprecated longer fields
|
|
|
|
{ "activeSprite", App_get_sprite, App_set_sprite },
|
|
|
|
{ "activeLayer", App_get_layer, App_set_layer },
|
|
|
|
{ "activeFrame", App_get_frame, App_set_frame },
|
|
|
|
{ "activeCel", App_get_cel, App_set_cel },
|
|
|
|
{ "activeImage", App_get_image, App_set_image },
|
|
|
|
{ "activeTag", App_get_tag, nullptr },
|
|
|
|
{ "activeTool", App_get_tool, App_set_tool },
|
|
|
|
{ "activeBrush", App_get_brush, App_set_brush },
|
|
|
|
|
|
|
|
// New shorter fields
|
|
|
|
{ "sprite", App_get_sprite, App_set_sprite },
|
|
|
|
{ "layer", App_get_layer, App_set_layer },
|
|
|
|
{ "frame", App_get_frame, App_set_frame },
|
|
|
|
{ "cel", App_get_cel, App_set_cel },
|
|
|
|
{ "image", App_get_image, App_set_image },
|
|
|
|
{ "tag", App_get_tag, nullptr },
|
|
|
|
{ "tool", App_get_tool, App_set_tool },
|
|
|
|
{ "brush", App_get_brush, App_set_brush },
|
2024-12-17 01:52:19 +08:00
|
|
|
|
2023-04-20 00:41:11 +08:00
|
|
|
{ "sprites", App_get_sprites, nullptr },
|
|
|
|
{ "fgColor", App_get_fgColor, App_set_fgColor },
|
|
|
|
{ "bgColor", App_get_bgColor, App_set_bgColor },
|
2024-08-27 22:04:56 +08:00
|
|
|
{ "fgTile", App_get_fgTile, App_set_fgTile },
|
|
|
|
{ "bgTile", App_get_bgTile, App_set_bgTile },
|
2023-04-20 00:41:11 +08:00
|
|
|
{ "version", App_get_version, nullptr },
|
|
|
|
{ "apiVersion", App_get_apiVersion, nullptr },
|
|
|
|
{ "site", App_get_site, nullptr },
|
|
|
|
{ "range", App_get_range, nullptr },
|
|
|
|
{ "isUIAvailable", App_get_isUIAvailable, nullptr },
|
2019-04-25 06:08:25 +08:00
|
|
|
{ "defaultPalette", App_get_defaultPalette, App_set_defaultPalette },
|
2023-07-19 04:54:57 +08:00
|
|
|
{ "window", App_get_window, nullptr },
|
2023-04-20 00:41:11 +08:00
|
|
|
{ "events", App_get_events, nullptr },
|
|
|
|
{ "theme", App_get_theme, nullptr },
|
|
|
|
{ "uiScale", App_get_uiScale, nullptr },
|
2023-04-20 06:10:28 +08:00
|
|
|
{ "editor", App_get_editor, nullptr },
|
2024-12-03 08:44:10 +08:00
|
|
|
{ "clipboard", App_get_clipboard, nullptr },
|
2023-04-20 00:41:11 +08:00
|
|
|
{ nullptr, nullptr, nullptr }
|
2016-04-07 02:37:13 +08:00
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
DEF_MTNAME(App);
|
|
|
|
|
|
|
|
void register_app_object(lua_State* L)
|
2016-04-07 02:37:13 +08:00
|
|
|
{
|
2018-08-20 22:25:08 +08:00
|
|
|
REG_CLASS(L, App);
|
|
|
|
REG_CLASS_PROPERTIES(L, App);
|
|
|
|
|
|
|
|
lua_newtable(L); // Create a table which will be the "app" object
|
|
|
|
lua_pushvalue(L, -1);
|
|
|
|
luaL_getmetatable(L, get_mtname<App>());
|
|
|
|
lua_setmetatable(L, -2);
|
|
|
|
lua_setglobal(L, "app");
|
|
|
|
lua_pop(L, 1); // Pop app table
|
2016-04-07 02:37:13 +08:00
|
|
|
}
|
|
|
|
|
2018-12-11 03:44:49 +08:00
|
|
|
void set_app_params(lua_State* L, const Params& params)
|
|
|
|
{
|
|
|
|
lua_getglobal(L, "app");
|
|
|
|
lua_newtable(L);
|
|
|
|
for (const auto& kv : params) {
|
|
|
|
lua_pushstring(L, kv.second.c_str());
|
|
|
|
lua_setfield(L, -2, kv.first.c_str());
|
|
|
|
}
|
|
|
|
lua_setfield(L, -2, "params");
|
|
|
|
lua_pop(L, 1);
|
|
|
|
}
|
|
|
|
|
2018-08-20 22:25:08 +08:00
|
|
|
}} // namespace app::script
|