From e70bbbd3699ee1709e769234179dc1cb73a61c42 Mon Sep 17 00:00:00 2001 From: Gaspar Capello Date: Wed, 4 Sep 2024 17:37:24 -0300 Subject: [PATCH] Add 'x' and 'y' as input parameters to app.command.Paste() --- src/app/commands/cmd_paste.cpp | 17 ++++- src/app/script/script_input_chain.cpp | 5 +- src/app/script/script_input_chain.h | 3 +- src/app/ui/color_bar.cpp | 5 +- src/app/ui/color_bar.h | 3 +- src/app/ui/doc_view.cpp | 5 +- src/app/ui/doc_view.h | 3 +- src/app/ui/editor/editor.cpp | 16 ++-- src/app/ui/editor/editor.h | 4 +- src/app/ui/home_view.cpp | 3 +- src/app/ui/home_view.h | 3 +- src/app/ui/input_chain.cpp | 6 +- src/app/ui/input_chain.h | 6 +- src/app/ui/input_chain_element.h | 5 +- src/app/ui/timeline/timeline.cpp | 3 +- src/app/ui/timeline/timeline.h | 3 +- src/app/ui/workspace.cpp | 7 +- src/app/ui/workspace.h | 5 +- src/app/util/clipboard.cpp | 104 ++++++++++++++++++++------ src/app/util/clipboard.h | 3 +- tests/scripts/app_cut_paste.lua | 96 ++++++++++++++---------- 21 files changed, 212 insertions(+), 93 deletions(-) diff --git a/src/app/commands/cmd_paste.cpp b/src/app/commands/cmd_paste.cpp index 1f7939b34..d698a4e5d 100644 --- a/src/app/commands/cmd_paste.cpp +++ b/src/app/commands/cmd_paste.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2024 Igara Studio S.A. // Copyright (C) 2001-2017 David Capello // // This program is distributed under the terms of @@ -10,6 +11,7 @@ #include "app/app.h" #include "app/commands/command.h" +#include "app/commands/params.h" #include "app/ui/input_chain.h" namespace app { @@ -19,8 +21,11 @@ public: PasteCommand(); protected: + void onLoadParams(const Params& params) override; bool onEnabled(Context* ctx) override; void onExecute(Context* ctx) override; +private: + std::shared_ptr m_position; }; 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("x"); + m_position->y = params.get_as("y"); + } +} + bool PasteCommand::onEnabled(Context* ctx) { return App::instance()->inputChain().canPaste(ctx); @@ -35,7 +50,7 @@ bool PasteCommand::onEnabled(Context* ctx) void PasteCommand::onExecute(Context* ctx) { - App::instance()->inputChain().paste(ctx); + App::instance()->inputChain().paste(ctx, m_position.get()); } Command* CommandFactory::createPasteCommand() diff --git a/src/app/script/script_input_chain.cpp b/src/app/script/script_input_chain.cpp index 1bae43a4f..0bc7e419a 100644 --- a/src/app/script/script_input_chain.cpp +++ b/src/app/script/script_input_chain.cpp @@ -84,13 +84,14 @@ bool ScriptInputChain::onCopy(Context* ctx) return false; } -bool ScriptInputChain::onPaste(Context* ctx) +bool ScriptInputChain::onPaste(Context* ctx, + const gfx::Point* position) { Clipboard* clipboard = ctx->clipboard(); if (!clipboard) return false; if (clipboard->format() == ClipboardFormat::Image) { - clipboard->paste(ctx, false); + clipboard->paste(ctx, false, position); return true; } return false; diff --git a/src/app/script/script_input_chain.h b/src/app/script/script_input_chain.h index 8333adf92..2b9ee7290 100644 --- a/src/app/script/script_input_chain.h +++ b/src/app/script/script_input_chain.h @@ -27,7 +27,8 @@ namespace app { bool onCanClear(Context* ctx) override; bool onCut(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; void onCancel(Context* ctx) override; }; diff --git a/src/app/ui/color_bar.cpp b/src/app/ui/color_bar.cpp index ccd48578a..570982ed6 100644 --- a/src/app/ui/color_bar.cpp +++ b/src/app/ui/color_bar.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2023 Igara Studio S.A. +// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -1692,7 +1692,8 @@ bool ColorBar::onCopy(Context* ctx) return true; } -bool ColorBar::onPaste(Context* ctx) +bool ColorBar::onPaste(Context* ctx, + const gfx::Point* position) { if (m_tilemapMode == TilemapMode::Tiles) { showRemapTiles(); diff --git a/src/app/ui/color_bar.h b/src/app/ui/color_bar.h index db18caca4..219176e1e 100644 --- a/src/app/ui/color_bar.h +++ b/src/app/ui/color_bar.h @@ -119,7 +119,8 @@ namespace app { bool onCanClear(Context* ctx) override; bool onCut(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; void onCancel(Context* ctx) override; diff --git a/src/app/ui/doc_view.cpp b/src/app/ui/doc_view.cpp index 7c27fe9ed..dbfe2ed8a 100644 --- a/src/app/ui/doc_view.cpp +++ b/src/app/ui/doc_view.cpp @@ -589,12 +589,13 @@ bool DocView::onCopy(Context* ctx) return false; } -bool DocView::onPaste(Context* ctx) +bool DocView::onPaste(Context* ctx, + const gfx::Point* position) { auto clipboard = ctx->clipboard(); if (clipboard->format() == ClipboardFormat::Image || clipboard->format() == ClipboardFormat::Tilemap) { - clipboard->paste(ctx, true); + clipboard->paste(ctx, true, position); return true; } else diff --git a/src/app/ui/doc_view.h b/src/app/ui/doc_view.h index 696965f75..3f12f5bea 100644 --- a/src/app/ui/doc_view.h +++ b/src/app/ui/doc_view.h @@ -102,7 +102,8 @@ namespace app { bool onCanClear(Context* ctx) override; bool onCut(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; void onCancel(Context* ctx) override; diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index 508509b7a..5d60196c4 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -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); @@ -2686,11 +2688,14 @@ void Editor::pasteImage(const Image* image, const Mask* mask) Sprite* sprite = this->sprite(); // Check bounds where the image will be pasted. - int x = mask->bounds().x; - int y = mask->bounds().y; + int x = (position ? position->x : mask->bounds().x); + int y = (position ? position->y : mask->bounds().y); { 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 // 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(); Mask mask2(*mask); - mask2.setOrigin(x, y); + position ? mask2.setOrigin(position->x, position->y) + : mask2.setOrigin(x, y); PixelsMovementPtr pixelsMovement( new PixelsMovement(UIContext::instance(), site, diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index 5a160a9cf..afed8b110 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -252,7 +252,9 @@ namespace app { void setZoomAndCenterInMouse(const render::Zoom& zoom, 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 startFlipTransformation(doc::algorithm::FlipType flipType); diff --git a/src/app/ui/home_view.cpp b/src/app/ui/home_view.cpp index 2d72d8a09..56df797eb 100644 --- a/src/app/ui/home_view.cpp +++ b/src/app/ui/home_view.cpp @@ -230,7 +230,8 @@ bool HomeView::onCopy(Context* ctx) return false; } -bool HomeView::onPaste(Context* ctx) +bool HomeView::onPaste(Context* ctx, + const gfx::Point* position) { auto clipboard = ctx->clipboard(); if (clipboard->format() == ClipboardFormat::Image) { diff --git a/src/app/ui/home_view.h b/src/app/ui/home_view.h index d9211ccad..381cedd6e 100644 --- a/src/app/ui/home_view.h +++ b/src/app/ui/home_view.h @@ -74,7 +74,8 @@ namespace app { bool onCanClear(Context* ctx) override; bool onCut(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; void onCancel(Context* ctx) override; diff --git a/src/app/ui/input_chain.cpp b/src/app/ui/input_chain.cpp index 1e1955fbd..20f0afb88 100644 --- a/src/app/ui/input_chain.cpp +++ b/src/app/ui/input_chain.cpp @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2024 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // 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) { - if (e->onCanPaste(ctx) && e->onPaste(ctx)) + if (e->onCanPaste(ctx) && e->onPaste(ctx, position)) break; } } diff --git a/src/app/ui/input_chain.h b/src/app/ui/input_chain.h index 51f6e78be..a4035218e 100644 --- a/src/app/ui/input_chain.h +++ b/src/app/ui/input_chain.h @@ -1,4 +1,5 @@ // Aseprite +// Copyright (C) 2024 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -8,6 +9,8 @@ #define APP_INPUT_CHAIN_H_INCLUDED #pragma once + #include "gfx/point.h" + #include namespace ui { @@ -35,7 +38,8 @@ namespace app { void cut(Context* ctx); void copy(Context* ctx); - void paste(Context* ctx); + void paste(Context* ctx, + const gfx::Point* position); void clear(Context* ctx); void cancel(Context* ctx); diff --git a/src/app/ui/input_chain_element.h b/src/app/ui/input_chain_element.h index 17df4bec2..6593017fc 100644 --- a/src/app/ui/input_chain_element.h +++ b/src/app/ui/input_chain_element.h @@ -9,6 +9,8 @@ #define APP_INPUT_CHAIN_ELEMENT_H_INCLUDED #pragma once +#include "gfx/point.h" + namespace ui { class Message; } @@ -34,7 +36,8 @@ namespace app { // which catch any exception that is thrown. virtual bool onCut(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 void onCancel(Context* ctx) = 0; }; diff --git a/src/app/ui/timeline/timeline.cpp b/src/app/ui/timeline/timeline.cpp index 35186bf5e..7f1c0329f 100644 --- a/src/app/ui/timeline/timeline.cpp +++ b/src/app/ui/timeline/timeline.cpp @@ -4398,7 +4398,8 @@ bool Timeline::onCopy(Context* ctx) return false; } -bool Timeline::onPaste(Context* ctx) +bool Timeline::onPaste(Context* ctx, + const gfx::Point* position) { auto clipboard = ctx->clipboard(); if (clipboard->format() == ClipboardFormat::DocRange) { diff --git a/src/app/ui/timeline/timeline.h b/src/app/ui/timeline/timeline.h index 53d58e229..02ab07a01 100644 --- a/src/app/ui/timeline/timeline.h +++ b/src/app/ui/timeline/timeline.h @@ -190,7 +190,8 @@ namespace app { bool onCanClear(Context* ctx) override; bool onCut(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; void onCancel(Context* ctx) override; diff --git a/src/app/ui/workspace.cpp b/src/app/ui/workspace.cpp index 650c452c6..1dedbf13c 100644 --- a/src/app/ui/workspace.cpp +++ b/src/app/ui/workspace.cpp @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2018-2022 Igara Studio S.A. +// Copyright (C) 2018-2024 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -381,12 +381,13 @@ bool Workspace::onCopy(Context* ctx) return false; } -bool Workspace::onPaste(Context* ctx) +bool Workspace::onPaste(Context* ctx, + const gfx::Point* position) { WorkspaceView* view = activeView(); InputChainElement* activeElement = (view ? view->onGetInputChainElement(): nullptr); if (activeElement) - return activeElement->onPaste(ctx); + return activeElement->onPaste(ctx, position); else return false; } diff --git a/src/app/ui/workspace.h b/src/app/ui/workspace.h index 30e0245ca..4a64e22f3 100644 --- a/src/app/ui/workspace.h +++ b/src/app/ui/workspace.h @@ -1,5 +1,5 @@ // Aseprite -// Copyright (C) 2022 Igara Studio S.A. +// Copyright (C) 2022-2024 Igara Studio S.A. // Copyright (C) 2001-2018 David Capello // // This program is distributed under the terms of @@ -71,7 +71,8 @@ namespace app { bool onCanClear(Context* ctx) override; bool onCut(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; void onCancel(Context* ctx) override; diff --git a/src/app/util/clipboard.cpp b/src/app/util/clipboard.cpp index f8f18d0fc..52bc803ef 100644 --- a/src/app/util/clipboard.cpp +++ b/src/app/util/clipboard.cpp @@ -34,6 +34,8 @@ #include "app/util/new_image_from_mask.h" #include "app/util/range_utils.h" #include "clip/clip.h" +#include "doc/algorithm/shrink_bounds.h" +#include "doc/blend_image.h" #include "doc/doc.h" #include "render/dithering.h" #include "render/ordered_dither.h" @@ -425,7 +427,7 @@ void Clipboard::copyImage(const Image* image, (mask ? new Mask(*mask): nullptr), (pal ? new Palette(*pal): nullptr), nullptr, - true, false); + App::instance()->isGui(), false); } void Clipboard::copyTilemap(const Image* image, @@ -463,7 +465,8 @@ void Clipboard::copyPalette(const Palette* palette, } void Clipboard::paste(Context* ctx, - const bool interactive) + const bool interactive, + const gfx::Point* position) { const Site site = ctx->activeSite(); Doc* dstDoc = site.document(); @@ -518,38 +521,94 @@ void Clipboard::paste(Context* ctx, // Change to MovingPixelsState editor->pasteImage(src_image.get(), - m_data->mask.get()); + m_data->mask.get(), + position); } 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(); ASSERT(dstLayer); - if (!dstLayer || !dstLayer->isImage()) + if (!dstLayer || + !dstLayer->isImage() || + (src_image->pixelFormat() != dstSpr->pixelFormat())) 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); Tx tx(writer, "Paste Image"); DocApi api = dstDoc->getApi(tx); - Cel* dstCel = api.addCel( - static_cast(dstLayer), site.frame(), - ImageRef(Image::createCopy(src_image.get()))); + Cel* dstCel; + if (isAnImageOnDstCel) + 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(dstLayer), + site.frame(), + result); + // Set cel bounds if (dstCel) { - if (m_data->mask) { - if (dstLayer->isReference()) { - dstCel->setBounds(dstSpr->bounds()); - - Mask emptyMask; - tx(new cmd::SetMask(dstDoc, &emptyMask)); - } - else { - dstCel->setBounds(m_data->mask->bounds()); - tx(new cmd::SetMask(dstDoc, m_data->mask.get())); - } + const Mask emptyMask; + if (dstLayer->isReference()) { + dstCel->setBounds(dstSpr->bounds()); + tx(new cmd::SetMask(dstDoc, &emptyMask)); + } + else { + dstCel->setBounds(resultBounds); + tx(new cmd::SetMask(dstDoc, m_data->mask ? m_data->mask.get() + : &emptyMask)); } } - tx.commit(); } break; @@ -562,7 +621,8 @@ void Clipboard::paste(Context* ctx, // Change to MovingTilemapState editor->pasteImage(m_data->tilemap.get(), - m_data->mask.get()); + m_data->mask.get(), + position); } else { // TODO non-interactive version (for scripts) diff --git a/src/app/util/clipboard.h b/src/app/util/clipboard.h index 1a8385209..18c96aeea 100644 --- a/src/app/util/clipboard.h +++ b/src/app/util/clipboard.h @@ -75,7 +75,8 @@ namespace app { void copyPalette(const doc::Palette* palette, const doc::PalettePicks& picks); void paste(Context* ctx, - const bool interactive); + const bool interactive, + const gfx::Point* position = nullptr); doc::ImageRef getImage(doc::Palette* palette); diff --git a/tests/scripts/app_cut_paste.lua b/tests/scripts/app_cut_paste.lua index 5de9b6284..c344a1851 100644 --- a/tests/scripts/app_cut_paste.lua +++ b/tests/scripts/app_cut_paste.lua @@ -28,19 +28,10 @@ do { 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] - if (app.isUIAvailable) then - assert(app.cel.position == Point(1, 1)) - expect_img(app.activeImage, - { 1, 1 }) - else - assert(app.cel.position == Point(0, 1)) - expect_img(app.activeImage, - { 0, 1, 1, 0, 0 }) - end - + assert(app.cel.position == Point(1, 1)) + expect_img(app.activeImage, + { 1, 1 }) app.command.FlattenLayers() assert(app.cel.position == Point(1, 1)) expect_img(app.activeImage, @@ -53,6 +44,18 @@ do app.undo() -- New Layer 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 app.layer = sprite.layers[1] app.useTool { @@ -62,30 +65,21 @@ do } app.command.Cut() + assert(app.cel.position == Point(1, 1)) + expect_img(app.activeImage, + { 1, 1, + 1, 0 }) sprite:newLayer() app.command.Paste() - -- TO DO: Fix this difference between running this script with - -- 'UI Available' versus 'UI Not Available' app.layer = sprite.layers[3] - if (app.isUIAvailable) then - assert(app.cel.position == Point(2, 2)) - expect_img(app.activeImage, - { 1 }) - else - assert(app.cel.position == Point(2, 2)) - expect_img(app.activeImage, - { 1, 0, 0 }) - end + assert(app.cel.position == Point(2, 2)) + expect_img(app.activeImage, + { 1 }) app.undo() -- Paste app.undo() -- New Layer 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 app.layer = sprite.layers[1] @@ -108,19 +102,10 @@ do expect_img(app.activeImage, { 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] - if (app.isUIAvailable) then - assert(app.cel.position == Point(1, 1)) - expect_img(app.activeImage, - { 1, 1 }) - else - assert(app.cel.position == Point(0, 1)) - expect_img(app.activeImage, - { 0, 1, 1, 0, 0 }) - end + assert(app.cel.position == Point(1, 1)) + expect_img(app.activeImage, + { 1, 1 }) app.command.FlattenLayers() assert(app.cel.position == Point(1, 1)) @@ -130,6 +115,17 @@ do 0, 2, 2 }) -- 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 { tool = "rectangular_marquee", points = {Point(0,1), Point(4,2)}, @@ -141,6 +137,8 @@ do { 2, 2 }) app.undo() + app.undo() + app.undo() assert(app.cel.position == Point(1, 1)) expect_img(app.activeImage, @@ -151,7 +149,7 @@ do -- Test app.command.Cancel() app.useTool { tool = "rectangular_marquee", - points = {Point(0,1), Point(4,2)}, + points = {Point(2,2), Point(4,2)}, selection = SelectionMode.REPLACE } app.command.Cancel() @@ -161,4 +159,20 @@ do { 1, 1, 0, 1, 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 \ No newline at end of file