Don't apply Flip/Rotate commands to locked layers

Several issues fixed from https://community.aseprite.org/t/750

- Don't apply flip/rotate commands to cels from locked layers
- Show a status bar tooltip when all selected layers are locked
- Keep the timeline range enabled after the flip/rotate command
This commit is contained in:
David Capello 2017-12-01 17:14:18 -03:00
parent 7682971acb
commit 21248b96dc
8 changed files with 85 additions and 25 deletions

View File

@ -6,6 +6,9 @@ title = Warning - Important
description = You are going to enter in "Advanced Mode".
dont_show_again = Don't show this again
[statusbar_tips]
all_layers_are_locked = All selected layers are locked
[alerts]
applying_filter = FX<<Applying effect...||&Cancel
auto_remap = <<<END

View File

@ -23,6 +23,7 @@
#include "app/i18n/strings.h"
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "app/util/expand_cel_canvas.h"
#include "app/util/range_utils.h"
@ -67,24 +68,31 @@ void FlipCommand::onExecute(Context* context)
Sprite* sprite = writer.sprite();
{
Transaction transaction(writer.context(),
m_flipMask ?
(m_flipType == doc::algorithm::FlipHorizontal ?
"Flip Horizontal":
"Flip Vertical"):
(m_flipType == doc::algorithm::FlipHorizontal ?
"Flip Canvas Horizontal":
"Flip Canvas Vertical"));
Transaction transaction(writer.context(), friendlyName());
DocumentApi api = document->getApi(transaction);
Timeline* timeline = App::instance()->timeline();
LockTimelineRange lockRange(timeline);
CelList cels;
if (m_flipMask) {
auto range = App::instance()->timeline()->range();
if (range.enabled())
cels = get_unique_cels(sprite, range);
else if (writer.cel())
auto range = timeline->range();
if (range.enabled()) {
cels = get_unlocked_unique_cels(sprite, range);
}
else if (writer.cel() &&
writer.layer() &&
writer.layer()->isEditable()) {
cels.push_back(writer.cel());
}
if (cels.empty()) {
StatusBar::instance()->showTip(
1000, Strings::statusbar_tips_all_layers_are_locked().c_str());
return;
}
}
// Flip the whole sprite (even locked layers)
else {
for (Cel* cel : sprite->uniqueCels())
cels.push_back(cel);

View File

@ -23,6 +23,7 @@
#include "app/transaction.h"
#include "app/ui/color_bar.h"
#include "app/ui/editor/editor.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui/toolbar.h"
#include "app/util/range_utils.h"
@ -196,12 +197,17 @@ void RotateCommand::onExecute(Context* context)
CelList cels;
bool rotateSprite = false;
Timeline* timeline = App::instance()->timeline();
LockTimelineRange lockRange(timeline);
// Flip the mask or current cel
if (m_flipMask) {
auto range = App::instance()->timeline()->range();
if (range.enabled())
cels = get_unique_cels(site.sprite(), range);
else if (site.cel()) {
cels = get_unlocked_unique_cels(site.sprite(), range);
else if (site.cel() &&
site.layer() &&
site.layer()->isEditable()) {
// If we want to rotate the visible mask for the current cel,
// we can go to MovingPixelsState.
if (static_cast<app::Document*>(site.document())->isMaskVisible()) {
@ -216,8 +222,14 @@ void RotateCommand::onExecute(Context* context)
cels.push_back(site.cel());
}
if (cels.empty()) {
StatusBar::instance()->showTip(
1000, Strings::statusbar_tips_all_layers_are_locked().c_str());
return;
}
}
// Flip the whole sprite
// Flip the whole sprite (even locked layers)
else if (site.sprite()) {
for (Cel* cel : site.sprite()->uniqueCels())
cels.push_back(cel);

View File

@ -64,7 +64,7 @@ MovingCelCollect::MovingCelCollect(Editor* editor, Layer* layer)
}
// Record start positions of all cels in selected range
for (Cel* cel : get_unique_cels(editor->sprite(), range2)) {
for (Cel* cel : get_unlocked_unique_cels(editor->sprite(), range2)) {
Layer* layer = cel->layer();
ASSERT(layer);

View File

@ -201,6 +201,7 @@ Timeline::Timeline()
, m_editor(NULL)
, m_document(NULL)
, m_sprite(NULL)
, m_rangeLocks(0)
, m_state(STATE_STANDBY)
, m_tagBands(0)
, m_tagFocusBand(-1)
@ -284,7 +285,8 @@ void Timeline::updateUsingEditor(Editor* editor)
detachDocument();
if (m_range.enabled()) {
if (m_range.enabled() &&
m_rangeLocks == 0) {
m_range.clearRange();
invalidate();
}
@ -1560,7 +1562,8 @@ void Timeline::onRemoveFrame(doc::DocumentEvent& ev)
void Timeline::onSelectionChanged(doc::DocumentEvent& ev)
{
m_range.clearRange();
if (m_rangeLocks == 0)
m_range.clearRange();
invalidate();
}
@ -3378,6 +3381,17 @@ void Timeline::setViewScroll(const gfx::Point& pt)
invalidate();
}
void Timeline::lockRange()
{
++m_rangeLocks;
}
void Timeline::unlockRange()
{
--m_rangeLocks;
}
void Timeline::updateDropRange(const gfx::Point& pt)
{
DropTarget::HHit oldHHit = m_dropTarget.hhit;
@ -3544,7 +3558,9 @@ void Timeline::onNewInputPriority(InputChainElement* element)
// That is why we don't disable the range in this case.
Workspace* workspace = dynamic_cast<Workspace*>(element);
if (!workspace) {
m_range.clearRange();
if (m_rangeLocks == 0)
m_range.clearRange();
invalidate();
}
}
@ -3629,7 +3645,9 @@ bool Timeline::onClear(Context* ctx)
void Timeline::onCancel(Context* ctx)
{
m_range.clearRange();
if (m_rangeLocks == 0)
m_range.clearRange();
clearClipboardRange();
invalidate();
}

View File

@ -112,6 +112,9 @@ namespace app {
gfx::Point viewScroll() const override;
void setViewScroll(const gfx::Point& pt) override;
void lockRange();
void unlockRange();
protected:
bool onProcessMessage(ui::Message* msg) override;
void onInitTheme(ui::InitThemeEvent& ev) override;
@ -327,6 +330,7 @@ namespace app {
Sprite* m_sprite;
Layer* m_layer;
frame_t m_frame;
int m_rangeLocks;
Range m_range;
Range m_startRange;
Range m_dropRange;
@ -380,6 +384,19 @@ namespace app {
} m_moveRangeData;
};
class LockTimelineRange {
public:
LockTimelineRange(Timeline* timeline)
: m_timeline(timeline) {
m_timeline->lockRange();
}
~LockTimelineRange() {
m_timeline->unlockRange();
}
private:
Timeline* m_timeline;
};
} // namespace app
#endif

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2016 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -24,7 +24,7 @@ namespace app {
using namespace doc;
// TODO the DocumentRange should be "iteratable" to replace this function
CelList get_unique_cels(Sprite* sprite, const DocumentRange& inrange)
CelList get_unlocked_unique_cels(Sprite* sprite, const DocumentRange& inrange)
{
DocumentRange range = inrange;
CelList cels;
@ -34,7 +34,9 @@ CelList get_unique_cels(Sprite* sprite, const DocumentRange& inrange)
std::set<ObjectId> visited;
for (Layer* layer : range.selectedLayers()) {
if (!layer || !layer->isImage())
if (!layer ||
!layer->isImage() ||
!layer->isEditable())
continue;
LayerImage* layerImage = static_cast<LayerImage*>(layer);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2001-2015 David Capello
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -21,7 +21,7 @@ namespace app {
class DocumentRange;
doc::CelList get_unique_cels(doc::Sprite* sprite, const DocumentRange& range);
doc::CelList get_unlocked_unique_cels(doc::Sprite* sprite, const DocumentRange& range);
} // namespace app