diff --git a/data/pref.xml b/data/pref.xml
index 8f39d81e0..ec9cec667 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -133,12 +133,6 @@
-
diff --git a/data/skins/default/sheet.png b/data/skins/default/sheet.png
index c769f36fb..6394a0115 100644
Binary files a/data/skins/default/sheet.png and b/data/skins/default/sheet.png differ
diff --git a/data/skins/default/skin.xml b/data/skins/default/skin.xml
index 954926aaf..d9c67d38f 100644
--- a/data/skins/default/skin.xml
+++ b/data/skins/default/skin.xml
@@ -10,6 +10,8 @@
+
+
@@ -348,7 +350,7 @@
-
+
diff --git a/data/widgets/frame_tag_properties.xml b/data/widgets/frame_tag_properties.xml
index e7eee175c..048edf77d 100644
--- a/data/widgets/frame_tag_properties.xml
+++ b/data/widgets/frame_tag_properties.xml
@@ -5,10 +5,10 @@
-
+
-
+
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 7975fa072..315b20517 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -258,6 +258,7 @@ add_library(app-lib
job.cpp
launcher.cpp
log.cpp
+ loop_tag.cpp
modules.cpp
modules/editors.cpp
modules/gfx.cpp
@@ -312,6 +313,7 @@ add_library(app-lib
ui/editor/zooming_state.cpp
ui/file_list.cpp
ui/file_selector.cpp
+ ui/frame_tag_window.cpp
ui/hex_color_entry.cpp
ui/home_view.cpp
ui/keyboard_shortcuts.cpp
diff --git a/src/app/commands/cmd_frame_tag_properties.cpp b/src/app/commands/cmd_frame_tag_properties.cpp
index b0880873d..9a03dd2b9 100644
--- a/src/app/commands/cmd_frame_tag_properties.cpp
+++ b/src/app/commands/cmd_frame_tag_properties.cpp
@@ -16,14 +16,13 @@
#include "app/color.h"
#include "app/commands/command.h"
#include "app/context_access.h"
+#include "app/loop_tag.h"
#include "app/transaction.h"
-#include "app/ui/color_button.h"
+#include "app/ui/frame_tag_window.h"
#include "doc/anidir.h"
#include "doc/frame_tag.h"
#include "doc/sprite.h"
-#include "generated_frame_tag_properties.h"
-
namespace app {
using namespace ui;
@@ -56,76 +55,39 @@ void FrameTagPropertiesCommand::onExecute(Context* context)
{
const ContextReader reader(context);
const Sprite* sprite = reader.sprite();
-
frame_t frame = reader.frame();
-
- const FrameTag* best = nullptr;
- for (const FrameTag* tag : sprite->frameTags()) {
- if (frame >= tag->fromFrame() &&
- frame <= tag->toFrame()) {
- if (!best ||
- (tag->toFrame() - tag->fromFrame()) < (best->toFrame() - best->fromFrame())) {
- best = tag;
- }
- }
- }
-
- if (!best)
+ const FrameTag* foundTag = get_shortest_tag(sprite, frame);
+ if (!foundTag)
return;
- app::gen::FrameTagProperties window;
+ FrameTagWindow window(sprite, foundTag);
+ if (!window.show())
+ return;
- window.name()->setText(best->name());
- window.from()->setTextf("%d", best->fromFrame()+1);
- window.to()->setTextf("%d", best->toFrame()+1);
- window.color()->setColor(app::Color::fromRgb(
- doc::rgba_getr(best->color()),
- doc::rgba_getg(best->color()),
- doc::rgba_getb(best->color())));
+ ContextWriter writer(reader);
+ Transaction transaction(writer.context(), "Change Frame Tag Properties");
+ FrameTag* tag = const_cast(foundTag);
- static_assert(
- int(doc::AniDir::FORWARD) == 0 &&
- int(doc::AniDir::REVERSE) == 1 &&
- int(doc::AniDir::PING_PONG) == 2, "doc::AniDir has changed");
- window.anidir()->addItem("Forward");
- window.anidir()->addItem("Reverse");
- window.anidir()->addItem("Ping-pong");
- window.anidir()->setSelectedItemIndex(int(best->aniDir()));
+ std::string name = window.nameValue();
+ if (tag->name() != name)
+ transaction.execute(new cmd::SetFrameTagName(tag, name));
- window.openWindowInForeground();
- if (window.getKiller() == window.ok()) {
- std::string name = window.name()->getText();
- frame_t first = 0;
- frame_t last = sprite->lastFrame();
- frame_t from = window.from()->getTextInt()-1;
- frame_t to = window.to()->getTextInt()-1;
- from = MID(first, from, last);
- to = MID(from, to, last);
- app::Color color = window.color()->getColor();
- doc::color_t docColor = doc::rgba(
- color.getRed(), color.getGreen(), color.getBlue(), 255);
- doc::AniDir anidir = (doc::AniDir)window.anidir()->getSelectedItemIndex();
-
- ContextWriter writer(reader);
- Transaction transaction(writer.context(), "Change Frame Tag Properties");
-
- FrameTag* tag = const_cast(best);
-
- if (tag->name() != name)
- transaction.execute(new cmd::SetFrameTagName(tag, name));
-
- if (tag->fromFrame() != from ||
- tag->toFrame() != to)
- transaction.execute(new cmd::SetFrameTagRange(tag, from, to));
-
- if (tag->color() != docColor)
- transaction.execute(new cmd::SetFrameTagColor(tag, docColor));
-
- if (tag->aniDir() != anidir)
- transaction.execute(new cmd::SetFrameTagAniDir(tag, anidir));
-
- transaction.commit();
+ doc::frame_t from, to;
+ window.rangeValue(from, to);
+ if (tag->fromFrame() != from ||
+ tag->toFrame() != to) {
+ transaction.execute(new cmd::SetFrameTagRange(tag, from, to));
}
+
+ doc::color_t docColor = window.colorValue();
+ if (tag->color() != docColor)
+ transaction.execute(new cmd::SetFrameTagColor(tag, docColor));
+
+ doc::AniDir anidir = window.aniDirValue();
+ if (tag->aniDir() != anidir)
+ transaction.execute(new cmd::SetFrameTagAniDir(tag, anidir));
+
+ transaction.commit();
}
Command* CommandFactory::createFrameTagPropertiesCommand()
diff --git a/src/app/commands/cmd_new_frame_tag.cpp b/src/app/commands/cmd_new_frame_tag.cpp
index 14870846a..5d54b0404 100644
--- a/src/app/commands/cmd_new_frame_tag.cpp
+++ b/src/app/commands/cmd_new_frame_tag.cpp
@@ -15,6 +15,7 @@
#include "app/context.h"
#include "app/context_access.h"
#include "app/transaction.h"
+#include "app/ui/frame_tag_window.h"
#include "app/ui/main_window.h"
#include "app/ui/timeline.h"
#include "doc/frame_tag.h"
@@ -50,11 +51,10 @@ bool NewFrameTagCommand::onEnabled(Context* context)
void NewFrameTagCommand::onExecute(Context* context)
{
- ContextWriter writer(context);
- Sprite* sprite(writer.sprite());
-
- frame_t from = writer.frame();
- frame_t to = writer.frame();
+ const ContextReader reader(context);
+ const Sprite* sprite(reader.sprite());
+ frame_t from = reader.frame();
+ frame_t to = reader.frame();
Timeline::Range range = App::instance()->getMainWindow()->getTimeline()->range();
if (range.enabled() &&
@@ -64,12 +64,22 @@ void NewFrameTagCommand::onExecute(Context* context)
to = range.frameEnd();
}
+ base::UniquePtr frameTag(new FrameTag(from, to));
+ FrameTagWindow window(sprite, frameTag);
+ if (!window.show())
+ return;
+
+ window.rangeValue(from, to);
+ frameTag->setFrameRange(from, to);
+ frameTag->setName(window.nameValue());
+ frameTag->setColor(window.colorValue());
+ frameTag->setAniDir(window.aniDirValue());
+
{
+ ContextWriter writer(reader);
Transaction transaction(writer.context(), "New Frames Tag");
-
- FrameTag* frameTag = new FrameTag(from, to);
- transaction.execute(new cmd::AddFrameTag(sprite, frameTag));
-
+ transaction.execute(new cmd::AddFrameTag(writer.sprite(), frameTag));
+ frameTag.release();
transaction.commit();
}
diff --git a/src/app/commands/cmd_play_animation.cpp b/src/app/commands/cmd_play_animation.cpp
index 1d333ae37..934e0b04d 100644
--- a/src/app/commands/cmd_play_animation.cpp
+++ b/src/app/commands/cmd_play_animation.cpp
@@ -16,6 +16,7 @@
#include "app/context.h"
#include "app/context_access.h"
#include "app/handle_anidir.h"
+#include "app/loop_tag.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/modules/palettes.h"
@@ -69,18 +70,19 @@ protected:
if (m_nextFrameTime >= 0) {
m_nextFrameTime -= (ui::clock() - m_curFrameTick);
+ FrameTag* loopTag = get_loop_tag(m_editor->sprite());
+
while (m_nextFrameTime <= 0) {
frame_t frame = calculate_next_frame(
m_editor->sprite(),
- m_editor->frame(),
- m_docPref,
+ m_editor->frame(), loopTag,
m_pingPongForward);
m_editor->setFrame(frame);
m_nextFrameTime += m_editor->sprite()->frameDuration(frame);
- invalidate();
}
+ invalidate();
m_curFrameTick = ui::clock();
}
}
diff --git a/src/app/commands/cmd_set_loop_section.cpp b/src/app/commands/cmd_set_loop_section.cpp
index bbe327d72..eaf32de17 100644
--- a/src/app/commands/cmd_set_loop_section.cpp
+++ b/src/app/commands/cmd_set_loop_section.cpp
@@ -10,12 +10,18 @@
#endif
#include "app/app.h"
+#include "app/cmd/add_frame_tag.h"
+#include "app/cmd/remove_frame_tag.h"
+#include "app/cmd/set_frame_tag_range.h"
#include "app/commands/command.h"
+#include "app/commands/commands.h"
#include "app/commands/params.h"
#include "app/context_access.h"
-#include "app/pref/preferences.h"
+#include "app/loop_tag.h"
+#include "app/transaction.h"
#include "app/ui/main_window.h"
#include "app/ui/timeline.h"
+#include "doc/frame_tag.h"
namespace app {
@@ -32,7 +38,7 @@ protected:
void onExecute(Context* context) override;
Action m_action;
- frame_t m_begin, m_end;
+ doc::frame_t m_begin, m_end;
};
SetLoopSectionCommand::SetLoopSectionCommand()
@@ -66,13 +72,13 @@ bool SetLoopSectionCommand::onEnabled(Context* ctx)
void SetLoopSectionCommand::onExecute(Context* ctx)
{
- Document* doc = ctx->activeDocument();
+ doc::Document* doc = ctx->activeDocument();
if (!doc)
return;
- DocumentPreferences& docPref = App::instance()->preferences().document(doc);
- frame_t begin = m_begin;
- frame_t end = m_end;
+ doc::Sprite* sprite = doc->sprite();
+ doc::frame_t begin = m_begin;
+ doc::frame_t end = m_end;
bool on = false;
switch (m_action) {
@@ -100,13 +106,39 @@ void SetLoopSectionCommand::onExecute(Context* ctx)
}
+ doc::FrameTag* loopTag = get_loop_tag(sprite);
if (on) {
- docPref.loop.visible(true);
- docPref.loop.from(begin);
- docPref.loop.to(end);
+ if (!loopTag) {
+ loopTag = create_loop_tag(begin, end);
+
+ ContextWriter writer(ctx);
+ Transaction transaction(writer.context(), "Add Loop");
+ transaction.execute(new cmd::AddFrameTag(sprite, loopTag));
+ transaction.commit();
+ }
+ else if (loopTag->fromFrame() != begin ||
+ loopTag->toFrame() != end) {
+ ContextWriter writer(ctx);
+ Transaction transaction(writer.context(), "Set Loop Range");
+ transaction.execute(new cmd::SetFrameTagRange(loopTag, begin, end));
+ transaction.commit();
+ }
+ else {
+ Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::FrameTagProperties);
+ ctx->executeCommand(cmd);
+ }
}
- else
- docPref.loop.visible(false);
+ else {
+ if (loopTag) {
+ ContextWriter writer(ctx);
+ Transaction transaction(writer.context(), "Remove Loop");
+ transaction.execute(new cmd::RemoveFrameTag(sprite, loopTag));
+ transaction.commit();
+ delete loopTag;
+ }
+ }
+
+ App::instance()->getMainWindow()->getTimeline()->invalidate();
}
Command* CommandFactory::createSetLoopSectionCommand()
diff --git a/src/app/file/ase_format.cpp b/src/app/file/ase_format.cpp
index 39ece0e75..c0e12a5e7 100644
--- a/src/app/file/ase_format.cpp
+++ b/src/app/file/ase_format.cpp
@@ -133,7 +133,8 @@ class AseFormat : public FileFormat {
FILE_SUPPORT_INDEXED |
FILE_SUPPORT_LAYERS |
FILE_SUPPORT_FRAMES |
- FILE_SUPPORT_PALETTES;
+ FILE_SUPPORT_PALETTES |
+ FILE_SUPPORT_FRAME_TAGS;
}
bool onLoad(FileOp* fop) override;
@@ -1285,6 +1286,11 @@ static void ase_file_read_frame_tags_chunk(FILE* f, FrameTags* frameTags)
frame_t from = fgetw(f);
frame_t to = fgetw(f);
int aniDir = fgetc(f);
+ if (aniDir != int(AniDir::FORWARD) &&
+ aniDir != int(AniDir::REVERSE) &&
+ aniDir != int(AniDir::PING_PONG)) {
+ aniDir = int(AniDir::FORWARD);
+ }
fgetl(f); // 8 reserved bytes
fgetl(f);
diff --git a/src/app/file/file.cpp b/src/app/file/file.cpp
index 5bcf5807a..c50c82ef2 100644
--- a/src/app/file/file.cpp
+++ b/src/app/file/file.cpp
@@ -294,7 +294,7 @@ FileOp* fop_to_save_document(Context* context, Document* document, const char* f
break;
}
- // check frames support
+ // Frames support
if (fop->document->sprite()->totalFrames() > 1) {
if (!fop->format->support(FILE_SUPPORT_FRAMES) &&
!fop->format->support(FILE_SUPPORT_SEQUENCES)) {
@@ -302,14 +302,14 @@ FileOp* fop_to_save_document(Context* context, Document* document, const char* f
}
}
- // layers support
+ // Layers support
if (fop->document->sprite()->folder()->getLayersCount() > 1) {
if (!(fop->format->support(FILE_SUPPORT_LAYERS))) {
warnings += "<<- Layers";
}
}
- // Palettes support.
+ // Palettes support
if (fop->document->sprite()->getPalettes().size() > 1) {
if (!fop->format->support(FILE_SUPPORT_PALETTES) &&
!fop->format->support(FILE_SUPPORT_SEQUENCES)) {
@@ -317,6 +317,13 @@ FileOp* fop_to_save_document(Context* context, Document* document, const char* f
}
}
+ // Check frames support
+ if (!fop->document->sprite()->frameTags().empty()) {
+ if (!fop->format->support(FILE_SUPPORT_FRAME_TAGS)) {
+ warnings += "<<- Frame tags";
+ }
+ }
+
// Show the confirmation alert
if (!warnings.empty()) {
// Interative
diff --git a/src/app/file/file_format.h b/src/app/file/file_format.h
index ea1cfa206..6336ef676 100644
--- a/src/app/file/file_format.h
+++ b/src/app/file/file_format.h
@@ -25,6 +25,7 @@
#define FILE_SUPPORT_PALETTES 0x00000200
#define FILE_SUPPORT_SEQUENCES 0x00000400
#define FILE_SUPPORT_GET_FORMAT_OPTIONS 0x00000800
+#define FILE_SUPPORT_FRAME_TAGS 0x00001000
namespace app {
diff --git a/src/app/handle_anidir.cpp b/src/app/handle_anidir.cpp
index 5c320014d..638094a0f 100644
--- a/src/app/handle_anidir.cpp
+++ b/src/app/handle_anidir.cpp
@@ -11,6 +11,8 @@
#include "app/handle_anidir.h"
+#include "doc/frame.h"
+#include "doc/frame_tag.h"
#include "doc/sprite.h"
namespace app {
@@ -18,24 +20,27 @@ namespace app {
doc::frame_t calculate_next_frame(
doc::Sprite* sprite,
doc::frame_t frame,
- DocumentPreferences& docPref,
+ doc::FrameTag* tag,
bool& pingPongForward)
{
- frame_t first = frame_t(0);
- frame_t last = sprite->lastFrame();
+ doc::frame_t first = doc::frame_t(0);
+ doc::frame_t last = sprite->lastFrame();
+ doc::AniDir aniDir = doc::AniDir::FORWARD;
- if (docPref.loop.visible()) {
- frame_t loopBegin, loopEnd;
- loopBegin = docPref.loop.from();
- loopEnd = docPref.loop.to();
- loopBegin = MID(first, loopBegin, last);
- loopEnd = MID(first, loopEnd, last);
+ if (tag) {
+ doc::frame_t loopFrom, loopTo;
- first = loopBegin;
- last = loopEnd;
+ loopFrom = tag->fromFrame();
+ loopTo = tag->toFrame();
+ loopFrom = MID(first, loopFrom, last);
+ loopTo = MID(first, loopTo, last);
+
+ first = loopFrom;
+ last = loopTo;
+ aniDir = tag->aniDir();
}
- switch (docPref.loop.aniDir()) {
+ switch (aniDir) {
case doc::AniDir::FORWARD:
++frame;
diff --git a/src/app/handle_anidir.h b/src/app/handle_anidir.h
index 029a0bddd..7d5bd93c0 100644
--- a/src/app/handle_anidir.h
+++ b/src/app/handle_anidir.h
@@ -9,10 +9,10 @@
#define APP_HANDLE_ANIDIR_H_INCLUDED
#pragma once
-#include "app/pref/preferences.h"
#include "doc/frame.h"
namespace doc {
+ class FrameTag;
class Sprite;
}
@@ -21,7 +21,7 @@ namespace app {
doc::frame_t calculate_next_frame(
doc::Sprite* sprite,
doc::frame_t frame,
- DocumentPreferences& docPref,
+ doc::FrameTag* tag,
bool& pingPongForward);
} // namespace app
diff --git a/src/app/loop_tag.cpp b/src/app/loop_tag.cpp
new file mode 100644
index 000000000..d5eb9b3d8
--- /dev/null
+++ b/src/app/loop_tag.cpp
@@ -0,0 +1,53 @@
+// Aseprite
+// Copyright (C) 2001-2015 David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/loop_tag.h"
+
+#include "doc/sprite.h"
+#include "doc/frame_tag.h"
+
+namespace app {
+
+const char* kLoopTagName = "Loop";
+
+doc::FrameTag* get_shortest_tag(const doc::Sprite* sprite, doc::frame_t frame)
+{
+ const doc::FrameTag* found = nullptr;
+ for (const doc::FrameTag* tag : sprite->frameTags()) {
+ if (frame >= tag->fromFrame() &&
+ frame <= tag->toFrame()) {
+ if (!found ||
+ (tag->toFrame() - tag->fromFrame()) < (found->toFrame() - found->fromFrame())) {
+ found = tag;
+ }
+ }
+ }
+ return const_cast(found);
+}
+
+doc::FrameTag* get_loop_tag(doc::Sprite* sprite)
+{
+ // Get tag with special "Loop" name
+ for (doc::FrameTag* tag : sprite->frameTags())
+ if (tag->name() == kLoopTagName)
+ return tag;
+
+ return nullptr;
+}
+
+doc::FrameTag* create_loop_tag(doc::frame_t from, doc::frame_t to)
+{
+ doc::FrameTag* tag = new doc::FrameTag(from, to);
+ tag->setName(kLoopTagName);
+ return tag;
+}
+
+} // app
diff --git a/src/app/loop_tag.h b/src/app/loop_tag.h
new file mode 100644
index 000000000..e32aafb2e
--- /dev/null
+++ b/src/app/loop_tag.h
@@ -0,0 +1,27 @@
+// Aseprite
+// Copyright (C) 2001-2015 David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifndef APP_LOOP_TAG_H_INCLUDED
+#define APP_LOOP_TAG_H_INCLUDED
+#pragma once
+
+#include "doc/frame.h"
+
+namespace doc {
+ class FrameTag;
+ class Sprite;
+}
+
+namespace app {
+
+ doc::FrameTag* get_shortest_tag(const doc::Sprite* sprite, doc::frame_t frame);
+ doc::FrameTag* get_loop_tag(doc::Sprite* sprite);
+ doc::FrameTag* create_loop_tag(doc::frame_t from, doc::frame_t to);
+
+} // namespace app
+
+#endif
diff --git a/src/app/ui/configure_timeline_popup.cpp b/src/app/ui/configure_timeline_popup.cpp
index 45bd77dbf..30e0bed31 100644
--- a/src/app/ui/configure_timeline_popup.cpp
+++ b/src/app/ui/configure_timeline_popup.cpp
@@ -12,17 +12,23 @@
#include "app/ui/configure_timeline_popup.h"
#include "app/app.h"
+#include "app/cmd/remove_frame_tag.h"
+#include "app/cmd/set_frame_tag_anidir.h"
+#include "app/commands/commands.h"
#include "app/context.h"
+#include "app/context_access.h"
#include "app/document.h"
#include "app/find_widget.h"
#include "app/load_widget.h"
+#include "app/loop_tag.h"
#include "app/settings/settings.h"
-#include "app/commands/commands.h"
+#include "app/transaction.h"
#include "app/ui/main_window.h"
#include "app/ui/timeline.h"
#include "app/ui_context.h"
#include "base/bind.h"
#include "base/scoped_value.h"
+#include "doc/frame_tag.h"
#include "ui/box.h"
#include "ui/button.h"
#include "ui/message.h"
@@ -66,10 +72,14 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
m_pingPongDir->Click.connect(Bind(&ConfigureTimelinePopup::onAniDir, this, doc::AniDir::PING_PONG));
}
+app::Document* ConfigureTimelinePopup::doc()
+{
+ return UIContext::instance()->activeDocument();
+}
+
DocumentPreferences& ConfigureTimelinePopup::docPref()
{
- return App::instance()->preferences().document(
- UIContext::instance()->activeDocument());
+ return App::instance()->preferences().document(doc());
}
void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
@@ -97,7 +107,17 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
break;
}
- switch (docPref.loop.aniDir()) {
+ doc::AniDir aniDir = doc::AniDir::FORWARD;
+ if (doc()) {
+ if (doc::FrameTag* tag = get_loop_tag(doc()->sprite())) {
+ aniDir = tag->aniDir();
+ }
+ }
+ else {
+ ASSERT(false && "We should have an active sprite at this moment");
+ }
+
+ switch (aniDir) {
case doc::AniDir::FORWARD:
m_normalDir->setSelected(true);
break;
@@ -167,12 +187,32 @@ void ConfigureTimelinePopup::onSetLoopSection()
void ConfigureTimelinePopup::onResetLoopSection()
{
- docPref().loop.visible(false);
+ ContextWriter writer(UIContext::instance());
+ Transaction transaction(writer.context(), "Remove Loop");
+ transaction.execute(new cmd::RemoveFrameTag(writer.sprite(),
+ get_loop_tag(writer.sprite())));
+ transaction.commit();
}
void ConfigureTimelinePopup::onAniDir(doc::AniDir aniDir)
{
- docPref().loop.aniDir(aniDir);
+ ContextWriter writer(UIContext::instance());
+ doc::Sprite* sprite = writer.sprite();
+ if (sprite) {
+ ASSERT(false);
+ return;
+ }
+
+ doc::FrameTag* loopTag = get_loop_tag(sprite);
+ Transaction transaction(writer.context(), "Set Loop Direction");
+ if (loopTag)
+ transaction.execute(new cmd::SetFrameTagAniDir(loopTag, aniDir));
+ else {
+ loopTag = create_loop_tag(doc::frame_t(0), sprite->lastFrame());
+ loopTag->setAniDir(aniDir);
+ transaction.execute(new cmd::AddFrameTag(sprite, loopTag));
+ }
+ transaction.commit();
}
} // namespace app
diff --git a/src/app/ui/configure_timeline_popup.h b/src/app/ui/configure_timeline_popup.h
index 6cdd421dc..d1eb93da0 100644
--- a/src/app/ui/configure_timeline_popup.h
+++ b/src/app/ui/configure_timeline_popup.h
@@ -21,6 +21,7 @@ namespace ui {
}
namespace app {
+ class Document;
class ConfigureTimelinePopup : public ui::PopupWindow {
public:
@@ -38,6 +39,7 @@ namespace app {
private:
void updateWidgetsFromCurrentSettings();
+ app::Document* doc();
DocumentPreferences& docPref();
ui::RadioButton* m_merge;
diff --git a/src/app/ui/frame_tag_window.cpp b/src/app/ui/frame_tag_window.cpp
new file mode 100644
index 000000000..354659bcb
--- /dev/null
+++ b/src/app/ui/frame_tag_window.cpp
@@ -0,0 +1,73 @@
+// Aseprite
+// Copyright (C) 2001-2015 David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "app/ui/frame_tag_window.h"
+
+#include "doc/frame_tag.h"
+#include "doc/sprite.h"
+
+namespace app {
+
+FrameTagWindow::FrameTagWindow(const doc::Sprite* sprite, const doc::FrameTag* frameTag)
+ : m_sprite(sprite)
+{
+ name()->setText(frameTag->name());
+ from()->setTextf("%d", frameTag->fromFrame()+1);
+ to()->setTextf("%d", frameTag->toFrame()+1);
+ color()->setColor(app::Color::fromRgb(
+ doc::rgba_getr(frameTag->color()),
+ doc::rgba_getg(frameTag->color()),
+ doc::rgba_getb(frameTag->color())));
+
+ static_assert(
+ int(doc::AniDir::FORWARD) == 0 &&
+ int(doc::AniDir::REVERSE) == 1 &&
+ int(doc::AniDir::PING_PONG) == 2, "doc::AniDir has changed");
+ anidir()->addItem("Forward");
+ anidir()->addItem("Reverse");
+ anidir()->addItem("Ping-pong");
+ anidir()->setSelectedItemIndex(int(frameTag->aniDir()));
+}
+
+bool FrameTagWindow::show()
+{
+ openWindowInForeground();
+ return (getKiller() == ok());
+}
+
+std::string FrameTagWindow::nameValue()
+{
+ return name()->getText();
+}
+
+void FrameTagWindow::rangeValue(doc::frame_t& from, doc::frame_t& to)
+{
+ doc::frame_t first = 0;
+ doc::frame_t last = m_sprite->lastFrame();
+
+ from = this->from()->getTextInt()-1;
+ to = this->to()->getTextInt()-1;
+ from = MID(first, from, last);
+ to = MID(from, to, last);
+}
+
+doc::color_t FrameTagWindow::colorValue()
+{
+ app::Color color = this->color()->getColor();
+ return doc::rgba(color.getRed(), color.getGreen(), color.getBlue(), 255);
+}
+
+doc::AniDir FrameTagWindow::aniDirValue()
+{
+ return (doc::AniDir)anidir()->getSelectedItemIndex();
+}
+
+} // namespace app
diff --git a/src/app/ui/frame_tag_window.h b/src/app/ui/frame_tag_window.h
new file mode 100644
index 000000000..0cb3b95f4
--- /dev/null
+++ b/src/app/ui/frame_tag_window.h
@@ -0,0 +1,46 @@
+// Aseprite
+// Copyright (C) 2001-2015 David Capello
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License version 2 as
+// published by the Free Software Foundation.
+
+#ifndef APP_UI_FRAME_TAG_WINDOW_H_INCLUDED
+#define APP_UI_FRAME_TAG_WINDOW_H_INCLUDED
+#pragma once
+
+#include "app/ui/color_button.h"
+#include "doc/anidir.h"
+#include "doc/frame.h"
+
+#include "generated_frame_tag_properties.h"
+
+namespace doc {
+ class FrameTag;
+ class Sprite;
+}
+
+namespace ui {
+ class Splitter;
+}
+
+namespace app {
+
+ class FrameTagWindow : protected app::gen::FrameTagProperties {
+ public:
+ FrameTagWindow(const doc::Sprite* sprite, const doc::FrameTag* frameTag);
+
+ bool show();
+
+ std::string nameValue();
+ void rangeValue(doc::frame_t& from, doc::frame_t& to);
+ doc::color_t colorValue();
+ doc::AniDir aniDirValue();
+
+ private:
+ const doc::Sprite* m_sprite;
+ };
+
+}
+
+#endif
diff --git a/src/app/ui/preview_editor.cpp b/src/app/ui/preview_editor.cpp
index 88e938e26..0fd4777cc 100644
--- a/src/app/ui/preview_editor.cpp
+++ b/src/app/ui/preview_editor.cpp
@@ -15,6 +15,7 @@
#include "app/document.h"
#include "app/handle_anidir.h"
#include "app/ini_file.h"
+#include "app/loop_tag.h"
#include "app/modules/editors.h"
#include "app/modules/gui.h"
#include "app/pref/preferences.h"
@@ -35,6 +36,8 @@
#include "ui/message.h"
#include "ui/system.h"
+#include "doc/frame_tag.h"
+
namespace app {
using namespace app::skin;
@@ -157,6 +160,7 @@ PreviewEditorWindow::PreviewEditorWindow()
, m_playButton(new MiniPlayButton())
, m_playTimer(10)
, m_pingPongForward(true)
+ , m_refFrame(0)
{
child_spacing = 0;
setAutoRemap(false);
@@ -305,7 +309,7 @@ void PreviewEditorWindow::updateUsingEditor(Editor* editor)
miniEditor->centerInSpritePoint(centerPoint);
miniEditor->setLayer(editor->layer());
- miniEditor->setFrame(editor->frame());
+ miniEditor->setFrame(m_refFrame = editor->frame());
}
void PreviewEditorWindow::uncheckCenterButton()
@@ -329,22 +333,24 @@ void PreviewEditorWindow::onPlaybackTick()
if (!miniEditor)
return;
- Document* document = miniEditor->document();
- Sprite* sprite = miniEditor->sprite();
+ doc::Document* document = miniEditor->document();
+ doc::Sprite* sprite = miniEditor->sprite();
if (!document || !sprite)
return;
- DocumentPreferences& docPref =
- App::instance()->preferences().document(document);
-
if (m_nextFrameTime >= 0) {
m_nextFrameTime -= (ui::clock() - m_curFrameTick);
+ // TODO get the frame tag in updateUsingEditor()
+ doc::FrameTag* tag = get_shortest_tag(sprite, m_refFrame);
+ if (!tag)
+ tag = get_loop_tag(sprite);
+
while (m_nextFrameTime <= 0) {
- frame_t frame = calculate_next_frame(
+ doc::frame_t frame = calculate_next_frame(
sprite,
miniEditor->frame(),
- docPref,
+ tag,
m_pingPongForward);
miniEditor->setFrame(frame);
diff --git a/src/app/ui/preview_editor.h b/src/app/ui/preview_editor.h
index b1fee23b5..52b70a25c 100644
--- a/src/app/ui/preview_editor.h
+++ b/src/app/ui/preview_editor.h
@@ -10,6 +10,7 @@
#pragma once
#include "app/ui/document_view.h"
+#include "doc/frame.h"
#include "ui/timer.h"
#include "ui/window.h"
@@ -51,6 +52,7 @@ namespace app {
int m_curFrameTick;
bool m_pingPongForward;
+ doc::frame_t m_refFrame;
};
} // namespace app
diff --git a/src/app/ui/timeline.cpp b/src/app/ui/timeline.cpp
index 349e7c2e8..b857a99cc 100644
--- a/src/app/ui/timeline.cpp
+++ b/src/app/ui/timeline.cpp
@@ -13,6 +13,7 @@
#include "app/app.h"
#include "app/app_menus.h"
+#include "app/color_utils.h"
#include "app/commands/command.h"
#include "app/commands/commands.h"
#include "app/commands/params.h"
@@ -22,6 +23,7 @@
#include "app/document_api.h"
#include "app/document_range_ops.h"
#include "app/document_undo.h"
+#include "app/loop_tag.h"
#include "app/modules/editors.h"
#include "app/modules/gfx.h"
#include "app/modules/gui.h"
@@ -40,6 +42,7 @@
#include "doc/frame_tag.h"
#include "gfx/point.h"
#include "gfx/rect.h"
+#include "she/font.h"
#include "ui/ui.h"
#include
@@ -76,6 +79,7 @@ using namespace ui;
enum {
A_PART_NOTHING,
+ A_PART_TOP,
A_PART_SEPARATOR,
A_PART_HEADER_EYE,
A_PART_HEADER_PADLOCK,
@@ -86,6 +90,7 @@ enum {
A_PART_HEADER_ONIONSKIN_RANGE_RIGHT,
A_PART_HEADER_LAYER,
A_PART_HEADER_FRAME,
+ A_PART_HEADER_FRAME_TAGS,
A_PART_LAYER,
A_PART_LAYER_EYE_ICON,
A_PART_LAYER_PADLOCK_ICON,
@@ -758,6 +763,8 @@ void Timeline::onPaint(ui::PaintEvent& ev)
getDrawableLayers(g, &first_layer, &last_layer);
getDrawableFrames(g, &first_frame, &last_frame);
+ drawTop(g);
+
// Draw the header for layers.
drawHeader(g);
@@ -811,7 +818,6 @@ void Timeline::onPaint(ui::PaintEvent& ev)
}
drawPaddings(g);
- drawLoopRange(g);
drawFrameTags(g);
drawRangeOutline(g);
drawClipboardRange(g);
@@ -1037,6 +1043,12 @@ void Timeline::drawClipboardRange(ui::Graphics* g)
g->drawRect(0, getRangeBounds(clipboard_range));
}
+void Timeline::drawTop(ui::Graphics* g)
+{
+ g->fillRect(skinTheme()->colors.workspace(),
+ getPartBounds(A_PART_TOP));
+}
+
void Timeline::drawHeader(ui::Graphics* g)
{
SkinTheme::Styles& styles = skinTheme()->styles;
@@ -1253,41 +1265,50 @@ void Timeline::drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
}
}
-void Timeline::drawLoopRange(ui::Graphics* g)
+void Timeline::drawFrameTags(ui::Graphics* g)
{
- DocumentPreferences& docPref = this->docPref();
- if (!docPref.loop.visible())
- return;
-
- frame_t begin = docPref.loop.from();
- frame_t end = docPref.loop.to();
- if (begin > end)
- return;
-
- gfx::Rect bounds1 = getPartBounds(A_PART_HEADER_FRAME, firstLayer(), begin);
- gfx::Rect bounds2 = getPartBounds(A_PART_HEADER_FRAME, firstLayer(), end);
- gfx::Rect bounds = bounds1.createUnion(bounds2);
-
- IntersectClip clip(g, bounds);
+ IntersectClip clip(g, getPartBounds(A_PART_HEADER_FRAME_TAGS));
if (!clip)
return;
- drawPart(g, bounds, NULL,
- skinTheme()->styles.timelineLoopRange());
-}
+ SkinTheme* theme = skinTheme();
+ SkinTheme::Styles& styles = theme->styles;
-void Timeline::drawFrameTags(ui::Graphics* g)
-{
- SkinTheme::Styles& styles = skinTheme()->styles;
+ if (!m_sprite->frameTags().empty()) {
+ g->fillRect(theme->colors.workspace(),
+ gfx::Rect(
+ 0, getFont()->height(),
+ getClientBounds().w,
+ theme->dimensions.timelineTagsAreaHeight()));
+ }
for (FrameTag* frameTag : m_sprite->frameTags()) {
gfx::Rect bounds1 = getPartBounds(A_PART_HEADER_FRAME, firstLayer(), frameTag->fromFrame());
gfx::Rect bounds2 = getPartBounds(A_PART_HEADER_FRAME, firstLayer(), frameTag->toFrame());
gfx::Rect bounds = bounds1.createUnion(bounds2);
+ bounds.y -= theme->dimensions.timelineTagsAreaHeight();
- IntersectClip clip(g, bounds);
- if (clip) {
- drawPart(g, bounds, NULL, styles.timelineLoopRange());
+ {
+ IntersectClip clip(g, bounds);
+ if (clip)
+ drawPart(g, bounds, NULL, styles.timelineLoopRange());
+ }
+
+ {
+ int textHeight = getFont()->height();
+ bounds.y -= textHeight + 2*ui::guiscale();
+ bounds.x += 3*ui::guiscale();
+ bounds.w = getFont()->textLength(frameTag->name().c_str()) + 4*ui::guiscale();
+ bounds.h = getFont()->height() + 2*ui::guiscale();
+ g->fillRect(frameTag->color(), bounds);
+
+ bounds.y += 2*ui::guiscale();
+ bounds.x += 2*ui::guiscale();
+ g->drawString(
+ frameTag->name(),
+ color_utils::blackandwhite_neg(frameTag->color()),
+ gfx::ColorNone,
+ bounds.getOrigin());
}
}
}
@@ -1365,6 +1386,7 @@ void Timeline::drawPaddings(ui::Graphics* g)
gfx::Rect client = getClientBounds();
gfx::Rect bottomLayer;
gfx::Rect lastFrame;
+ int top = topHeight();
if (!m_layers.empty()) {
bottomLayer = getPartBounds(A_PART_LAYER, firstLayer());
@@ -1376,7 +1398,7 @@ void Timeline::drawPaddings(ui::Graphics* g)
}
drawPart(g,
- gfx::Rect(lastFrame.x+lastFrame.w, client.y,
+ gfx::Rect(lastFrame.x+lastFrame.w, client.y + top,
client.w - (lastFrame.x+lastFrame.w),
bottomLayer.y+bottomLayer.h),
NULL, styles.timelinePaddingTr());
@@ -1397,8 +1419,9 @@ gfx::Rect Timeline::getLayerHeadersBounds() const
{
gfx::Rect rc = getClientBounds();
rc.w = m_separator_x;
- rc.y += HDRSIZE;
- rc.h -= HDRSIZE;
+ int h = topHeight() + HDRSIZE;
+ rc.y += h;
+ rc.h -= h;
return rc;
}
@@ -1406,6 +1429,7 @@ gfx::Rect Timeline::getFrameHeadersBounds() const
{
gfx::Rect rc = getClientBounds();
rc.x += m_separator_x;
+ rc.y += topHeight();
rc.w -= m_separator_x;
rc.h = HDRSIZE;
return rc;
@@ -1435,78 +1459,89 @@ gfx::Rect Timeline::getCelsBounds() const
gfx::Rect rc = getClientBounds();
rc.x += m_separator_x;
rc.w -= m_separator_x;
- rc.y += HDRSIZE;
- rc.h -= HDRSIZE;
+ rc.y += HDRSIZE + topHeight();
+ rc.h -= HDRSIZE - topHeight();
return rc;
}
gfx::Rect Timeline::getPartBounds(int part, LayerIndex layer, frame_t frame) const
{
- const gfx::Rect bounds = getClientBounds();
+ gfx::Rect bounds = getClientBounds();
+ int y = topHeight();
switch (part) {
case A_PART_NOTHING:
break;
+ case A_PART_TOP:
+ return gfx::Rect(bounds.x, bounds.y, bounds.w, y);
+
case A_PART_SEPARATOR:
- return gfx::Rect(m_separator_x, 0,
- m_separator_x + m_separator_w, bounds.h);
+ return gfx::Rect(bounds.x + m_separator_x, bounds.y + y,
+ m_separator_x + m_separator_w, bounds.h - y);
case A_PART_HEADER_EYE:
- return gfx::Rect(FRMSIZE*0, 0, FRMSIZE, HDRSIZE);
+ return gfx::Rect(bounds.x + FRMSIZE*0, bounds.y + y, FRMSIZE, HDRSIZE);
case A_PART_HEADER_PADLOCK:
- return gfx::Rect(FRMSIZE*1, 0, FRMSIZE, HDRSIZE);
+ return gfx::Rect(bounds.x + FRMSIZE*1, bounds.y + y, FRMSIZE, HDRSIZE);
case A_PART_HEADER_CONTINUOUS:
- return gfx::Rect(FRMSIZE*2, 0, FRMSIZE, HDRSIZE);
+ return gfx::Rect(bounds.x + FRMSIZE*2, bounds.y + y, FRMSIZE, HDRSIZE);
case A_PART_HEADER_GEAR:
- return gfx::Rect(FRMSIZE*3, 0, FRMSIZE, HDRSIZE);
+ return gfx::Rect(bounds.x + FRMSIZE*3, bounds.y + y, FRMSIZE, HDRSIZE);
case A_PART_HEADER_ONIONSKIN:
- return gfx::Rect(FRMSIZE*4, 0, FRMSIZE, HDRSIZE);
+ return gfx::Rect(bounds.x + FRMSIZE*4, bounds.y + y, FRMSIZE, HDRSIZE);
case A_PART_HEADER_LAYER:
- return gfx::Rect(FRMSIZE*5, 0,
+ return gfx::Rect(bounds.x + FRMSIZE*5, bounds.y + y,
m_separator_x - FRMSIZE*5, HDRSIZE);
case A_PART_HEADER_FRAME:
if (validFrame(frame)) {
- return gfx::Rect(m_separator_x + m_separator_w - 1 + FRMSIZE*frame - m_scroll_x,
- 0, FRMSIZE, HDRSIZE);
+ return gfx::Rect(
+ bounds.x + m_separator_x + m_separator_w - 1 + FRMSIZE*frame - m_scroll_x,
+ bounds.y + y, FRMSIZE, HDRSIZE);
}
break;
+ case A_PART_HEADER_FRAME_TAGS:
+ return gfx::Rect(
+ bounds.x + m_separator_x + m_separator_w - 1,
+ bounds.y,
+ bounds.w - m_separator_x - m_separator_w + 1, y);
+
case A_PART_LAYER:
if (validLayer(layer)) {
- return gfx::Rect(0,
- HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
+ return gfx::Rect(bounds.x,
+ bounds.y + y + HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
m_separator_x, LAYSIZE);
}
break;
case A_PART_LAYER_EYE_ICON:
if (validLayer(layer)) {
- return gfx::Rect(0,
- HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
+ return gfx::Rect(bounds.x,
+ bounds.y + y + HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
FRMSIZE, LAYSIZE);
}
break;
case A_PART_LAYER_PADLOCK_ICON:
if (validLayer(layer)) {
- return gfx::Rect(FRMSIZE,
- HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
+ return gfx::Rect(bounds.x + FRMSIZE,
+ bounds.y + y + HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
FRMSIZE, LAYSIZE);
}
break;
case A_PART_LAYER_CONTINUOUS_ICON:
if (validLayer(layer)) {
- return gfx::Rect(2*FRMSIZE,
- HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
+ return gfx::Rect(bounds.x + 2*FRMSIZE,
+ bounds.y + y + HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
FRMSIZE, LAYSIZE);
}
break;
@@ -1514,8 +1549,8 @@ gfx::Rect Timeline::getPartBounds(int part, LayerIndex layer, frame_t frame) con
case A_PART_LAYER_TEXT:
if (validLayer(layer)) {
int x = FRMSIZE*3;
- return gfx::Rect(x,
- HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
+ return gfx::Rect(bounds.x + x,
+ bounds.y + y + HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
m_separator_x - x, LAYSIZE);
}
break;
@@ -1523,8 +1558,8 @@ gfx::Rect Timeline::getPartBounds(int part, LayerIndex layer, frame_t frame) con
case A_PART_CEL:
if (validLayer(layer) && frame >= frame_t(0)) {
return gfx::Rect(
- m_separator_x + m_separator_w - 1 + FRMSIZE*frame - m_scroll_x,
- HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
+ bounds.x + m_separator_x + m_separator_w - 1 + FRMSIZE*frame - m_scroll_x,
+ bounds.y + y + HDRSIZE + LAYSIZE*(lastLayer()-layer) - m_scroll_y,
FRMSIZE, LAYSIZE);
}
break;
@@ -1612,8 +1647,11 @@ void Timeline::updateHot(ui::Message* msg, const gfx::Point& mousePos, int& hot_
hot_part = A_PART_SEPARATOR;
}
else {
+ int top = topHeight();
+
hot_layer = lastLayer() - LayerIndex(
(mousePos.y
+ - top
- HDRSIZE
+ m_scroll_y) / LAYSIZE);
@@ -1648,7 +1686,7 @@ void Timeline::updateHot(ui::Message* msg, const gfx::Point& mousePos, int& hot_
hot_part = A_PART_SEPARATOR;
}
// Is the mouse on the headers?
- else if (mousePos.y < HDRSIZE) {
+ else if (mousePos.y >= top && mousePos.y < top+HDRSIZE) {
if (mousePos.x < m_separator_x) {
if (getPartBounds(A_PART_HEADER_EYE).contains(mousePos))
hot_part = A_PART_HEADER_EYE;
@@ -2168,4 +2206,14 @@ skin::SkinTheme* Timeline::skinTheme() const
return static_cast(getTheme());
}
+int Timeline::topHeight() const
+{
+ int h = skinTheme()->dimensions.timelineTopBorder();
+ if (m_sprite && !m_sprite->frameTags().empty()) {
+ h += getFont()->height();
+ h += skinTheme()->dimensions.timelineTagsAreaHeight();
+ }
+ return h;
+}
+
} // namespace app
diff --git a/src/app/ui/timeline.h b/src/app/ui/timeline.h
index 09ba1ace7..bf8ec8243 100644
--- a/src/app/ui/timeline.h
+++ b/src/app/ui/timeline.h
@@ -146,13 +146,13 @@ namespace app {
void drawPart(ui::Graphics* g, const gfx::Rect& bounds,
const char* text, skin::Style* style,
bool is_active = false, bool is_hover = false, bool is_clicked = false);
+ void drawTop(ui::Graphics* g);
void drawHeader(ui::Graphics* g);
void drawHeaderFrame(ui::Graphics* g, frame_t frame);
void drawLayer(ui::Graphics* g, LayerIndex layerIdx);
void drawCel(ui::Graphics* g, LayerIndex layerIdx, frame_t frame, Cel* cel);
void drawCelLinkDecorators(ui::Graphics* g, const gfx::Rect& bounds,
Cel* cel, Cel* activeCel, frame_t frame, bool is_active, bool is_hover);
- void drawLoopRange(ui::Graphics* g);
void drawFrameTags(ui::Graphics* g);
void drawRangeOutline(ui::Graphics* g);
void drawPaddings(ui::Graphics* g);
@@ -195,6 +195,8 @@ namespace app {
bool validLayer(LayerIndex layer) const { return layer >= firstLayer() && layer <= lastLayer(); }
bool validFrame(frame_t frame) const { return frame >= firstFrame() && frame <= lastFrame(); }
+ int topHeight() const;
+
DocumentPreferences& docPref() const;
skin::SkinTheme* skinTheme() const;
diff --git a/src/doc/frame_tag.cpp b/src/doc/frame_tag.cpp
index 0dda32285..d609bc0f9 100644
--- a/src/doc/frame_tag.cpp
+++ b/src/doc/frame_tag.cpp
@@ -18,6 +18,7 @@ FrameTag::FrameTag(frame_t from, frame_t to)
, m_to(to)
, m_color(rgba(0, 0, 0, 255))
, m_name("Tag")
+ , m_aniDir(AniDir::FORWARD)
{
}
@@ -39,6 +40,10 @@ void FrameTag::setColor(color_t color)
void FrameTag::setAniDir(AniDir aniDir)
{
+ ASSERT(m_aniDir == AniDir::FORWARD ||
+ m_aniDir == AniDir::REVERSE ||
+ m_aniDir == AniDir::PING_PONG);
+
m_aniDir = aniDir;
}
diff --git a/src/doc/frame_tags.h b/src/doc/frame_tags.h
index 7d7103976..28aad817d 100644
--- a/src/doc/frame_tags.h
+++ b/src/doc/frame_tags.h
@@ -37,6 +37,7 @@ namespace doc {
const_iterator end() const { return m_tags.end(); }
std::size_t size() const { return m_tags.size(); }
+ bool empty() const { return m_tags.empty(); }
private:
Sprite* m_sprite;