diff --git a/data/gui.xml b/data/gui.xml
index 90cd94b7f..8f1763df5 100644
--- a/data/gui.xml
+++ b/data/gui.xml
@@ -1203,6 +1203,7 @@
diff --git a/data/strings/en.ini b/data/strings/en.ini
index f8c0fc3a2..c39e2d345 100644
--- a/data/strings/en.ini
+++ b/data/strings/en.ini
@@ -265,6 +265,7 @@ Despeckle = Despeckle
DeveloperConsole = Developer Console
DiscardBrush = Discard Brush
DuplicateLayer = Duplicate Layer
+DuplicateSlice = Duplicate Slice
DuplicateSprite = Duplicate Sprite
DuplicateView = Duplicate View
Exit = Exit
@@ -1658,6 +1659,10 @@ from = From:
to = To:
tolerance = Tolerance:
+[duplicate_slice]
+x_duplicated = Slice "{}" duplicated
+n_slices_duplicated = {} slice(s) duplicated
+
[remove_slice]
x_removed = Slice "{}" removed
n_slices_removed = {} slice(s) removed
@@ -1736,6 +1741,7 @@ delete_file = Delete file, I've already sent it
[slice_popup_menu]
properties = Slice &Properties...
+duplicate = D&uplicate Slice
delete = &Delete Slice
[slice_properties]
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index ae287e5ac..e0c92c7bf 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -394,6 +394,7 @@ target_sources(app-lib PRIVATE
commands/cmd_deselect_mask.cpp
commands/cmd_discard_brush.cpp
commands/cmd_duplicate_layer.cpp
+ commands/cmd_duplicate_slice.cpp
commands/cmd_duplicate_sprite.cpp
commands/cmd_duplicate_view.cpp
commands/cmd_enter_license.cpp
@@ -713,6 +714,7 @@ target_sources(app-lib PRIVATE
util/render_text.cpp
util/resize_image.cpp
util/shader_helpers.cpp
+ util/slice_utils.cpp
util/tile_flags_utils.cpp
util/tileset_utils.cpp
util/wrap_point.cpp
diff --git a/src/app/commands/cmd_duplicate_slice.cpp b/src/app/commands/cmd_duplicate_slice.cpp
new file mode 100644
index 000000000..9e1ea21e8
--- /dev/null
+++ b/src/app/commands/cmd_duplicate_slice.cpp
@@ -0,0 +1,108 @@
+// Aseprite
+// Copyright (C) 2025 Igara Studio S.A.
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#ifdef HAVE_CONFIG_H
+ #include "config.h"
+#endif
+
+#include "app/cmd/add_slice.h"
+#include "app/commands/command.h"
+#include "app/context.h"
+#include "app/context_access.h"
+#include "app/context_flags.h"
+#include "app/i18n/strings.h"
+#include "app/site.h"
+#include "app/tx.h"
+#include "app/ui/status_bar.h"
+#include "app/util/slice_utils.h"
+#include "base/convert_to.h"
+#include "doc/object_id.h"
+#include "doc/slice.h"
+
+namespace app {
+
+class DuplicateSliceCommand : public Command {
+public:
+ DuplicateSliceCommand();
+
+protected:
+ void onLoadParams(const Params& params) override;
+ bool onEnabled(Context* context) override;
+ void onExecute(Context* context) override;
+
+private:
+ ObjectId m_sliceId;
+};
+
+DuplicateSliceCommand::DuplicateSliceCommand()
+ : Command(CommandId::DuplicateSlice(), CmdRecordableFlag)
+{
+}
+
+void DuplicateSliceCommand::onLoadParams(const Params& params)
+{
+ std::string id = params.get("id");
+ if (!id.empty())
+ m_sliceId = ObjectId(base::convert_to(id));
+ else
+ m_sliceId = NullId;
+}
+
+bool DuplicateSliceCommand::onEnabled(Context* context)
+{
+ return context->checkFlags(ContextFlags::ActiveDocumentIsWritable |
+ ContextFlags::HasActiveSprite | ContextFlags::HasActiveLayer);
+}
+
+void DuplicateSliceCommand::onExecute(Context* context)
+{
+ std::vector selectedSlices;
+ {
+ const ContextReader reader(context);
+ if (m_sliceId == NullId) {
+ selectedSlices = get_selected_slices(reader.site());
+ if (selectedSlices.empty())
+ return;
+ }
+ else
+ selectedSlices.push_back(reader.sprite()->slices().getById(m_sliceId));
+ }
+
+ ContextWriter writer(context);
+ Tx tx(writer, "Duplicate Slice");
+ Sprite* sprite = writer.site().sprite();
+
+ Doc* doc = static_cast(sprite->document());
+ doc->notifyBeforeSlicesDuplication();
+ for (auto* s : selectedSlices) {
+ Slice* slice = new Slice(*s);
+ slice->setName(get_unique_slice_name(sprite, s->name()));
+ tx(new cmd::AddSlice(sprite, slice));
+ doc->notifySliceDuplicated(slice);
+ }
+ tx.commit();
+
+ std::string sliceName;
+ if (selectedSlices.size() == 1)
+ sliceName = selectedSlices[0]->name();
+
+ StatusBar::instance()->invalidate();
+ if (!sliceName.empty()) {
+ StatusBar::instance()->showTip(1000, Strings::duplicate_slice_x_duplicated(sliceName));
+ }
+ else {
+ StatusBar::instance()->showTip(
+ 1000,
+ Strings::duplicate_slice_n_slices_duplicated(selectedSlices.size()));
+ }
+}
+
+Command* CommandFactory::createDuplicateSliceCommand()
+{
+ return new DuplicateSliceCommand;
+}
+
+} // namespace app
diff --git a/src/app/commands/commands_list.h b/src/app/commands/commands_list.h
index 4177aec0b..7df6ff229 100644
--- a/src/app/commands/commands_list.h
+++ b/src/app/commands/commands_list.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2023 Igara Studio S.A.
+// Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -40,6 +40,7 @@ FOR_EACH_COMMAND(DeselectMask)
FOR_EACH_COMMAND(Despeckle)
FOR_EACH_COMMAND(DiscardBrush)
FOR_EACH_COMMAND(DuplicateLayer)
+FOR_EACH_COMMAND(DuplicateSlice)
FOR_EACH_COMMAND(DuplicateSprite)
FOR_EACH_COMMAND(DuplicateView)
FOR_EACH_COMMAND(Exit)
diff --git a/src/app/doc.cpp b/src/app/doc.cpp
index 21a8708b1..2523aa04c 100644
--- a/src/app/doc.cpp
+++ b/src/app/doc.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2024 Igara Studio S.A.
+// Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -338,6 +338,19 @@ void Doc::notifyAfterAddTile(LayerTilemap* layer, frame_t frame, tile_index ti)
notify_observers(&DocObserver::onAfterAddTile, ev);
}
+void Doc::notifyBeforeSlicesDuplication()
+{
+ DocEvent ev(this);
+ notify_observers(&DocObserver::onBeforeSlicesDuplication, ev);
+}
+
+void Doc::notifySliceDuplicated(Slice* slice)
+{
+ DocEvent ev(this);
+ ev.slice(slice);
+ notify_observers(&DocObserver::onSliceDuplicated, ev);
+}
+
bool Doc::isModified() const
{
return !m_undo->isInSavedStateOrSimilar();
diff --git a/src/app/doc.h b/src/app/doc.h
index 6e692ac99..f6dd07245 100644
--- a/src/app/doc.h
+++ b/src/app/doc.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2024 Igara Studio S.A.
+// Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -141,6 +141,8 @@ public:
void notifyTilesetChanged(Tileset* tileset);
void notifyLayerGroupCollapseChange(Layer* layer);
void notifyAfterAddTile(LayerTilemap* layer, frame_t frame, tile_index ti);
+ void notifyBeforeSlicesDuplication();
+ void notifySliceDuplicated(Slice* slice);
//////////////////////////////////////////////////////////////////////
// File related properties
diff --git a/src/app/doc_observer.h b/src/app/doc_observer.h
index 033c91516..208a5b0a7 100644
--- a/src/app/doc_observer.h
+++ b/src/app/doc_observer.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2024 Igara Studio S.A.
+// Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -90,6 +90,8 @@ public:
// Slices
virtual void onSliceNameChange(DocEvent& ev) {}
+ virtual void onBeforeSlicesDuplication(DocEvent& ev) {}
+ virtual void onSliceDuplicated(DocEvent& ev) {}
// The tileset has changed.
virtual void onTilesetChanged(DocEvent& ev) {}
diff --git a/src/app/ui/doc_view.cpp b/src/app/ui/doc_view.cpp
index 18aebbda0..43fe8a763 100644
--- a/src/app/ui/doc_view.cpp
+++ b/src/app/ui/doc_view.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2024 Igara Studio S.A.
+// Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -36,9 +36,11 @@
#include "app/ui/workspace.h"
#include "app/ui_context.h"
#include "app/util/clipboard.h"
+#include "app/util/slice_utils.h"
#include "base/fs.h"
#include "doc/color.h"
#include "doc/layer.h"
+#include "doc/slice.h"
#include "doc/sprite.h"
#include "fmt/format.h"
#include "ui/accelerator.h"
@@ -510,6 +512,8 @@ bool DocView::onCanCopy(Context* ctx)
return true;
else if (m_editor->isMovingPixels())
return true;
+ else if (m_editor->hasSelectedSlices())
+ return true;
else
return false;
}
@@ -528,6 +532,11 @@ bool DocView::onCanPaste(Context* ctx)
return true;
}
}
+
+ if (ctx->checkFlags(ContextFlags::ActiveDocumentIsWritable) &&
+ ctx->clipboard()->format() == ClipboardFormat::Slices) {
+ return true;
+ }
return false;
}
@@ -560,15 +569,22 @@ bool DocView::onCopy(Context* ctx)
ctx->clipboard()->copy(reader);
return true;
}
- else
- return false;
+
+ std::vector selectedSlices = get_selected_slices(reader.site());
+ if (!selectedSlices.empty()) {
+ ctx->clipboard()->copySlices(selectedSlices);
+ return true;
+ }
+
+ return false;
}
bool DocView::onPaste(Context* ctx, const gfx::Point* position)
{
auto clipboard = ctx->clipboard();
if (clipboard->format() == ClipboardFormat::Image ||
- clipboard->format() == ClipboardFormat::Tilemap) {
+ clipboard->format() == ClipboardFormat::Tilemap ||
+ clipboard->format() == ClipboardFormat::Slices) {
clipboard->paste(ctx, true, position);
return true;
}
diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp
index 069cc2173..16de20a58 100644
--- a/src/app/ui/editor/editor.cpp
+++ b/src/app/ui/editor/editor.cpp
@@ -2534,6 +2534,16 @@ void Editor::onBeforeLayerEditableChange(DocEvent& ev, bool newState)
m_state->onBeforeLayerEditableChange(this, ev.layer(), newState);
}
+void Editor::onBeforeSlicesDuplication(DocEvent& ev)
+{
+ clearSlicesSelection();
+}
+
+void Editor::onSliceDuplicated(DocEvent& ev)
+{
+ selectSlice(ev.slice());
+}
+
void Editor::setCursor(const gfx::Point& mouseDisplayPos)
{
Rect vp = View::getView(this)->viewportBounds();
diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h
index 038fa5361..4c378d97a 100644
--- a/src/app/ui/editor/editor.h
+++ b/src/app/ui/editor/editor.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2018-2024 Igara Studio S.A.
+// Copyright (C) 2018-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -343,6 +343,8 @@ protected:
void onRemoveSlice(DocEvent& ev) override;
void onBeforeLayerVisibilityChange(DocEvent& ev, bool newState) override;
void onBeforeLayerEditableChange(DocEvent& ev, bool newState) override;
+ void onBeforeSlicesDuplication(DocEvent& ev) override;
+ void onSliceDuplicated(DocEvent& ev) override;
// ActiveToolObserver impl
void onActiveToolChange(tools::Tool* tool) override;
diff --git a/src/app/ui/editor/tool_loop_impl.cpp b/src/app/ui/editor/tool_loop_impl.cpp
index 71d9150e4..c867278a8 100644
--- a/src/app/ui/editor/tool_loop_impl.cpp
+++ b/src/app/ui/editor/tool_loop_impl.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2019-2024 Igara Studio S.A.
+// Copyright (C) 2019-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -45,6 +45,7 @@
#include "app/ui_context.h"
#include "app/util/expand_cel_canvas.h"
#include "app/util/layer_utils.h"
+#include "app/util/slice_utils.h"
#include "doc/brush.h"
#include "doc/cel.h"
#include "doc/image.h"
@@ -693,7 +694,7 @@ public:
// popup menu to create a new one.
if (!m_editor->selectSliceBox(bounds) && (bounds.w > 1 || bounds.h > 1)) {
Slice* slice = new Slice;
- slice->setName(getUniqueSliceName());
+ slice->setName(get_unique_slice_name(m_sprite));
SliceKey key(bounds);
slice->insert(getFrame(), key);
@@ -716,18 +717,6 @@ private:
// EditorObserver impl
void onScrollChanged(Editor* editor) override { updateAllVisibleRegion(); }
void onZoomChanged(Editor* editor) override { updateAllVisibleRegion(); }
-
- std::string getUniqueSliceName() const
- {
- std::string prefix = "Slice";
- int max = 0;
-
- for (Slice* slice : m_sprite->slices())
- if (std::strncmp(slice->name().c_str(), prefix.c_str(), prefix.size()) == 0)
- max = std::max(max, (int)std::strtol(slice->name().c_str() + prefix.size(), nullptr, 10));
-
- return fmt::format("{} {}", prefix, max + 1);
- }
};
//////////////////////////////////////////////////////////////////////
diff --git a/src/app/util/clipboard.cpp b/src/app/util/clipboard.cpp
index 92026df1d..c8f5fb6af 100644
--- a/src/app/util/clipboard.cpp
+++ b/src/app/util/clipboard.cpp
@@ -10,6 +10,7 @@
#endif
#include "app/app.h"
+#include "app/cmd/add_slice.h"
#include "app/cmd/clear_mask.h"
#include "app/cmd/deselect_mask.h"
#include "app/cmd/set_mask.h"
@@ -32,6 +33,7 @@
#include "app/util/cel_ops.h"
#include "app/util/clipboard.h"
#include "app/util/new_image_from_mask.h"
+#include "app/util/slice_utils.h"
#include "clip/clip.h"
#include "doc/algorithm/shrink_bounds.h"
#include "doc/blend_image.h"
@@ -114,6 +116,9 @@ struct Clipboard::Data {
// Selected set of layers/layers/cels
ClipboardRange range;
+ // Selected slices
+ std::vector slices;
+
Data() { range.observeUIContext(); }
~Data()
@@ -132,6 +137,7 @@ struct Clipboard::Data {
picks.clear();
mask.reset();
range.invalidate();
+ slices.clear();
}
ClipboardFormat format() const
@@ -146,6 +152,8 @@ struct Clipboard::Data {
return ClipboardFormat::PaletteEntries;
else if (tileset && picks.picks())
return ClipboardFormat::Tileset;
+ else if (!slices.empty())
+ return ClipboardFormat::Slices;
else
return ClipboardFormat::None;
}
@@ -212,6 +220,7 @@ void Clipboard::setData(Image* image,
Mask* mask,
Palette* palette,
Tileset* tileset,
+ const std::vector* slices,
bool set_native_clipboard,
bool image_source_is_transparent)
{
@@ -226,6 +235,11 @@ void Clipboard::setData(Image* image,
else
m_data->image.reset(image);
+ if (slices) {
+ for (auto* slice : *slices)
+ m_data->slices.push_back(*slice);
+ }
+
if (set_native_clipboard && use_native_clipboard()) {
// Copy tilemap to the native clipboard
if (isTilemap) {
@@ -262,6 +276,7 @@ bool Clipboard::copyFromDocument(const Site& site, bool merged)
(mask ? new Mask(*mask) : nullptr),
(pal ? new Palette(*pal) : nullptr),
Tileset::MakeCopyCopyingImages(ts),
+ nullptr,
true, // set native clipboard
site.layer() && !site.layer()->isBackground());
@@ -277,6 +292,7 @@ bool Clipboard::copyFromDocument(const Site& site, bool merged)
(mask ? new Mask(*mask) : nullptr),
(pal ? new Palette(*pal) : nullptr),
nullptr,
+ nullptr,
true, // set native clipboard
site.layer() && !site.layer()->isBackground());
@@ -401,6 +417,7 @@ void Clipboard::copyImage(const Image* image, const Mask* mask, const Palette* p
(mask ? new Mask(*mask) : nullptr),
(pal ? new Palette(*pal) : nullptr),
nullptr,
+ nullptr,
App::instance()->isGui(),
false);
}
@@ -415,6 +432,7 @@ void Clipboard::copyTilemap(const Image* image,
(mask ? new Mask(*mask) : nullptr),
(pal ? new Palette(*pal) : nullptr),
Tileset::MakeCopyCopyingImages(tileset),
+ nullptr,
true,
false);
}
@@ -428,6 +446,7 @@ void Clipboard::copyPalette(const Palette* palette, const PalettePicks& picks)
nullptr,
new Palette(*palette),
nullptr,
+ nullptr,
false, // Don't touch the native clipboard now
false);
@@ -438,6 +457,20 @@ void Clipboard::copyPalette(const Palette* palette, const PalettePicks& picks)
m_data->picks = picks;
}
+void Clipboard::copySlices(const std::vector slices)
+{
+ if (slices.empty())
+ return;
+
+ setData(nullptr,
+ nullptr,
+ nullptr,
+ nullptr,
+ &slices,
+ false, // Don't touch the native clipboard now
+ false);
+}
+
void Clipboard::paste(Context* ctx, const bool interactive, const gfx::Point* position)
{
const Site site = ctx->activeSite();
@@ -782,6 +815,26 @@ void Clipboard::paste(Context* ctx, const bool interactive, const gfx::Point* po
}
break;
}
+
+ case ClipboardFormat::Slices: {
+ auto& slices = m_data->slices;
+
+ if (slices.empty())
+ return;
+
+ ContextWriter writer(ctx);
+ Tx tx(writer, "Paste Slices");
+ editor->clearSlicesSelection();
+ for (auto& s : slices) {
+ Slice* slice = new Slice(s);
+ slice->setName(get_unique_slice_name(dstSpr, s.name()));
+ tx(new cmd::AddSlice(dstSpr, slice));
+ editor->selectSlice(slice);
+ }
+ tx.commit();
+ updateDstDoc = true;
+ break;
+ }
}
// Update all editors/views showing this document
@@ -799,7 +852,7 @@ ImageRef Clipboard::getImage(Palette* palette)
Tileset* native_tileset = nullptr;
getNativeBitmap(&native_image, &native_mask, &native_palette, &native_tileset);
if (native_image) {
- setData(native_image, native_mask, native_palette, native_tileset, false, false);
+ setData(native_image, native_mask, native_palette, native_tileset, nullptr, false, false);
}
}
if (m_data->palette && palette)
diff --git a/src/app/util/clipboard.h b/src/app/util/clipboard.h
index 5b36b1b3a..bf77d2f4a 100644
--- a/src/app/util/clipboard.h
+++ b/src/app/util/clipboard.h
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2019-2024 Igara Studio S.A.
+// Copyright (C) 2019-2025 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@@ -23,6 +23,7 @@ class Image;
class Mask;
class Palette;
class PalettePicks;
+class Slice;
class Tileset;
} // namespace doc
@@ -45,6 +46,7 @@ enum class ClipboardFormat {
PaletteEntries,
Tilemap,
Tileset,
+ Slices,
};
class Clipboard : public ui::ClipboardDelegate {
@@ -74,6 +76,7 @@ public:
const doc::Palette* pal,
const doc::Tileset* tileset);
void copyPalette(const doc::Palette* palette, const doc::PalettePicks& picks);
+ void copySlices(const std::vector slices);
void paste(Context* ctx, const bool interactive, const gfx::Point* position = nullptr);
doc::ImageRef getImage(doc::Palette* palette);
@@ -106,6 +109,7 @@ private:
doc::Mask* mask,
doc::Palette* palette,
doc::Tileset* tileset,
+ const std::vector* slices,
bool set_native_clipboard,
bool image_source_is_transparent);
bool copyFromDocument(const Site& site, bool merged = false);
diff --git a/src/app/util/slice_utils.cpp b/src/app/util/slice_utils.cpp
new file mode 100644
index 000000000..cdc34c6c5
--- /dev/null
+++ b/src/app/util/slice_utils.cpp
@@ -0,0 +1,42 @@
+// Aseprite
+// Copyright (C) 2025 Igara Studio S.A.
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#include "app/util/slice_utils.h"
+
+#include "app/context_access.h"
+#include "app/site.h"
+#include "doc/slice.h"
+#include "doc/sprite.h"
+#include "fmt/format.h"
+
+namespace app {
+
+std::string get_unique_slice_name(const doc::Sprite* sprite, const std::string& namePrefix)
+{
+ std::string prefix = namePrefix.empty() ? "Slice" : namePrefix;
+ int max = 0;
+
+ for (doc::Slice* slice : sprite->slices())
+ if (std::strncmp(slice->name().c_str(), prefix.c_str(), prefix.size()) == 0)
+ max = std::max(max, (int)std::strtol(slice->name().c_str() + prefix.size(), nullptr, 10));
+
+ return fmt::format("{} {}", prefix, max + 1);
+}
+
+std::vector get_selected_slices(const Site& site)
+{
+ std::vector selectedSlices;
+ if (site.sprite() && !site.selectedSlices().empty()) {
+ for (auto* slice : site.sprite()->slices()) {
+ if (site.selectedSlices().contains(slice->id())) {
+ selectedSlices.push_back(slice);
+ }
+ }
+ }
+ return selectedSlices;
+}
+
+} // namespace app
diff --git a/src/app/util/slice_utils.h b/src/app/util/slice_utils.h
new file mode 100644
index 000000000..a7d778fa8
--- /dev/null
+++ b/src/app/util/slice_utils.h
@@ -0,0 +1,30 @@
+// Aseprite
+// Copyright (C) 2025 Igara Studio S.A.
+//
+// This program is distributed under the terms of
+// the End-User License Agreement for Aseprite.
+
+#ifndef APP_SLICE_UTILS_H_INCLUDED
+#define APP_SLICE_UTILS_H_INCLUDED
+#pragma once
+
+#include
+#include
+
+namespace doc {
+class Slice;
+class Sprite;
+} // namespace doc
+
+namespace app {
+
+class Site;
+
+std::string get_unique_slice_name(const doc::Sprite* sprite,
+ const std::string& namePrefix = std::string());
+
+std::vector get_selected_slices(const Site& site);
+
+} // namespace app
+
+#endif
diff --git a/src/doc/slices.cpp b/src/doc/slices.cpp
index 09572d0c2..71b349a9e 100644
--- a/src/doc/slices.cpp
+++ b/src/doc/slices.cpp
@@ -31,7 +31,10 @@ Slices::~Slices()
void Slices::add(Slice* slice)
{
- m_slices.push_back(slice);
+ // Insert the slice at the begining to display it at the front of the others.
+ // This is useful when duplicating (or copy & pasting) slices, because the
+ // user can drag the new slices instead of the originally selected ones.
+ m_slices.insert(m_slices.begin(), slice);
slice->setOwner(this);
}
diff --git a/tests/scripts/slices.lua b/tests/scripts/slices.lua
index 42e3d2589..7109514a7 100644
--- a/tests/scripts/slices.lua
+++ b/tests/scripts/slices.lua
@@ -14,7 +14,7 @@ do
assert(b.bounds == Rectangle(0, 2, 8, 10))
assert(c.bounds == Rectangle(0, 0, 32, 32))
- local bounds = { nil, Rectangle(0, 2, 8, 10), Rectangle(0, 0, 32, 32) }
+ local bounds = { Rectangle(0, 0, 32, 32), Rectangle(0, 2, 8, 10), nil }
local i = 1
for k,v in ipairs(s.slices) do
@@ -25,8 +25,8 @@ do
end
s:deleteSlice(b)
- assert(a == s.slices[1])
- assert(c == s.slices[2])
+ assert(c == s.slices[1])
+ assert(a == s.slices[2])
assert(2 == #s.slices)
app.undo()