mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-01 10:13:22 +00:00
Fix Timeline operations with ranges using virtual vs real ranges (#3904)
- Now we have a view::RealRange (with real frames, fr_t) and a view::VirtualRange (with virtual frames, columns, col_t). The timeline uses a virtual range internally and makes the conversion to a real range when it's appropiated (e.g. to execute an old DocRange operation using ranges with real frames). - Added Context::range() to access to the real selected range instead of accessing directly to the timeline. In this way commands that were using the DocRange can access to the real range using the context, instead of the timeline's virtual range. - Added a new ShowTagTimelineAdapter that can show just one tag in the timeline filtering out/hiding all other frames/tags.
This commit is contained in:
parent
8a2df3b01d
commit
be17c48324
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2023 Igara Studio SA
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -43,6 +44,8 @@ namespace app {
|
|||||||
virtual size_t onMemSize() const;
|
virtual size_t onMemSize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
// TODO I think we could just remove this field (but we'll need to
|
||||||
|
// include the Context* in all onEvent() member functions)
|
||||||
Context* m_ctx;
|
Context* m_ctx;
|
||||||
#if _DEBUG
|
#if _DEBUG
|
||||||
enum class State { NotExecuted, Executed, Undone, Redone };
|
enum class State { NotExecuted, Executed, Undone, Redone };
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -14,8 +14,6 @@
|
|||||||
#include "app/app.h"
|
#include "app/app.h"
|
||||||
#include "app/context.h"
|
#include "app/context.h"
|
||||||
#include "app/site.h"
|
#include "app/site.h"
|
||||||
#include "app/sprite_position.h"
|
|
||||||
#include "app/ui/timeline/timeline.h"
|
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
@ -41,7 +39,7 @@ CmdTransaction* CmdTransaction::moveToEmptyCopy()
|
|||||||
return copy;
|
return copy;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CmdTransaction::setNewDocRange(const DocRange& range)
|
void CmdTransaction::setNewDocRange(const view::RealRange& range)
|
||||||
{
|
{
|
||||||
if (m_ranges)
|
if (m_ranges)
|
||||||
range.write(m_ranges->m_after);
|
range.write(m_ranges->m_after);
|
||||||
@ -131,24 +129,12 @@ SpritePosition CmdTransaction::calcSpritePosition() const
|
|||||||
|
|
||||||
bool CmdTransaction::isDocRangeEnabled() const
|
bool CmdTransaction::isDocRangeEnabled() const
|
||||||
{
|
{
|
||||||
if (App::instance()) {
|
return context()->range().enabled();
|
||||||
Timeline* timeline = App::instance()->timeline();
|
|
||||||
if (timeline && timeline->range().enabled())
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
DocRange CmdTransaction::calcDocRange() const
|
view::RealRange CmdTransaction::calcDocRange() const
|
||||||
{
|
{
|
||||||
// TODO We cannot use Context::activeSite() because it losts
|
return context()->range();
|
||||||
// important information about the DocRange() (type and
|
|
||||||
// flags).
|
|
||||||
if (App* app = App::instance()) {
|
|
||||||
if (Timeline* timeline = app->timeline())
|
|
||||||
return timeline->range();
|
|
||||||
}
|
|
||||||
return DocRange();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace app
|
} // namespace app
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -10,8 +10,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "app/cmd_sequence.h"
|
#include "app/cmd_sequence.h"
|
||||||
#include "app/doc_range.h"
|
|
||||||
#include "app/sprite_position.h"
|
#include "app/sprite_position.h"
|
||||||
|
#include "view/range.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
@ -32,7 +32,7 @@ namespace app {
|
|||||||
// the new CmdTransaction.
|
// the new CmdTransaction.
|
||||||
CmdTransaction* moveToEmptyCopy();
|
CmdTransaction* moveToEmptyCopy();
|
||||||
|
|
||||||
void setNewDocRange(const DocRange& range);
|
void setNewDocRange(const view::RealRange& range);
|
||||||
void updateSpritePositionAfter();
|
void updateSpritePositionAfter();
|
||||||
|
|
||||||
SpritePosition spritePositionBeforeExecute() const { return m_spritePositionBefore; }
|
SpritePosition spritePositionBeforeExecute() const { return m_spritePositionBefore; }
|
||||||
@ -51,7 +51,7 @@ namespace app {
|
|||||||
private:
|
private:
|
||||||
SpritePosition calcSpritePosition() const;
|
SpritePosition calcSpritePosition() const;
|
||||||
bool isDocRangeEnabled() const;
|
bool isDocRangeEnabled() const;
|
||||||
DocRange calcDocRange() const;
|
view::RealRange calcDocRange() const;
|
||||||
|
|
||||||
struct Ranges {
|
struct Ranges {
|
||||||
std::stringstream m_before;
|
std::stringstream m_before;
|
||||||
|
@ -95,7 +95,7 @@ public:
|
|||||||
m_timer.stop();
|
m_timer.stop();
|
||||||
m_document = doc;
|
m_document = doc;
|
||||||
m_cel = cel;
|
m_cel = cel;
|
||||||
m_range = App::instance()->timeline()->range();
|
m_range = UIContext::instance()->range();
|
||||||
|
|
||||||
if (m_document)
|
if (m_document)
|
||||||
m_document->add_observer(this);
|
m_document->add_observer(this);
|
||||||
@ -276,7 +276,7 @@ private:
|
|||||||
m_lastValues.text = newUserData.text();
|
m_lastValues.text = newUserData.text();
|
||||||
|
|
||||||
if (redrawTimeline)
|
if (redrawTimeline)
|
||||||
App::instance()->timeline()->invalidate();
|
App::instance()->timeline()->invalidate(); // TODO avoid this invalidating in tx.commit()
|
||||||
|
|
||||||
tx.commit();
|
tx.commit();
|
||||||
}
|
}
|
||||||
@ -378,7 +378,7 @@ private:
|
|||||||
bool m_pendingChanges = false;
|
bool m_pendingChanges = false;
|
||||||
Doc* m_document = nullptr;
|
Doc* m_document = nullptr;
|
||||||
Cel* m_cel = nullptr;
|
Cel* m_cel = nullptr;
|
||||||
DocRange m_range;
|
view::RealRange m_range;
|
||||||
bool m_selfUpdate = false;
|
bool m_selfUpdate = false;
|
||||||
UserDataView m_userDataView;
|
UserDataView m_userDataView;
|
||||||
CelPropsLastValues m_lastValues;
|
CelPropsLastValues m_lastValues;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2017 David Capello
|
// Copyright (C) 2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -12,7 +13,6 @@
|
|||||||
#include "app/commands/command.h"
|
#include "app/commands/command.h"
|
||||||
#include "app/context_access.h"
|
#include "app/context_access.h"
|
||||||
#include "app/modules/gui.h"
|
#include "app/modules/gui.h"
|
||||||
#include "app/ui/timeline/timeline.h"
|
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ bool LayerLockCommand::onChecked(Context* context)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
SelectedLayers selLayers;
|
SelectedLayers selLayers;
|
||||||
auto range = App::instance()->timeline()->range();
|
const view::RealRange& range = context->range();
|
||||||
if (range.enabled()) {
|
if (range.enabled()) {
|
||||||
selLayers = range.selectedLayers();
|
selLayers = range.selectedLayers();
|
||||||
}
|
}
|
||||||
@ -68,7 +68,7 @@ void LayerLockCommand::onExecute(Context* context)
|
|||||||
{
|
{
|
||||||
ContextWriter writer(context);
|
ContextWriter writer(context);
|
||||||
SelectedLayers selLayers;
|
SelectedLayers selLayers;
|
||||||
auto range = App::instance()->timeline()->range();
|
auto range = context->range();
|
||||||
if (range.enabled()) {
|
if (range.enabled()) {
|
||||||
selLayers = range.selectedLayers();
|
selLayers = range.selectedLayers();
|
||||||
}
|
}
|
||||||
|
@ -157,7 +157,7 @@ public:
|
|||||||
m_timer.stop();
|
m_timer.stop();
|
||||||
m_document = doc;
|
m_document = doc;
|
||||||
m_layer = layer;
|
m_layer = layer;
|
||||||
m_range = App::instance()->timeline()->range();
|
m_range = UIContext::instance()->range();
|
||||||
|
|
||||||
if (m_document)
|
if (m_document)
|
||||||
m_document->add_observer(this);
|
m_document->add_observer(this);
|
||||||
@ -267,11 +267,11 @@ private:
|
|||||||
ContextWriter writer(UIContext::instance());
|
ContextWriter writer(UIContext::instance());
|
||||||
Tx tx(writer, "Set Layer Properties");
|
Tx tx(writer, "Set Layer Properties");
|
||||||
|
|
||||||
DocRange range;
|
view::RealRange range;
|
||||||
if (m_range.enabled())
|
if (m_range.enabled())
|
||||||
range = m_range;
|
range = m_range;
|
||||||
else {
|
else {
|
||||||
range.startRange(m_layer, -1, DocRange::kLayers);
|
range.startRange(m_layer, -1, view::Range::kLayers);
|
||||||
range.endRange(m_layer, -1);
|
range.endRange(m_layer, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -474,7 +474,7 @@ private:
|
|||||||
bool m_pendingChanges = false;
|
bool m_pendingChanges = false;
|
||||||
Doc* m_document = nullptr;
|
Doc* m_document = nullptr;
|
||||||
Layer* m_layer = nullptr;
|
Layer* m_layer = nullptr;
|
||||||
DocRange m_range;
|
view::RealRange m_range;
|
||||||
bool m_selfUpdate = false;
|
bool m_selfUpdate = false;
|
||||||
UserDataView m_userDataView;
|
UserDataView m_userDataView;
|
||||||
};
|
};
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2024 Igara Studio S.A.
|
// Copyright (C) 2023-2024 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2017 David Capello
|
// Copyright (C) 2001-2017 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -12,7 +12,6 @@
|
|||||||
#include "app/commands/command.h"
|
#include "app/commands/command.h"
|
||||||
#include "app/context_access.h"
|
#include "app/context_access.h"
|
||||||
#include "app/modules/gui.h"
|
#include "app/modules/gui.h"
|
||||||
#include "app/ui/timeline/timeline.h"
|
|
||||||
#include "doc/image.h"
|
#include "doc/image.h"
|
||||||
#include "doc/layer.h"
|
#include "doc/layer.h"
|
||||||
|
|
||||||
@ -49,7 +48,7 @@ bool LayerVisibilityCommand::onChecked(Context* context)
|
|||||||
return false;
|
return false;
|
||||||
|
|
||||||
SelectedLayers selLayers;
|
SelectedLayers selLayers;
|
||||||
DocRange range = context->activeSite().range();
|
auto range = context->range();
|
||||||
if (range.enabled()) {
|
if (range.enabled()) {
|
||||||
selLayers = range.selectedLayers();
|
selLayers = range.selectedLayers();
|
||||||
}
|
}
|
||||||
@ -69,7 +68,7 @@ void LayerVisibilityCommand::onExecute(Context* context)
|
|||||||
ContextWriter writer(context);
|
ContextWriter writer(context);
|
||||||
Doc* doc = writer.document();
|
Doc* doc = writer.document();
|
||||||
SelectedLayers selLayers;
|
SelectedLayers selLayers;
|
||||||
DocRange range = context->activeSite().range();
|
auto range = context->range();
|
||||||
if (range.enabled()) {
|
if (range.enabled()) {
|
||||||
selLayers = range.selectedLayers();
|
selLayers = range.selectedLayers();
|
||||||
}
|
}
|
||||||
|
@ -129,7 +129,7 @@ void NewFrameCommand::onExecute(Context* context)
|
|||||||
if (timeline)
|
if (timeline)
|
||||||
timeline->prepareToMoveRange();
|
timeline->prepareToMoveRange();
|
||||||
|
|
||||||
DocRange range = site.range();
|
view::RealRange range = site.range();
|
||||||
|
|
||||||
SelectedLayers selLayers;
|
SelectedLayers selLayers;
|
||||||
if (site.inFrames())
|
if (site.inFrames())
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -16,7 +16,6 @@
|
|||||||
#include "app/context_access.h"
|
#include "app/context_access.h"
|
||||||
#include "app/tx.h"
|
#include "app/tx.h"
|
||||||
#include "app/ui/tag_window.h"
|
#include "app/ui/tag_window.h"
|
||||||
#include "app/ui/timeline/timeline.h"
|
|
||||||
#include "doc/tag.h"
|
#include "doc/tag.h"
|
||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
@ -52,10 +51,10 @@ void NewFrameTagCommand::onExecute(Context* context)
|
|||||||
frame_t from = reader.frame();
|
frame_t from = reader.frame();
|
||||||
frame_t to = reader.frame();
|
frame_t to = reader.frame();
|
||||||
|
|
||||||
auto range = App::instance()->timeline()->range();
|
view::RealRange range = context->range();
|
||||||
if (range.enabled() &&
|
if (range.enabled() &&
|
||||||
(range.type() == DocRange::kFrames ||
|
(range.type() == view::Range::kFrames ||
|
||||||
range.type() == DocRange::kCels)) {
|
range.type() == view::Range::kCels)) {
|
||||||
from = range.selectedFrames().firstFrame();
|
from = range.selectedFrames().firstFrame();
|
||||||
to = range.selectedFrames().lastFrame();
|
to = range.selectedFrames().lastFrame();
|
||||||
}
|
}
|
||||||
@ -79,8 +78,6 @@ void NewFrameTagCommand::onExecute(Context* context)
|
|||||||
tag.release();
|
tag.release();
|
||||||
tx.commit();
|
tx.commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
App::instance()->timeline()->invalidate();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Command* CommandFactory::createNewFrameTagCommand()
|
Command* CommandFactory::createNewFrameTagCommand()
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
|
// Copyright (C) 2023 Igara Studio SA
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -33,7 +34,7 @@ ReverseFramesCommand::ReverseFramesCommand()
|
|||||||
|
|
||||||
bool ReverseFramesCommand::onEnabled(Context* context)
|
bool ReverseFramesCommand::onEnabled(Context* context)
|
||||||
{
|
{
|
||||||
auto range = App::instance()->timeline()->range();
|
const view::RealRange& range = context->range();
|
||||||
return
|
return
|
||||||
context->checkFlags(ContextFlags::ActiveDocumentIsWritable) &&
|
context->checkFlags(ContextFlags::ActiveDocumentIsWritable) &&
|
||||||
range.enabled() &&
|
range.enabled() &&
|
||||||
@ -42,7 +43,7 @@ bool ReverseFramesCommand::onEnabled(Context* context)
|
|||||||
|
|
||||||
void ReverseFramesCommand::onExecute(Context* context)
|
void ReverseFramesCommand::onExecute(Context* context)
|
||||||
{
|
{
|
||||||
auto range = App::instance()->timeline()->range();
|
const view::RealRange& range = context->range();
|
||||||
if (!range.enabled())
|
if (!range.enabled())
|
||||||
return; // Nothing to do
|
return; // Nothing to do
|
||||||
|
|
||||||
|
@ -113,12 +113,12 @@ void SelectPaletteColorsCommand::onExecute(Context* context)
|
|||||||
{
|
{
|
||||||
Site site = context->activeSite();
|
Site site = context->activeSite();
|
||||||
Sprite* sprite = site.sprite();
|
Sprite* sprite = site.sprite();
|
||||||
DocRange range = site.range();
|
view::RealRange range = site.range();
|
||||||
SelectedFrames selectedFrames;
|
SelectedFrames selectedFrames;
|
||||||
SelectedLayers selectedLayers;
|
SelectedLayers selectedLayers;
|
||||||
if (range.type() == DocRange::Type::kNone) {
|
if (range.type() == view::Range::Type::kNone) {
|
||||||
// If there isn't a cels range selected, it assumes the whole sprite:
|
// If there isn't a cels range selected, it assumes the whole sprite:
|
||||||
range.startRange(site.layer(), 0, DocRange::Type::kFrames);
|
range.startRange(site.layer(), 0, view::Range::Type::kFrames);
|
||||||
range.endRange(site.layer(), sprite->lastFrame());
|
range.endRange(site.layer(), sprite->lastFrame());
|
||||||
selectedFrames = range.selectedFrames();
|
selectedFrames = range.selectedFrames();
|
||||||
selectedLayers.selectAllLayers(sprite->root());
|
selectedLayers.selectAllLayers(sprite->root());
|
||||||
|
@ -80,7 +80,7 @@ void SetLoopSectionCommand::onExecute(Context* ctx)
|
|||||||
switch (m_action) {
|
switch (m_action) {
|
||||||
|
|
||||||
case Action::Auto: {
|
case Action::Auto: {
|
||||||
auto range = App::instance()->timeline()->range();
|
const view::RealRange& range = ctx->range();
|
||||||
if (range.enabled() && (range.frames() > 1)) {
|
if (range.enabled() && (range.frames() > 1)) {
|
||||||
begin = range.selectedFrames().firstFrame();
|
begin = range.selectedFrames().firstFrame();
|
||||||
end = range.selectedFrames().lastFrame();
|
end = range.selectedFrames().lastFrame();
|
||||||
|
@ -144,12 +144,9 @@ void UndoCommand::onExecute(Context* context)
|
|||||||
// this point when objects (possible layers) are re-created after
|
// this point when objects (possible layers) are re-created after
|
||||||
// the undo and we can deserialize them.
|
// the undo and we can deserialize them.
|
||||||
if (docRangeStream) {
|
if (docRangeStream) {
|
||||||
Timeline* timeline = App::instance()->timeline();
|
view::Range docRange;
|
||||||
if (timeline) {
|
if (docRange.read(*docRangeStream))
|
||||||
DocRange docRange;
|
context->setRange(docRange);
|
||||||
if (docRange.read(*docRangeStream))
|
|
||||||
timeline->setRange(docRange);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
document->generateMaskBoundaries();
|
document->generateMaskBoundaries();
|
||||||
|
@ -91,6 +91,14 @@ Doc* Context::activeDocument() const
|
|||||||
return site.document();
|
return site.document();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const view::RealRange& Context::range() const
|
||||||
|
{
|
||||||
|
Site site;
|
||||||
|
onGetActiveSite(&site);
|
||||||
|
m_range = site.range(); // TODO cache this value as much as possible
|
||||||
|
return m_range;
|
||||||
|
}
|
||||||
|
|
||||||
void Context::setActiveDocument(Doc* document)
|
void Context::setActiveDocument(Doc* document)
|
||||||
{
|
{
|
||||||
onSetActiveDocument(document, true);
|
onSetActiveDocument(document, true);
|
||||||
@ -106,7 +114,7 @@ void Context::setActiveFrame(const doc::frame_t frame)
|
|||||||
onSetActiveFrame(frame);
|
onSetActiveFrame(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::setRange(const DocRange& range)
|
void Context::setRange(const view::RealRange& range)
|
||||||
{
|
{
|
||||||
onSetRange(range);
|
onSetRange(range);
|
||||||
}
|
}
|
||||||
@ -299,7 +307,7 @@ void Context::onSetActiveFrame(const doc::frame_t frame)
|
|||||||
notifyActiveSiteChanged();
|
notifyActiveSiteChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::onSetRange(const DocRange& range)
|
void Context::onSetRange(const view::RealRange& range)
|
||||||
{
|
{
|
||||||
if (m_lastSelectedDoc)
|
if (m_lastSelectedDoc)
|
||||||
activeSiteHandler()->setRangeInDoc(m_lastSelectedDoc, range);
|
activeSiteHandler()->setRangeInDoc(m_lastSelectedDoc, range);
|
||||||
|
@ -22,6 +22,7 @@
|
|||||||
#include "obs/observable.h"
|
#include "obs/observable.h"
|
||||||
#include "obs/signal.h"
|
#include "obs/signal.h"
|
||||||
#include "os/surface.h"
|
#include "os/surface.h"
|
||||||
|
#include "view/range.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -31,10 +32,6 @@ namespace doc {
|
|||||||
class PalettePicks;
|
class PalettePicks;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace view {
|
|
||||||
class Range;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
class ActiveSiteHandler;
|
class ActiveSiteHandler;
|
||||||
class Clipboard;
|
class Clipboard;
|
||||||
@ -134,10 +131,11 @@ namespace app {
|
|||||||
|
|
||||||
Site activeSite() const;
|
Site activeSite() const;
|
||||||
Doc* activeDocument() const;
|
Doc* activeDocument() const;
|
||||||
|
const view::RealRange& range() const;
|
||||||
void setActiveDocument(Doc* document);
|
void setActiveDocument(Doc* document);
|
||||||
void setActiveLayer(doc::Layer* layer);
|
void setActiveLayer(doc::Layer* layer);
|
||||||
void setActiveFrame(doc::frame_t frame);
|
void setActiveFrame(doc::frame_t frame);
|
||||||
void setRange(const view::Range& range);
|
void setRange(const view::RealRange& range);
|
||||||
void setSelectedColors(const doc::PalettePicks& picks);
|
void setSelectedColors(const doc::PalettePicks& picks);
|
||||||
void setSelectedTiles(const doc::PalettePicks& picks);
|
void setSelectedTiles(const doc::PalettePicks& picks);
|
||||||
bool hasModifiedDocuments() const;
|
bool hasModifiedDocuments() const;
|
||||||
@ -170,7 +168,7 @@ namespace app {
|
|||||||
virtual void onSetActiveDocument(Doc* doc, bool notify);
|
virtual void onSetActiveDocument(Doc* doc, bool notify);
|
||||||
virtual void onSetActiveLayer(doc::Layer* layer);
|
virtual void onSetActiveLayer(doc::Layer* layer);
|
||||||
virtual void onSetActiveFrame(const doc::frame_t frame);
|
virtual void onSetActiveFrame(const doc::frame_t frame);
|
||||||
virtual void onSetRange(const view::Range& range);
|
virtual void onSetRange(const view::RealRange& range);
|
||||||
virtual void onSetSelectedColors(const doc::PalettePicks& picks);
|
virtual void onSetSelectedColors(const doc::PalettePicks& picks);
|
||||||
virtual void onSetSelectedTiles(const doc::PalettePicks& picks);
|
virtual void onSetSelectedTiles(const doc::PalettePicks& picks);
|
||||||
virtual void onCloseDocument(Doc* doc);
|
virtual void onCloseDocument(Doc* doc);
|
||||||
@ -188,6 +186,7 @@ namespace app {
|
|||||||
Doc* m_lastSelectedDoc;
|
Doc* m_lastSelectedDoc;
|
||||||
mutable std::unique_ptr<Preferences> m_preferences;
|
mutable std::unique_ptr<Preferences> m_preferences;
|
||||||
std::unique_ptr<DraggedData> m_draggedData = nullptr;
|
std::unique_ptr<DraggedData> m_draggedData = nullptr;
|
||||||
|
mutable view::RealRange m_range; // Last/current range
|
||||||
|
|
||||||
// Result of the execution of a command.
|
// Result of the execution of a command.
|
||||||
CommandResult m_result;
|
CommandResult m_result;
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
// Aseprite
|
// Aseprite
|
||||||
// Copyright (C) 2019-2022 Igara Studio S.A.
|
// Copyright (C) 2019-2023 Igara Studio S.A.
|
||||||
// Copyright (C) 2001-2018 David Capello
|
// Copyright (C) 2001-2018 David Capello
|
||||||
//
|
//
|
||||||
// This program is distributed under the terms of
|
// This program is distributed under the terms of
|
||||||
@ -65,13 +65,13 @@ Palette* Site::palette() const
|
|||||||
return (m_sprite ? m_sprite->palette(m_frame): nullptr);
|
return (m_sprite ? m_sprite->palette(m_frame): nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Site::range(const DocRange& range)
|
void Site::range(const view::RealRange& range)
|
||||||
{
|
{
|
||||||
m_range = range;
|
m_range = range;
|
||||||
switch (range.type()) {
|
switch (range.type()) {
|
||||||
case DocRange::kCels: m_focus = Site::InCels; break;
|
case view::Range::kCels: m_focus = Site::InCels; break;
|
||||||
case DocRange::kFrames: m_focus = Site::InFrames; break;
|
case view::Range::kFrames: m_focus = Site::InFrames; break;
|
||||||
case DocRange::kLayers: m_focus = Site::InLayers; break;
|
case view::Range::kLayers: m_focus = Site::InLayers; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@
|
|||||||
#define APP_SITE_H_INCLUDED
|
#define APP_SITE_H_INCLUDED
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "app/doc_range.h"
|
|
||||||
#include "app/tilemap_mode.h"
|
#include "app/tilemap_mode.h"
|
||||||
#include "app/tileset_mode.h"
|
#include "app/tileset_mode.h"
|
||||||
#include "doc/cel_list.h"
|
#include "doc/cel_list.h"
|
||||||
@ -17,6 +16,7 @@
|
|||||||
#include "doc/palette_picks.h"
|
#include "doc/palette_picks.h"
|
||||||
#include "doc/selected_objects.h"
|
#include "doc/selected_objects.h"
|
||||||
#include "gfx/fwd.h"
|
#include "gfx/fwd.h"
|
||||||
|
#include "view/range.h"
|
||||||
|
|
||||||
namespace doc {
|
namespace doc {
|
||||||
class Grid;
|
class Grid;
|
||||||
@ -70,14 +70,14 @@ namespace app {
|
|||||||
doc::Layer* layer() const { return m_layer; }
|
doc::Layer* layer() const { return m_layer; }
|
||||||
doc::frame_t frame() const { return m_frame; }
|
doc::frame_t frame() const { return m_frame; }
|
||||||
doc::Cel* cel() const;
|
doc::Cel* cel() const;
|
||||||
const DocRange& range() const { return m_range; }
|
const view::RealRange& range() const { return m_range; }
|
||||||
|
|
||||||
void focus(Focus focus) { m_focus = focus; }
|
void focus(Focus focus) { m_focus = focus; }
|
||||||
void document(Doc* document) { m_document = document; }
|
void document(Doc* document) { m_document = document; }
|
||||||
void sprite(doc::Sprite* sprite) { m_sprite = sprite; }
|
void sprite(doc::Sprite* sprite) { m_sprite = sprite; }
|
||||||
void layer(doc::Layer* layer) { m_layer = layer; }
|
void layer(doc::Layer* layer) { m_layer = layer; }
|
||||||
void frame(doc::frame_t frame) { m_frame = frame; }
|
void frame(doc::frame_t frame) { m_frame = frame; }
|
||||||
void range(const DocRange& range);
|
void range(const view::RealRange& range);
|
||||||
|
|
||||||
const doc::SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); }
|
const doc::SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); }
|
||||||
const doc::SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); }
|
const doc::SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); }
|
||||||
@ -132,7 +132,7 @@ namespace app {
|
|||||||
doc::Sprite* m_sprite;
|
doc::Sprite* m_sprite;
|
||||||
doc::Layer* m_layer;
|
doc::Layer* m_layer;
|
||||||
doc::frame_t m_frame;
|
doc::frame_t m_frame;
|
||||||
DocRange m_range;
|
view::RealRange m_range;
|
||||||
doc::PalettePicks m_selectedColors;
|
doc::PalettePicks m_selectedColors;
|
||||||
doc::PalettePicks m_selectedTiles;
|
doc::PalettePicks m_selectedTiles;
|
||||||
doc::SelectedObjects m_selectedSlices;
|
doc::SelectedObjects m_selectedSlices;
|
||||||
|
@ -19,6 +19,10 @@
|
|||||||
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
namespace view {
|
||||||
|
class Range;
|
||||||
|
}
|
||||||
|
|
||||||
namespace app {
|
namespace app {
|
||||||
|
|
||||||
// Wrapper to create a new transaction or get the current
|
// Wrapper to create a new transaction or get the current
|
||||||
@ -105,7 +109,7 @@ namespace app {
|
|||||||
m_transaction->commit();
|
m_transaction->commit();
|
||||||
}
|
}
|
||||||
|
|
||||||
void setNewDocRange(const DocRange& range) {
|
void setNewDocRange(const view::Range& range) {
|
||||||
m_transaction->setNewDocRange(range);
|
m_transaction->setNewDocRange(range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -441,8 +441,8 @@ void Editor::getSite(Site* site) const
|
|||||||
Timeline* timeline = App::instance()->timeline();
|
Timeline* timeline = App::instance()->timeline();
|
||||||
if (timeline &&
|
if (timeline &&
|
||||||
timeline->isVisible() &&
|
timeline->isVisible() &&
|
||||||
timeline->range().enabled()) {
|
timeline->isRangeEnabled()) {
|
||||||
site->range(timeline->range());
|
site->range(timeline->realRange());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_layer && m_layer->isTilemap()) {
|
if (m_layer && m_layer->isTilemap()) {
|
||||||
|
@ -49,7 +49,7 @@ MovingCelCollect::MovingCelCollect(Editor* editor, Layer* layer)
|
|||||||
m_mainCel = layer->cel(editor->frame());
|
m_mainCel = layer->cel(editor->frame());
|
||||||
|
|
||||||
Timeline* timeline = App::instance()->timeline();
|
Timeline* timeline = App::instance()->timeline();
|
||||||
DocRange range = timeline->range();
|
view::RealRange range = timeline->realRange();
|
||||||
if (!range.enabled() ||
|
if (!range.enabled() ||
|
||||||
!timeline->isVisible()) {
|
!timeline->isVisible()) {
|
||||||
range.startRange(editor->layer(), editor->frame(), DocRange::kCels);
|
range.startRange(editor->layer(), editor->frame(), DocRange::kCels);
|
||||||
|
@ -66,7 +66,7 @@ MovingSliceState::MovingSliceState(Editor* editor,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (editor->slicesTransforms() && !m_items.empty()) {
|
if (editor->slicesTransforms() && !m_items.empty()) {
|
||||||
DocRange range = m_site.range();
|
view::Range range = m_site.range();
|
||||||
SelectedLayers selectedLayers = range.selectedLayers();
|
SelectedLayers selectedLayers = range.selectedLayers();
|
||||||
// Do not take into account invisible layers.
|
// Do not take into account invisible layers.
|
||||||
for (auto it = selectedLayers.begin(); it != selectedLayers.end(); ++it) {
|
for (auto it = selectedLayers.begin(); it != selectedLayers.end(); ++it) {
|
||||||
|
@ -187,7 +187,7 @@ bool PixelsMovement::editMultipleCels() const
|
|||||||
return
|
return
|
||||||
(m_site.range().enabled() &&
|
(m_site.range().enabled() &&
|
||||||
(Preferences::instance().selection.multicelWhenLayersOrFrames() ||
|
(Preferences::instance().selection.multicelWhenLayersOrFrames() ||
|
||||||
m_site.range().type() == DocRange::kCels));
|
m_site.range().type() == view::Range::kCels));
|
||||||
}
|
}
|
||||||
|
|
||||||
void PixelsMovement::setDelegate(PixelsMovementDelegate* delegate)
|
void PixelsMovement::setDelegate(PixelsMovementDelegate* delegate)
|
||||||
|
@ -49,7 +49,6 @@
|
|||||||
#include "app/ui/main_window.h"
|
#include "app/ui/main_window.h"
|
||||||
#include "app/ui/skin/skin_theme.h"
|
#include "app/ui/skin/skin_theme.h"
|
||||||
#include "app/ui/status_bar.h"
|
#include "app/ui/status_bar.h"
|
||||||
#include "app/ui/timeline/timeline.h"
|
|
||||||
#include "app/ui_context.h"
|
#include "app/ui_context.h"
|
||||||
#include "app/util/layer_utils.h"
|
#include "app/util/layer_utils.h"
|
||||||
#include "app/util/new_image_from_mask.h"
|
#include "app/util/new_image_from_mask.h"
|
||||||
@ -165,7 +164,7 @@ bool StandbyState::onMouseDown(Editor* editor, MouseMessage* msg)
|
|||||||
editor->projection(),
|
editor->projection(),
|
||||||
ColorPicker::FromComposition);
|
ColorPicker::FromComposition);
|
||||||
|
|
||||||
auto range = App::instance()->timeline()->range();
|
const view::RealRange& range = context->range();
|
||||||
if (picker.layer() &&
|
if (picker.layer() &&
|
||||||
!range.contains(picker.layer())) {
|
!range.contains(picker.layer())) {
|
||||||
layer = picker.layer();
|
layer = picker.layer();
|
||||||
|
@ -1077,7 +1077,8 @@ void KeyboardShortcuts::disableAccel(const ui::Accelerator& accel,
|
|||||||
|
|
||||||
KeyContext KeyboardShortcuts::getCurrentKeyContext() const
|
KeyContext KeyboardShortcuts::getCurrentKeyContext() const
|
||||||
{
|
{
|
||||||
Doc* doc = UIContext::instance()->activeDocument();
|
auto ctx = UIContext::instance();
|
||||||
|
Doc* doc = ctx->activeDocument();
|
||||||
if (doc &&
|
if (doc &&
|
||||||
doc->isMaskVisible() &&
|
doc->isMaskVisible() &&
|
||||||
// The active key context will be the selectedTool() (in the
|
// The active key context will be the selectedTool() (in the
|
||||||
@ -1095,11 +1096,11 @@ KeyContext KeyboardShortcuts::getCurrentKeyContext() const
|
|||||||
return KeyContext::SelectionTool;
|
return KeyContext::SelectionTool;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto timeline = App::instance()->timeline();
|
const view::RealRange& range = ctx->range();
|
||||||
if (doc && timeline &&
|
if (doc &&
|
||||||
!timeline->selectedFrames().empty() &&
|
!range.selectedFrames().empty() &&
|
||||||
(timeline->range().type() == DocRange::kFrames ||
|
(range.type() == view::Range::kFrames ||
|
||||||
timeline->range().type() == DocRange::kCels)) {
|
range.type() == view::Range::kCels)) {
|
||||||
return KeyContext::FramesSelection;
|
return KeyContext::FramesSelection;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
#include "ui/ui.h"
|
#include "ui/ui.h"
|
||||||
#include "view/layers.h"
|
#include "view/layers.h"
|
||||||
#include "view/timeline_adapter.h"
|
#include "view/timeline_adapter.h"
|
||||||
|
#include "view/utils.h"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cstdio>
|
#include <cstdio>
|
||||||
@ -383,7 +384,9 @@ void Timeline::updateUsingEditor(Editor* editor)
|
|||||||
m_document = site.document();
|
m_document = site.document();
|
||||||
m_sprite = site.sprite();
|
m_sprite = site.sprite();
|
||||||
m_layer = site.layer();
|
m_layer = site.layer();
|
||||||
m_adapter = std::make_unique<view::FullSpriteTimelineAdapter>(m_sprite);
|
|
||||||
|
updateTimelineAdapter(false);
|
||||||
|
|
||||||
m_frame = m_adapter->toColFrame(fr_t(site.frame()));
|
m_frame = m_adapter->toColFrame(fr_t(site.frame()));
|
||||||
m_state = STATE_STANDBY;
|
m_state = STATE_STANDBY;
|
||||||
m_hot.part = PART_NOTHING;
|
m_hot.part = PART_NOTHING;
|
||||||
@ -532,6 +535,11 @@ void Timeline::setFrame(col_t frame, bool byUser)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
view::RealRange Timeline::realRange() const
|
||||||
|
{
|
||||||
|
return view::to_real_range(m_adapter.get(), m_range);
|
||||||
|
}
|
||||||
|
|
||||||
void Timeline::prepareToMoveRange()
|
void Timeline::prepareToMoveRange()
|
||||||
{
|
{
|
||||||
ASSERT(m_range.enabled());
|
ASSERT(m_range.enabled());
|
||||||
@ -554,7 +562,7 @@ void Timeline::prepareToMoveRange()
|
|||||||
m_moveRangeData.activeRelativeFrame = j;
|
m_moveRangeData.activeRelativeFrame = j;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::moveRange(const Range& range)
|
void Timeline::moveRange(const VirtualRange& range)
|
||||||
{
|
{
|
||||||
regenerateCols();
|
regenerateCols();
|
||||||
regenerateRows();
|
regenerateRows();
|
||||||
@ -587,12 +595,18 @@ void Timeline::moveRange(const Range& range)
|
|||||||
m_range = range;
|
m_range = range;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::setRange(const Range& range)
|
void Timeline::setVirtualRange(const VirtualRange& range)
|
||||||
{
|
{
|
||||||
m_range = range;
|
m_range = range;
|
||||||
invalidate();
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Timeline::setRealRange(const RealRange& range)
|
||||||
|
{
|
||||||
|
m_range = view::to_virtual_range(m_adapter.get(), range);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
void Timeline::activateClipboardRange()
|
void Timeline::activateClipboardRange()
|
||||||
{
|
{
|
||||||
m_clipboard_timer.start();
|
m_clipboard_timer.start();
|
||||||
@ -964,7 +978,7 @@ bool Timeline::onProcessMessage(Message* msg)
|
|||||||
|
|
||||||
if (m_range.layers() > 0) {
|
if (m_range.layers() > 0) {
|
||||||
layer_t layerFirst, layerLast;
|
layer_t layerFirst, layerLast;
|
||||||
if (selectedLayersBounds(selectedLayers(),
|
if (selectedLayersBounds(m_range.selectedLayers(),
|
||||||
&layerFirst, &layerLast)) {
|
&layerFirst, &layerLast)) {
|
||||||
layer_t layerIdx = m_clk.layer;
|
layer_t layerIdx = m_clk.layer;
|
||||||
layerIdx = std::clamp(layerIdx, layerFirst, layerLast);
|
layerIdx = std::clamp(layerIdx, layerFirst, layerLast);
|
||||||
@ -1331,6 +1345,8 @@ bool Timeline::onProcessMessage(Message* msg)
|
|||||||
}
|
}
|
||||||
|
|
||||||
case PART_TAG: {
|
case PART_TAG: {
|
||||||
|
m_resizeTagData.reset(); // Reset resize info
|
||||||
|
|
||||||
Tag* tag = m_clk.getTag();
|
Tag* tag = m_clk.getTag();
|
||||||
if (tag) {
|
if (tag) {
|
||||||
Params params;
|
Params params;
|
||||||
@ -1403,9 +1419,13 @@ bool Timeline::onProcessMessage(Message* msg)
|
|||||||
ContextWriter writer(m_context);
|
ContextWriter writer(m_context);
|
||||||
Tx tx(writer, Strings::commands_FrameTagProperties());
|
Tx tx(writer, Strings::commands_FrameTagProperties());
|
||||||
tx(new cmd::SetTagRange(
|
tx(new cmd::SetTagRange(
|
||||||
tag,
|
tag,
|
||||||
(m_state == STATE_RESIZING_TAG_LEFT ? m_resizeTagData.from: tag->fromFrame()),
|
(m_state == STATE_RESIZING_TAG_LEFT ?
|
||||||
(m_state == STATE_RESIZING_TAG_RIGHT ? m_resizeTagData.to: tag->toFrame())));
|
m_adapter->toRealFrame(m_resizeTagData.from) :
|
||||||
|
tag->fromFrame()),
|
||||||
|
(m_state == STATE_RESIZING_TAG_RIGHT ?
|
||||||
|
m_adapter->toRealFrame(m_resizeTagData.to) :
|
||||||
|
tag->toFrame())));
|
||||||
tx.commit();
|
tx.commit();
|
||||||
}
|
}
|
||||||
catch (const base::Exception& e) {
|
catch (const base::Exception& e) {
|
||||||
@ -1765,6 +1785,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
|
|
||||||
// Draw each visible layer.
|
// Draw each visible layer.
|
||||||
DrawCelData data;
|
DrawCelData data;
|
||||||
|
const view::fr_t realActiveFrame = m_adapter->toRealFrame(m_frame);
|
||||||
for (layer=lastLayer; layer>=firstLayer; --layer) {
|
for (layer=lastLayer; layer>=firstLayer; --layer) {
|
||||||
{
|
{
|
||||||
IntersectClip clip(g, getLayerHeadersBounds());
|
IntersectClip clip(g, getLayerHeadersBounds());
|
||||||
@ -1785,12 +1806,19 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO Draw the set of cels by "column blocks" (set of
|
||||||
|
// consecutive frame columns)
|
||||||
|
|
||||||
// Get the first CelIterator to be drawn (it is the first cel with cel->frame >= first_frame)
|
// Get the first CelIterator to be drawn (it is the first cel with cel->frame >= first_frame)
|
||||||
LayerImage* layerImagePtr = static_cast<LayerImage*>(layerPtr);
|
LayerImage* layerImagePtr = static_cast<LayerImage*>(layerPtr);
|
||||||
data.begin = layerImagePtr->getCelBegin();
|
data.begin = layerImagePtr->getCelBegin();
|
||||||
data.end = layerImagePtr->getCelEnd();
|
data.end = layerImagePtr->getCelEnd();
|
||||||
data.it = layerImagePtr->findFirstCelIteratorAfter(firstFrame-1);
|
|
||||||
if (firstFrame > 0 && data.it != data.begin)
|
const frame_t firstRealFrame(m_adapter->toRealFrame(firstFrame));
|
||||||
|
const frame_t lastRealFrame(m_adapter->toRealFrame(lastFrame));
|
||||||
|
data.it = layerImagePtr->findFirstCelIteratorAfter(firstRealFrame-1);
|
||||||
|
|
||||||
|
if (firstRealFrame > 0 && data.it != data.begin)
|
||||||
data.prevIt = data.it-1;
|
data.prevIt = data.it-1;
|
||||||
else
|
else
|
||||||
data.prevIt = data.end;
|
data.prevIt = data.end;
|
||||||
@ -1801,7 +1829,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
data.lastLink = data.end;
|
data.lastLink = data.end;
|
||||||
|
|
||||||
if (layerPtr == m_layer) {
|
if (layerPtr == m_layer) {
|
||||||
data.activeIt = layerImagePtr->findCelIterator(m_frame);
|
data.activeIt = layerImagePtr->findCelIterator(frame_t(realActiveFrame));
|
||||||
if (data.activeIt != data.end) {
|
if (data.activeIt != data.end) {
|
||||||
data.firstLink = data.activeIt;
|
data.firstLink = data.activeIt;
|
||||||
data.lastLink = data.activeIt;
|
data.lastLink = data.activeIt;
|
||||||
@ -1814,7 +1842,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
--it2;
|
--it2;
|
||||||
if ((*it2)->image()->id() == imageId) {
|
if ((*it2)->image()->id() == imageId) {
|
||||||
data.firstLink = it2;
|
data.firstLink = it2;
|
||||||
if ((*data.firstLink)->frame() < firstFrame)
|
if ((*it2)->frame() < firstRealFrame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
} while (it2 != data.begin);
|
} while (it2 != data.begin);
|
||||||
@ -1824,7 +1852,7 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
while (it2 != data.end) {
|
while (it2 != data.end) {
|
||||||
if ((*it2)->image()->id() == imageId) {
|
if ((*it2)->image()->id() == imageId) {
|
||||||
data.lastLink = it2;
|
data.lastLink = it2;
|
||||||
if ((*data.lastLink)->frame() > lastFrame)
|
if ((*it2)->frame() > lastRealFrame)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
++it2;
|
++it2;
|
||||||
@ -1836,9 +1864,10 @@ void Timeline::onPaint(ui::PaintEvent& ev)
|
|||||||
|
|
||||||
// Draw every visible cel for each layer.
|
// Draw every visible cel for each layer.
|
||||||
for (frame=firstFrame; frame<=lastFrame; frame=col_t(frame+1)) {
|
for (frame=firstFrame; frame<=lastFrame; frame=col_t(frame+1)) {
|
||||||
|
const view::fr_t realFrame = m_adapter->toRealFrame(frame);
|
||||||
Cel* cel =
|
Cel* cel =
|
||||||
(data.it != data.end &&
|
(data.it != data.end &&
|
||||||
(*data.it)->frame() == frame ? *data.it: nullptr);
|
(*data.it)->frame() == realFrame ? *data.it: nullptr);
|
||||||
|
|
||||||
drawCel(g, layer, frame, cel, &data);
|
drawCel(g, layer, frame, cel, &data);
|
||||||
|
|
||||||
@ -2026,10 +2055,15 @@ void Timeline::onAddTag(DocEvent& ev)
|
|||||||
updateScrollBars();
|
updateScrollBars();
|
||||||
layout();
|
layout();
|
||||||
}
|
}
|
||||||
|
invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::onRemoveTag(DocEvent& ev)
|
void Timeline::onRemoveTag(DocEvent& ev)
|
||||||
{
|
{
|
||||||
|
if (m_adapter && m_adapter->isViewingTag(ev.tag())) {
|
||||||
|
updateTimelineAdapter(true);
|
||||||
|
}
|
||||||
|
|
||||||
onAddTag(ev);
|
onAddTag(ev);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2167,6 +2201,26 @@ void Timeline::getDrawableFrames(col_t* firstFrame, col_t* lastFrame)
|
|||||||
*lastFrame = getFrameInXPos(viewScroll().x + availW);
|
*lastFrame = getFrameInXPos(viewScroll().x + availW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gets the range of columns used by the tag. Returns false if the tag
|
||||||
|
// is not visible (or is partially visible) with the current timeline
|
||||||
|
// adapter/view.
|
||||||
|
bool Timeline::getTagFrames(const doc::Tag* tag, col_t* fromFrame, col_t* toFrame) const
|
||||||
|
{
|
||||||
|
ASSERT(tag);
|
||||||
|
|
||||||
|
*fromFrame = m_adapter->toColFrame(fr_t(tag->fromFrame()));
|
||||||
|
*toFrame = m_adapter->toColFrame(fr_t(tag->toFrame()));
|
||||||
|
|
||||||
|
if (m_resizeTagData.tag == tag->id()) {
|
||||||
|
*fromFrame = m_resizeTagData.from;
|
||||||
|
*toFrame = m_resizeTagData.to;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO show partial tags
|
||||||
|
return (*fromFrame != kNoCol &&
|
||||||
|
*toFrame != kNoCol);
|
||||||
|
}
|
||||||
|
|
||||||
void Timeline::drawPart(ui::Graphics* g, const gfx::Rect& bounds,
|
void Timeline::drawPart(ui::Graphics* g, const gfx::Rect& bounds,
|
||||||
const std::string* text, ui::Style* style,
|
const std::string* text, ui::Style* style,
|
||||||
const bool is_active,
|
const bool is_active,
|
||||||
@ -2265,19 +2319,19 @@ void Timeline::drawHeader(ui::Graphics* g)
|
|||||||
NULL, styles.timelineBox(), false, false, false);
|
NULL, styles.timelineBox(), false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::drawHeaderFrame(ui::Graphics* g, col_t frame)
|
void Timeline::drawHeaderFrame(ui::Graphics* g, col_t col)
|
||||||
{
|
{
|
||||||
bool is_active = isFrameActive(frame);
|
bool is_active = isFrameActive(col);
|
||||||
bool is_hover = (m_hot.part == PART_HEADER_FRAME && m_hot.frame == frame);
|
bool is_hover = (m_hot.part == PART_HEADER_FRAME && m_hot.frame == col);
|
||||||
bool is_clicked = (m_clk.part == PART_HEADER_FRAME && m_clk.frame == frame);
|
bool is_clicked = (m_clk.part == PART_HEADER_FRAME && m_clk.frame == col);
|
||||||
gfx::Rect bounds = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), frame));
|
gfx::Rect bounds = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), col));
|
||||||
IntersectClip clip(g, bounds);
|
IntersectClip clip(g, bounds);
|
||||||
if (!clip)
|
if (!clip)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Draw the header for the layers.
|
// Draw the header for the layers.
|
||||||
const fr_t realFrame = m_adapter->toRealFrame(frame);
|
const fr_t frame = m_adapter->toRealFrame(col);
|
||||||
const int n = (docPref().timeline.firstFrame() + realFrame);
|
const int n = (docPref().timeline.firstFrame() + frame);
|
||||||
std::string text = base::convert_to<std::string, int>(n % 100);
|
std::string text = base::convert_to<std::string, int>(n % 100);
|
||||||
if (n >= 100 && (n % 100) < 10)
|
if (n >= 100 && (n % 100) < 10)
|
||||||
text.insert(0, 1, '0');
|
text.insert(0, 1, '0');
|
||||||
@ -2430,7 +2484,7 @@ void Timeline::drawLayer(ui::Graphics* g, const int layerIdx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::drawCel(ui::Graphics* g,
|
void Timeline::drawCel(ui::Graphics* g,
|
||||||
const layer_t layerIndex, const col_t frame,
|
const layer_t layerIndex, const col_t col,
|
||||||
const Cel* cel, const DrawCelData* data)
|
const Cel* cel, const DrawCelData* data)
|
||||||
{
|
{
|
||||||
auto& styles = skinTheme()->styles;
|
auto& styles = skinTheme()->styles;
|
||||||
@ -2438,18 +2492,20 @@ void Timeline::drawCel(ui::Graphics* g,
|
|||||||
Image* image = (cel ? cel->image(): nullptr);
|
Image* image = (cel ? cel->image(): nullptr);
|
||||||
bool is_hover = (m_hot.part == PART_CEL &&
|
bool is_hover = (m_hot.part == PART_CEL &&
|
||||||
m_hot.layer == layerIndex &&
|
m_hot.layer == layerIndex &&
|
||||||
m_hot.frame == frame);
|
m_hot.frame == col);
|
||||||
const bool is_active = isCelActive(layerIndex, frame);
|
const bool is_active = isCelActive(layerIndex, col);
|
||||||
const bool is_loosely_active = isCelLooselyActive(layerIndex, frame);
|
const bool is_loosely_active = isCelLooselyActive(layerIndex, col);
|
||||||
const bool is_empty = (image == nullptr);
|
const bool is_empty = (image == nullptr);
|
||||||
gfx::Rect bounds = getPartBounds(Hit(PART_CEL, layerIndex, frame));
|
gfx::Rect bounds = getPartBounds(Hit(PART_CEL, layerIndex, col));
|
||||||
gfx::Rect full_bounds = bounds;
|
gfx::Rect full_bounds = bounds;
|
||||||
IntersectClip clip(g, bounds);
|
IntersectClip clip(g, bounds);
|
||||||
if (!clip)
|
if (!clip)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const fr_t frame = m_adapter->toRealFrame(col);
|
||||||
|
|
||||||
// Draw background
|
// Draw background
|
||||||
if (layer == m_layer && frame == m_frame)
|
if (layer == m_layer && col == m_frame)
|
||||||
drawPart(g, bounds, nullptr,
|
drawPart(g, bounds, nullptr,
|
||||||
m_range.enabled() ? styles.timelineFocusedCel():
|
m_range.enabled() ? styles.timelineFocusedCel():
|
||||||
styles.timelineSelectedCel(), false, is_hover, true);
|
styles.timelineSelectedCel(), false, is_hover, true);
|
||||||
@ -2525,7 +2581,7 @@ void Timeline::drawCel(ui::Graphics* g,
|
|||||||
|
|
||||||
// Draw decorators to link the activeCel with its links.
|
// Draw decorators to link the activeCel with its links.
|
||||||
if (data && data->activeIt != data->end)
|
if (data && data->activeIt != data->end)
|
||||||
drawCelLinkDecorators(g, full_bounds, cel, frame, is_loosely_active, is_hover, data);
|
drawCelLinkDecorators(g, full_bounds, cel, col, is_loosely_active, is_hover, data);
|
||||||
|
|
||||||
// Draw 'z' if this cel has a custom z-index (non-zero)
|
// Draw 'z' if this cel has a custom z-index (non-zero)
|
||||||
if (cel && cel->zIndex() != 0) {
|
if (cel && cel->zIndex() != 0) {
|
||||||
@ -2617,7 +2673,7 @@ void Timeline::drawCelOverlay(ui::Graphics* g)
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
void Timeline::drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
||||||
const Cel* cel, const col_t frame,
|
const Cel* cel, const col_t col,
|
||||||
const bool is_active, const bool is_hover,
|
const bool is_active, const bool is_hover,
|
||||||
const DrawCelData* data)
|
const DrawCelData* data)
|
||||||
{
|
{
|
||||||
@ -2628,6 +2684,7 @@ void Timeline::drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
|||||||
ui::Style* style2 = nullptr;
|
ui::Style* style2 = nullptr;
|
||||||
|
|
||||||
// Links at the left or right side
|
// Links at the left or right side
|
||||||
|
fr_t frame = m_adapter->toRealFrame(col);
|
||||||
bool left = (data->firstLink != data->end ? frame > (*data->firstLink)->frame(): false);
|
bool left = (data->firstLink != data->end ? frame > (*data->firstLink)->frame(): false);
|
||||||
bool right = (data->lastLink != data->end ? frame < (*data->lastLink)->frame(): false);
|
bool right = (data->lastLink != data->end ? frame < (*data->lastLink)->frame(): false);
|
||||||
|
|
||||||
@ -2691,12 +2748,9 @@ void Timeline::drawTags(ui::Graphics* g)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
col_t fromFrame = m_adapter->toColFrame(fr_t(tag->fromFrame()));
|
col_t fromFrame, toFrame;
|
||||||
col_t toFrame = m_adapter->toColFrame(fr_t(tag->toFrame()));
|
if (!getTagFrames(tag, &fromFrame, &toFrame))
|
||||||
if (m_resizeTagData.tag == tag->id()) {
|
continue;
|
||||||
fromFrame = m_resizeTagData.from;
|
|
||||||
toFrame = m_resizeTagData.to;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), fromFrame));
|
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), fromFrame));
|
||||||
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), toFrame));
|
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), toFrame));
|
||||||
@ -2711,19 +2765,19 @@ void Timeline::drawTags(ui::Graphics* g)
|
|||||||
m_dropRange.type() == DocRange::kFrames) {
|
m_dropRange.type() == DocRange::kFrames) {
|
||||||
switch (m_dropTarget.hhit) {
|
switch (m_dropTarget.hhit) {
|
||||||
case DropTarget::Before:
|
case DropTarget::Before:
|
||||||
if (m_dropRange.firstFrame() == tag->fromFrame()) {
|
if (m_dropRange.firstFrame() == fromFrame) {
|
||||||
dx = +frameBoxWidth()/4;
|
dx = +frameBoxWidth()/4;
|
||||||
dw = -frameBoxWidth()/4;
|
dw = -frameBoxWidth()/4;
|
||||||
}
|
}
|
||||||
else if (m_dropRange.firstFrame()-1 == tag->toFrame()) {
|
else if (m_dropRange.firstFrame()-1 == toFrame) {
|
||||||
dw = -frameBoxWidth()/4;
|
dw = -frameBoxWidth()/4;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case DropTarget::After:
|
case DropTarget::After:
|
||||||
if (m_dropRange.lastFrame() == tag->toFrame()) {
|
if (m_dropRange.lastFrame() == toFrame) {
|
||||||
dw = -frameBoxWidth()/4;
|
dw = -frameBoxWidth()/4;
|
||||||
}
|
}
|
||||||
else if (m_dropRange.lastFrame()+1 == tag->fromFrame()) {
|
else if (m_dropRange.lastFrame()+1 == fromFrame) {
|
||||||
dx = +frameBoxWidth()/4;
|
dx = +frameBoxWidth()/4;
|
||||||
dw = -frameBoxWidth()/4;
|
dw = -frameBoxWidth()/4;
|
||||||
}
|
}
|
||||||
@ -2839,10 +2893,9 @@ void Timeline::drawRangeOutline(ui::Graphics* g)
|
|||||||
theme()->paintWidgetPart(
|
theme()->paintWidgetPart(
|
||||||
g, styles.timelineRangeOutline(), bounds, info);
|
g, styles.timelineRangeOutline(), bounds, info);
|
||||||
|
|
||||||
Range drop = m_dropRange;
|
gfx::Rect dropBounds = getRangeBounds(m_dropRange);
|
||||||
gfx::Rect dropBounds = getRangeBounds(drop);
|
|
||||||
|
|
||||||
switch (drop.type()) {
|
switch (m_dropRange.type()) {
|
||||||
|
|
||||||
case Range::kCels: {
|
case Range::kCels: {
|
||||||
dropBounds = dropBounds.enlarge(outlineWidth());
|
dropBounds = dropBounds.enlarge(outlineWidth());
|
||||||
@ -2857,7 +2910,7 @@ void Timeline::drawRangeOutline(ui::Graphics* g)
|
|||||||
|
|
||||||
if (m_dropTarget.hhit == DropTarget::Before)
|
if (m_dropTarget.hhit == DropTarget::Before)
|
||||||
dropBounds.x -= w/2;
|
dropBounds.x -= w/2;
|
||||||
else if (drop == m_range)
|
else if (m_dropRange == m_range)
|
||||||
dropBounds.x = dropBounds.x + getRangeBounds(m_range).w - w/2;
|
dropBounds.x = dropBounds.x + getRangeBounds(m_range).w - w/2;
|
||||||
else
|
else
|
||||||
dropBounds.x = dropBounds.x + dropBounds.w - w/2;
|
dropBounds.x = dropBounds.x + dropBounds.w - w/2;
|
||||||
@ -2875,7 +2928,7 @@ void Timeline::drawRangeOutline(ui::Graphics* g)
|
|||||||
|
|
||||||
if (m_dropTarget.vhit == DropTarget::Top)
|
if (m_dropTarget.vhit == DropTarget::Top)
|
||||||
dropBounds.y -= h/2;
|
dropBounds.y -= h/2;
|
||||||
else if (drop == m_range)
|
else if (m_dropRange == m_range)
|
||||||
dropBounds.y = dropBounds.y + getRangeBounds(m_range).h - h/2;
|
dropBounds.y = dropBounds.y + getRangeBounds(m_range).h - h/2;
|
||||||
else
|
else
|
||||||
dropBounds.y = dropBounds.y + dropBounds.h - h/2;
|
dropBounds.y = dropBounds.y + dropBounds.h - h/2;
|
||||||
@ -3087,12 +3140,8 @@ gfx::Rect Timeline::getPartBounds(const Hit& hit) const
|
|||||||
case PART_TAG: {
|
case PART_TAG: {
|
||||||
Tag* tag = hit.getTag();
|
Tag* tag = hit.getTag();
|
||||||
if (tag) {
|
if (tag) {
|
||||||
col_t fromFrame = m_adapter->toColFrame(fr_t(tag->fromFrame()));
|
col_t fromFrame, toFrame;
|
||||||
col_t toFrame = m_adapter->toColFrame(fr_t(tag->toFrame()));
|
getTagFrames(tag, &fromFrame, &toFrame);
|
||||||
if (m_resizeTagData.tag == tag->id()) {
|
|
||||||
fromFrame = m_resizeTagData.from;
|
|
||||||
toFrame = m_resizeTagData.to;
|
|
||||||
}
|
|
||||||
|
|
||||||
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), fromFrame));
|
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), fromFrame));
|
||||||
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), toFrame));
|
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), toFrame));
|
||||||
@ -3326,28 +3375,30 @@ void Timeline::regenerateTagBands()
|
|||||||
const bool oldEmptyTagBand = m_tagBand.empty();
|
const bool oldEmptyTagBand = m_tagBand.empty();
|
||||||
|
|
||||||
// TODO improve this implementation
|
// TODO improve this implementation
|
||||||
std::vector<unsigned char> tagsPerFrame(m_sprite->totalFrames(), 0);
|
std::vector<unsigned char> tagsPerFrame(m_adapter->totalFrames(), 0);
|
||||||
std::vector<Tag*> bands(4, nullptr);
|
std::vector<Tag*> bands(4, nullptr);
|
||||||
m_tagBand.clear();
|
m_tagBand.clear();
|
||||||
for (Tag* tag : m_sprite->tags()) {
|
for (Tag* tag : m_sprite->tags()) {
|
||||||
frame_t f = tag->fromFrame();
|
col_t fromFrame, toFrame;
|
||||||
|
if (!getTagFrames(tag, &fromFrame, &toFrame))
|
||||||
|
continue; // Ignore tags that are not inside the timeline adapter/view
|
||||||
|
|
||||||
int b=0;
|
int b=0;
|
||||||
for (; b<int(bands.size()); ++b) {
|
for (; b<int(bands.size()); ++b) {
|
||||||
if (!bands[b] ||
|
if (!bands[b] ||
|
||||||
tag->fromFrame() > calcTagVisibleToFrame(bands[b])) {
|
fromFrame > calcTagVisibleToFrame(bands[b])) {
|
||||||
bands[b] = tag;
|
bands[b] = tag;
|
||||||
m_tagBand[tag] = b;
|
m_tagBand[tag] = b;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (b == int(bands.size()))
|
if (b == int(bands.size()))
|
||||||
m_tagBand[tag] = tagsPerFrame[f];
|
m_tagBand[tag] = tagsPerFrame[fromFrame];
|
||||||
|
|
||||||
frame_t toFrame = calcTagVisibleToFrame(tag);
|
toFrame = calcTagVisibleToFrame(tag);
|
||||||
if (toFrame >= frame_t(tagsPerFrame.size()))
|
if (toFrame >= col_t(tagsPerFrame.size()))
|
||||||
tagsPerFrame.resize(toFrame+1, 0);
|
tagsPerFrame.resize(toFrame+1, 0);
|
||||||
for (; f<=toFrame; ++f) {
|
for (col_t f=fromFrame; f<=toFrame; f=col_t(f+1)) {
|
||||||
ASSERT(f < frame_t(tagsPerFrame.size()));
|
ASSERT(f < frame_t(tagsPerFrame.size()));
|
||||||
if (tagsPerFrame[f] < 255)
|
if (tagsPerFrame[f] < 255)
|
||||||
++tagsPerFrame[f];
|
++tagsPerFrame[f];
|
||||||
@ -3478,6 +3529,10 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
|||||||
// Mouse in frame tags
|
// Mouse in frame tags
|
||||||
if (hit.part == PART_NOTHING) {
|
if (hit.part == PART_NOTHING) {
|
||||||
for (Tag* tag : m_sprite->tags()) {
|
for (Tag* tag : m_sprite->tags()) {
|
||||||
|
col_t fromFrame, toFrame;
|
||||||
|
if (!getTagFrames(tag, &fromFrame, &toFrame))
|
||||||
|
continue;
|
||||||
|
|
||||||
const int band = m_tagBand[tag];
|
const int band = m_tagBand[tag];
|
||||||
|
|
||||||
// Skip unfocused bands
|
// Skip unfocused bands
|
||||||
@ -3495,8 +3550,8 @@ Timeline::Hit Timeline::hitTest(ui::Message* msg, const gfx::Point& mousePos)
|
|||||||
}
|
}
|
||||||
// Check if we are in the left/right handles to resize the tag
|
// Check if we are in the left/right handles to resize the tag
|
||||||
else {
|
else {
|
||||||
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), m_adapter->toColFrame(fr_t(tag->fromFrame()))));
|
gfx::Rect bounds1 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), fromFrame));
|
||||||
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), m_adapter->toColFrame(fr_t(tag->toFrame()))));
|
gfx::Rect bounds2 = getPartBounds(Hit(PART_HEADER_FRAME, firstLayer(), toFrame));
|
||||||
gfx::Rect bounds = bounds1.createUnion(bounds2);
|
gfx::Rect bounds = bounds1.createUnion(bounds2);
|
||||||
bounds.h = bounds.y2() - tagBounds.y2();
|
bounds.h = bounds.y2() - tagBounds.y2();
|
||||||
bounds.y = tagBounds.y2();
|
bounds.y = tagBounds.y2();
|
||||||
@ -3822,15 +3877,16 @@ void Timeline::updateStatusBar(ui::Message* msg)
|
|||||||
sb->showDefaultText();
|
sb->showDefaultText();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Timeline::updateStatusBarForFrame(const col_t frame,
|
void Timeline::updateStatusBarForFrame(const col_t col,
|
||||||
const Tag* tag,
|
const Tag* tag,
|
||||||
const Cel* cel)
|
const Cel* cel)
|
||||||
{
|
{
|
||||||
if (!m_sprite)
|
if (!m_sprite || !m_adapter)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::string buf;
|
std::string buf;
|
||||||
frame_t base = docPref().timeline.firstFrame();
|
frame_t base = docPref().timeline.firstFrame();
|
||||||
|
const fr_t frame = m_adapter->toRealFrame(col);
|
||||||
frame_t firstFrame = frame;
|
frame_t firstFrame = frame;
|
||||||
frame_t lastFrame = frame;
|
frame_t lastFrame = frame;
|
||||||
|
|
||||||
@ -3840,8 +3896,8 @@ void Timeline::updateStatusBarForFrame(const col_t frame,
|
|||||||
}
|
}
|
||||||
else if (m_range.enabled() &&
|
else if (m_range.enabled() &&
|
||||||
m_range.frames() > 1) {
|
m_range.frames() > 1) {
|
||||||
firstFrame = m_range.firstFrame();
|
firstFrame = m_adapter->toRealFrame(col_t(m_range.firstFrame()));
|
||||||
lastFrame = m_range.lastFrame();
|
lastFrame = m_adapter->toRealFrame(col_t(m_range.lastFrame()));
|
||||||
}
|
}
|
||||||
|
|
||||||
buf += fmt::format(":frame: {}",
|
buf += fmt::format(":frame: {}",
|
||||||
@ -3853,7 +3909,7 @@ void Timeline::updateStatusBarForFrame(const col_t frame,
|
|||||||
}
|
}
|
||||||
|
|
||||||
buf += fmt::format(" :clock: {}",
|
buf += fmt::format(" :clock: {}",
|
||||||
human_readable_time(m_adapter->frameDuration(frame)));
|
human_readable_time(m_adapter->frameDuration(col)));
|
||||||
if (firstFrame != lastFrame) {
|
if (firstFrame != lastFrame) {
|
||||||
buf += fmt::format(" [{}]",
|
buf += fmt::format(" [{}]",
|
||||||
tag ?
|
tag ?
|
||||||
@ -4092,9 +4148,9 @@ bool Timeline::isCelLooselyActive(const layer_t layerIdx, const col_t frame) con
|
|||||||
void Timeline::dropRange(DropOp op)
|
void Timeline::dropRange(DropOp op)
|
||||||
{
|
{
|
||||||
bool copy = (op == Timeline::kCopy);
|
bool copy = (op == Timeline::kCopy);
|
||||||
Range newFromRange;
|
VirtualRange newFromRange;
|
||||||
DocRangePlace place = kDocRangeAfter;
|
DocRangePlace place = kDocRangeAfter;
|
||||||
Range dropRange = m_dropRange;
|
RealRange dropRange = view::to_real_range(m_adapter.get(), m_dropRange);
|
||||||
bool outside = m_dropTarget.outside;
|
bool outside = m_dropTarget.outside;
|
||||||
|
|
||||||
switch (m_range.type()) {
|
switch (m_range.type()) {
|
||||||
@ -4130,13 +4186,14 @@ void Timeline::dropRange(DropOp op)
|
|||||||
try {
|
try {
|
||||||
TagsHandling tagsHandling = (outside ? kFitOutsideTags:
|
TagsHandling tagsHandling = (outside ? kFitOutsideTags:
|
||||||
kFitInsideTags);
|
kFitInsideTags);
|
||||||
|
const RealRange realRange = this->realRange();
|
||||||
|
|
||||||
invalidateRange();
|
invalidateRange();
|
||||||
if (copy)
|
if (copy)
|
||||||
newFromRange = copy_range(m_document, m_range, dropRange,
|
newFromRange = copy_range(m_document, realRange, dropRange,
|
||||||
place, tagsHandling);
|
place, tagsHandling);
|
||||||
else
|
else
|
||||||
newFromRange = move_range(m_document, m_range, dropRange,
|
newFromRange = move_range(m_document, realRange, dropRange,
|
||||||
place, tagsHandling);
|
place, tagsHandling);
|
||||||
|
|
||||||
// If we drop a cel in the same frame (but in another layer),
|
// If we drop a cel in the same frame (but in another layer),
|
||||||
@ -4144,6 +4201,7 @@ void Timeline::dropRange(DropOp op)
|
|||||||
// all views.
|
// all views.
|
||||||
m_document->notifyGeneralUpdate();
|
m_document->notifyGeneralUpdate();
|
||||||
|
|
||||||
|
newFromRange = view::to_virtual_range(m_adapter.get(), newFromRange);
|
||||||
moveRange(newFromRange);
|
moveRange(newFromRange);
|
||||||
|
|
||||||
invalidateRange();
|
invalidateRange();
|
||||||
@ -4375,8 +4433,8 @@ double Timeline::zoom() const
|
|||||||
return 1.0;
|
return 1.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the last frame where the frame tag (or frame tag label)
|
// Returns the last frame where the frame tag (or tag label) is
|
||||||
// is visible in the timeline.
|
// visible in the timeline.
|
||||||
view::col_t Timeline::calcTagVisibleToFrame(Tag* tag) const
|
view::col_t Timeline::calcTagVisibleToFrame(Tag* tag) const
|
||||||
{
|
{
|
||||||
col_t frame =
|
col_t frame =
|
||||||
@ -4793,6 +4851,13 @@ void Timeline::setSeparatorX(int newValue)
|
|||||||
m_separator_x = std::max(0, newValue) / guiscale();
|
m_separator_x = std::max(0, newValue) / guiscale();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Re-constructs the timeline adapter
|
||||||
|
void Timeline::updateTimelineAdapter(bool allTags)
|
||||||
|
{
|
||||||
|
m_adapter = std::make_unique<view::FullSpriteTimelineAdapter>(m_sprite);
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
//static
|
//static
|
||||||
gfx::Color Timeline::highlightColor(const gfx::Color color)
|
gfx::Color Timeline::highlightColor(const gfx::Color color)
|
||||||
{
|
{
|
||||||
|
@ -10,7 +10,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "app/doc_observer.h"
|
#include "app/doc_observer.h"
|
||||||
#include "app/doc_range.h"
|
|
||||||
#include "app/docs_observer.h"
|
#include "app/docs_observer.h"
|
||||||
#include "app/loop_tag.h"
|
#include "app/loop_tag.h"
|
||||||
#include "app/pref/preferences.h"
|
#include "app/pref/preferences.h"
|
||||||
@ -32,6 +31,7 @@
|
|||||||
#include "ui/scroll_bar.h"
|
#include "ui/scroll_bar.h"
|
||||||
#include "ui/timer.h"
|
#include "ui/timer.h"
|
||||||
#include "ui/widget.h"
|
#include "ui/widget.h"
|
||||||
|
#include "view/range.h"
|
||||||
#include "view/timeline_adapter.h"
|
#include "view/timeline_adapter.h"
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
@ -74,7 +74,9 @@ namespace app {
|
|||||||
public InputChainElement,
|
public InputChainElement,
|
||||||
public TagProvider {
|
public TagProvider {
|
||||||
public:
|
public:
|
||||||
using Range = DocRange;
|
using Range = view::Range;
|
||||||
|
using RealRange = view::RealRange;
|
||||||
|
using VirtualRange = view::VirtualRange;
|
||||||
using fr_t = view::fr_t;
|
using fr_t = view::fr_t;
|
||||||
using col_t = view::col_t;
|
using col_t = view::col_t;
|
||||||
static constexpr const auto kNoCol = view::kNoCol;
|
static constexpr const auto kNoCol = view::kNoCol;
|
||||||
@ -117,13 +119,16 @@ namespace app {
|
|||||||
// The range is specified in "virtual frames" (not real sprite
|
// The range is specified in "virtual frames" (not real sprite
|
||||||
// frames, we'll have to do a conversion each time we want to use
|
// frames, we'll have to do a conversion each time we want to use
|
||||||
// this range with the sprite).
|
// this range with the sprite).
|
||||||
Range range() const { return m_range; }
|
VirtualRange virtualRange() const { return m_range; }
|
||||||
const SelectedLayers& selectedLayers() const { return m_range.selectedLayers(); }
|
bool isRangeEnabled() const { return m_range.enabled(); }
|
||||||
const SelectedFrames& selectedFrames() const { return m_range.selectedFrames(); }
|
|
||||||
|
// Returns the range in "real sprite frames."
|
||||||
|
RealRange realRange() const;
|
||||||
|
|
||||||
void prepareToMoveRange();
|
void prepareToMoveRange();
|
||||||
void moveRange(const Range& range);
|
void moveRange(const VirtualRange& range);
|
||||||
void setRange(const Range& range);
|
void setVirtualRange(const VirtualRange& range);
|
||||||
|
void setRealRange(const RealRange& range);
|
||||||
|
|
||||||
void activateClipboardRange();
|
void activateClipboardRange();
|
||||||
|
|
||||||
@ -294,6 +299,7 @@ namespace app {
|
|||||||
void setCursor(ui::Message* msg, const Hit& hit);
|
void setCursor(ui::Message* msg, const Hit& hit);
|
||||||
void getDrawableLayers(layer_t* firstLayer, layer_t* lastLayer);
|
void getDrawableLayers(layer_t* firstLayer, layer_t* lastLayer);
|
||||||
void getDrawableFrames(col_t* firstFrame, col_t* lastFrame);
|
void getDrawableFrames(col_t* firstFrame, col_t* lastFrame);
|
||||||
|
bool getTagFrames(const doc::Tag* tag, col_t* fromFrame, col_t* toFrame) const;
|
||||||
void drawPart(ui::Graphics* g, const gfx::Rect& bounds,
|
void drawPart(ui::Graphics* g, const gfx::Rect& bounds,
|
||||||
const std::string* text,
|
const std::string* text,
|
||||||
ui::Style* style,
|
ui::Style* style,
|
||||||
@ -303,13 +309,13 @@ namespace app {
|
|||||||
const bool is_disabled = false);
|
const bool is_disabled = false);
|
||||||
void drawTop(ui::Graphics* g);
|
void drawTop(ui::Graphics* g);
|
||||||
void drawHeader(ui::Graphics* g);
|
void drawHeader(ui::Graphics* g);
|
||||||
void drawHeaderFrame(ui::Graphics* g, const col_t frame);
|
void drawHeaderFrame(ui::Graphics* g, const col_t col);
|
||||||
void drawLayer(ui::Graphics* g, const layer_t layerIdx);
|
void drawLayer(ui::Graphics* g, const layer_t layerIdx);
|
||||||
void drawCel(ui::Graphics* g,
|
void drawCel(ui::Graphics* g,
|
||||||
const layer_t layerIdx, const col_t frame,
|
const layer_t layerIdx, const col_t col,
|
||||||
const Cel* cel, const DrawCelData* data);
|
const Cel* cel, const DrawCelData* data);
|
||||||
void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
|
||||||
const Cel* cel, const col_t frame,
|
const Cel* cel, const col_t col,
|
||||||
const bool is_active, const bool is_hover,
|
const bool is_active, const bool is_hover,
|
||||||
const DrawCelData* data);
|
const DrawCelData* data);
|
||||||
void drawTags(ui::Graphics* g);
|
void drawTags(ui::Graphics* g);
|
||||||
@ -409,6 +415,7 @@ namespace app {
|
|||||||
|
|
||||||
int separatorX() const;
|
int separatorX() const;
|
||||||
void setSeparatorX(int newValue);
|
void setSeparatorX(int newValue);
|
||||||
|
void updateTimelineAdapter(bool allTags);
|
||||||
|
|
||||||
static gfx::Color highlightColor(const gfx::Color color);
|
static gfx::Color highlightColor(const gfx::Color color);
|
||||||
|
|
||||||
@ -424,9 +431,9 @@ namespace app {
|
|||||||
Layer* m_layer;
|
Layer* m_layer;
|
||||||
col_t m_frame;
|
col_t m_frame;
|
||||||
int m_rangeLocks;
|
int m_rangeLocks;
|
||||||
Range m_range;
|
VirtualRange m_range;
|
||||||
Range m_startRange;
|
VirtualRange m_startRange;
|
||||||
Range m_dropRange;
|
VirtualRange m_dropRange;
|
||||||
State m_state;
|
State m_state;
|
||||||
|
|
||||||
// Version of the sprite before executing a command. Used to check
|
// Version of the sprite before executing a command. Used to check
|
||||||
@ -493,13 +500,13 @@ namespace app {
|
|||||||
}
|
}
|
||||||
void reset(const view::TimelineAdapter& adapter,
|
void reset(const view::TimelineAdapter& adapter,
|
||||||
const doc::ObjectId tagId) {
|
const doc::ObjectId tagId) {
|
||||||
if (auto tag = doc::get<doc::Tag>(tagId)) {
|
if (auto t = doc::get<doc::Tag>(tagId)) {
|
||||||
this->tag = tagId;
|
tag = tagId;
|
||||||
this->from = adapter.toColFrame(fr_t(tag->fromFrame()));
|
from = adapter.toColFrame(fr_t(t->fromFrame()));
|
||||||
this->to = adapter.toColFrame(fr_t(tag->toFrame()));
|
to = adapter.toColFrame(fr_t(t->toFrame()));
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
this->tag = doc::NullId;
|
tag = doc::NullId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} m_resizeTagData;
|
} m_resizeTagData;
|
||||||
|
@ -176,13 +176,13 @@ void UIContext::onSetActiveFrame(const doc::frame_t frame)
|
|||||||
Context::onSetActiveFrame(frame);
|
Context::onSetActiveFrame(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
void UIContext::onSetRange(const DocRange& range)
|
void UIContext::onSetRange(const view::RealRange& range)
|
||||||
{
|
{
|
||||||
Timeline* timeline =
|
Timeline* timeline =
|
||||||
(App::instance()->mainWindow() ?
|
(App::instance()->mainWindow() ?
|
||||||
App::instance()->mainWindow()->getTimeline(): nullptr);
|
App::instance()->mainWindow()->getTimeline(): nullptr);
|
||||||
if (timeline) {
|
if (timeline) {
|
||||||
timeline->setRange(range);
|
timeline->setRealRange(range);
|
||||||
}
|
}
|
||||||
else if (!isUIAvailable()) {
|
else if (!isUIAvailable()) {
|
||||||
Context::onSetRange(range);
|
Context::onSetRange(range);
|
||||||
@ -353,8 +353,8 @@ void UIContext::onGetActiveSite(Site* site) const
|
|||||||
Timeline* timeline = App::instance()->timeline();
|
Timeline* timeline = App::instance()->timeline();
|
||||||
if (timeline &&
|
if (timeline &&
|
||||||
timeline->isVisible() &&
|
timeline->isVisible() &&
|
||||||
timeline->range().enabled()) {
|
timeline->isRangeEnabled()) {
|
||||||
site->range(timeline->range());
|
site->range(timeline->realRange());
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
ColorBar* colorBar = ColorBar::instance();
|
ColorBar* colorBar = ColorBar::instance();
|
||||||
|
@ -4,7 +4,8 @@
|
|||||||
add_library(view-lib
|
add_library(view-lib
|
||||||
cels.cpp
|
cels.cpp
|
||||||
layers.cpp
|
layers.cpp
|
||||||
range.cpp)
|
range.cpp
|
||||||
|
utils.cpp)
|
||||||
|
|
||||||
target_link_libraries(view-lib
|
target_link_libraries(view-lib
|
||||||
doc-lib
|
doc-lib
|
||||||
|
@ -285,29 +285,37 @@ void Range::setType(const Type type)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Range::setSelectedLayers(const SelectedLayers& layers)
|
void Range::setSelectedLayers(const SelectedLayers& layers,
|
||||||
|
const bool touchFlags)
|
||||||
{
|
{
|
||||||
if (layers.empty()) {
|
if (layers.empty()) {
|
||||||
m_type = kNone;
|
if (touchFlags)
|
||||||
|
m_type = kNone;
|
||||||
m_selectedLayers.clear();
|
m_selectedLayers.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_type = kLayers;
|
if (touchFlags) {
|
||||||
m_flags |= kLayers;
|
m_type = kLayers;
|
||||||
|
m_flags |= kLayers;
|
||||||
|
}
|
||||||
m_selectedLayers = layers;
|
m_selectedLayers = layers;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Range::setSelectedFrames(const SelectedFrames& frames)
|
void Range::setSelectedFrames(const SelectedFrames& frames,
|
||||||
|
const bool touchFlags)
|
||||||
{
|
{
|
||||||
if (frames.empty()) {
|
if (frames.empty()) {
|
||||||
m_type = kNone;
|
if (touchFlags)
|
||||||
|
m_type = kNone;
|
||||||
m_selectedFrames.clear();
|
m_selectedFrames.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
m_type = kFrames;
|
if (touchFlags) {
|
||||||
m_flags |= kFrames;
|
m_type = kFrames;
|
||||||
|
m_flags |= kFrames;
|
||||||
|
}
|
||||||
m_selectedFrames = frames;
|
m_selectedFrames = frames;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,8 +43,10 @@ namespace view {
|
|||||||
const doc::SelectedFrames& selectedFrames() const { return m_selectedFrames; }
|
const doc::SelectedFrames& selectedFrames() const { return m_selectedFrames; }
|
||||||
|
|
||||||
void setType(const Type type);
|
void setType(const Type type);
|
||||||
void setSelectedLayers(const doc::SelectedLayers& layers);
|
void setSelectedLayers(const doc::SelectedLayers& layers,
|
||||||
void setSelectedFrames(const doc::SelectedFrames& frames);
|
const bool touchFlags = true);
|
||||||
|
void setSelectedFrames(const doc::SelectedFrames& frames,
|
||||||
|
const bool touchFlags = true);
|
||||||
|
|
||||||
void displace(const doc::layer_t layerDelta,
|
void displace(const doc::layer_t layerDelta,
|
||||||
const doc::frame_t frameDelta);
|
const doc::frame_t frameDelta);
|
||||||
@ -95,6 +97,13 @@ namespace view {
|
|||||||
doc::frame_t m_selectingFromFrame;
|
doc::frame_t m_selectingFromFrame;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TODO We should make these types strongly-typed and not just aliases.
|
||||||
|
// E.g.
|
||||||
|
// using VirtualRange = RangeT<col_t>;
|
||||||
|
// using RealRange = RangeT<fr_t>;
|
||||||
|
using VirtualRange = Range;
|
||||||
|
using RealRange = Range;
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
|
|
||||||
#endif // VIEW_RANGE_H_INCLUDED
|
#endif // VIEW_RANGE_H_INCLUDED
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "doc/sprite.h"
|
#include "doc/sprite.h"
|
||||||
|
#include "doc/tag.h"
|
||||||
#include "view/frames.h"
|
#include "view/frames.h"
|
||||||
|
|
||||||
namespace view {
|
namespace view {
|
||||||
@ -35,6 +36,12 @@ public:
|
|||||||
// frame of the sprite. Returns -1 if the frame is not visible given
|
// frame of the sprite. Returns -1 if the frame is not visible given
|
||||||
// the current timeline filters/hidden frames.
|
// the current timeline filters/hidden frames.
|
||||||
virtual col_t toColFrame(fr_t frame) const = 0;
|
virtual col_t toColFrame(fr_t frame) const = 0;
|
||||||
|
|
||||||
|
// Returns true if this tag is being used for this specific timeline
|
||||||
|
// adapter/view. E.g. if a tag is going to be removed and this
|
||||||
|
// timeline view is using that tag, we must switch to other kind of
|
||||||
|
// timeline view that doesn't use this tag anymore.
|
||||||
|
virtual bool isViewingTag(doc::Tag* tag) const = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Represents the default timeline view where the whole sprite is
|
// Represents the default timeline view where the whole sprite is
|
||||||
@ -46,10 +53,38 @@ public:
|
|||||||
int frameDuration(col_t frame) const override { return m_sprite->frameDuration(frame); }
|
int frameDuration(col_t frame) const override { return m_sprite->frameDuration(frame); }
|
||||||
fr_t toRealFrame(col_t frame) const override { return fr_t(frame); }
|
fr_t toRealFrame(col_t frame) const override { return fr_t(frame); }
|
||||||
col_t toColFrame(fr_t frame) const override { return col_t(frame); }
|
col_t toColFrame(fr_t frame) const override { return col_t(frame); }
|
||||||
|
bool isViewingTag(doc::Tag*) const override { return false; }
|
||||||
private:
|
private:
|
||||||
doc::Sprite* m_sprite = nullptr;
|
doc::Sprite* m_sprite = nullptr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Represents an alternative timeline to show only the specified tag.
|
||||||
|
class ShowTagTimelineAdapter : public TimelineAdapter {
|
||||||
|
public:
|
||||||
|
ShowTagTimelineAdapter(doc::Sprite* sprite,
|
||||||
|
doc::Tag* tag) : m_sprite(sprite)
|
||||||
|
, m_tag(tag) { }
|
||||||
|
col_t totalFrames() const override { return col_t(m_tag->frames()); }
|
||||||
|
int frameDuration(col_t frame) const override {
|
||||||
|
return m_sprite->frameDuration(doc::frame_t(toRealFrame(frame)));
|
||||||
|
}
|
||||||
|
fr_t toRealFrame(col_t frame) const override {
|
||||||
|
return fr_t(frame + m_tag->fromFrame());
|
||||||
|
}
|
||||||
|
col_t toColFrame(fr_t frame) const override {
|
||||||
|
if (m_tag->contains(frame))
|
||||||
|
return col_t(frame - m_tag->fromFrame());
|
||||||
|
else
|
||||||
|
return kNoCol;
|
||||||
|
}
|
||||||
|
bool isViewingTag(doc::Tag* tag) const override {
|
||||||
|
return m_tag == tag;
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
doc::Sprite* m_sprite = nullptr;
|
||||||
|
doc::Tag* m_tag = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace view
|
} // namespace view
|
||||||
|
|
||||||
#endif // VIEW_TIMELINE_ADAPTER_H_INCLUDED
|
#endif // VIEW_TIMELINE_ADAPTER_H_INCLUDED
|
||||||
|
47
src/view/utils.cpp
Normal file
47
src/view/utils.cpp
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
// Aseprite View Library
|
||||||
|
// Copyright (c) 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/utils.h"
|
||||||
|
|
||||||
|
#include "doc/selected_frames.h"
|
||||||
|
#include "view/timeline_adapter.h"
|
||||||
|
|
||||||
|
namespace view {
|
||||||
|
|
||||||
|
VirtualRange to_virtual_range(const TimelineAdapter* adapter,
|
||||||
|
const RealRange& realRange)
|
||||||
|
{
|
||||||
|
VirtualRange range(realRange);
|
||||||
|
if (range.enabled()) {
|
||||||
|
doc::SelectedFrames frames;
|
||||||
|
for (auto frame : realRange.selectedFrames()) {
|
||||||
|
col_t col = adapter->toColFrame(fr_t(frame));
|
||||||
|
if (col != kNoCol)
|
||||||
|
frames.insert(doc::frame_t(col));
|
||||||
|
}
|
||||||
|
range.setSelectedFrames(frames, false);
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
RealRange to_real_range(const TimelineAdapter* adapter,
|
||||||
|
const VirtualRange& virtualRange)
|
||||||
|
{
|
||||||
|
RealRange range(virtualRange);
|
||||||
|
if (range.enabled()) {
|
||||||
|
doc::SelectedFrames frames;
|
||||||
|
for (auto frame : virtualRange.selectedFrames())
|
||||||
|
frames.insert(doc::frame_t(adapter->toRealFrame(col_t(frame))));
|
||||||
|
range.setSelectedFrames(frames, false);
|
||||||
|
}
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace view
|
25
src/view/utils.h
Normal file
25
src/view/utils.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
// Aseprite View Library
|
||||||
|
// Copyright (c) 2023 Igara Studio S.A.
|
||||||
|
//
|
||||||
|
// This program is distributed under the terms of
|
||||||
|
// the End-User License Agreement for Aseprite.
|
||||||
|
|
||||||
|
#ifndef VIEW_UTILS_H_INCLUDED
|
||||||
|
#define VIEW_UTILS_H_INCLUDED
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "view/range.h"
|
||||||
|
|
||||||
|
namespace view {
|
||||||
|
|
||||||
|
class TimelineAdapter;
|
||||||
|
|
||||||
|
VirtualRange to_virtual_range(const TimelineAdapter* adapter,
|
||||||
|
const RealRange& realRange);
|
||||||
|
|
||||||
|
RealRange to_real_range(const TimelineAdapter* adapter,
|
||||||
|
const VirtualRange& virtualRange);
|
||||||
|
|
||||||
|
} // namespace view
|
||||||
|
|
||||||
|
#endif // VIEW_RANGE_H_INCLUDED
|
Loading…
x
Reference in New Issue
Block a user