Initial refactor to create the view module (#3904)

* Moved app::DocRange to view::Range
* Moved range_utils to view/cels.cpp
* Moved layer_utils to view/layers.cpp
* Created more functions in app::Site to access selected cels
  (they use the functions from view/cels.h)
This commit is contained in:
David Capello 2023-05-31 12:02:33 -03:00
parent 37f55fb635
commit b2c6240a7e
56 changed files with 459 additions and 383 deletions

View File

@ -51,6 +51,7 @@ add_subdirectory(undo)
add_subdirectory(cfg)
add_subdirectory(doc)
add_subdirectory(view)
add_subdirectory(filters)
add_subdirectory(fixmath)
add_subdirectory(flic)
@ -208,6 +209,7 @@ if(ENABLE_TESTS)
include(FindTests)
find_tests(doc doc-lib)
find_tests(doc/algorithm doc-lib)
find_tests(view view-lib)
find_tests(render render-lib)
find_tests(ui ui-lib)
find_tests(app/cli app-lib)

View File

@ -41,10 +41,11 @@ because they don't depend on any other component.
* [dio](dio/) (base, doc, fixmath, flic): Load/save sprites/documents.
* [filters](filters/) (base, doc, gfx): Effects for images.
* [render](render/) (base, doc, gfx): Library to render documents.
* [view](view/) (base, doc): Abstract timeline/range view/helpers.
## Level 4
* [app](app/) (base, doc, dio, filters, fixmath, flic, gfx, pen, render, scripting, os, ui, undo, updater)
* [app](app/) (base, doc, dio, filters, fixmath, flic, gfx, pen, render, scripting, os, ui, undo, updater, view)
* [desktop](desktop/) (base, doc, dio, render): Integration with the desktop (Windows Explorer, Finder, GNOME, KDE, etc.)
## Level 5

View File

@ -527,7 +527,6 @@ target_sources(app-lib PRIVATE
doc_api.cpp
doc_diff.cpp
doc_exporter.cpp
doc_range.cpp
doc_range_ops.cpp
doc_undo.cpp
docs.cpp
@ -707,7 +706,6 @@ target_sources(app-lib PRIVATE
util/pal_ops.cpp
util/pic_file.cpp
util/pixel_ratio.cpp
util/range_utils.cpp
util/readable_time.cpp
util/render_text.cpp
util/resize_image.cpp
@ -745,6 +743,7 @@ target_link_libraries(app-lib
cfg-lib
clip
doc-lib
view-lib
dio-lib
filters-lib
fixmath-lib

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -12,8 +12,8 @@
#include "app/doc.h"
#include "app/doc_event.h"
#include "app/site.h"
#include "app/util/layer_utils.h"
#include "doc/layer.h"
#include "view/layers.h"
namespace app {
@ -143,7 +143,8 @@ void ActiveSiteHandler::onBeforeRemoveLayer(DocEvent& ev)
data.range.eraseAndAdjust(ev.layer());
// Select other layer as active
doc::Layer* layerToSelect = candidate_if_layer_is_deleted(selectedLayer, ev.layer());
doc::Layer* layerToSelect =
view::candidate_if_layer_is_deleted(selectedLayer, ev.layer());
if (selectedLayer != layerToSelect) {
data.layer = (layerToSelect ? layerToSelect->id():
doc::NullId);

View File

@ -63,8 +63,9 @@ bool CelOpacityCommand::onEnabled(Context* context)
void CelOpacityCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Layer* layer = writer.layer();
Cel* cel = writer.cel();
const Site& site = writer.site();
Layer* layer = site.layer();
Cel* cel = site.cel();
if (!cel ||
layer->isBackground() ||
!layer->isEditable() ||
@ -74,24 +75,11 @@ void CelOpacityCommand::onExecute(Context* context)
{
Tx tx(writer, "Set Cel Opacity");
// TODO the range of selected cels should be in app::Site.
DocRange range;
if (context->isUIAvailable())
range = App::instance()->timeline()->range();
if (!range.enabled()) {
range.startRange(layer, cel->frame(), DocRange::kCels);
range.endRange(layer, cel->frame());
}
for (Cel* c : cel->sprite()->uniqueCels(range.selectedFrames())) {
if (range.contains(c->layer())) {
if (!c->layer()->isBackground() &&
c->layer()->isEditable() &&
m_opacity != c->opacity()) {
tx(new cmd::SetCelOpacity(c, m_opacity));
}
for (Cel* c : site.selectedUniqueCels()) {
if (!c->layer()->isBackground() &&
c->layer()->isEditable() &&
m_opacity != c->opacity()) {
tx(new cmd::SetCelOpacity(c, m_opacity));
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -50,11 +50,11 @@ void ClearCelCommand::onExecute(Context* context)
{
Tx tx(writer, "Clear Cel");
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty() &&
!site->selectedFrames().empty()) {
for (Layer* layer : site->selectedLayers()) {
const Site& site = writer.site();
if (site.inTimeline() &&
!site.selectedLayers().empty() &&
!site.selectedFrames().empty()) {
for (Layer* layer : site.selectedLayers()) {
if (!layer->isImage())
continue;
@ -63,7 +63,7 @@ void ClearCelCommand::onExecute(Context* context)
continue;
}
for (frame_t frame : site->selectedFrames().reversed()) {
for (frame_t frame : site.selectedFrames().reversed()) {
if (Cel* cel = layer->cel(frame))
document->getApi(tx).clearCel(cel);
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2018 David Capello
//
// This program is distributed under the terms of
@ -61,7 +61,7 @@ bool FillCommand::onEnabled(Context* ctx)
void FillCommand::onExecute(Context* ctx)
{
ContextWriter writer(ctx);
Site site = *writer.site();
const Site& site = writer.site();
Doc* doc = site.document();
Sprite* sprite = site.sprite();
Layer* layer = site.layer();

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -15,9 +15,9 @@
#include "app/doc_range.h"
#include "app/i18n/strings.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
#include "app/tx.h"
#include "app/ui/color_bar.h"
#include "app/ui/timeline/timeline.h"
#include "doc/layer.h"
#include "doc/sprite.h"
@ -55,12 +55,12 @@ bool FlattenLayersCommand::onEnabled(Context* context)
void FlattenLayersCommand::onExecute(Context* context)
{
ContextWriter writer(context);
Sprite* sprite = writer.sprite();
const Site& site = writer.site();
Sprite* sprite = site.sprite();
{
Tx tx(writer, "Flatten Layers");
// TODO the range of selected layers should be in app::Site.
DocRange range;
view::Range range;
if (m_visibleOnly) {
for (auto layer : sprite->root()->layers())
@ -68,8 +68,7 @@ void FlattenLayersCommand::onExecute(Context* context)
range.selectLayer(layer);
}
else {
if (context->isUIAvailable())
range = App::instance()->timeline()->range();
range = site.range();
// If the range is not selected or we have only one image layer
// selected, we'll flatten all layers.

View File

@ -31,7 +31,6 @@
#include "app/ui/timeline/timeline.h"
#include "app/ui/toolbar.h"
#include "app/util/expand_cel_canvas.h"
#include "app/util/range_utils.h"
#include "doc/algorithm/flip_image.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
@ -41,7 +40,6 @@
#include "doc/sprite.h"
#include "gfx/size.h"
namespace app {
FlipCommand::FlipCommand()
@ -88,16 +86,7 @@ void FlipCommand::onExecute(Context* ctx)
}
}
auto range = site.range();
if (range.enabled()) {
cels = get_unique_cels_to_edit_pixels(site.sprite(), range);
}
else if (site.cel() &&
site.layer() &&
site.layer()->canEditPixels()) {
cels.push_back(site.cel());
}
cels = site.selectedUniqueCelsToEditPixels();
if (cels.empty()) {
if (ctx->isUIAvailable()) {
StatusBar::instance()->showTip(
@ -108,8 +97,7 @@ void FlipCommand::onExecute(Context* ctx)
}
// Flip the whole sprite (even locked layers)
else {
for (Cel* cel : site.sprite()->uniqueCels())
cels.push_back(cel);
cels = site.sprite()->uniqueCels().toList();
}
ContextWriter writer(ctx);
@ -120,7 +108,7 @@ void FlipCommand::onExecute(Context* ctx)
Mask* mask = document->mask();
if (m_flipMask && document->isMaskVisible()) {
Site site = *writer.site();
Site site = writer.site();
for (Cel* cel : cels) {
// TODO add support to flip masked part of a reference layer

View File

@ -70,9 +70,9 @@ void LayerOpacityCommand::onExecute(Context* context)
{
Tx tx(writer, "Set Layer Opacity");
// TODO the range of selected frames should be in app::Site.
Site site = writer.site();
SelectedLayers selLayers;
auto range = App::instance()->timeline()->range();
auto range = site.range();
if (range.enabled()) {
selLayers = range.selectedLayers();
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -63,7 +63,7 @@ void MaskContentCommand::onExecute(Context* context)
gfx::Color color;
if (writer.layer()->isBackground()) {
ColorPicker picker;
picker.pickColor(*writer.site(),
picker.pickColor(writer.site(),
gfx::PointF(0.0, 0.0),
Editor::activeEditor()->projection(),
ColorPicker::FromComposition);

View File

@ -121,29 +121,31 @@ void NewFrameCommand::onExecute(Context* context)
case Content::DUPLICATE_CELS_LINKED: continuous.reset(new bool(true)); break;
}
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty() &&
!site->selectedFrames().empty()) {
auto timeline = App::instance()->timeline();
timeline->prepareToMoveRange();
DocRange range = timeline->range();
const Site& site = writer.site();
if (site.inTimeline() &&
!site.selectedLayers().empty() &&
!site.selectedFrames().empty()) {
auto* timeline = App::instance()->timeline();
if (timeline)
timeline->prepareToMoveRange();
DocRange range = site.range();
SelectedLayers selLayers;
if (site->inFrames())
if (site.inFrames())
selLayers.selectAllLayers(writer.sprite()->root());
else {
selLayers = site->selectedLayers();
selLayers = site.selectedLayers();
selLayers.expandCollapsedGroups();
}
frame_t frameRange =
(site->selectedFrames().lastFrame() -
site->selectedFrames().firstFrame() + 1);
(site.selectedFrames().lastFrame() -
site.selectedFrames().firstFrame() + 1);
for (Layer* layer : selLayers) {
if (layer->isImage()) {
for (frame_t srcFrame : site->selectedFrames().reversed()) {
for (frame_t srcFrame : site.selectedFrames().reversed()) {
frame_t dstFrame = srcFrame+frameRange;
api.copyCel(
static_cast<LayerImage*>(layer), srcFrame,
@ -153,7 +155,8 @@ void NewFrameCommand::onExecute(Context* context)
}
range.displace(0, frameRange);
timeline->moveRange(range);
if (timeline)
timeline->moveRange(range);
}
else if (auto layer = static_cast<LayerImage*>(writer.layer())) {
api.copyCel(

View File

@ -34,7 +34,6 @@
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "app/util/new_image_from_mask.h"
#include "app/util/range_utils.h"
#include "doc/layer.h"
#include "doc/layer_tilemap.h"
#include "doc/primitives.h"

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -55,10 +55,10 @@ void RemoveFrameCommand::onExecute(Context* context)
{
Tx tx(writer, "Remove Frame");
DocApi api = document->getApi(tx);
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedFrames().empty()) {
for (frame_t frame : site->selectedFrames().reversed()) {
const Site& site = writer.site();
if (site.inTimeline() &&
!site.selectedFrames().empty()) {
for (frame_t frame : site.selectedFrames().reversed()) {
api.removeFrame(sprite, frame);
}
}

View File

@ -140,10 +140,10 @@ void RemoveLayerCommand::onExecute(Context* context)
// the std::greater Compare.
std::set<tileset_index, std::greater<tileset_index>> tsiToDelete;
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty()) {
SelectedLayers selLayers = site->selectedLayers();
const Site& site = writer.site();
if (site.inTimeline() &&
!site.selectedLayers().empty()) {
SelectedLayers selLayers = site.selectedLayers();
selLayers.removeChildrenIfParentIsSelected();
layer_t deletedTopLevelLayers = 0;

View File

@ -82,7 +82,7 @@ void RemoveSliceCommand::onExecute(Context* context)
if (slice)
slicesToDelete.insert(slice->id());
else
slicesToDelete = reader.site()->selectedSlices();
slicesToDelete = reader.site().selectedSlices();
}
// Nothing to delete

View File

@ -25,7 +25,6 @@
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui/toolbar.h"
#include "app/util/range_utils.h"
#include "base/convert_to.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
@ -221,15 +220,7 @@ void RotateCommand::onExecute(Context* context)
}
}
auto range = App::instance()->timeline()->range();
if (range.enabled())
cels = get_unique_cels_to_edit_pixels(site.sprite(), range);
else if (site.cel() &&
site.layer() &&
site.layer()->canEditPixels()) {
cels.push_back(site.cel());
}
cels = site.selectedUniqueCelsToEditPixels();
if (cels.empty()) {
StatusBar::instance()->showTip(
1000, Strings::statusbar_tips_all_layers_are_locked());
@ -238,9 +229,7 @@ void RotateCommand::onExecute(Context* context)
}
// Flip the whole sprite (even locked layers)
else if (site.sprite()) {
for (Cel* cel : site.sprite()->uniqueCels())
cels.push_back(cel);
cels = site.sprite()->uniqueCels().toList();
rotateSprite = true;
}

View File

@ -80,7 +80,7 @@ void SelectTileCommand::onExecute(Context* ctx)
mask->copyFrom(doc->mask());
{
gfx::Rect gridBounds = writer.site()->gridBounds();
gfx::Rect gridBounds = writer.site().gridBounds();
gfx::Point pos = editor->screenToEditor(editor->mousePosInDisplay());
pos = snap_to_grid(gridBounds, pos, PreferSnapTo::BoxOrigin);
gridBounds.setOrigin(pos);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2020 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2017-2018 David Capello
//
// This program is distributed under the terms of
@ -77,7 +77,7 @@ void SlicePropertiesCommand::onExecute(Context* context)
if (slice)
slices.insert(slice->id());
else
slices = reader.site()->selectedSlices();
slices = reader.site().selectedSlices();
}
// Nothing to delete

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -71,8 +71,8 @@ void UndoCommand::onExecute(Context* context)
(Preferences::instance().undo.gotoModified() &&
context->isUIAvailable() && editor);
if (gotoModified) {
SpritePosition currentPosition(writer.site()->layer(),
writer.site()->frame());
SpritePosition currentPosition(writer.site().layer(),
writer.site().frame());
if (m_type == Undo)
spritePosition = undo->nextUndoSpritePosition();

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -49,10 +50,10 @@ void UnlinkCelCommand::onExecute(Context* context)
{
Tx tx(writer, "Unlink Cel");
const Site* site = writer.site();
if (site->inTimeline() &&
!site->selectedLayers().empty()) {
for (Layer* layer : site->selectedLayers()) {
const Site& site = writer.site();
if (site.inTimeline() &&
!site.selectedLayers().empty()) {
for (Layer* layer : site.selectedLayers()) {
if (!layer->isImage())
continue;
@ -63,7 +64,7 @@ void UnlinkCelCommand::onExecute(Context* context)
LayerImage* layerImage = static_cast<LayerImage*>(layer);
for (frame_t frame : site->selectedFrames().reversed()) {
for (frame_t frame : site.selectedFrames().reversed()) {
Cel* cel = layerImage->cel(frame);
if (cel && cel->links())
tx(new cmd::UnlinkCel(cel));

View File

@ -28,7 +28,6 @@
#include "app/ui/timeline/timeline.h"
#include "app/ui_context.h"
#include "app/util/cel_ops.h"
#include "app/util/range_utils.h"
#include "doc/algorithm/shrink_bounds.h"
#include "doc/cel.h"
#include "doc/cels_range.h"
@ -40,6 +39,7 @@
#include "ui/manager.h"
#include "ui/view.h"
#include "ui/widget.h"
#include "view/cels.h"
#include <cstdlib>
#include <cstring>
@ -52,7 +52,7 @@ using namespace ui;
FilterManagerImpl::FilterManagerImpl(Context* context, Filter* filter)
: m_reader(context)
, m_site(*const_cast<Site*>(m_reader.site()))
, m_site(const_cast<Site&>(m_reader.site()))
, m_filter(filter)
, m_cel(nullptr)
, m_src(nullptr)
@ -279,16 +279,7 @@ void FilterManagerImpl::applyToTarget()
switch (m_celsTarget) {
case CelsTarget::Selected: {
auto range = m_site.range();
if (range.enabled()) {
for (Cel* cel : get_unique_cels_to_edit_pixels(m_site.sprite(), range))
cels.push_back(cel);
}
else if (m_site.cel() &&
m_site.layer() &&
m_site.layer()->canEditPixels()) {
cels.push_back(m_site.cel());
}
cels = m_site.selectedUniqueCelsToEditPixels();
break;
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (c) 2019 Igara Studio S.A.
// Copyright (c) 2019-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -53,7 +53,7 @@ protected:
if (!tileset)
return;
PalettePicks picks = writer.site()->selectedTiles();
PalettePicks picks = writer.site().selectedTiles();
if (picks.picks() == 0)
return;

View File

@ -31,12 +31,15 @@ namespace doc {
class PalettePicks;
}
namespace view {
class Range;
}
namespace app {
class ActiveSiteHandler;
class Clipboard;
class Command;
class Doc;
class DocRange;
class DocView;
class Preferences;
@ -134,7 +137,7 @@ namespace app {
void setActiveDocument(Doc* document);
void setActiveLayer(doc::Layer* layer);
void setActiveFrame(doc::frame_t frame);
void setRange(const DocRange& range);
void setRange(const view::Range& range);
void setSelectedColors(const doc::PalettePicks& picks);
void setSelectedTiles(const doc::PalettePicks& picks);
bool hasModifiedDocuments() const;
@ -167,7 +170,7 @@ namespace app {
virtual void onSetActiveDocument(Doc* doc, bool notify);
virtual void onSetActiveLayer(doc::Layer* layer);
virtual void onSetActiveFrame(const doc::frame_t frame);
virtual void onSetRange(const DocRange& range);
virtual void onSetRange(const view::Range& range);
virtual void onSetSelectedColors(const doc::PalettePicks& picks);
virtual void onSetSelectedTiles(const doc::PalettePicks& picks);
virtual void onCloseDocument(Doc* doc);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -20,16 +20,13 @@ namespace app {
class ContextAccess {
public:
const Context* context() const { return m_context; }
const Site* site() const { return &m_site; }
const Site& site() const { return m_site; }
const DocumentAccessT& document() const { return m_document; }
const Sprite* sprite() const { return m_site.sprite(); }
const Layer* layer() const { return m_site.layer(); }
frame_t frame() const { return m_site.frame(); }
const Cel* cel() const { return m_site.cel(); }
// You cannot change the site directly from a writable ContextAccess anyway.
const Site* site() { return &m_site; }
Context* context() { return const_cast<Context*>(m_context); }
DocumentAccessT& document() { return m_document; }
Sprite* sprite() { return m_site.sprite(); }

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,90 +9,14 @@
#define APP_DOC_RANGE_H_INCLUDED
#pragma once
#include "doc/frame.h"
#include "doc/selected_frames.h"
#include "doc/selected_layers.h"
#include <iosfwd>
namespace doc {
class Cel;
class Sprite;
}
#include "view/range.h"
namespace app {
using namespace doc;
class DocRange {
public:
enum Type { kNone = 0,
kCels = 1,
kFrames = 2,
kLayers = 4 };
DocRange();
DocRange(Cel* cel);
DocRange(const DocRange&) = default;
DocRange& operator=(const DocRange&) = default;
Type type() const { return m_type; }
bool enabled() const { return m_type != kNone; }
layer_t layers() const { return int(m_selectedLayers.size()); }
frame_t frames() const { return int(m_selectedFrames.size()); }
const SelectedLayers& selectedLayers() const { return m_selectedLayers; }
const SelectedFrames& selectedFrames() const { return m_selectedFrames; }
void setType(const Type type);
void setSelectedLayers(const SelectedLayers& layers);
void setSelectedFrames(const SelectedFrames& frames);
void displace(layer_t layerDelta, frame_t frameDelta);
bool contains(const Layer* layer) const;
bool contains(const frame_t frame) const {
return m_selectedFrames.contains(frame);
}
bool contains(const Layer* layer,
const frame_t frame) const;
// If the range includes the given layer, it will be erased from
// the selection and other candidates might be selected (e.g. a
// sibling, parent, etc.) using the
// candidate_if_layer_is_deleted() function.
void eraseAndAdjust(const Layer* layer);
void clearRange();
void startRange(Layer* fromLayer, frame_t fromFrame, Type type);
void endRange(Layer* toLayer, frame_t toFrame);
void selectLayer(Layer* layer);
void selectLayers(const SelectedLayers& selLayers);
frame_t firstFrame() const { return m_selectedFrames.firstFrame(); }
frame_t lastFrame() const { return m_selectedFrames.lastFrame(); }
bool operator==(const DocRange& o) const {
return (m_type == o.m_type &&
m_selectedLayers == o.m_selectedLayers &&
m_selectedFrames == o.m_selectedFrames);
}
bool convertToCels(const Sprite* sprite);
bool write(std::ostream& os) const;
bool read(std::istream& is);
private:
void selectLayerRange(Layer* fromLayer, Layer* toLayer);
void selectFrameRange(frame_t fromFrame, frame_t toFrame);
Type m_type; // Last used type of the range
int m_flags; // All used types in startRange()
SelectedLayers m_selectedLayers;
SelectedFrames m_selectedFrames;
Layer* m_selectingFromLayer;
frame_t m_selectingFromFrame;
};
// Now DocRange is just an alias for view::Range
//
// TODO replace DocRange with view::Range in the future
using DocRange = view::Range;
} // namespace app

View File

@ -1,4 +1,5 @@
// Aseprite
// Copyright (C) 2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -8,13 +9,13 @@
#define APP_DOC_RANGE_OPS_H_INCLUDED
#pragma once
#include "app/doc_range.h"
#include "app/tags_handling.h"
#include <vector>
namespace app {
class Doc;
class DocRange;
enum DocRangePlace {
kDocRangeBefore,

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2018-2022 Igara Studio S.A.
// Copyright (C) 2018-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -16,7 +16,6 @@
#include "app/script/luacpp.h"
#include "app/site.h"
#include "app/ui/editor/editor.h"
#include "app/util/range_utils.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/object_ids.h"
@ -24,6 +23,7 @@
#include "doc/slices.h"
#include "doc/sprite.h"
#include "doc/tile.h"
#include "view/cels.h"
#include <set>
#include <vector>
@ -77,7 +77,7 @@ struct RangeObj { // This is like DocRange but referencing objects with IDs
// it might not be possible because we have to save the IDs of the
// objects (and we cannot store the DocRange because it contains
// pointers instead of IDs).
for (const Cel* cel : get_cels(site.sprite(), range))
for (const Cel* cel : view::get_cels(site.sprite(), range))
cels.insert(cel->id());
}
else {

View File

@ -19,6 +19,8 @@
#include "doc/sprite.h"
#include "doc/tileset.h"
#include "ui/system.h"
#include "view/cels.h"
#include "view/range.h"
namespace app {
@ -135,4 +137,29 @@ bool Site::shouldTrimCel(Cel* cel) const
cel->layer()->isTilemap()));
}
CelList Site::selectedUniqueCels() const
{
if (m_range.enabled()) {
return view::get_unique_cels(m_sprite, m_range);
}
else if (auto c = cel()) {
return CelList{ c };
}
else
return CelList();
}
CelList Site::selectedUniqueCelsToEditPixels() const
{
if (m_range.enabled()) {
return view::get_unique_cels_to_edit_pixels(m_sprite, m_range);
}
else if (auto c = cel()) {
if (m_layer &&
m_layer->canEditPixels())
return CelList{ c };
}
return CelList();
}
} // namespace app

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2021 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -12,13 +12,13 @@
#include "app/doc_range.h"
#include "app/tilemap_mode.h"
#include "app/tileset_mode.h"
#include "doc/cel_list.h"
#include "doc/frame.h"
#include "doc/palette_picks.h"
#include "doc/selected_objects.h"
#include "gfx/fwd.h"
namespace doc {
class Cel;
class Grid;
class Image;
class Layer;
@ -116,7 +116,15 @@ namespace app {
TilemapMode tilemapMode() const { return m_tilemapMode; }
TilesetMode tilesetMode() const { return m_tilesetMode; }
bool shouldTrimCel(Cel* cel) const;
bool shouldTrimCel(doc::Cel* cel) const;
// Returns the list of selected "unique" cels (to avoid iterating
// two or more times through linked cels), or just a list of the
// active cel if there is no range in the given site.
doc::CelList selectedUniqueCels() const;
// Same as uniqueCels() with cels from non-locked/editable layers.
doc::CelList selectedUniqueCelsToEditPixels() const;
private:
Focus m_focus;

View File

@ -15,11 +15,14 @@
#include <string>
namespace view {
class Range;
}
namespace app {
class Cmd;
class Context;
class DocRange;
class DocUndo;
enum Modification {
@ -69,7 +72,7 @@ namespace app {
// Can be used to change the new document range resulting from
// executing this transaction. This range can be used then in
// undo/redo operations to restore the Timeline selection/range.
void setNewDocRange(const DocRange& range);
void setNewDocRange(const view::Range& range);
// This must be called to commit all the changes, so the undo will
// be finally added in the sprite.

View File

@ -36,7 +36,6 @@
#include "app/ui/workspace.h"
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "app/util/range_utils.h"
#include "base/fs.h"
#include "doc/color.h"
#include "doc/layer.h"
@ -579,9 +578,9 @@ bool DocView::onCut(Context* ctx)
bool DocView::onCopy(Context* ctx)
{
const ContextReader reader(ctx);
if (reader.site()->document() &&
static_cast<const Doc*>(reader.site()->document())->isMaskVisible() &&
reader.site()->image()) {
if (reader.site().document() &&
static_cast<const Doc*>(reader.site().document())->isMaskVisible() &&
reader.site().image()) {
ctx->clipboard()->copy(reader);
return true;
}
@ -618,14 +617,7 @@ bool DocView::onClear(Context* ctx)
Doc* document = site.document();
bool visibleMask = document->isMaskVisible();
CelList cels;
if (site.range().enabled()) {
cels = get_unique_cels_to_edit_pixels(site.sprite(), site.range());
}
else if (site.cel()) {
cels.push_back(site.cel());
}
CelList cels = site.selectedUniqueCelsToEditPixels();
if (cels.empty()) // No cels to modify
return false;

View File

@ -67,6 +67,7 @@
#include "os/system.h"
#include "render/rasterize.h"
#include "ui/ui.h"
#include "view/layers.h"
#include <algorithm>
#include <cmath>
@ -2472,7 +2473,8 @@ void Editor::onBeforeRemoveLayer(DocEvent& ev)
// If the layer that was removed is the selected one in the editor,
// or is an ancestor of the selected one.
Layer* layerToSelect = candidate_if_layer_is_deleted(layer(), ev.layer());
Layer* layerToSelect =
view::candidate_if_layer_is_deleted(layer(), ev.layer());
if (layer() != layerToSelect)
setLayer(layerToSelect);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020-2022 Igara Studio S.A.
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -25,13 +25,13 @@
#include "app/ui/status_bar.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui_context.h"
#include "app/util/range_utils.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/mask.h"
#include "doc/sprite.h"
#include "fmt/format.h"
#include "ui/message.h"
#include "view/cels.h"
#include <algorithm>
#include <cmath>
@ -71,7 +71,7 @@ MovingCelCollect::MovingCelCollect(Editor* editor, Layer* layer)
}
// Record start positions of all cels in selected range
for (Cel* cel : get_unique_cels_to_move_cel(editor->sprite(), range2)) {
for (Cel* cel : view::get_unique_cels_to_move_cel(editor->sprite(), range2)) {
Layer* layer = cel->layer();
ASSERT(layer);

View File

@ -31,7 +31,6 @@
#include "app/util/cel_ops.h"
#include "app/util/expand_cel_canvas.h"
#include "app/util/new_image_from_mask.h"
#include "app/util/range_utils.h"
#include "base/pi.h"
#include "doc/algorithm/flip_image.h"
#include "doc/algorithm/rotate.h"
@ -1455,8 +1454,7 @@ CelList PixelsMovement::getEditableCels()
CelList cels;
if (editMultipleCels()) {
cels = get_unique_cels_to_edit_pixels(m_site.sprite(),
m_site.range());
cels = m_site.selectedUniqueCelsToEditPixels();
}
else {
// TODO This case is used in paste too, where the cel() can be

View File

@ -165,11 +165,11 @@ void calculate_visible_layers(const Site& site,
if (layersValue == kSelectedLayers) {
if (!site.selectedLayers().empty()) {
layersVisibility.showSelectedLayers(
const_cast<Sprite*>(site.sprite()),
const_cast<doc::Sprite*>(site.sprite()),
site.selectedLayers());
}
else {
layersVisibility.showLayer(const_cast<Layer*>(site.layer()));
layersVisibility.showLayer(const_cast<doc::Layer*>(site.layer()));
}
}
else if (layersValue != kAllLayers) {

View File

@ -35,7 +35,6 @@
#include "app/ui/toolbar.h"
#include "app/ui/zoom_entry.h"
#include "app/ui_context.h"
#include "app/util/range_utils.h"
#include "app/util/tile_flags_utils.h"
#include "base/fs.h"
#include "base/string.h"

View File

@ -46,7 +46,6 @@
#include "app/ui_context.h"
#include "app/util/clipboard.h"
#include "app/util/layer_boundaries.h"
#include "app/util/layer_utils.h"
#include "app/util/readable_time.h"
#include "base/convert_to.h"
#include "base/memory.h"
@ -62,6 +61,7 @@
#include "os/system.h"
#include "text/font.h"
#include "ui/ui.h"
#include "view/layers.h"
#include <algorithm>
#include <cstdio>
@ -1934,7 +1934,7 @@ void Timeline::onAddLayer(DocEvent& ev)
// TODO similar to ActiveSiteHandler::onBeforeRemoveLayer() and Editor::onBeforeRemoveLayer()
void Timeline::onBeforeRemoveLayer(DocEvent& ev)
{
Layer* layerToSelect = candidate_if_layer_is_deleted(m_layer, ev.layer());
Layer* layerToSelect = view::candidate_if_layer_is_deleted(m_layer, ev.layer());
if (m_layer != layerToSelect)
setLayer(layerToSelect);

View File

@ -73,7 +73,7 @@ namespace app {
void onSetActiveDocument(Doc* doc, bool notify) override;
void onSetActiveLayer(doc::Layer* layer) override;
void onSetActiveFrame(const doc::frame_t frame) override;
void onSetRange(const DocRange& range) override;
void onSetRange(const view::Range& range) override;
void onSetSelectedColors(const doc::PalettePicks& picks) override;
void onSetSelectedTiles(const doc::PalettePicks& picks) override;
void onCloseDocument(Doc* doc) override;

View File

@ -32,7 +32,6 @@
#include "app/util/cel_ops.h"
#include "app/util/clipboard.h"
#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"
@ -40,6 +39,7 @@
#include "render/dithering.h"
#include "render/ordered_dither.h"
#include "render/quantization.h"
#include "view/cels.h"
#include <memory>
#include <stdexcept>
@ -345,7 +345,7 @@ void Clipboard::cut(ContextWriter& writer)
ASSERT(writer.sprite() != NULL);
ASSERT(writer.layer() != NULL);
if (!copyFromDocument(*writer.site())) {
if (!copyFromDocument(writer.site())) {
Console console;
console.printf("Can't copying an image portion from the current layer\n");
}
@ -354,13 +354,7 @@ void Clipboard::cut(ContextWriter& writer)
{
Tx tx(writer, "Cut");
Site site = writer.context()->activeSite();
CelList cels;
if (site.range().enabled()) {
cels = get_unique_cels_to_edit_pixels(site.sprite(), site.range());
}
else if (site.cel()) {
cels.push_back(site.cel());
}
CelList cels = site.selectedUniqueCelsToEditPixels();
clearMaskFromCels(tx,
writer.document(),
site,
@ -377,7 +371,7 @@ void Clipboard::copy(const ContextReader& reader)
{
ASSERT(reader.document() != NULL);
if (!copyFromDocument(*reader.site())) {
if (!copyFromDocument(reader.site())) {
Console console;
console.printf("Can't copying an image portion from the current layer\n");
return;
@ -388,7 +382,7 @@ void Clipboard::copyMerged(const ContextReader& reader)
{
ASSERT(reader.document() != NULL);
copyFromDocument(*reader.site(), true);
copyFromDocument(reader.site(), true);
}
void Clipboard::copyRange(const ContextReader& reader, const DocRange& range)

View File

@ -26,12 +26,15 @@ namespace doc {
class Tileset;
}
namespace view {
class Range;
}
namespace app {
class Context;
class ContextReader;
class ContextWriter;
class Doc;
class DocRange;
class Site;
class Tx;
@ -52,7 +55,7 @@ namespace app {
~Clipboard();
ClipboardFormat format() const;
void getDocumentRangeInfo(Doc** document, DocRange* range);
void getDocumentRangeInfo(Doc** document, view::Range* range);
void clearMaskFromCels(Tx& tx,
Doc* doc,
@ -64,7 +67,7 @@ namespace app {
void cut(ContextWriter& context);
void copy(const ContextReader& context);
void copyMerged(const ContextReader& context);
void copyRange(const ContextReader& context, const DocRange& range);
void copyRange(const ContextReader& context, const view::Range& range);
void copyImage(const doc::Image* image,
const doc::Mask* mask,
const doc::Palette* palette);

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2019-2022 Igara Studio S.A.
// Copyright (C) 2019-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -23,7 +23,6 @@
#include "app/doc.h"
#include "app/site.h"
#include "app/util/cel_ops.h"
#include "app/util/range_utils.h"
#include "base/debug.h"
#include "doc/algorithm/shrink_bounds.h"
#include "doc/cel.h"

View File

@ -18,36 +18,9 @@
namespace app {
using namespace doc;
Layer* candidate_if_layer_is_deleted(
const Layer* selectedLayer,
const Layer* layerToDelete)
{
const Layer* layerToSelect = selectedLayer;
if ((selectedLayer == layerToDelete) ||
(selectedLayer &&
selectedLayer->hasAncestor(layerToDelete))) {
Sprite* sprite = selectedLayer->sprite();
LayerGroup* parent = layerToDelete->parent();
// Select previous layer, or next layer, or the parent (if it is
// not the main layer of sprite set).
if (layerToDelete->getPrevious())
layerToSelect = layerToDelete->getPrevious();
else if (layerToDelete->getNext())
layerToSelect = layerToDelete->getNext();
else if (parent != sprite->root())
layerToSelect = parent;
}
return const_cast<Layer*>(layerToSelect);
}
bool layer_is_locked(Editor* editor)
{
Layer* layer = editor->layer();
doc::Layer* layer = editor->layer();
if (!layer)
return false;

View File

@ -19,13 +19,6 @@ namespace app {
class Editor;
// Calculates a possible candidate to be selected in case that we
// have a specific "selectedLayer" and are going to delete the given
// "layerToDelete".
doc::Layer* candidate_if_layer_is_deleted(
const doc::Layer* selectedLayer,
const doc::Layer* layerToDelete);
// True if the active layer is locked (itself or its hierarchy),
// also, it sends a tip to the user 'Layer ... is locked'
bool layer_is_locked(Editor* editor);

View File

@ -1,32 +0,0 @@
// Aseprite
// Copyright (C) 2020-2021 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UTIL_RANGE_UTILS_H_INCLUDED
#define APP_UTIL_RANGE_UTILS_H_INCLUDED
#pragma once
#include "doc/cel_list.h"
#include <vector>
namespace doc {
class Sprite;
}
namespace app {
using namespace doc;
class DocRange;
doc::CelList get_cels(const doc::Sprite* sprite, const DocRange& range);
doc::CelList get_unique_cels(const doc::Sprite* sprite, const DocRange& range);
doc::CelList get_unique_cels_to_edit_pixels(const doc::Sprite* sprite, const DocRange& range);
doc::CelList get_unique_cels_to_move_cel(const doc::Sprite* sprite, const DocRange& range);
} // namespace app
#endif

View File

@ -1,4 +1,4 @@
Copyright (c) 2018-2022 Igara Studio S.A.
Copyright (c) 2018-2023 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 Igara Studio S.A.
// Copyright (c) 2019-2023 Igara Studio S.A.
// Copyright (c) 2001-2016 David Capello
//
// This file is released under the terms of the MIT license.
@ -100,4 +100,16 @@ CelsRange::iterator& CelsRange::iterator::operator++()
return *this;
}
CelList CelsRange::toList()
{
CelList list;
int n = size();
if (n > 0) {
list.reserve(n);
for (Cel* cel : *this)
list.push_back(cel);
}
return list;
}
} // namespace doc

View File

@ -1,4 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2023 Igara Studio S.A.
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
@ -8,6 +9,7 @@
#define DOC_CELS_RANGE_H_INCLUDED
#pragma once
#include "doc/cel_list.h"
#include "doc/frame.h"
#include "doc/object_id.h"
#include "doc/selected_frames.h"
@ -77,6 +79,10 @@ namespace doc {
return count;
}
// Converts the CelsRange into a CelList (which indeed it's an
// array).
CelList toList();
private:
SelectedFrames m_selFrames;
iterator m_begin, m_end;

11
src/view/CMakeLists.txt Normal file
View File

@ -0,0 +1,11 @@
# Aseprite View Library
# Copyright (c) 2023 Igara Studio S.A.
add_library(view-lib
cels.cpp
layers.cpp
range.cpp)
target_link_libraries(view-lib
doc-lib
laf-base)

6
src/view/README.md Normal file
View File

@ -0,0 +1,6 @@
# Aseprite View Library
> Distributed under the terms of the [End-User License Agreement for Aseprite](../../EULA.txt)
Library to represent an abstract view (and concepts) of the timeline
like the range of selected frames/layers.

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020-2021 Igara Studio S.A.
// Aseprite View Library
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,18 +9,14 @@
#include "config.h"
#endif
#include "app/util/range_utils.h"
#include "view/cels.h"
#include "app/context_access.h"
#include "app/doc.h"
#include "app/doc_range.h"
#include "doc/cel.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "view/range.h"
#include <set>
namespace app {
namespace view {
using namespace doc;
@ -31,10 +27,10 @@ enum class Target {
kUniqueCanEditPixelsCels,
};
// TODO the DocRange should be "iteratable" to replace this function
// TODO the view::Range 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,
Range range,
const Target target)
{
CelList cels;
@ -71,24 +67,24 @@ static CelList get_cels_templ(const Sprite* sprite,
return cels;
}
CelList get_cels(const doc::Sprite* sprite, const DocRange& range)
CelList get_cels(const Sprite* sprite, const Range& range)
{
return get_cels_templ(sprite, range, Target::kAllCels);
}
CelList get_unique_cels(const Sprite* sprite, const DocRange& range)
CelList get_unique_cels(const Sprite* sprite, const Range& range)
{
return get_cels_templ(sprite, range, Target::kUniqueCels);
}
CelList get_unique_cels_to_move_cel(const Sprite* sprite, const DocRange& range)
CelList get_unique_cels_to_move_cel(const Sprite* sprite, const Range& range)
{
return get_cels_templ(sprite, range, Target::kUniqueCanMoveCels);
}
CelList get_unique_cels_to_edit_pixels(const Sprite* sprite, const DocRange& range)
CelList get_unique_cels_to_edit_pixels(const Sprite* sprite, const Range& range)
{
return get_cels_templ(sprite, range, Target::kUniqueCanEditPixelsCels);
}
} // namespace app
} // namespace view

28
src/view/cels.h Normal file
View File

@ -0,0 +1,28 @@
// Aseprite View Library
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef VIEW_CELS_H_INCLUDED
#define VIEW_CELS_H_INCLUDED
#pragma once
#include "doc/cel_list.h"
namespace doc {
class Sprite;
}
namespace view {
class Range;
doc::CelList get_cels(const doc::Sprite* sprite, const Range& range);
doc::CelList get_unique_cels(const doc::Sprite* sprite, const Range& range);
doc::CelList get_unique_cels_to_edit_pixels(const doc::Sprite* sprite, const Range& range);
doc::CelList get_unique_cels_to_move_cel(const doc::Sprite* sprite, const Range& range);
} // namespace view
#endif // VIEW_CELS_H_INCLUDED

45
src/view/layers.cpp Normal file
View File

@ -0,0 +1,45 @@
// Aseprite View Library
// Copyright (c) 2020-2023 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 "view/layers.h"
#include "doc/layer.h"
#include "doc/sprite.h"
namespace view {
using namespace doc;
Layer* candidate_if_layer_is_deleted(
const Layer* selectedLayer,
const Layer* layerToDelete)
{
const Layer* layerToSelect = selectedLayer;
if ((selectedLayer == layerToDelete) ||
(selectedLayer &&
selectedLayer->hasAncestor(layerToDelete))) {
Sprite* sprite = selectedLayer->sprite();
LayerGroup* parent = layerToDelete->parent();
// Select previous layer, or next layer, or the parent (if it is
// not the main layer of sprite set).
if (layerToDelete->getPrevious())
layerToSelect = layerToDelete->getPrevious();
else if (layerToDelete->getNext())
layerToSelect = layerToDelete->getNext();
else if (parent != sprite->root())
layerToSelect = parent;
}
return const_cast<Layer*>(layerToSelect);
}
} // namespace view

26
src/view/layers.h Normal file
View File

@ -0,0 +1,26 @@
// Aseprite View Library
// Copyright (c) 2020-2023 Igara Studio S.A.
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef VIEW_LAYERS_H_INCLUDED
#define VIEW_LAYERS_H_INCLUDED
#pragma once
namespace doc {
class Layer;
}
namespace view {
// Calculates a possible candidate to be selected in case that we
// have a specific "selectedLayer" and are going to delete the given
// "layerToDelete".
doc::Layer* candidate_if_layer_is_deleted(
const doc::Layer* selectedLayer,
const doc::Layer* layerToDelete);
} // namespace view
#endif // VIEW_LAYERS_H_INCLUDED

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2020 Igara Studio S.A.
// Aseprite View Library
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
@ -9,24 +9,24 @@
#include "config.h"
#endif
#include "app/doc_range.h"
#include "view/range.h"
#include "app/util/layer_utils.h"
#include "base/serialization.h"
#include "doc/cel.h"
#include "doc/image.h"
#include "doc/layer.h"
#include "doc/sprite.h"
#include "view/layers.h"
#include <iostream>
namespace app {
namespace view {
using namespace base::serialization;
using namespace base::serialization::little_endian;
using namespace doc;
DocRange::DocRange()
Range::Range()
: m_type(kNone)
, m_flags(m_type)
, m_selectingFromLayer(nullptr)
@ -34,7 +34,7 @@ DocRange::DocRange()
{
}
DocRange::DocRange(Cel* cel)
Range::Range(Cel* cel)
: m_type(kCels)
, m_flags(m_type)
, m_selectingFromLayer(nullptr)
@ -44,7 +44,7 @@ DocRange::DocRange(Cel* cel)
m_selectedFrames.insert(cel->frame());
}
void DocRange::clearRange()
void Range::clearRange()
{
m_type = kNone;
m_flags = kNone;
@ -58,7 +58,7 @@ void DocRange::clearRange()
m_selectingFromFrame = -1;
}
void DocRange::startRange(Layer* fromLayer, frame_t fromFrame, Type type)
void Range::startRange(Layer* fromLayer, frame_t fromFrame, Type type)
{
m_type = type;
m_flags |= type;
@ -71,7 +71,7 @@ void DocRange::startRange(Layer* fromLayer, frame_t fromFrame, Type type)
m_selectedFrames.insert(fromFrame);
}
void DocRange::endRange(Layer* toLayer, frame_t toFrame)
void Range::endRange(Layer* toLayer, frame_t toFrame)
{
ASSERT(enabled());
@ -82,7 +82,7 @@ void DocRange::endRange(Layer* toLayer, frame_t toFrame)
selectFrameRange(m_selectingFromFrame, toFrame);
}
void DocRange::selectLayer(Layer* layer)
void Range::selectLayer(Layer* layer)
{
if (m_type == kNone)
m_type = kLayers;
@ -91,7 +91,7 @@ void DocRange::selectLayer(Layer* layer)
m_selectedLayers.insert(layer);
}
void DocRange::selectLayers(const SelectedLayers& selLayers)
void Range::selectLayers(const SelectedLayers& selLayers)
{
if (m_type == kNone)
m_type = kLayers;
@ -101,7 +101,7 @@ void DocRange::selectLayers(const SelectedLayers& selLayers)
m_selectedLayers.insert(layer);
}
void DocRange::eraseAndAdjust(const Layer* layer)
void Range::eraseAndAdjust(const Layer* layer)
{
if (!enabled())
return;
@ -126,7 +126,7 @@ void DocRange::eraseAndAdjust(const Layer* layer)
}
}
bool DocRange::contains(const Layer* layer) const
bool Range::contains(const Layer* layer) const
{
if (enabled())
return m_selectedLayers.contains(const_cast<Layer*>(layer));
@ -134,15 +134,15 @@ bool DocRange::contains(const Layer* layer) const
return false;
}
bool DocRange::contains(const Layer* layer,
bool Range::contains(const Layer* layer,
const frame_t frame) const
{
switch (m_type) {
case DocRange::kNone:
case Range::kNone:
return false;
case DocRange::kCels:
case Range::kCels:
return contains(layer) && contains(frame);
case DocRange::kFrames:
case Range::kFrames:
if (contains(frame)) {
if ((m_flags & (kCels | kLayers)) != 0)
return contains(layer);
@ -150,7 +150,7 @@ bool DocRange::contains(const Layer* layer,
return true;
}
break;
case DocRange::kLayers:
case Range::kLayers:
if (contains(layer)) {
if ((m_flags & (kCels | kFrames)) != 0)
return contains(frame);
@ -162,37 +162,38 @@ bool DocRange::contains(const Layer* layer,
return false;
}
void DocRange::displace(layer_t layerDelta, frame_t frameDelta)
void Range::displace(const layer_t layerDelta,
const frame_t frameDelta)
{
m_selectedLayers.displace(layerDelta);
m_selectedFrames.displace(frameDelta);
}
bool DocRange::convertToCels(const Sprite* sprite)
bool Range::convertToCels(const Sprite* sprite)
{
switch (m_type) {
case DocRange::kNone:
case Range::kNone:
return false;
case DocRange::kCels:
case Range::kCels:
break;
case DocRange::kFrames: {
case Range::kFrames: {
if ((m_flags & (kCels | kLayers)) == 0) {
for (auto layer : sprite->allBrowsableLayers())
m_selectedLayers.insert(layer);
}
m_type = DocRange::kCels;
m_type = Range::kCels;
break;
}
case DocRange::kLayers:
case Range::kLayers:
if ((m_flags & (kCels | kFrames)) == 0)
selectFrameRange(0, sprite->lastFrame());
m_type = DocRange::kCels;
m_type = Range::kCels;
break;
}
return true;
}
bool DocRange::write(std::ostream& os) const
bool Range::write(std::ostream& os) const
{
write32(os, m_type);
write32(os, m_flags);
@ -205,7 +206,7 @@ bool DocRange::write(std::ostream& os) const
return os.good();
}
bool DocRange::read(std::istream& is)
bool Range::read(std::istream& is)
{
clearRange();
@ -221,7 +222,7 @@ bool DocRange::read(std::istream& is)
return is.good();
}
void DocRange::selectLayerRange(Layer* fromLayer, Layer* toLayer)
void Range::selectLayerRange(Layer* fromLayer, Layer* toLayer)
{
ASSERT(fromLayer);
ASSERT(toLayer);
@ -267,12 +268,12 @@ void DocRange::selectLayerRange(Layer* fromLayer, Layer* toLayer)
} while (it);
}
void DocRange::selectFrameRange(frame_t fromFrame, frame_t toFrame)
void Range::selectFrameRange(frame_t fromFrame, frame_t toFrame)
{
m_selectedFrames.insert(fromFrame, toFrame);
}
void DocRange::setType(const Type type)
void Range::setType(const Type type)
{
if (type != kNone) {
m_type = type;
@ -284,7 +285,7 @@ void DocRange::setType(const Type type)
}
}
void DocRange::setSelectedLayers(const SelectedLayers& layers)
void Range::setSelectedLayers(const SelectedLayers& layers)
{
if (layers.empty()) {
m_type = kNone;
@ -297,7 +298,7 @@ void DocRange::setSelectedLayers(const SelectedLayers& layers)
m_selectedLayers = layers;
}
void DocRange::setSelectedFrames(const SelectedFrames& frames)
void Range::setSelectedFrames(const SelectedFrames& frames)
{
if (frames.empty()) {
m_type = kNone;
@ -310,4 +311,4 @@ void DocRange::setSelectedFrames(const SelectedFrames& frames)
m_selectedFrames = frames;
}
} // namespace app
} // namespace view

100
src/view/range.h Normal file
View File

@ -0,0 +1,100 @@
// Aseprite View Library
// Copyright (C) 2020-2023 Igara Studio S.A.
// Copyright (C) 2001-2018 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef VIEW_RANGE_H_INCLUDED
#define VIEW_RANGE_H_INCLUDED
#pragma once
#include "doc/cel_list.h"
#include "doc/frame.h"
#include "doc/selected_frames.h"
#include "doc/selected_layers.h"
#include <iosfwd>
namespace doc {
class Cel;
class Sprite;
}
namespace view {
class Range {
public:
enum Type { kNone = 0,
kCels = 1,
kFrames = 2,
kLayers = 4 };
Range();
Range(doc::Cel* cel);
Range(const Range&) = default;
Range& operator=(const Range&) = default;
Type type() const { return m_type; }
bool enabled() const { return m_type != kNone; }
doc::layer_t layers() const { return int(m_selectedLayers.size()); }
doc::frame_t frames() const { return int(m_selectedFrames.size()); }
const doc::SelectedLayers& selectedLayers() const { return m_selectedLayers; }
const doc::SelectedFrames& selectedFrames() const { return m_selectedFrames; }
void setType(const Type type);
void setSelectedLayers(const doc::SelectedLayers& layers);
void setSelectedFrames(const doc::SelectedFrames& frames);
void displace(const doc::layer_t layerDelta,
const doc::frame_t frameDelta);
bool contains(const doc::Layer* layer) const;
bool contains(const doc::frame_t frame) const {
return m_selectedFrames.contains(frame);
}
bool contains(const doc::Layer* layer,
const doc::frame_t frame) const;
// If the range includes the given layer, it will be erased from
// the selection and other candidates might be selected (e.g. a
// sibling, parent, etc.) using the
// candidate_if_layer_is_deleted() function.
void eraseAndAdjust(const doc::Layer* layer);
void clearRange();
void startRange(doc::Layer* fromLayer, doc::frame_t fromFrame, Type type);
void endRange(doc::Layer* toLayer, doc::frame_t toFrame);
void selectLayer(doc::Layer* layer);
void selectLayers(const doc::SelectedLayers& selLayers);
doc::frame_t firstFrame() const { return m_selectedFrames.firstFrame(); }
doc::frame_t lastFrame() const { return m_selectedFrames.lastFrame(); }
bool operator==(const Range& o) const {
return (m_type == o.m_type &&
m_selectedLayers == o.m_selectedLayers &&
m_selectedFrames == o.m_selectedFrames);
}
bool convertToCels(const doc::Sprite* sprite);
bool write(std::ostream& os) const;
bool read(std::istream& is);
private:
void selectLayerRange(doc::Layer* fromLayer, doc::Layer* toLayer);
void selectFrameRange(doc::frame_t fromFrame, doc::frame_t toFrame);
Type m_type; // Last used type of the range
int m_flags; // All used types in startRange()
doc::SelectedLayers m_selectedLayers;
doc::SelectedFrames m_selectedFrames;
doc::Layer* m_selectingFromLayer;
doc::frame_t m_selectingFromFrame;
};
} // namespace view
#endif // VIEW_RANGE_H_INCLUDED