Merge branch 'main' into beta

This commit is contained in:
David Capello 2021-04-08 16:15:02 -03:00
commit ae904428fa
24 changed files with 175 additions and 37 deletions

View File

@ -96,8 +96,8 @@ void FlipCommand::onExecute(Context* ctx)
cels = get_unlocked_unique_cels(site.sprite(), range);
}
else if (site.cel() &&
site.layer() &&
site.layer()->isEditable()) {
site.layer() &&
site.layer()->canEditPixels()) {
cels.push_back(site.cel());
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -217,7 +217,7 @@ void RotateCommand::onExecute(Context* context)
cels = get_unlocked_unique_cels(site.sprite(), range);
else if (site.cel() &&
site.layer() &&
site.layer()->isEditable()) {
site.layer()->canEditPixels()) {
cels.push_back(site.cel());
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -281,8 +281,7 @@ void FilterManagerImpl::applyToTarget()
}
else if (m_site.cel() &&
m_site.layer() &&
m_site.layer()->isEditable() &&
!m_site.layer()->isReference()) {
m_site.layer()->canEditPixels()) {
cels.push_back(m_site.cel());
}
break;
@ -290,8 +289,7 @@ void FilterManagerImpl::applyToTarget()
case CelsTarget::All: {
for (Cel* cel : m_site.sprite()->uniqueCels()) {
if (cel->layer()->isEditable() &&
!cel->layer()->isReference())
if (cel->layer()->canEditPixels())
cels.push_back(cel);
}
break;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2019 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -81,7 +81,7 @@ ConfigModule::ConfigModule()
#elif !defined(_WIN32)
// On Linux we migrate the old configuration file name
// (.asepriterc -> ~/.config/aseprite/aseprite.ini)
// (~/.asepriterc -> ~/.config/aseprite/aseprite.ini)
{
ResourceFinder old_rf;
old_rf.includeHomeDir(".asepriterc");

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -191,6 +191,10 @@ DocumentPreferences& Preferences::document(const Doc* doc)
void Preferences::resetToolPreferences(tools::Tool* tool)
{
if (tool->prefAlreadyResetFromScript())
return;
tool->markPrefAlreadyResetFromScript();
auto it = m_tools.find(tool->getId());
if (it != m_tools.end())
m_tools.erase(it);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
@ -105,8 +105,8 @@ void ResourceFinder::includeDataDir(const char* filename)
#else
// $HOME/.config/aseprite/filename
sprintf(buf, ".config/aseprite/data/%s", filename);
includeHomeDir(buf);
sprintf(buf, "aseprite/data/%s", filename);
includeHomeConfigDir(buf);
// $BINDIR/data/filename
sprintf(buf, "data/%s", filename);
@ -150,6 +150,24 @@ void ResourceFinder::includeHomeDir(const char* filename)
#endif
}
#if !defined(_WIN32) && !defined(__APPLE__)
// For Linux: It's $XDG_CONFIG_HOME or $HOME/.config
void ResourceFinder::includeHomeConfigDir(const char* filename)
{
char* configHome = std::getenv("XDG_CONFIG_HOME");
if (configHome && *configHome) {
// $XDG_CONFIG_HOME/filename
addPath(base::join_path(configHome, filename));
}
else {
// $HOME/.config/filename
includeHomeDir(base::join_path(std::string(".config"), filename).c_str());
}
}
#endif // !defined(_WIN32) && !defined(__APPLE__)
void ResourceFinder::includeUserDir(const char* filename)
{
#ifdef _WIN32
@ -185,7 +203,7 @@ void ResourceFinder::includeUserDir(const char* filename)
#else // !__APPLE__
// $HOME/.config/aseprite/filename
includeHomeDir((std::string(".config/aseprite/") + filename).c_str());
includeHomeConfigDir((std::string("aseprite/") + filename).c_str());
#endif
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -42,6 +42,11 @@ namespace app {
void includeDataDir(const char* filename);
void includeHomeDir(const char* filename);
#if !defined(_WIN32) && !defined(__APPLE__)
// For Linux: It's $XDG_CONFIG_HOME or $HOME/.config
void includeHomeConfigDir(const char* filename);
#endif
// Tries to add the given filename in these locations:
// For Windows:
// - If ASEPRITE_USER_FOLDER environment variable is defined, it

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -61,6 +62,9 @@ namespace app {
void setIntertwine(int button, Intertwine* intertwine) { m_button[button].m_intertwine = intertwine; }
void setTracePolicy(int button, TracePolicy trace_policy) { m_button[button].m_trace_policy = trace_policy; }
bool prefAlreadyResetFromScript() const { return m_prefAlreadyResetFromScript; }
void markPrefAlreadyResetFromScript() { m_prefAlreadyResetFromScript = true; }
private:
ToolGroup* m_group;
std::string m_id;
@ -68,6 +72,13 @@ namespace app {
std::string m_tips;
int m_default_brush_size;
// Flag used to indicate that the preferences of this tool were
// already reset from scripts when they are executed in CLI mode
// (without GUI). This is needed to reset the preferences only
// once, but if the script then modifies the preferences, they
// are not reset again.
bool m_prefAlreadyResetFromScript = false;
struct {
Fill m_fill;
Ink* m_ink;

View File

@ -409,6 +409,7 @@ void Editor::getSite(Site* site) const
// TODO we should not access timeline directly here
Timeline* timeline = App::instance()->timeline();
if (timeline &&
timeline->isVisible() &&
timeline->range().enabled()) {
site->range(timeline->range());
}

View File

@ -47,8 +47,10 @@ MovingCelCollect::MovingCelCollect(Editor* editor, Layer* layer)
if (layer && layer->isImage())
m_mainCel = layer->cel(editor->frame());
DocRange range = App::instance()->timeline()->range();
if (!range.enabled()) {
Timeline* timeline = App::instance()->timeline();
DocRange range = timeline->range();
if (!range.enabled() ||
!timeline->isVisible()) {
range.startRange(editor->layer(), editor->frame(), DocRange::kCels);
range.endRange(editor->layer(), editor->frame());
}
@ -86,8 +88,6 @@ MovingCelState::MovingCelState(Editor* editor,
, m_celList(collect.celList())
, m_celOffset(0.0, 0.0)
, m_celScale(1.0, 1.0)
, m_hasReference(false)
, m_scaled(false)
, m_handle(handle)
, m_editor(editor)
{
@ -188,6 +188,13 @@ bool MovingCelState::onMouseUp(Editor* editor, MouseMessage* msg)
// like to update all the editors.
document->notifyGeneralUpdate();
}
// Just a click in the current layer
else if (!m_moved & !m_scaled) {
// Deselect the whole range if we are in "Auto Select Layer"
if (editor->isAutoSelectLayer()) {
App::instance()->timeline()->clearAndInvalidateRange();
}
}
// Restore the mask visibility.
if (m_maskVisible) {
@ -218,6 +225,8 @@ bool MovingCelState::onMouseMove(Editor* editor, MouseMessage* msg)
m_celOffset.y = 0;
}
}
if (!m_moved && intCelOffset() != gfx::Point(0, 0))
m_moved = true;
break;
case ScaleSEHandle: {
@ -246,6 +255,7 @@ bool MovingCelState::onMouseMove(Editor* editor, MouseMessage* msg)
if (cel->layer()->isReference()) {
celBounds.x += m_celOffset.x;
celBounds.y += m_celOffset.y;
m_moved = true;
if (m_scaled) {
celBounds.w *= m_celScale.w;
celBounds.h *= m_celScale.h;

View File

@ -68,8 +68,9 @@ namespace app {
gfx::SizeF m_celMainSize;
gfx::SizeF m_celScale;
bool m_maskVisible;
bool m_hasReference;
bool m_scaled;
bool m_hasReference = false;
bool m_moved = false;
bool m_scaled = false;
HandleType m_handle;
Editor* m_editor;

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -36,6 +36,7 @@
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/main_window.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "app/util/layer_utils.h"
@ -114,10 +115,14 @@ MovingPixelsState::MovingPixelsState(Editor* editor, MouseMessage* msg, PixelsMo
ContextBar* contextBar = App::instance()->contextBar();
contextBar->updateForMovingPixels(getTransformation(editor));
contextBar->add_observer(this);
App::instance()->mainWindow()->getTimeline()->add_observer(this);
}
MovingPixelsState::~MovingPixelsState()
{
App::instance()->mainWindow()->getTimeline()->remove_observer(this);
ContextBar* contextBar = App::instance()->contextBar();
contextBar->remove_observer(this);
contextBar->updateForActiveTool();
@ -723,6 +728,15 @@ void MovingPixelsState::onBeforeLayerChanged(Editor* editor)
dropPixels();
}
void MovingPixelsState::onBeforeRangeChanged(Timeline* timeline)
{
if (!isActiveDocument())
return;
if (m_pixelsMovement)
dropPixels();
}
void MovingPixelsState::onTransparentColorChange()
{
ASSERT(m_pixelsMovement);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2017 David Capello
//
// This program is distributed under the terms of
@ -15,6 +15,7 @@
#include "app/ui/editor/pixels_movement.h"
#include "app/ui/editor/standby_state.h"
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline_observer.h"
#include "obs/connection.h"
#include "ui/timer.h"
@ -29,6 +30,7 @@ namespace app {
class MovingPixelsState
: public StandbyState
, EditorObserver
, TimelineObserver
, ContextBarObserver
, PixelsMovementDelegate {
public:
@ -66,6 +68,9 @@ namespace app {
virtual void onBeforeFrameChanged(Editor* editor) override;
virtual void onBeforeLayerChanged(Editor* editor) override;
// TimelineObserver
virtual void onBeforeRangeChanged(Timeline* timeline) override;
// ContextBarObserver
virtual void onDropPixels(ContextBarObserver::DropAction action) override;

View File

@ -1293,16 +1293,32 @@ CelList PixelsMovement::getEditableCels()
// TODO This case is used in paste too, where the cel() can be
// nullptr (e.g. we paste the clipboard image into an empty
// cel).
if (m_site.layer() && m_site.layer()->isEditableHierarchy())
if (m_site.layer() &&
m_site.layer()->canEditPixels()) {
cels.push_back(m_site.cel());
}
return cels;
}
// Current cel (m_site.cel()) can be nullptr when we paste in an
// empty cel (Ctrl+V) and cut (Ctrl+X) the floating pixels.
if (m_site.cel() &&
m_site.cel()->layer()->isEditableHierarchy()) {
auto it = std::find(cels.begin(), cels.end(), m_site.cel());
m_site.cel()->layer()->canEditPixels()) {
CelList::iterator it;
// If we are in a linked cel, remove the cel that matches the
// linked cel. In this way we avoid having two Cel in cels
// pointing to the same CelData.
if (Cel* link = m_site.cel()->link()) {
it = std::find_if(cels.begin(), cels.end(),
[link](const Cel* cel){
return (cel == link ||
cel->link() == link);
});
}
else {
it = std::find(cels.begin(), cels.end(), m_site.cel());
}
if (it != cels.end())
cels.erase(it);
cels.insert(cels.begin(), m_site.cel());

View File

@ -4051,6 +4051,8 @@ void Timeline::invalidateRange()
void Timeline::clearAndInvalidateRange()
{
if (m_range.enabled()) {
notify_observers(&TimelineObserver::onBeforeRangeChanged, this);
invalidateRange();
m_range.clearRange();
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2020 Igara Studio S.A.
// Copyright (C) 2018-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -17,6 +17,7 @@
#include "app/ui/editor/editor_observer.h"
#include "app/ui/input_chain_element.h"
#include "app/ui/timeline/ani_controls.h"
#include "app/ui/timeline/timeline_observer.h"
#include "base/debug.h"
#include "doc/frame.h"
#include "doc/layer.h"
@ -26,6 +27,7 @@
#include "doc/tag.h"
#include "gfx/color.h"
#include "obs/connection.h"
#include "obs/observable.h"
#include "ui/scroll_bar.h"
#include "ui/timer.h"
#include "ui/widget.h"
@ -60,6 +62,7 @@ namespace app {
class Timeline : public ui::Widget,
public ui::ScrollableViewDelegate,
public obs::observable<TimelineObserver>,
public ContextObserver,
public DocsObserver,
public DocObserver,

View File

@ -0,0 +1,24 @@
// Aseprite
// Copyright (C) 2021 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UI_TIMELINE_TIMELINE_OBSERVER_H_INCLUDED
#define APP_UI_TIMELINE_TIMELINE_OBSERVER_H_INCLUDED
#pragma once
namespace app {
class Timeline;
class TimelineObserver {
public:
virtual ~TimelineObserver() { }
// Called when the current timeline range is going to change.
virtual void onBeforeRangeChanged(Timeline* timeline) { }
};
} // namespace app
#endif

View File

@ -338,9 +338,14 @@ void UIContext::onGetActiveSite(Site* site) const
view->getSite(site);
if (site->sprite()) {
// Selected range in the timeline
// Selected range in the timeline. We use it only if the
// timeline is visible. A common scenario might be
// undoing/redoing actions where the range is re-selected, that
// could enable the range even if the timeline is hidden. In
// this way we avoid using the timeline selection unexpectedly.
Timeline* timeline = App::instance()->timeline();
if (timeline &&
timeline->isVisible() &&
timeline->range().enabled()) {
site->range(timeline->range());
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -25,6 +25,7 @@ namespace app {
using namespace doc;
// TODO the DocRange should be "iteratable" to replace this function
// or we can wait to C++20 coroutines and co_yield
static CelList get_cels_templ(const Sprite* sprite,
DocRange range,
const bool onlyUniqueCels,
@ -39,8 +40,9 @@ static CelList get_cels_templ(const Sprite* sprite,
for (Layer* layer : range.selectedLayers()) {
if (!layer ||
!layer->isImage() ||
(onlyUnlockedCel && !layer->isEditableHierarchy()))
(onlyUnlockedCel && !layer->canEditPixels())) {
continue;
}
LayerImage* layerImage = static_cast<LayerImage*>(layer);
for (frame_t frame : range.selectedFrames()) {

View File

@ -1,4 +1,4 @@
Copyright (c) 2018-2020 Igara Studio S.A.
Copyright (c) 2018-2021 Igara Studio S.A.
Copyright (c) 2001-2018 David Capello
Permission is hereby granted, free of charge, to any person obtaining

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -180,6 +180,24 @@ bool Layer::isEditableHierarchy() const
return true;
}
// It's like isVisibleHierarchy + isEditableHierarchy. Returns true if
// the whole layer hierarchy is unlocked and visible, so the user can
// edit its pixels without unexpected side-effects (e.g. editing
// hidden layers).
bool Layer::canEditPixels() const
{
const Layer* layer = this;
while (layer) {
if (!layer->isVisible() ||
!layer->isEditable() ||
layer->isReference()) { // Cannot edit pixels from reference layers
return false;
}
layer = layer->parent();
}
return true;
}
bool Layer::hasAncestor(const Layer* ancestor) const
{
Layer* it = parent();

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This file is released under the terms of the MIT license.
@ -92,6 +92,7 @@ namespace doc {
bool isVisibleHierarchy() const;
bool isEditableHierarchy() const;
bool canEditPixels() const;
bool hasAncestor(const Layer* ancestor) const;
void setBackground(bool state) { switchFlags(LayerFlags::Background, state); }

@ -1 +1 @@
Subproject commit 1e0630d310b55abf7d16d3d89feeb13936d540f8
Subproject commit 876ef60df5fec606f8eb0638ee893e4967db4673

@ -1 +1 @@
Subproject commit ea8005303f42925fa1180d1f6e973a816c0b80af
Subproject commit 16b8bb25e34bfb0193609012257502be62539d35