Add Edit > Paste Special > Paste As New (Reference) Layer (fix #672, fix #1748)

This commit is contained in:
David Capello 2019-06-27 15:34:56 -03:00
parent 056eb28670
commit ff6538a68e
8 changed files with 144 additions and 82 deletions

View File

@ -39,6 +39,9 @@
<key command="CopyMerged" shortcut="Ctrl+Shift+C" mac="Cmd+Shift+C" />
<key command="Paste" shortcut="Ctrl+V" mac="Cmd+V" />
<key command="Paste" shortcut="Shift+Ins" />
<key command="NewLayer" shortcut="Ctrl+Shift+V" mac="Cmd+Shift+V">
<param name="fromClipboard" value="true" />
</key>
<key command="Clear" shortcut="Del" />
<key command="Clear" shortcut="Backspace" />
<key command="Fill" shortcut="F" />
@ -623,6 +626,15 @@
<item command="Copy" text="@.edit_copy" />
<item command="CopyMerged" text="@.edit_copy_merged" />
<item command="Paste" text="@.edit_paste" />
<menu text="@.edit_paste_special">
<item command="NewLayer" text="@.edit_paste_special_new_layer">
<param name="fromClipboard" value="true" />
</item>
<item command="NewLayer" text="@.edit_paste_special_new_ref_layer">
<param name="reference" value="true" />
<param name="fromClipboard" value="true" />
</item>
</menu>
<item command="Clear" text="@.edit_clear" />
<separator />
<item command="Fill" text="@.edit_fill" />
@ -772,7 +784,7 @@
<separator />
<item command="NewLayer" text="@.layer_add_reference_layer">
<param name="reference" value="true" />
<param name="from-file" value="true" />
<param name="fromFile" value="true" />
</item>
</menu>
<menu text="@.frame">

View File

@ -337,10 +337,12 @@ NewFrame_NewEmptyFrame = New Empty Frame
NewFrame_DuplicateCels = Duplicate Linked Cels
NewFrame_DuplicateCelsBlock = Duplicate Cels
NewFrameTag = New Frame Tag
NewLayer = New Layer
NewLayer_BeforeActiveLayer = New Layer Below
NewLayer_Group = New Group
NewLayer_ReferenceLayer = New Reference Layer
NewLayer = New {}
NewLayer_BeforeActiveLayer = New {} Below
NewLayer_Layer = Layer
NewLayer_Group = Group
NewLayer_ReferenceLayer = Reference Layer
NewLayer_FromClipboard = {} (From Clipboard)
NewSpriteFromSelection = New Sprite From Selection
OpenBrowser = Open Browser
OpenFile = Open Sprite
@ -716,6 +718,9 @@ edit_cut = Cu&t
edit_copy = &Copy
edit_copy_merged = Copy Mer&ged
edit_paste = &Paste
edit_paste_special = Paste Specia&l
edit_paste_special_new_layer = Paste as New &Layer
edit_paste_special_new_ref_layer = Paste as New &Reference Layer
edit_clear = &Delete
edit_fill = &Fill
edit_stroke = Stroke

View File

@ -13,6 +13,7 @@
#include "app/cmd/move_layer.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/new_params.h"
#include "app/commands/params.h"
#include "app/context_access.h"
#include "app/doc_api.h"
@ -24,9 +25,11 @@
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "doc/layer.h"
#include "doc/primitives.h"
#include "doc/sprite.h"
#include "fmt/format.h"
#include "render/dithering.h"
#include "render/ordered_dither.h"
#include "render/quantization.h"
@ -42,7 +45,18 @@ namespace app {
using namespace ui;
class NewLayerCommand : public Command {
struct NewLayerParams : public NewParams {
Param<std::string> name { this, std::string(), "name" };
Param<bool> group { this, false, "group" };
Param<bool> reference { this, false, "reference" };
Param<bool> ask { this, false, "ask" };
Param<bool> fromFile { this, false, { "fromFile", "from-file" } };
Param<bool> fromClipboard { this, false, "fromClipboard" };
Param<bool> top { this, false, "top" };
Param<bool> before { this, false, "before" };
};
class NewLayerCommand : public CommandWithNewParams<NewLayerParams> {
public:
enum class Type { Layer, Group, ReferenceLayer };
enum class Place { AfterActiveLayer, BeforeActiveLayer, Top };
@ -58,46 +72,40 @@ protected:
private:
std::string getUniqueLayerName(const Sprite* sprite) const;
int getMaxLayerNum(const Layer* layer) const;
const char* layerPrefix() const;
std::string layerPrefix() const;
std::string m_name;
Type m_type;
Place m_place;
bool m_ask;
bool m_fromFile;
};
NewLayerCommand::NewLayerCommand()
: Command(CommandId::NewLayer(), CmdRecordableFlag)
: CommandWithNewParams(CommandId::NewLayer(), CmdRecordableFlag)
{
m_name = "";
m_type = Type::Layer;
m_place = Place::AfterActiveLayer;
m_ask = false;
}
void NewLayerCommand::onLoadParams(const Params& params)
void NewLayerCommand::onLoadParams(const Params& commandParams)
{
m_name = params.get("name");
CommandWithNewParams<NewLayerParams>::onLoadParams(commandParams);
m_type = Type::Layer;
if (params.get_as<bool>("group"))
if (params().group())
m_type = Type::Group;
else if (params.get_as<bool>("reference"))
else if (params().reference())
m_type = Type::ReferenceLayer;
m_ask = params.get_as<bool>("ask");
m_fromFile = params.get_as<bool>("from-file");
m_place = Place::AfterActiveLayer;
if (params.get_as<bool>("top"))
if (params().top())
m_place = Place::Top;
else if (params.get_as<bool>("before"))
else if (params().before())
m_place = Place::BeforeActiveLayer;
}
bool NewLayerCommand::onEnabled(Context* context)
{
return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
ContextFlags::HasActiveSprite);
ContextFlags::HasActiveSprite)
&& (!params().fromClipboard() ||
(clipboard::get_current_format() == clipboard::ClipboardImage));
}
namespace {
@ -126,14 +134,14 @@ void NewLayerCommand::onExecute(Context* context)
}
});
// Default name (m_name is a name specified in params)
if (!m_name.empty())
name = m_name;
// Default name
if (params().name.isSet())
name = params().name();
else
name = getUniqueLayerName(sprite);
// Select a file to copy its content
if (m_fromFile) {
if (params().fromFile()) {
Doc* oldActiveDocument = context->activeDocument();
Command* openFile = Commands::instance()->byId(CommandId::OpenFile());
Params params;
@ -154,7 +162,7 @@ void NewLayerCommand::onExecute(Context* context)
#ifdef ENABLE_UI
// If params specify to ask the user about the name...
if (m_ask) {
if (params().ask()) {
// We open the window to ask the name
app::gen::NewLayer window;
window.name()->setText(name.c_str());
@ -186,7 +194,7 @@ void NewLayerCommand::onExecute(Context* context)
{
Tx tx(
writer.context(),
std::string("New ") + layerPrefix());
fmt::format(Strings::commands_NewLayer(), layerPrefix()));
DocApi api = document->getApi(tx);
bool afterBackground = false;
@ -319,6 +327,10 @@ void NewLayerCommand::onExecute(Context* context)
}
}
}
// Paste new layer from clipboard
else if (params().fromClipboard() && layer->isImage()) {
clipboard::paste(context, false);
}
tx.commit();
}
@ -330,7 +342,7 @@ void NewLayerCommand::onExecute(Context* context)
StatusBar::instance()->invalidate();
StatusBar::instance()->showTip(
1000, "%s '%s' created",
layerPrefix(),
layerPrefix().c_str(),
name.c_str());
App::instance()->mainWindow()->popTimeline();
@ -341,32 +353,20 @@ void NewLayerCommand::onExecute(Context* context)
std::string NewLayerCommand::onGetFriendlyName() const
{
std::string text;
switch (m_type) {
case Type::Layer:
if (m_place == Place::BeforeActiveLayer)
text = Strings::commands_NewLayer_BeforeActiveLayer();
else
text = Strings::commands_NewLayer();
break;
case Type::Group:
text = Strings::commands_NewLayer_Group();
break;
case Type::ReferenceLayer:
text = Strings::commands_NewLayer_ReferenceLayer();
break;
}
if (m_place == Place::BeforeActiveLayer)
text = fmt::format(Strings::commands_NewLayer_BeforeActiveLayer(), layerPrefix());
else
text = fmt::format(Strings::commands_NewLayer(), layerPrefix());
if (params().fromClipboard())
text = fmt::format(Strings::commands_NewLayer_FromClipboard(), text);
return text;
}
std::string NewLayerCommand::getUniqueLayerName(const Sprite* sprite) const
{
char buf[1024];
std::sprintf(buf, "%s %d",
layerPrefix(),
getMaxLayerNum(sprite->root())+1);
return buf;
return fmt::format("{} {}",
layerPrefix(),
getMaxLayerNum(sprite->root())+1);
}
int NewLayerCommand::getMaxLayerNum(const Layer* layer) const
@ -388,12 +388,12 @@ int NewLayerCommand::getMaxLayerNum(const Layer* layer) const
return max;
}
const char* NewLayerCommand::layerPrefix() const
std::string NewLayerCommand::layerPrefix() const
{
switch (m_type) {
case Type::Layer: return "Layer";
case Type::Group: return "Group";
case Type::ReferenceLayer: return "Reference Layer";
case Type::Layer: return Strings::commands_NewLayer_Layer();
case Type::Group: return Strings::commands_NewLayer_Group();
case Type::ReferenceLayer: return Strings::commands_NewLayer_ReferenceLayer();
}
return "Unknown";
}

View File

@ -136,9 +136,8 @@ namespace app {
CommandWithNewParams(Args&&...args)
: CommandWithNewParamsBase(std::forward<Args>(args)...) { }
T& params() {
return m_params;
}
T& params() { return m_params; }
const T& params() const { return m_params; }
private:
void onResetValues() override {

View File

@ -552,7 +552,7 @@ bool DocView::onCopy(Context* ctx)
bool DocView::onPaste(Context* ctx)
{
if (clipboard::get_current_format() == clipboard::ClipboardImage) {
clipboard::paste();
clipboard::paste(ctx, true);
return true;
}
else

View File

@ -4017,7 +4017,7 @@ bool Timeline::onCopy(Context* ctx)
bool Timeline::onPaste(Context* ctx)
{
if (clipboard::get_current_format() == clipboard::ClipboardDocRange) {
clipboard::paste();
clipboard::paste(ctx, true);
return true;
}
else

View File

@ -12,6 +12,7 @@
#include "app/app.h"
#include "app/cmd/clear_mask.h"
#include "app/cmd/deselect_mask.h"
#include "app/cmd/set_mask.h"
#include "app/cmd/trim_cel.h"
#include "app/console.h"
#include "app/context_access.h"
@ -324,14 +325,16 @@ void copy_palette(const Palette* palette, const doc::PalettePicks& picks)
clipboard_picks = picks;
}
void paste()
void paste(Context* ctx, const bool interactive)
{
Editor* editor = current_editor;
if (editor == NULL)
Site site = ctx->activeSite();
Doc* dstDoc = site.document();
if (!dstDoc)
return;
Doc* dstDoc = editor->document();
Sprite* dstSpr = dstDoc->sprite();
Sprite* dstSpr = site.sprite();
if (!dstSpr)
return;
switch (get_current_format()) {
@ -350,7 +353,7 @@ void paste()
if (!clipboard_image)
return;
Palette* dst_palette = dstSpr->palette(editor->frame());
Palette* dst_palette = dstSpr->palette(site.frame());
// Source image (clipboard or a converted copy to the destination 'imgtype')
ImageRef src_image;
@ -362,7 +365,7 @@ void paste()
src_image = clipboard_image;
}
else {
RgbMap* dst_rgbmap = dstSpr->rgbMap(editor->frame());
RgbMap* dst_rgbmap = dstSpr->rgbMap(site.frame());
src_image.reset(
render::convert_pixel_format(
@ -373,9 +376,42 @@ void paste()
0));
}
// Change to MovingPixelsState
editor->pasteImage(src_image.get(),
clipboard_mask.get());
if (current_editor && interactive) {
// Change to MovingPixelsState
current_editor->pasteImage(src_image.get(),
clipboard_mask.get());
}
else {
// Non-interactive version (just copy the image to the cel)
Layer* dstLayer = site.layer();
ASSERT(dstLayer);
if (!dstLayer || !dstLayer->isImage())
return;
Tx tx(ctx, "Paste Image");
DocApi api = dstDoc->getApi(tx);
Cel* dstCel = api.addCel(
static_cast<LayerImage*>(dstLayer), site.frame(),
ImageRef(Image::createCopy(src_image.get())));
// Adjust bounds
if (dstCel) {
if (clipboard_mask) {
if (dstLayer->isReference()) {
dstCel->setBounds(dstSpr->bounds());
Mask emptyMask;
tx(new cmd::SetMask(dstDoc, &emptyMask));
}
else {
dstCel->setBounds(clipboard_mask->bounds());
tx(new cmd::SetMask(dstDoc, clipboard_mask.get()));
}
}
}
tx.commit();
}
break;
}
@ -387,8 +423,12 @@ void paste()
switch (srcRange.type()) {
case DocRange::kCels: {
Layer* dstLayer = editor->layer();
frame_t dstFrameFirst = editor->frame();
Layer* dstLayer = site.layer();
ASSERT(dstLayer);
if (!dstLayer)
return;
frame_t dstFrameFirst = site.frame();
DocRange dstRange;
dstRange.startRange(dstLayer, dstFrameFirst, DocRange::kCels);
@ -405,11 +445,12 @@ void paste()
// This is the app::copy_range (not clipboard::copy_range()).
if (srcRange.layers() == dstRange.layers())
app::copy_range(srcDoc, srcRange, dstRange, kDocRangeBefore);
editor->invalidate();
if (current_editor)
current_editor->invalidate(); // TODO check if this is necessary
return;
}
Tx tx(UIContext::instance(), "Paste Cels");
Tx tx(ctx, "Paste Cels");
DocApi api = dstDoc->getApi(tx);
// Add extra frames if needed
@ -484,12 +525,13 @@ void paste()
}
tx.commit();
editor->invalidate();
if (current_editor)
current_editor->invalidate(); // TODO check if this is necessary
break;
}
case DocRange::kFrames: {
frame_t dstFrame = editor->frame();
frame_t dstFrame = site.frame();
// We use a DocRange operation to copy frames inside
// the same sprite.
@ -501,7 +543,7 @@ void paste()
break;
}
Tx tx(UIContext::instance(), "Paste Frames");
Tx tx(ctx, "Paste Frames");
DocApi api = dstDoc->getApi(tx);
auto srcLayers = srcSpr->allBrowsableLayers();
@ -536,7 +578,8 @@ void paste()
}
tx.commit();
editor->invalidate();
if (current_editor)
current_editor->invalidate(); // TODO check if this is necessary
break;
}
@ -544,7 +587,7 @@ void paste()
if (srcDoc->colorMode() != dstDoc->colorMode())
throw std::runtime_error("You cannot copy layers of document with different color modes");
Tx tx(UIContext::instance(), "Paste Layers");
Tx tx(ctx, "Paste Layers");
DocApi api = dstDoc->getApi(tx);
// Remove children if their parent is selected so we only
@ -587,7 +630,8 @@ void paste()
}
tx.commit();
editor->invalidate();
if (current_editor)
current_editor->invalidate(); // TODO check if this is necessary
break;
}
}

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -22,6 +23,7 @@ namespace doc {
namespace app {
class Doc;
class Context;
class ContextReader;
class ContextWriter;
class DocRange;
@ -61,7 +63,7 @@ namespace app {
void copy_range(const ContextReader& context, const DocRange& range);
void copy_image(const Image* image, const Mask* mask, const Palette* palette);
void copy_palette(const Palette* palette, const PalettePicks& picks);
void paste();
void paste(Context* ctx, const bool interactive);
// Returns true and fills the specified "size"" with the image's
// size in the clipboard, or return false in case that the clipboard