mirror of
https://github.com/aseprite/aseprite.git
synced 2025-04-16 14:42:44 +00:00
Merge branch 'master' into beta
This commit is contained in:
commit
20d763ca4b
2
laf
2
laf
@ -1 +1 @@
|
||||
Subproject commit c8e6b958fdcca7809ab2b50f9cfed7d9387a2c86
|
||||
Subproject commit ab08b047defdd3390e3aaa55a02c6a10225496ac
|
@ -347,6 +347,7 @@ int App::initialize(const AppOptions& options)
|
||||
return code;
|
||||
}
|
||||
|
||||
LOG("APP: Finish launching...\n");
|
||||
system->finishLaunching();
|
||||
return 0;
|
||||
}
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "doc/slice.h"
|
||||
#include "doc/tag.h"
|
||||
#include "doc/tags.h"
|
||||
#include "os/system.h"
|
||||
#include "render/dithering_algorithm.h"
|
||||
|
||||
#include <algorithm>
|
||||
@ -576,8 +577,14 @@ int CliProcessor::process(Context* ctx)
|
||||
else {
|
||||
cof.document = nullptr;
|
||||
cof.filename = base::normalize_path(value.value());
|
||||
if (openFile(ctx, cof))
|
||||
|
||||
if (// Check that the filename wasn't used loading a sequence
|
||||
// of images as one sprite
|
||||
m_usedFiles.find(cof.filename) == m_usedFiles.end() &&
|
||||
// Open sprite
|
||||
openFile(ctx, cof)) {
|
||||
lastDoc = cof.document;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -610,12 +617,19 @@ bool CliProcessor::openFile(Context* ctx, CliOpenFile& cof)
|
||||
m_delegate->beforeOpenFile(cof);
|
||||
|
||||
Doc* oldDoc = ctx->activeDocument();
|
||||
Command* openCommand = Commands::instance()->byId(CommandId::OpenFile());
|
||||
Params params;
|
||||
params.set("filename", cof.filename.c_str());
|
||||
if (cof.oneFrame)
|
||||
params.set("oneframe", "true");
|
||||
ctx->executeCommand(openCommand, params);
|
||||
|
||||
m_batch.open(ctx,
|
||||
cof.filename,
|
||||
cof.oneFrame);
|
||||
|
||||
// Mark used file names as "already processed" so we don't try to
|
||||
// open then again
|
||||
for (const auto& usedFn : m_batch.usedFiles()) {
|
||||
auto fn = base::normalize_path(usedFn);
|
||||
m_usedFiles.insert(fn);
|
||||
|
||||
os::instance()->markCliFileAsProcessed(fn);
|
||||
}
|
||||
|
||||
Doc* doc = ctx->activeDocument();
|
||||
// If the active document is equal to the previous one, it
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -12,9 +12,11 @@
|
||||
#include "app/cli/cli_delegate.h"
|
||||
#include "app/cli/cli_open_file.h"
|
||||
#include "app/doc_exporter.h"
|
||||
#include "app/util/open_batch.h"
|
||||
#include "doc/selected_layers.h"
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@ -58,6 +60,11 @@ namespace app {
|
||||
CliDelegate* m_delegate;
|
||||
const AppOptions& m_options;
|
||||
std::unique_ptr<DocExporter> m_exporter;
|
||||
|
||||
// Files already used in the CLI processing (e.g. when used to
|
||||
// load a sequence of files) so we don't ask for them again.
|
||||
std::set<std::string> m_usedFiles;
|
||||
OpenBatchOfFiles m_batch;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -11,7 +11,11 @@
|
||||
|
||||
#include "app/cmd/set_tag_range.h"
|
||||
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_event.h"
|
||||
#include "doc/sprite.h"
|
||||
#include "doc/tag.h"
|
||||
#include "doc/tags.h"
|
||||
|
||||
namespace app {
|
||||
namespace cmd {
|
||||
@ -37,5 +41,16 @@ void SetTagRange::onUndo()
|
||||
tag()->incrementVersion();
|
||||
}
|
||||
|
||||
void SetTagRange::onFireNotifications()
|
||||
{
|
||||
Tag* tag = this->tag();
|
||||
Sprite* sprite = tag->owner()->sprite();
|
||||
Doc* doc = static_cast<Doc*>(sprite->document());
|
||||
DocEvent ev(doc);
|
||||
ev.sprite(sprite);
|
||||
ev.tag(tag);
|
||||
doc->notify_observers<DocEvent&>(&DocObserver::onTagChange, ev);
|
||||
}
|
||||
|
||||
} // namespace cmd
|
||||
} // namespace app
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2015 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -25,6 +25,7 @@ namespace cmd {
|
||||
protected:
|
||||
void onExecute() override;
|
||||
void onUndo() override;
|
||||
void onFireNotifications() override;
|
||||
size_t onMemSize() const override {
|
||||
return sizeof(*this);
|
||||
}
|
||||
|
@ -42,10 +42,12 @@ using namespace app::skin;
|
||||
#endif
|
||||
|
||||
struct CanvasSizeParams : public NewParams {
|
||||
Param<bool> ui { this, true, "ui" };
|
||||
Param<int> left { this, 0, "left" };
|
||||
Param<int> right { this, 0, "right" };
|
||||
Param<int> top { this, 0, "top" };
|
||||
Param<int> bottom { this, 0, "bottom" };
|
||||
Param<gfx::Rect> bounds { this, gfx::Rect(0, 0, 0, 0), "bounds" };
|
||||
Param<bool> trimOutside { this, false, "trimOutside" };
|
||||
};
|
||||
|
||||
@ -58,9 +60,10 @@ class CanvasSizeWindow : public app::gen::CanvasSize
|
||||
public:
|
||||
enum class Dir { NW, N, NE, W, C, E, SW, S, SE };
|
||||
|
||||
CanvasSizeWindow(const CanvasSizeParams& params)
|
||||
CanvasSizeWindow(const CanvasSizeParams& params,
|
||||
const gfx::Rect& bounds)
|
||||
: m_editor(current_editor)
|
||||
, m_rect(0, 0, current_editor->sprite()->width(), current_editor->sprite()->height())
|
||||
, m_rect(bounds)
|
||||
, m_selectBoxState(
|
||||
new SelectBoxState(
|
||||
this, m_rect,
|
||||
@ -73,6 +76,7 @@ public:
|
||||
setRight(0);
|
||||
setTop(0);
|
||||
setBottom(0);
|
||||
updateBorderFromRect();
|
||||
|
||||
width() ->Change.connect([this]{ onSizeChange(); });
|
||||
height()->Change.connect([this]{ onSizeChange(); });
|
||||
@ -307,18 +311,31 @@ bool CanvasSizeCommand::onEnabled(Context* context)
|
||||
|
||||
void CanvasSizeCommand::onExecute(Context* context)
|
||||
{
|
||||
#ifdef ENABLE_UI
|
||||
const bool ui = (params().ui() && context->isUIAvailable());
|
||||
#endif
|
||||
const ContextReader reader(context);
|
||||
const Sprite* sprite(reader.sprite());
|
||||
auto& params = this->params();
|
||||
|
||||
gfx::Rect bounds(0, 0, sprite->width(), sprite->height());
|
||||
if (params.bounds.isSet()) {
|
||||
bounds = params.bounds();
|
||||
}
|
||||
else {
|
||||
bounds.enlarge(
|
||||
gfx::Border(params.left(), params.top(),
|
||||
params.right(), params.bottom()));
|
||||
}
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
if (context->isUIAvailable()) {
|
||||
if (ui) {
|
||||
if (!params.trimOutside.isSet()) {
|
||||
params.trimOutside(Preferences::instance().canvasSize.trimOutside());
|
||||
}
|
||||
|
||||
// load the window widget
|
||||
std::unique_ptr<CanvasSizeWindow> window(new CanvasSizeWindow(params));
|
||||
std::unique_ptr<CanvasSizeWindow> window(new CanvasSizeWindow(params, bounds));
|
||||
|
||||
window->remapWindow();
|
||||
|
||||
@ -346,18 +363,15 @@ void CanvasSizeCommand::onExecute(Context* context)
|
||||
params.trimOutside(window->getTrimOutside());
|
||||
|
||||
Preferences::instance().canvasSize.trimOutside(params.trimOutside());
|
||||
|
||||
bounds.enlarge(
|
||||
gfx::Border(params.left(), params.top(),
|
||||
params.right(), params.bottom()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// Resize canvas
|
||||
|
||||
int x1 = -params.left();
|
||||
int y1 = -params.top();
|
||||
int x2 = sprite->width() + params.right();
|
||||
int y2 = sprite->height() + params.bottom();
|
||||
|
||||
if (x2 <= x1) x2 = x1+1;
|
||||
if (y2 <= y1) y2 = y1+1;
|
||||
if (bounds.w < 1) bounds.w = 1;
|
||||
if (bounds.h < 1) bounds.h = 1;
|
||||
|
||||
{
|
||||
ContextWriter writer(reader);
|
||||
@ -365,12 +379,7 @@ void CanvasSizeCommand::onExecute(Context* context)
|
||||
Sprite* sprite = writer.sprite();
|
||||
Tx tx(writer.context(), "Canvas Size");
|
||||
DocApi api = doc->getApi(tx);
|
||||
|
||||
api.cropSprite(sprite,
|
||||
gfx::Rect(x1, y1,
|
||||
base::clamp(x2-x1, 1, DOC_SPRITE_MAX_WIDTH),
|
||||
base::clamp(y2-y1, 1, DOC_SPRITE_MAX_HEIGHT)),
|
||||
params.trimOutside());
|
||||
api.cropSprite(sprite, bounds, params.trimOutside());
|
||||
tx.commit();
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
|
@ -122,6 +122,12 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
// The user cancelled the operation through UI
|
||||
return;
|
||||
}
|
||||
|
||||
// If the user selected several files, show the checkbox to repeat
|
||||
// the action for future filenames in the batch of selected files
|
||||
// to open.
|
||||
if (filenames.size() > 1)
|
||||
m_repeatCheckbox = true;
|
||||
}
|
||||
else
|
||||
#endif // ENABLE_UI
|
||||
@ -152,7 +158,11 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
if (m_oneFrame)
|
||||
flags |= FILE_LOAD_ONE_FRAME;
|
||||
|
||||
for (const auto& filename : filenames) {
|
||||
std::string filename;
|
||||
while (!filenames.empty()) {
|
||||
filename = filenames[0];
|
||||
filenames.erase(filenames.begin());
|
||||
|
||||
std::unique_ptr<FileOp> fop(
|
||||
FileOp::createLoadDocumentOperation(
|
||||
context, filename, flags));
|
||||
@ -168,18 +178,29 @@ void OpenFileCommand::onExecute(Context* context)
|
||||
}
|
||||
else {
|
||||
if (fop->isSequence()) {
|
||||
|
||||
if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_YES) {
|
||||
m_seqDecision = SequenceDecision::Agree;
|
||||
flags &= ~FILE_LOAD_SEQUENCE_ASK;
|
||||
flags |= FILE_LOAD_SEQUENCE_YES;
|
||||
}
|
||||
else if (fop->sequenceFlags() & FILE_LOAD_SEQUENCE_NONE) {
|
||||
m_seqDecision = SequenceDecision::Skip;
|
||||
flags &= ~FILE_LOAD_SEQUENCE_ASK;
|
||||
flags |= FILE_LOAD_SEQUENCE_NONE;
|
||||
}
|
||||
|
||||
m_usedFiles = fop->filenames();
|
||||
for (std::string fn : fop->filenames()) {
|
||||
fn = base::normalize_path(fn);
|
||||
m_usedFiles.push_back(fn);
|
||||
|
||||
auto it = std::find(filenames.begin(), filenames.end(), fn);
|
||||
if (it != filenames.end())
|
||||
filenames.erase(it);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_usedFiles.push_back(fop->filename());
|
||||
auto fn = base::normalize_path(fop->filename());
|
||||
m_usedFiles.push_back(fn);
|
||||
}
|
||||
|
||||
OpenFileJob task(fop.get());
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2016-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -27,6 +28,10 @@ namespace app {
|
||||
return m_usedFiles;
|
||||
}
|
||||
|
||||
SequenceDecision seqDecision() const {
|
||||
return m_seqDecision;
|
||||
}
|
||||
|
||||
protected:
|
||||
void onLoadParams(const Params& params) override;
|
||||
void onExecute(Context* context) override;
|
||||
|
@ -24,6 +24,7 @@
|
||||
#include "filters/hue_saturation_filter.h"
|
||||
#include "filters/outline_filter.h"
|
||||
#include "filters/tiled_mode.h"
|
||||
#include "gfx/rect.h"
|
||||
|
||||
#ifdef ENABLE_SCRIPTING
|
||||
#include "app/script/engine.h"
|
||||
@ -61,6 +62,21 @@ void Param<std::string>::fromString(const std::string& value)
|
||||
setValue(value);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Param<gfx::Rect>::fromString(const std::string& value)
|
||||
{
|
||||
gfx::Rect rect;
|
||||
std::vector<std::string> parts;
|
||||
base::split_string(value, parts, ",");
|
||||
if (parts.size() == 4) {
|
||||
rect.x = base::convert_to<int>(parts[0]);
|
||||
rect.y = base::convert_to<int>(parts[1]);
|
||||
rect.w = base::convert_to<int>(parts[2]);
|
||||
rect.h = base::convert_to<int>(parts[3]);
|
||||
}
|
||||
setValue(rect);
|
||||
}
|
||||
|
||||
template<>
|
||||
void Param<doc::algorithm::ResizeMethod>::fromString(const std::string& value)
|
||||
{
|
||||
@ -237,6 +253,12 @@ void Param<std::string>::fromLua(lua_State* L, int index)
|
||||
setValue(std::string());
|
||||
}
|
||||
|
||||
template<>
|
||||
void Param<gfx::Rect>::fromLua(lua_State* L, int index)
|
||||
{
|
||||
setValue(script::convert_args_into_rect(L, index));
|
||||
}
|
||||
|
||||
template<>
|
||||
void Param<doc::algorithm::ResizeMethod>::fromLua(lua_State* L, int index)
|
||||
{
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2018-2019 Igara Studio S.A.
|
||||
// Copyright (C) 2018-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -73,6 +73,9 @@ namespace app {
|
||||
virtual void onSelectionChanged(DocEvent& ev) { }
|
||||
virtual void onSelectionBoundariesChanged(DocEvent& ev) { }
|
||||
|
||||
// Tags
|
||||
virtual void onTagChange(DocEvent& ev) { }
|
||||
|
||||
// Slices
|
||||
virtual void onSliceNameChange(DocEvent& ev) { }
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2019 Igara Studio S.A.
|
||||
// Copyright (C) 2019-2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -17,9 +17,13 @@
|
||||
#include "base/cfile.h"
|
||||
#include "base/file_handle.h"
|
||||
#include "doc/doc.h"
|
||||
#include "fmt/format.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
// Max supported .bmp size (to filter out invalid image sizes)
|
||||
const uint32_t kMaxBmpSize = 1024*1024*128; // 128 MB
|
||||
|
||||
using namespace base;
|
||||
|
||||
class BmpFormat : public FileFormat {
|
||||
@ -212,6 +216,9 @@ static void read_bmicolors(FileOp* fop, int bytes, FILE *f, bool win_flag)
|
||||
}
|
||||
}
|
||||
|
||||
// Set the number of colors in the palette
|
||||
fop->sequenceSetNColors(j);
|
||||
|
||||
for (; i<bytes; i++)
|
||||
fgetc(f);
|
||||
}
|
||||
@ -643,6 +650,28 @@ bool BmpFormat::onLoad(FileOp *fop)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check image size is valid
|
||||
{
|
||||
if (int(infoheader.biWidth) < 1 ||
|
||||
ABS(int(infoheader.biHeight)) == 0) {
|
||||
fop->setError("Invalid BMP size.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t size = infoheader.biWidth * uint32_t(ABS(int(infoheader.biHeight)));
|
||||
if (infoheader.biBitCount >= 8)
|
||||
size *= (infoheader.biBitCount / 8);
|
||||
else if (8 / infoheader.biBitCount > 0)
|
||||
size /= (8 / infoheader.biBitCount);
|
||||
|
||||
if (size > kMaxBmpSize) {
|
||||
fop->setError(fmt::format("BMP size unsupported ({:.2f} MB > {:.2f} MB).\n",
|
||||
size / 1024.0 / 1024.0,
|
||||
kMaxBmpSize / 1024.0 / 1024.0).c_str());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((infoheader.biBitCount == 32) ||
|
||||
(infoheader.biBitCount == 24) ||
|
||||
(infoheader.biBitCount == 16))
|
||||
@ -723,21 +752,48 @@ bool BmpFormat::onLoad(FileOp *fop)
|
||||
bool BmpFormat::onSave(FileOp *fop)
|
||||
{
|
||||
const Image* image = fop->sequenceImage();
|
||||
const int w = image->width();
|
||||
const int h = image->height();
|
||||
int bfSize;
|
||||
int biSizeImage;
|
||||
int bpp = (image->pixelFormat() == IMAGE_RGB) ? 24 : 8;
|
||||
int filler = 3 - ((image->width()*(bpp/8)-1) & 3);
|
||||
int ncolors = fop->sequenceGetNColors();
|
||||
int bpp = 0;
|
||||
switch (image->pixelFormat()) {
|
||||
case IMAGE_RGB:
|
||||
bpp = 24;
|
||||
break;
|
||||
case IMAGE_GRAYSCALE:
|
||||
bpp = 8;
|
||||
break;
|
||||
case IMAGE_INDEXED: {
|
||||
if (ncolors > 16)
|
||||
bpp = 8;
|
||||
else if (ncolors > 2)
|
||||
bpp = 4;
|
||||
else
|
||||
bpp = 1;
|
||||
ncolors = (1 << bpp);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
// TODO save IMAGE_BITMAP as 1bpp bmp?
|
||||
// Invalid image format
|
||||
fop->setError("Unsupported color mode.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int filler = int((32 - ((w*bpp-1) & 31)-1) / 8);
|
||||
int c, i, j, r, g, b;
|
||||
|
||||
if (bpp == 8) {
|
||||
biSizeImage = (image->width() + filler) * image->height();
|
||||
bfSize = (54 /* header */
|
||||
+ 256*4 /* palette */
|
||||
+ biSizeImage); /* image data */
|
||||
if (bpp <= 8) {
|
||||
biSizeImage = (w + filler)*bpp/8 * h;
|
||||
bfSize = (54 // header
|
||||
+ ncolors*4 // palette
|
||||
+ biSizeImage); // image data
|
||||
}
|
||||
else {
|
||||
biSizeImage = (image->width()*3 + filler) * image->height();
|
||||
bfSize = 54 + biSizeImage; /* header + image data */
|
||||
biSizeImage = (w*3 + filler) * h;
|
||||
bfSize = 54 + biSizeImage; // header + image data
|
||||
}
|
||||
|
||||
FileHandle handle(open_file_with_exception_sync_on_close(fop->filename(), "wb"));
|
||||
@ -749,15 +805,15 @@ bool BmpFormat::onSave(FileOp *fop)
|
||||
fputw(0, f); /* bfReserved1 */
|
||||
fputw(0, f); /* bfReserved2 */
|
||||
|
||||
if (bpp == 8) /* bfOffBits */
|
||||
fputl(54+256*4, f);
|
||||
if (bpp <= 8) /* bfOffBits */
|
||||
fputl(54+ncolors*4, f);
|
||||
else
|
||||
fputl(54, f);
|
||||
|
||||
/* info_header */
|
||||
fputl(40, f); /* biSize */
|
||||
fputl(image->width(), f); /* biWidth */
|
||||
fputl(image->height(), f); /* biHeight */
|
||||
fputl(w, f); /* biWidth */
|
||||
fputl(h, f); /* biHeight */
|
||||
fputw(1, f); /* biPlanes */
|
||||
fputw(bpp, f); /* biBitCount */
|
||||
fputl(0, f); /* biCompression */
|
||||
@ -765,12 +821,12 @@ bool BmpFormat::onSave(FileOp *fop)
|
||||
fputl(0xB12, f); /* biXPelsPerMeter (0xB12 = 72 dpi) */
|
||||
fputl(0xB12, f); /* biYPelsPerMeter */
|
||||
|
||||
if (bpp == 8) {
|
||||
fputl(256, f); /* biClrUsed */
|
||||
fputl(256, f); /* biClrImportant */
|
||||
if (bpp <= 8) {
|
||||
fputl(ncolors, f); /* biClrUsed */
|
||||
fputl(ncolors, f); /* biClrImportant */
|
||||
|
||||
/* palette */
|
||||
for (i=0; i<256; i++) {
|
||||
// Save the palette
|
||||
for (i=0; i<ncolors; i++) {
|
||||
fop->sequenceGetColor(i, &r, &g, &b);
|
||||
fputc(b, f);
|
||||
fputc(g, f);
|
||||
@ -783,27 +839,49 @@ bool BmpFormat::onSave(FileOp *fop)
|
||||
fputl(0, f); /* biClrImportant */
|
||||
}
|
||||
|
||||
/* image data */
|
||||
for (i=image->height()-1; i>=0; i--) {
|
||||
for (j=0; j<image->width(); j++) {
|
||||
if (bpp == 8) {
|
||||
if (image->pixelFormat() == IMAGE_INDEXED)
|
||||
fputc(get_pixel_fast<IndexedTraits>(image, j, i), f);
|
||||
else if (image->pixelFormat() == IMAGE_GRAYSCALE)
|
||||
fputc(graya_getv(get_pixel_fast<GrayscaleTraits>(image, j, i)), f);
|
||||
}
|
||||
else {
|
||||
c = get_pixel_fast<RgbTraits>(image, j, i);
|
||||
fputc(rgba_getb(c), f);
|
||||
fputc(rgba_getg(c), f);
|
||||
fputc(rgba_getr(c), f);
|
||||
}
|
||||
// Only used in indexed mode
|
||||
int colorsPerByte = std::max(1, 8/bpp);
|
||||
int colorMask;
|
||||
switch (bpp) {
|
||||
case 8: colorMask = 0xFF; break;
|
||||
case 4: colorMask = 0x0F; break;
|
||||
case 1: colorMask = 0x01; break;
|
||||
default: colorMask = 0; break;
|
||||
}
|
||||
|
||||
// Save image pixels (from bottom to top)
|
||||
for (i=h-1; i>=0; i--) {
|
||||
switch (image->pixelFormat()) {
|
||||
case IMAGE_RGB:
|
||||
for (j=0; j<w; ++j) {
|
||||
c = get_pixel_fast<RgbTraits>(image, j, i);
|
||||
fputc(rgba_getb(c), f);
|
||||
fputc(rgba_getg(c), f);
|
||||
fputc(rgba_getr(c), f);
|
||||
}
|
||||
break;
|
||||
case IMAGE_GRAYSCALE:
|
||||
for (j=0; j<w; ++j) {
|
||||
c = get_pixel_fast<GrayscaleTraits>(image, j, i);
|
||||
fputc(graya_getv(c), f);
|
||||
}
|
||||
break;
|
||||
case IMAGE_INDEXED:
|
||||
for (j=0; j<w; ) {
|
||||
uint8_t value = 0;
|
||||
for (int k=colorsPerByte-1; k>=0 && j<w; --k, ++j) {
|
||||
c = get_pixel_fast<IndexedTraits>(image, j, i);
|
||||
value |= (c & colorMask) << (bpp*k);
|
||||
}
|
||||
fputc(value, f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
for (j=0; j<filler; j++)
|
||||
fputc(0, f);
|
||||
|
||||
fop->setProgress((float)(image->height()-i) / (float)image->height());
|
||||
fop->setProgress((float)(h-i) / (float)h);
|
||||
}
|
||||
|
||||
if (ferror(f)) {
|
||||
|
@ -36,6 +36,7 @@
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui/toolbar.h"
|
||||
#include "app/ui_context.h"
|
||||
#include "app/util/open_batch.h"
|
||||
#include "base/clamp.h"
|
||||
#include "base/fs.h"
|
||||
#include "base/memory.h"
|
||||
@ -368,7 +369,9 @@ void defer_invalid_rect(const gfx::Rect& rc)
|
||||
defered_invalid_region.createUnion(defered_invalid_region, gfx::Region(rc));
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// Manager event handler.
|
||||
|
||||
bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
{
|
||||
#ifdef ENABLE_STEAM
|
||||
@ -393,6 +396,7 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
if (getForegroundWindow() == App::instance()->mainWindow()) {
|
||||
base::paths files = static_cast<DropFilesMessage*>(msg)->files();
|
||||
UIContext* ctx = UIContext::instance();
|
||||
OpenBatchOfFiles batch;
|
||||
|
||||
while (!files.empty()) {
|
||||
auto fn = files.front();
|
||||
@ -423,14 +427,11 @@ bool CustomizedGuiManager::onProcessMessage(Message* msg)
|
||||
}
|
||||
// Other extensions will be handled as an image/sprite
|
||||
else {
|
||||
OpenFileCommand cmd;
|
||||
Params params;
|
||||
params.set("filename", fn.c_str());
|
||||
params.set("repeat_checkbox", "true");
|
||||
ctx->executeCommandFromMenuOrShortcut(&cmd, params);
|
||||
batch.open(ctx, fn,
|
||||
false); // Open all frames
|
||||
|
||||
// Remove all used file names from the "dropped files"
|
||||
for (const auto& usedFn : cmd.usedFiles()) {
|
||||
for (const auto& usedFn : batch.usedFiles()) {
|
||||
auto it = std::find(files.begin(), files.end(), usedFn);
|
||||
if (it != files.end())
|
||||
files.erase(it);
|
||||
|
@ -10,6 +10,6 @@
|
||||
|
||||
// Increment this value if the scripting API is modified between two
|
||||
// released Aseprite versions.
|
||||
#define API_VERSION 11
|
||||
#define API_VERSION 12
|
||||
|
||||
#endif
|
||||
|
@ -68,7 +68,8 @@ int load_sprite_from_file(lua_State* L, const char* filename,
|
||||
Commands::instance()->byId(CommandId::OpenFile());
|
||||
Params params;
|
||||
params.set("filename", absFn.c_str());
|
||||
if (param == LoadSpriteFromFileParam::OneFrameAsImage)
|
||||
if (param == LoadSpriteFromFileParam::OneFrameAsSprite ||
|
||||
param == LoadSpriteFromFileParam::OneFrameAsImage)
|
||||
params.set("oneframe", "true");
|
||||
ctx->executeCommand(openCommand, params);
|
||||
|
||||
|
@ -40,7 +40,7 @@ int ColorSpace_new(lua_State* L)
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Create sRGB profile with ColorSpace{ sRGB }
|
||||
if (lua_getfield(L, 1, "sRGB") != LUA_TNONE) {
|
||||
if (lua_is_key_true(L, 1, "sRGB")) {
|
||||
lua_pop(L, 1);
|
||||
push_new<gfx::ColorSpace>(L, *gfx::ColorSpace::MakeSRGB());
|
||||
return 1;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "app/ui/expr_entry.h"
|
||||
#include "app/ui/filename_field.h"
|
||||
#include "base/paths.h"
|
||||
#include "base/remove_from_container.h"
|
||||
#include "ui/box.h"
|
||||
#include "ui/button.h"
|
||||
#include "ui/combobox.h"
|
||||
@ -33,18 +34,22 @@
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#define TRACE_DIALOG(...) // TRACEARGS
|
||||
#ifdef ENABLE_UI
|
||||
|
||||
#define TRACE_DIALOG(...) // TRACEARGS(__VA_ARGS__)
|
||||
|
||||
namespace app {
|
||||
namespace script {
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
|
||||
using namespace ui;
|
||||
|
||||
namespace {
|
||||
|
||||
struct Dialog;
|
||||
std::vector<Dialog*> all_dialogs;
|
||||
|
||||
struct Dialog {
|
||||
ui::Window window;
|
||||
ui::VBox vbox;
|
||||
@ -73,6 +78,11 @@ struct Dialog {
|
||||
: window(ui::Window::WithTitleBar, "Script"),
|
||||
grid(2, false) {
|
||||
window.addChild(&grid);
|
||||
all_dialogs.push_back(this);
|
||||
}
|
||||
|
||||
~Dialog() {
|
||||
base::remove_from_container(all_dialogs, this);
|
||||
}
|
||||
|
||||
void unrefShowOnClose() {
|
||||
@ -368,14 +378,9 @@ int Dialog_newrow(lua_State* L)
|
||||
dlg->autoNewRow = false;
|
||||
if (lua_istable(L, 2)) {
|
||||
// Dialog:newrow{ always }
|
||||
const int type = lua_getfield(L, 2, "always");
|
||||
if (type != LUA_TNONE) {
|
||||
if (type == LUA_TNIL ||
|
||||
lua_toboolean(L, -1)) {
|
||||
dlg->autoNewRow = true;
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
if (lua_is_key_true(L, 2, "always"))
|
||||
dlg->autoNewRow = true;
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
lua_pushvalue(L, 1);
|
||||
@ -1224,16 +1229,24 @@ const Property Dialog_properties[] = {
|
||||
|
||||
DEF_MTNAME(Dialog);
|
||||
|
||||
#endif // ENABLE_UI
|
||||
|
||||
void register_dialog_class(lua_State* L)
|
||||
{
|
||||
#ifdef ENABLE_UI
|
||||
REG_CLASS(L, Dialog);
|
||||
REG_CLASS_NEW(L, Dialog);
|
||||
REG_CLASS_PROPERTIES(L, Dialog);
|
||||
#endif
|
||||
}
|
||||
|
||||
// close all opened Dialogs before closing the UI
|
||||
void close_all_dialogs()
|
||||
{
|
||||
for (Dialog* dlg : all_dialogs) {
|
||||
ASSERT(dlg);
|
||||
if (dlg)
|
||||
dlg->window.closeWindow(nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
||||
|
||||
#endif // ENABLE_UI
|
||||
|
@ -151,7 +151,9 @@ void register_cel_class(lua_State* L);
|
||||
void register_cels_class(lua_State* L);
|
||||
void register_color_class(lua_State* L);
|
||||
void register_color_space_class(lua_State* L);
|
||||
#ifdef ENABLE_UI
|
||||
void register_dialog_class(lua_State* L);
|
||||
#endif
|
||||
void register_frame_class(lua_State* L);
|
||||
void register_frames_class(lua_State* L);
|
||||
void register_image_class(lua_State* L);
|
||||
@ -371,7 +373,9 @@ Engine::Engine()
|
||||
register_cels_class(L);
|
||||
register_color_class(L);
|
||||
register_color_space_class(L);
|
||||
#ifdef ENABLE_UI
|
||||
register_dialog_class(L);
|
||||
#endif
|
||||
register_frame_class(L);
|
||||
register_frames_class(L);
|
||||
register_image_class(L);
|
||||
@ -404,6 +408,9 @@ Engine::Engine()
|
||||
|
||||
Engine::~Engine()
|
||||
{
|
||||
#ifdef ENABLE_UI
|
||||
close_all_dialogs();
|
||||
#endif
|
||||
lua_close(L);
|
||||
}
|
||||
|
||||
|
@ -167,10 +167,16 @@ namespace app {
|
||||
|
||||
// Used by App.open(), Sprite{ fromFile }, and Image{ fromFile }
|
||||
enum class LoadSpriteFromFileParam { FullAniAsSprite,
|
||||
OneFrameAsSprite,
|
||||
OneFrameAsImage };
|
||||
int load_sprite_from_file(lua_State* L, const char* filename,
|
||||
const LoadSpriteFromFileParam param);
|
||||
|
||||
#ifdef ENABLE_UI
|
||||
// close all opened Dialogs before closing the UI
|
||||
void close_all_dialogs();
|
||||
#endif
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
||||
|
||||
|
@ -93,5 +93,15 @@ void create_mt_getters_setters(lua_State* L,
|
||||
ASSERT(lua_gettop(L) == top);
|
||||
}
|
||||
|
||||
bool lua_is_key_true(lua_State* L, int tableIndex, const char* keyName)
|
||||
{
|
||||
bool result = false;
|
||||
int type = lua_getfield(L, tableIndex, keyName);
|
||||
if (type != LUA_TNIL && lua_toboolean(L, -1))
|
||||
result = true;
|
||||
lua_pop(L, 1);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace script
|
||||
} // namespace app
|
||||
|
@ -128,6 +128,8 @@ void create_mt_getters_setters(lua_State* L,
|
||||
const char* tname,
|
||||
const Property* properties);
|
||||
|
||||
bool lua_is_key_true(lua_State* L, int tableIndex, const char* keyName);
|
||||
|
||||
#define REG_CLASS_PROPERTIES(L, T) { \
|
||||
luaL_getmetatable(L, get_mtname<T>()); \
|
||||
create_mt_getters_setters(L, get_mtname<T>(), T##_properties); \
|
||||
|
@ -30,8 +30,7 @@ gfx::Point Point_new(lua_State* L, int index)
|
||||
// Convert {x=int,y=int} or {int,int} into a Point
|
||||
else if (lua_istable(L, index)) {
|
||||
const int type = lua_getfield(L, index, "x");
|
||||
if (type != LUA_TNONE &&
|
||||
type != LUA_TNIL) {
|
||||
if (VALID_LUATYPE(type)) {
|
||||
lua_getfield(L, index, "y");
|
||||
pt.x = lua_tointeger(L, -2);
|
||||
pt.y = lua_tointeger(L, -1);
|
||||
|
@ -29,8 +29,7 @@ gfx::Rect Rectangle_new(lua_State* L, int index)
|
||||
// Convert { x, y, width, height } into a Rectangle
|
||||
else if (lua_istable(L, index)) {
|
||||
const int type = lua_getfield(L, index, "x");
|
||||
if (type != LUA_TNONE &&
|
||||
type != LUA_TNIL) {
|
||||
if (VALID_LUATYPE(type)) {
|
||||
lua_getfield(L, index, "y");
|
||||
lua_getfield(L, index, "width");
|
||||
lua_getfield(L, index, "height");
|
||||
|
@ -30,8 +30,7 @@ gfx::Size Size_new(lua_State* L, int index)
|
||||
// Convert {x=int,y=int} or {int,int} into a Size
|
||||
else if (lua_istable(L, index)) {
|
||||
const int type = lua_getfield(L, index, "width");
|
||||
if (type != LUA_TNONE &&
|
||||
type != LUA_TNIL) {
|
||||
if (VALID_LUATYPE(type)) {
|
||||
lua_getfield(L, index, "height");
|
||||
sz.w = lua_tointeger(L, -2);
|
||||
sz.h = lua_tointeger(L, -1);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include "app/cmd/assign_color_profile.h"
|
||||
#include "app/cmd/clear_cel.h"
|
||||
#include "app/cmd/convert_color_profile.h"
|
||||
#include "app/cmd/copy_region.h"
|
||||
#include "app/cmd/flatten_layers.h"
|
||||
#include "app/cmd/remove_layer.h"
|
||||
#include "app/cmd/remove_slice.h"
|
||||
@ -77,9 +78,13 @@ int Sprite_new(lua_State* L)
|
||||
if (const char* fromFile = lua_tostring(L, -1)) {
|
||||
std::string fn = fromFile;
|
||||
lua_pop(L, 1);
|
||||
|
||||
bool oneFrame = (lua_is_key_true(L, -1, "oneFrame"));
|
||||
|
||||
return load_sprite_from_file(
|
||||
L, fn.c_str(),
|
||||
LoadSpriteFromFileParam::FullAniAsSprite);
|
||||
(oneFrame ? LoadSpriteFromFileParam::OneFrameAsSprite:
|
||||
LoadSpriteFromFileParam::FullAniAsSprite));
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
@ -444,30 +449,46 @@ int Sprite_newCel(lua_State* L)
|
||||
if (frame < 0 || frame > sprite->lastFrame())
|
||||
return luaL_error(L, "frame index out of bounds %d", frame+1);
|
||||
|
||||
Doc* doc = static_cast<Doc*>(sprite->document());
|
||||
LayerImage* layer = static_cast<LayerImage*>(layerBase);
|
||||
ImageRef image(nullptr);
|
||||
gfx::Point pos(0, 0);
|
||||
|
||||
Image* srcImage = may_get_image_from_arg(L, 4);
|
||||
if (srcImage) {
|
||||
image.reset(Image::createCopy(srcImage));
|
||||
pos = convert_args_into_point(L, 5);
|
||||
}
|
||||
else {
|
||||
image.reset(Image::create(sprite->spec()));
|
||||
}
|
||||
gfx::Point pos = convert_args_into_point(L, 5);
|
||||
Cel* cel = nullptr;
|
||||
|
||||
auto cel = new Cel(frame, image);
|
||||
cel->setPosition(pos);
|
||||
// For background layers we just draw the image in the existent cel
|
||||
if (layer->isBackground()) {
|
||||
cel = layer->cel(frame);
|
||||
ASSERT(cel);
|
||||
|
||||
Doc* doc = static_cast<Doc*>(sprite->document());
|
||||
|
||||
Tx tx;
|
||||
DocApi api = doc->getApi(tx);
|
||||
if (layer->cel(frame))
|
||||
Tx tx;
|
||||
DocApi api = doc->getApi(tx);
|
||||
api.clearCel(layer, frame);
|
||||
api.addCel(layer, cel);
|
||||
tx.commit();
|
||||
if (srcImage) {
|
||||
tx(new cmd::CopyRegion(cel->image(), srcImage,
|
||||
gfx::Region(srcImage->bounds()),
|
||||
pos, false));
|
||||
}
|
||||
tx.commit();
|
||||
}
|
||||
// For transparent layers we just draw the image in the existent cel
|
||||
else {
|
||||
if (srcImage)
|
||||
image.reset(Image::createCopy(srcImage));
|
||||
else
|
||||
image.reset(Image::create(sprite->spec()));
|
||||
|
||||
cel = new Cel(frame, image);
|
||||
cel->setPosition(pos);
|
||||
|
||||
Tx tx;
|
||||
DocApi api = doc->getApi(tx);
|
||||
if (layer->cel(frame))
|
||||
api.clearCel(layer, frame);
|
||||
api.addCel(layer, cel);
|
||||
tx.commit();
|
||||
}
|
||||
|
||||
push_docobj(L, cel);
|
||||
return 1;
|
||||
|
@ -752,7 +752,7 @@ void ColorBar::onFgColorButtonBeforeChange(app::Color& color)
|
||||
if (m_fromPalView)
|
||||
return;
|
||||
|
||||
if (!inEditMode()) {
|
||||
if (!inEditMode() || color.getType() == app::Color::IndexType) {
|
||||
m_paletteView.deselect();
|
||||
return;
|
||||
}
|
||||
@ -814,8 +814,7 @@ void ColorBar::onColorButtonChange(const app::Color& color)
|
||||
{
|
||||
COLOR_BAR_TRACE("ColorBar::onColorButtonChange(%s)\n", color.toString().c_str());
|
||||
|
||||
if (!inEditMode() ||
|
||||
m_fromPref) {
|
||||
if (!inEditMode() || color.getType() == app::Color::IndexType || m_fromPref) {
|
||||
if (color.getType() == app::Color::IndexType)
|
||||
m_paletteView.selectColor(color.getIndex());
|
||||
else {
|
||||
|
@ -417,6 +417,12 @@ void DocView::onRemoveFrame(DocEvent& ev)
|
||||
}
|
||||
}
|
||||
|
||||
void DocView::onTagChange(DocEvent& ev)
|
||||
{
|
||||
if (m_previewDelegate)
|
||||
m_previewDelegate->onTagChangeEditor(m_editor, ev);
|
||||
}
|
||||
|
||||
void DocView::onAddCel(DocEvent& ev)
|
||||
{
|
||||
UIContext::instance()->notifyActiveSiteChanged();
|
||||
|
@ -30,6 +30,7 @@ namespace app {
|
||||
virtual void onScrollOtherEditor(Editor* editor) = 0;
|
||||
virtual void onDisposeOtherEditor(Editor* editor) = 0;
|
||||
virtual void onPreviewOtherEditor(Editor* editor) = 0;
|
||||
virtual void onTagChangeEditor(Editor* editor, DocEvent& ev) = 0;
|
||||
};
|
||||
|
||||
class DocView : public ui::Box,
|
||||
@ -75,6 +76,7 @@ namespace app {
|
||||
void onAddLayer(DocEvent& ev) override;
|
||||
void onAddFrame(DocEvent& ev) override;
|
||||
void onRemoveFrame(DocEvent& ev) override;
|
||||
void onTagChange(DocEvent& ev) override;
|
||||
void onAddCel(DocEvent& ev) override;
|
||||
void onRemoveCel(DocEvent& ev) override;
|
||||
void onTotalFramesChanged(DocEvent& ev) override;
|
||||
|
@ -51,6 +51,11 @@ PlayState::PlayState(const bool playOnce,
|
||||
&PlayState::onBeforeCommandExecution, this);
|
||||
}
|
||||
|
||||
Tag* PlayState::playingTag() const
|
||||
{
|
||||
return m_tag;
|
||||
}
|
||||
|
||||
void PlayState::onEnterState(Editor* editor)
|
||||
{
|
||||
StateWithWheelBehavior::onEnterState(editor);
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2017 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -27,6 +28,8 @@ namespace app {
|
||||
PlayState(const bool playOnce,
|
||||
const bool playAll);
|
||||
|
||||
doc::Tag* playingTag() const;
|
||||
|
||||
void onEnterState(Editor* editor) override;
|
||||
LeaveAction onLeaveState(Editor* editor, EditorState* newState) override;
|
||||
bool onMouseDown(Editor* editor, ui::MouseMessage* msg) override;
|
||||
|
@ -13,6 +13,7 @@
|
||||
|
||||
#include "app/app.h"
|
||||
#include "app/doc.h"
|
||||
#include "app/doc_event.h"
|
||||
#include "app/ini_file.h"
|
||||
#include "app/loop_tag.h"
|
||||
#include "app/modules/editors.h"
|
||||
@ -22,6 +23,7 @@
|
||||
#include "app/ui/editor/editor_customization_delegate.h"
|
||||
#include "app/ui/editor/editor_view.h"
|
||||
#include "app/ui/editor/navigate_state.h"
|
||||
#include "app/ui/editor/play_state.h"
|
||||
#include "app/ui/skin/skin_theme.h"
|
||||
#include "app/ui/status_bar.h"
|
||||
#include "app/ui/toolbar.h"
|
||||
@ -395,28 +397,7 @@ void PreviewEditorWindow::updateUsingEditor(Editor* editor)
|
||||
miniEditor->setFrame(editor->frame());
|
||||
}
|
||||
else {
|
||||
if (miniEditor->isPlaying()) {
|
||||
doc::Tag* tag = editor
|
||||
->getCustomizationDelegate()
|
||||
->getTagProvider()
|
||||
->getTagByFrame(editor->frame(), true);
|
||||
|
||||
doc::Tag* playingTag = editor
|
||||
->getCustomizationDelegate()
|
||||
->getTagProvider()
|
||||
->getTagByFrame(m_refFrame, true);
|
||||
|
||||
if (tag == playingTag)
|
||||
return;
|
||||
|
||||
miniEditor->stop();
|
||||
}
|
||||
|
||||
if (!miniEditor->isPlaying())
|
||||
miniEditor->setFrame(m_refFrame = editor->frame());
|
||||
|
||||
miniEditor->play(Preferences::instance().preview.playOnce(),
|
||||
Preferences::instance().preview.playAll());
|
||||
adjustPlayingTag();
|
||||
}
|
||||
}
|
||||
|
||||
@ -477,6 +458,11 @@ void PreviewEditorWindow::onPreviewOtherEditor(Editor* editor)
|
||||
updateUsingEditor(editor);
|
||||
}
|
||||
|
||||
void PreviewEditorWindow::onTagChangeEditor(Editor* editor, DocEvent& ev)
|
||||
{
|
||||
adjustPlayingTag();
|
||||
}
|
||||
|
||||
void PreviewEditorWindow::hideWindow()
|
||||
{
|
||||
destroyDocView();
|
||||
@ -494,4 +480,34 @@ void PreviewEditorWindow::destroyDocView()
|
||||
}
|
||||
}
|
||||
|
||||
void PreviewEditorWindow::adjustPlayingTag()
|
||||
{
|
||||
Editor* editor = m_relatedEditor;
|
||||
Editor* miniEditor = m_docView->editor();
|
||||
|
||||
ASSERT(editor);
|
||||
ASSERT(miniEditor);
|
||||
|
||||
if (miniEditor->isPlaying()) {
|
||||
doc::Tag* tag = editor
|
||||
->getCustomizationDelegate()
|
||||
->getTagProvider()
|
||||
->getTagByFrame(editor->frame(), true);
|
||||
|
||||
auto playState = dynamic_cast<PlayState*>(miniEditor->getState().get());
|
||||
doc::Tag* playingTag = (playState ? playState->playingTag(): nullptr);
|
||||
|
||||
if (tag == playingTag)
|
||||
return;
|
||||
|
||||
miniEditor->stop();
|
||||
}
|
||||
|
||||
if (!miniEditor->isPlaying())
|
||||
miniEditor->setFrame(m_refFrame = editor->frame());
|
||||
|
||||
miniEditor->play(Preferences::instance().preview.playOnce(),
|
||||
Preferences::instance().preview.playAll());
|
||||
}
|
||||
|
||||
} // namespace app
|
||||
|
@ -1,4 +1,5 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
// Copyright (C) 2001-2018 David Capello
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
@ -42,6 +43,7 @@ namespace app {
|
||||
void onScrollOtherEditor(Editor* editor) override;
|
||||
void onDisposeOtherEditor(Editor* editor) override;
|
||||
void onPreviewOtherEditor(Editor* editor) override;
|
||||
void onTagChangeEditor(Editor* editor, DocEvent& ev) override;
|
||||
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
@ -59,6 +61,7 @@ namespace app {
|
||||
void hideWindow();
|
||||
void destroyDocView();
|
||||
void saveScrollPref();
|
||||
void adjustPlayingTag();
|
||||
|
||||
bool m_isEnabled;
|
||||
DocView* m_docView;
|
||||
|
64
src/app/util/open_batch.h
Normal file
64
src/app/util/open_batch.h
Normal file
@ -0,0 +1,64 @@
|
||||
// Aseprite
|
||||
// Copyright (C) 2020 Igara Studio S.A.
|
||||
//
|
||||
// This program is distributed under the terms of
|
||||
// the End-User License Agreement for Aseprite.
|
||||
|
||||
#ifndef APP_UTIL_OPEN_BATCH_H_INCLUDED
|
||||
#define APP_UTIL_OPEN_BATCH_H_INCLUDED
|
||||
#pragma once
|
||||
|
||||
#include "app/commands/cmd_open_file.h"
|
||||
#include "app/context.h"
|
||||
|
||||
namespace app {
|
||||
|
||||
// Helper class to a batch of files and handle the loading of image
|
||||
// sequences and repeat the selected action of the user (Agree/Skip
|
||||
// to open the sequence, and Repeat the action for all other
|
||||
// elements)
|
||||
class OpenBatchOfFiles {
|
||||
public:
|
||||
void open(Context* ctx,
|
||||
const std::string& fn,
|
||||
const bool oneFrame) {
|
||||
Params params;
|
||||
params.set("filename", fn.c_str());
|
||||
|
||||
if (oneFrame)
|
||||
params.set("oneframe", "true");
|
||||
else {
|
||||
switch (m_lastDecision) {
|
||||
case OpenFileCommand::SequenceDecision::Ask:
|
||||
params.set("sequence", "ask");
|
||||
params.set("repeat_checkbox", "true");
|
||||
break;
|
||||
case OpenFileCommand::SequenceDecision::Skip:
|
||||
params.set("sequence", "skip");
|
||||
break;
|
||||
case OpenFileCommand::SequenceDecision::Agree:
|
||||
params.set("sequence", "agree");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ctx->executeCommandFromMenuOrShortcut(&m_cmd, params);
|
||||
|
||||
// Future decision for other files in the CLI
|
||||
auto d = m_cmd.seqDecision();
|
||||
if (d != OpenFileCommand::SequenceDecision::Ask)
|
||||
m_lastDecision = d;
|
||||
}
|
||||
|
||||
const base::paths& usedFiles() const {
|
||||
return m_cmd.usedFiles();
|
||||
}
|
||||
|
||||
private:
|
||||
OpenFileCommand m_cmd;
|
||||
OpenFileCommand::SequenceDecision m_lastDecision = OpenFileCommand::SequenceDecision::Ask;
|
||||
};
|
||||
|
||||
} // namespace app
|
||||
|
||||
#endif
|
Loading…
x
Reference in New Issue
Block a user