Add 'x' and 'y' as input parameters to app.command.Paste()

This commit is contained in:
Gaspar Capello 2024-09-04 17:37:24 -03:00 committed by David Capello
parent de1fc581f2
commit e70bbbd369
21 changed files with 212 additions and 93 deletions

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2024 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello // Copyright (C) 2001-2017 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -10,6 +11,7 @@
#include "app/app.h" #include "app/app.h"
#include "app/commands/command.h" #include "app/commands/command.h"
#include "app/commands/params.h"
#include "app/ui/input_chain.h" #include "app/ui/input_chain.h"
namespace app { namespace app {
@ -19,8 +21,11 @@ public:
PasteCommand(); PasteCommand();
protected: protected:
void onLoadParams(const Params& params) override;
bool onEnabled(Context* ctx) override; bool onEnabled(Context* ctx) override;
void onExecute(Context* ctx) override; void onExecute(Context* ctx) override;
private:
std::shared_ptr<gfx::Point> m_position;
}; };
PasteCommand::PasteCommand() PasteCommand::PasteCommand()
@ -28,6 +33,16 @@ PasteCommand::PasteCommand()
{ {
} }
void PasteCommand::onLoadParams(const Params& params)
{
m_position.reset();
if (params.has_param("x") || params.has_param("y")) {
m_position.reset(new gfx::Point);
m_position->x = params.get_as<int>("x");
m_position->y = params.get_as<int>("y");
}
}
bool PasteCommand::onEnabled(Context* ctx) bool PasteCommand::onEnabled(Context* ctx)
{ {
return App::instance()->inputChain().canPaste(ctx); return App::instance()->inputChain().canPaste(ctx);
@ -35,7 +50,7 @@ bool PasteCommand::onEnabled(Context* ctx)
void PasteCommand::onExecute(Context* ctx) void PasteCommand::onExecute(Context* ctx)
{ {
App::instance()->inputChain().paste(ctx); App::instance()->inputChain().paste(ctx, m_position.get());
} }
Command* CommandFactory::createPasteCommand() Command* CommandFactory::createPasteCommand()

View File

@ -84,13 +84,14 @@ bool ScriptInputChain::onCopy(Context* ctx)
return false; return false;
} }
bool ScriptInputChain::onPaste(Context* ctx) bool ScriptInputChain::onPaste(Context* ctx,
const gfx::Point* position)
{ {
Clipboard* clipboard = ctx->clipboard(); Clipboard* clipboard = ctx->clipboard();
if (!clipboard) if (!clipboard)
return false; return false;
if (clipboard->format() == ClipboardFormat::Image) { if (clipboard->format() == ClipboardFormat::Image) {
clipboard->paste(ctx, false); clipboard->paste(ctx, false, position);
return true; return true;
} }
return false; return false;

View File

@ -27,7 +27,8 @@ namespace app {
bool onCanClear(Context* ctx) override; bool onCanClear(Context* ctx) override;
bool onCut(Context* ctx) override; bool onCut(Context* ctx) override;
bool onCopy(Context* ctx) override; bool onCopy(Context* ctx) override;
bool onPaste(Context* ctx) override; bool onPaste(Context* ctx,
const gfx::Point* position) override;
bool onClear(Context* ctx) override; bool onClear(Context* ctx) override;
void onCancel(Context* ctx) override; void onCancel(Context* ctx) override;
}; };

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2023 Igara Studio S.A. // Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -1692,7 +1692,8 @@ bool ColorBar::onCopy(Context* ctx)
return true; return true;
} }
bool ColorBar::onPaste(Context* ctx) bool ColorBar::onPaste(Context* ctx,
const gfx::Point* position)
{ {
if (m_tilemapMode == TilemapMode::Tiles) { if (m_tilemapMode == TilemapMode::Tiles) {
showRemapTiles(); showRemapTiles();

View File

@ -119,7 +119,8 @@ namespace app {
bool onCanClear(Context* ctx) override; bool onCanClear(Context* ctx) override;
bool onCut(Context* ctx) override; bool onCut(Context* ctx) override;
bool onCopy(Context* ctx) override; bool onCopy(Context* ctx) override;
bool onPaste(Context* ctx) override; bool onPaste(Context* ctx,
const gfx::Point* position) override;
bool onClear(Context* ctx) override; bool onClear(Context* ctx) override;
void onCancel(Context* ctx) override; void onCancel(Context* ctx) override;

View File

@ -589,12 +589,13 @@ bool DocView::onCopy(Context* ctx)
return false; return false;
} }
bool DocView::onPaste(Context* ctx) bool DocView::onPaste(Context* ctx,
const gfx::Point* position)
{ {
auto clipboard = ctx->clipboard(); auto clipboard = ctx->clipboard();
if (clipboard->format() == ClipboardFormat::Image || if (clipboard->format() == ClipboardFormat::Image ||
clipboard->format() == ClipboardFormat::Tilemap) { clipboard->format() == ClipboardFormat::Tilemap) {
clipboard->paste(ctx, true); clipboard->paste(ctx, true, position);
return true; return true;
} }
else else

View File

@ -102,7 +102,8 @@ namespace app {
bool onCanClear(Context* ctx) override; bool onCanClear(Context* ctx) override;
bool onCut(Context* ctx) override; bool onCut(Context* ctx) override;
bool onCopy(Context* ctx) override; bool onCopy(Context* ctx) override;
bool onPaste(Context* ctx) override; bool onPaste(Context* ctx,
const gfx::Point* position) override;
bool onClear(Context* ctx) override; bool onClear(Context* ctx) override;
void onCancel(Context* ctx) override; void onCancel(Context* ctx) override;

View File

@ -2654,7 +2654,9 @@ void Editor::setZoomAndCenterInMouse(const Zoom& zoom,
} }
} }
void Editor::pasteImage(const Image* image, const Mask* mask) void Editor::pasteImage(const Image* image,
const Mask* mask,
const gfx::Point* position)
{ {
ASSERT(image); ASSERT(image);
@ -2686,11 +2688,14 @@ void Editor::pasteImage(const Image* image, const Mask* mask)
Sprite* sprite = this->sprite(); Sprite* sprite = this->sprite();
// Check bounds where the image will be pasted. // Check bounds where the image will be pasted.
int x = mask->bounds().x; int x = (position ? position->x : mask->bounds().x);
int y = mask->bounds().y; int y = (position ? position->y : mask->bounds().y);
{ {
const Rect visibleBounds = getViewportBounds(); const Rect visibleBounds = getViewportBounds();
const Point maskCenter = mask->bounds().center(); const Point maskCenter = mask->bounds().center() +
(position ? gfx::Point(position->x - mask->bounds().x,
position->y - mask->bounds().y)
: gfx::Point());
// If the pasted image original location center point isn't // If the pasted image original location center point isn't
// visible, we center the image in the editor's visible bounds. // visible, we center the image in the editor's visible bounds.
@ -2743,7 +2748,8 @@ void Editor::pasteImage(const Image* image, const Mask* mask)
m_brushPreview.hide(); m_brushPreview.hide();
Mask mask2(*mask); Mask mask2(*mask);
mask2.setOrigin(x, y); position ? mask2.setOrigin(position->x, position->y)
: mask2.setOrigin(x, y);
PixelsMovementPtr pixelsMovement( PixelsMovementPtr pixelsMovement(
new PixelsMovement(UIContext::instance(), site, new PixelsMovement(UIContext::instance(), site,

View File

@ -252,7 +252,9 @@ namespace app {
void setZoomAndCenterInMouse(const render::Zoom& zoom, void setZoomAndCenterInMouse(const render::Zoom& zoom,
const gfx::Point& mousePos, ZoomBehavior zoomBehavior); const gfx::Point& mousePos, ZoomBehavior zoomBehavior);
void pasteImage(const Image* image, const Mask* mask = nullptr); void pasteImage(const Image* image,
const Mask* mask = nullptr,
const gfx::Point* position = nullptr);
void startSelectionTransformation(const gfx::Point& move, double angle); void startSelectionTransformation(const gfx::Point& move, double angle);
void startFlipTransformation(doc::algorithm::FlipType flipType); void startFlipTransformation(doc::algorithm::FlipType flipType);

View File

@ -230,7 +230,8 @@ bool HomeView::onCopy(Context* ctx)
return false; return false;
} }
bool HomeView::onPaste(Context* ctx) bool HomeView::onPaste(Context* ctx,
const gfx::Point* position)
{ {
auto clipboard = ctx->clipboard(); auto clipboard = ctx->clipboard();
if (clipboard->format() == ClipboardFormat::Image) { if (clipboard->format() == ClipboardFormat::Image) {

View File

@ -74,7 +74,8 @@ namespace app {
bool onCanClear(Context* ctx) override; bool onCanClear(Context* ctx) override;
bool onCut(Context* ctx) override; bool onCut(Context* ctx) override;
bool onCopy(Context* ctx) override; bool onCopy(Context* ctx) override;
bool onPaste(Context* ctx) override; bool onPaste(Context* ctx,
const gfx::Point* position) override;
bool onClear(Context* ctx) override; bool onClear(Context* ctx) override;
void onCancel(Context* ctx) override; void onCancel(Context* ctx) override;

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -87,10 +88,11 @@ void InputChain::copy(Context* ctx)
} }
} }
void InputChain::paste(Context* ctx) void InputChain::paste(Context* ctx,
const gfx::Point* position)
{ {
for (auto e : m_elements) { for (auto e : m_elements) {
if (e->onCanPaste(ctx) && e->onPaste(ctx)) if (e->onCanPaste(ctx) && e->onPaste(ctx, position))
break; break;
} }
} }

View File

@ -1,4 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -8,6 +9,8 @@
#define APP_INPUT_CHAIN_H_INCLUDED #define APP_INPUT_CHAIN_H_INCLUDED
#pragma once #pragma once
#include "gfx/point.h"
#include <vector> #include <vector>
namespace ui { namespace ui {
@ -35,7 +38,8 @@ namespace app {
void cut(Context* ctx); void cut(Context* ctx);
void copy(Context* ctx); void copy(Context* ctx);
void paste(Context* ctx); void paste(Context* ctx,
const gfx::Point* position);
void clear(Context* ctx); void clear(Context* ctx);
void cancel(Context* ctx); void cancel(Context* ctx);

View File

@ -9,6 +9,8 @@
#define APP_INPUT_CHAIN_ELEMENT_H_INCLUDED #define APP_INPUT_CHAIN_ELEMENT_H_INCLUDED
#pragma once #pragma once
#include "gfx/point.h"
namespace ui { namespace ui {
class Message; class Message;
} }
@ -34,7 +36,8 @@ namespace app {
// which catch any exception that is thrown. // which catch any exception that is thrown.
virtual bool onCut(Context* ctx) = 0; virtual bool onCut(Context* ctx) = 0;
virtual bool onCopy(Context* ctx) = 0; virtual bool onCopy(Context* ctx) = 0;
virtual bool onPaste(Context* ctx) = 0; virtual bool onPaste(Context* ctx,
const gfx::Point* position) = 0;
virtual bool onClear(Context* ctx) = 0; virtual bool onClear(Context* ctx) = 0;
virtual void onCancel(Context* ctx) = 0; virtual void onCancel(Context* ctx) = 0;
}; };

View File

@ -4398,7 +4398,8 @@ bool Timeline::onCopy(Context* ctx)
return false; return false;
} }
bool Timeline::onPaste(Context* ctx) bool Timeline::onPaste(Context* ctx,
const gfx::Point* position)
{ {
auto clipboard = ctx->clipboard(); auto clipboard = ctx->clipboard();
if (clipboard->format() == ClipboardFormat::DocRange) { if (clipboard->format() == ClipboardFormat::DocRange) {

View File

@ -190,7 +190,8 @@ namespace app {
bool onCanClear(Context* ctx) override; bool onCanClear(Context* ctx) override;
bool onCut(Context* ctx) override; bool onCut(Context* ctx) override;
bool onCopy(Context* ctx) override; bool onCopy(Context* ctx) override;
bool onPaste(Context* ctx) override; bool onPaste(Context* ctx,
const gfx::Point* position) override;
bool onClear(Context* ctx) override; bool onClear(Context* ctx) override;
void onCancel(Context* ctx) override; void onCancel(Context* ctx) override;

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2018-2022 Igara Studio S.A. // Copyright (C) 2018-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -381,12 +381,13 @@ bool Workspace::onCopy(Context* ctx)
return false; return false;
} }
bool Workspace::onPaste(Context* ctx) bool Workspace::onPaste(Context* ctx,
const gfx::Point* position)
{ {
WorkspaceView* view = activeView(); WorkspaceView* view = activeView();
InputChainElement* activeElement = (view ? view->onGetInputChainElement(): nullptr); InputChainElement* activeElement = (view ? view->onGetInputChainElement(): nullptr);
if (activeElement) if (activeElement)
return activeElement->onPaste(ctx); return activeElement->onPaste(ctx, position);
else else
return false; return false;
} }

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2022 Igara Studio S.A. // Copyright (C) 2022-2024 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello // Copyright (C) 2001-2018 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
@ -71,7 +71,8 @@ namespace app {
bool onCanClear(Context* ctx) override; bool onCanClear(Context* ctx) override;
bool onCut(Context* ctx) override; bool onCut(Context* ctx) override;
bool onCopy(Context* ctx) override; bool onCopy(Context* ctx) override;
bool onPaste(Context* ctx) override; bool onPaste(Context* ctx,
const gfx::Point* position) override;
bool onClear(Context* ctx) override; bool onClear(Context* ctx) override;
void onCancel(Context* ctx) override; void onCancel(Context* ctx) override;

View File

@ -34,6 +34,8 @@
#include "app/util/new_image_from_mask.h" #include "app/util/new_image_from_mask.h"
#include "app/util/range_utils.h" #include "app/util/range_utils.h"
#include "clip/clip.h" #include "clip/clip.h"
#include "doc/algorithm/shrink_bounds.h"
#include "doc/blend_image.h"
#include "doc/doc.h" #include "doc/doc.h"
#include "render/dithering.h" #include "render/dithering.h"
#include "render/ordered_dither.h" #include "render/ordered_dither.h"
@ -425,7 +427,7 @@ void Clipboard::copyImage(const Image* image,
(mask ? new Mask(*mask): nullptr), (mask ? new Mask(*mask): nullptr),
(pal ? new Palette(*pal): nullptr), (pal ? new Palette(*pal): nullptr),
nullptr, nullptr,
true, false); App::instance()->isGui(), false);
} }
void Clipboard::copyTilemap(const Image* image, void Clipboard::copyTilemap(const Image* image,
@ -463,7 +465,8 @@ void Clipboard::copyPalette(const Palette* palette,
} }
void Clipboard::paste(Context* ctx, void Clipboard::paste(Context* ctx,
const bool interactive) const bool interactive,
const gfx::Point* position)
{ {
const Site site = ctx->activeSite(); const Site site = ctx->activeSite();
Doc* dstDoc = site.document(); Doc* dstDoc = site.document();
@ -518,38 +521,94 @@ void Clipboard::paste(Context* ctx,
// Change to MovingPixelsState // Change to MovingPixelsState
editor->pasteImage(src_image.get(), editor->pasteImage(src_image.get(),
m_data->mask.get()); m_data->mask.get(),
position);
} }
else { else {
// Non-interactive version (just copy the image to the cel) // CLI version:
// Paste the image according the position param.
// If there are no parameters, we assume the origin
// of the pasted image mask is the position.
// If there is no mask, x=0, y=0 is taken as position.
// TODO Support 'paste' command between images
// that do not match their pixel format.
Layer* dstLayer = site.layer(); Layer* dstLayer = site.layer();
ASSERT(dstLayer); ASSERT(dstLayer);
if (!dstLayer || !dstLayer->isImage()) if (!dstLayer ||
!dstLayer->isImage() ||
(src_image->pixelFormat() != dstSpr->pixelFormat()))
return; return;
ImageRef result;
// resultBounds starts with the same bounds as source image,
// but it'll be merged with the active cel bounds (if any).
gfx::Rect resultBounds =
gfx::Rect(position ? *position
: (m_data->mask ? m_data->mask->origin()
: gfx::Point()),
src_image->size());
const bool isAnImageOnDstCel =
ctx->activeSite().cel() && ctx->activeSite().cel()->image();
ASSERT(!ctx->activeSite().cel() || ctx->activeSite().cel()->image());
if (isAnImageOnDstCel) {
Cel* cel = ctx->activeSite().cel();
resultBounds = cel->bounds().createUnion(resultBounds);
// Create a new image (result) as a blend of the active cel image +
// the source image (clipboard image).
result.reset(Image::create(dstSpr->pixelFormat(), resultBounds.w, resultBounds.h));
doc::blend_image(result.get(), cel->image(),
gfx::Clip(cel->bounds().origin() - resultBounds.origin(),
cel->image()->bounds()),
site.palette(),
255, BlendMode::NORMAL);
doc::blend_image(result.get(), src_image.get(),
gfx::Clip(*position - resultBounds.origin(),
src_image->bounds()),
site.palette(),
255, BlendMode::NORMAL);
}
ContextWriter writer(ctx); ContextWriter writer(ctx);
Tx tx(writer, "Paste Image"); Tx tx(writer, "Paste Image");
DocApi api = dstDoc->getApi(tx); DocApi api = dstDoc->getApi(tx);
Cel* dstCel = api.addCel( Cel* dstCel;
static_cast<LayerImage*>(dstLayer), site.frame(), if (isAnImageOnDstCel)
ImageRef(Image::createCopy(src_image.get()))); api.clearCel(ctx->activeSite().cel());
else
result.reset(Image::createCopy(src_image.get()));
// Adjust bounds // Calculate the active image + pasted image bounds
const gfx::Rect startBounds(gfx::Point(), result->size());
const gfx::Point startOrigin(resultBounds.origin());
doc::algorithm::shrink_bounds(result.get(),
result->maskColor(),
dstLayer,
startBounds,
resultBounds);
// Cropped image according the shrink bounds
result.reset(crop_image(result.get(),
resultBounds,
result->maskColor()));
resultBounds.x = startOrigin.x + resultBounds.x;
resultBounds.y = startOrigin.y + resultBounds.y;
// Set image on the new Cel
dstCel = api.addCel(static_cast<LayerImage*>(dstLayer),
site.frame(),
result);
// Set cel bounds
if (dstCel) { if (dstCel) {
if (m_data->mask) { const Mask emptyMask;
if (dstLayer->isReference()) { if (dstLayer->isReference()) {
dstCel->setBounds(dstSpr->bounds()); dstCel->setBounds(dstSpr->bounds());
tx(new cmd::SetMask(dstDoc, &emptyMask));
Mask emptyMask; }
tx(new cmd::SetMask(dstDoc, &emptyMask)); else {
} dstCel->setBounds(resultBounds);
else { tx(new cmd::SetMask(dstDoc, m_data->mask ? m_data->mask.get()
dstCel->setBounds(m_data->mask->bounds()); : &emptyMask));
tx(new cmd::SetMask(dstDoc, m_data->mask.get()));
}
} }
} }
tx.commit(); tx.commit();
} }
break; break;
@ -562,7 +621,8 @@ void Clipboard::paste(Context* ctx,
// Change to MovingTilemapState // Change to MovingTilemapState
editor->pasteImage(m_data->tilemap.get(), editor->pasteImage(m_data->tilemap.get(),
m_data->mask.get()); m_data->mask.get(),
position);
} }
else { else {
// TODO non-interactive version (for scripts) // TODO non-interactive version (for scripts)

View File

@ -75,7 +75,8 @@ namespace app {
void copyPalette(const doc::Palette* palette, void copyPalette(const doc::Palette* palette,
const doc::PalettePicks& picks); const doc::PalettePicks& picks);
void paste(Context* ctx, void paste(Context* ctx,
const bool interactive); const bool interactive,
const gfx::Point* position = nullptr);
doc::ImageRef getImage(doc::Palette* palette); doc::ImageRef getImage(doc::Palette* palette);

View File

@ -28,19 +28,10 @@ do
{ 2, 2, { 2, 2,
2, 2 }) 2, 2 })
-- TO DO: Fix this difference between running this script with
-- 'UI Available' versus 'UI Not Available'
app.layer = sprite.layers[3] app.layer = sprite.layers[3]
if (app.isUIAvailable) then assert(app.cel.position == Point(1, 1))
assert(app.cel.position == Point(1, 1)) expect_img(app.activeImage,
expect_img(app.activeImage, { 1, 1 })
{ 1, 1 })
else
assert(app.cel.position == Point(0, 1))
expect_img(app.activeImage,
{ 0, 1, 1, 0, 0 })
end
app.command.FlattenLayers() app.command.FlattenLayers()
assert(app.cel.position == Point(1, 1)) assert(app.cel.position == Point(1, 1))
expect_img(app.activeImage, expect_img(app.activeImage,
@ -53,6 +44,18 @@ do
app.undo() -- New Layer app.undo() -- New Layer
app.undo() -- Cut app.undo() -- Cut
app.layer = sprite.layers[1]
assert(app.cel.position == Point(1, 1))
expect_img(app.activeImage,
{ 1, 1,
1, 1 })
app.layer = sprite.layers[2]
assert(app.cel.position == Point(2, 2))
expect_img(app.activeImage,
{ 2, 2,
2, 2 })
assert(#sprite.layers == 2)
-- Another test -- Another test
app.layer = sprite.layers[1] app.layer = sprite.layers[1]
app.useTool { app.useTool {
@ -62,30 +65,21 @@ do
} }
app.command.Cut() app.command.Cut()
assert(app.cel.position == Point(1, 1))
expect_img(app.activeImage,
{ 1, 1,
1, 0 })
sprite:newLayer() sprite:newLayer()
app.command.Paste() app.command.Paste()
-- TO DO: Fix this difference between running this script with
-- 'UI Available' versus 'UI Not Available'
app.layer = sprite.layers[3] app.layer = sprite.layers[3]
if (app.isUIAvailable) then assert(app.cel.position == Point(2, 2))
assert(app.cel.position == Point(2, 2)) expect_img(app.activeImage,
expect_img(app.activeImage, { 1 })
{ 1 })
else
assert(app.cel.position == Point(2, 2))
expect_img(app.activeImage,
{ 1, 0, 0 })
end
app.undo() -- Paste app.undo() -- Paste
app.undo() -- New Layer app.undo() -- New Layer
app.undo() -- Cut app.undo() -- Cut
app.undo() -- MoveMask
-- TO DO: at the moment useTool requires double undo to undo
-- the selection action (Just one undo should be enough).
app.undo() -- useTool
app.undo() -- useTool
-- Test app.command.Copy -- Test app.command.Copy
app.layer = sprite.layers[1] app.layer = sprite.layers[1]
@ -108,19 +102,10 @@ do
expect_img(app.activeImage, expect_img(app.activeImage,
{ 2, 2, { 2, 2,
2, 2 }) 2, 2 })
-- TO DO: Fix this difference between running this script with
-- 'UI Available' versus 'UI Not Available'
app.layer = sprite.layers[3] app.layer = sprite.layers[3]
if (app.isUIAvailable) then assert(app.cel.position == Point(1, 1))
assert(app.cel.position == Point(1, 1)) expect_img(app.activeImage,
expect_img(app.activeImage, { 1, 1 })
{ 1, 1 })
else
assert(app.cel.position == Point(0, 1))
expect_img(app.activeImage,
{ 0, 1, 1, 0, 0 })
end
app.command.FlattenLayers() app.command.FlattenLayers()
assert(app.cel.position == Point(1, 1)) assert(app.cel.position == Point(1, 1))
@ -130,6 +115,17 @@ do
0, 2, 2 }) 0, 2, 2 })
-- Test app.command.Clear() -- Test app.command.Clear()
app.useTool {
tool = "rectangular_marquee",
points = {Point(2,2), Point(4,2)},
selection = SelectionMode.REPLACE
}
app.command.Clear()
expect_img(app.activeImage,
{ 1, 1, 0,
1, 0, 0,
0, 2, 2 })
app.useTool { app.useTool {
tool = "rectangular_marquee", tool = "rectangular_marquee",
points = {Point(0,1), Point(4,2)}, points = {Point(0,1), Point(4,2)},
@ -141,6 +137,8 @@ do
{ 2, 2 }) { 2, 2 })
app.undo() app.undo()
app.undo()
app.undo()
assert(app.cel.position == Point(1, 1)) assert(app.cel.position == Point(1, 1))
expect_img(app.activeImage, expect_img(app.activeImage,
@ -151,7 +149,7 @@ do
-- Test app.command.Cancel() -- Test app.command.Cancel()
app.useTool { app.useTool {
tool = "rectangular_marquee", tool = "rectangular_marquee",
points = {Point(0,1), Point(4,2)}, points = {Point(2,2), Point(4,2)},
selection = SelectionMode.REPLACE selection = SelectionMode.REPLACE
} }
app.command.Cancel() app.command.Cancel()
@ -161,4 +159,20 @@ do
{ 1, 1, 0, { 1, 1, 0,
1, 2, 2, 1, 2, 2,
0, 2, 2 }) 0, 2, 2 })
app.useTool {
tool = "rectangular_marquee",
points = {Point(2,0), Point(4,1)},
selection = SelectionMode.REPLACE
}
app.command.Copy()
sprite:newLayer()
app.command.Paste { x=3, y=3 }
app.command.FlattenLayers()
assert(app.cel.position == Point(1, 1))
expect_img(app.activeImage,
{ 1, 1, 0,
1, 2, 2,
0, 2, 2,
0, 0, 1, })
end end