mirror of
https://github.com/aseprite/aseprite.git
synced 2025-03-29 19:20:09 +00:00
234 lines
5.8 KiB
C++
234 lines
5.8 KiB
C++
// Aseprite
|
|
// Copyright (C) 2001-2016 David Capello
|
|
//
|
|
// This program is distributed under the terms of
|
|
// the End-User License Agreement for Aseprite.
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include "config.h"
|
|
#endif
|
|
|
|
#include "app/ui/editor/play_state.h"
|
|
|
|
#include "app/commands/command.h"
|
|
#include "app/commands/commands.h"
|
|
#include "app/loop_tag.h"
|
|
#include "app/pref/preferences.h"
|
|
#include "app/ui/editor/editor.h"
|
|
#include "app/ui/editor/scrolling_state.h"
|
|
#include "app/ui_context.h"
|
|
#include "doc/frame_tag.h"
|
|
#include "doc/handle_anidir.h"
|
|
#include "ui/manager.h"
|
|
#include "ui/message.h"
|
|
#include "ui/system.h"
|
|
|
|
namespace app {
|
|
|
|
using namespace ui;
|
|
|
|
PlayState::PlayState(const bool playOnce,
|
|
const bool playAll)
|
|
: m_editor(nullptr)
|
|
, m_playOnce(playOnce)
|
|
, m_playAll(playAll)
|
|
, m_toScroll(false)
|
|
, m_playTimer(10)
|
|
, m_nextFrameTime(-1)
|
|
, m_pingPongForward(true)
|
|
, m_refFrame(0)
|
|
, m_tag(nullptr)
|
|
{
|
|
m_playTimer.Tick.connect(&PlayState::onPlaybackTick, this);
|
|
|
|
// Hook BeforeCommandExecution signal so we know if the user wants
|
|
// to execute other command, so we can stop the animation.
|
|
m_ctxConn = UIContext::instance()->BeforeCommandExecution.connect(
|
|
&PlayState::onBeforeCommandExecution, this);
|
|
}
|
|
|
|
void PlayState::onEnterState(Editor* editor)
|
|
{
|
|
StateWithWheelBehavior::onEnterState(editor);
|
|
|
|
if (!m_editor) {
|
|
m_editor = editor;
|
|
m_refFrame = editor->frame();
|
|
}
|
|
|
|
// Get the tag
|
|
if (!m_playAll)
|
|
m_tag = get_animation_tag(m_editor->sprite(), m_refFrame);
|
|
|
|
// Go to the first frame of the animation or active frame tag
|
|
if (m_playOnce) {
|
|
frame_t frame = 0;
|
|
|
|
if (m_tag) {
|
|
frame = (m_tag->aniDir() == AniDir::REVERSE ?
|
|
m_tag->toFrame():
|
|
m_tag->fromFrame());
|
|
}
|
|
|
|
m_editor->setFrame(frame);
|
|
}
|
|
|
|
m_toScroll = false;
|
|
m_nextFrameTime = getNextFrameTime();
|
|
m_curFrameTick = base::current_tick();
|
|
m_pingPongForward = true;
|
|
|
|
// Maybe we came from ScrollingState and the timer is already
|
|
// running.
|
|
if (!m_playTimer.isRunning())
|
|
m_playTimer.start();
|
|
}
|
|
|
|
EditorState::LeaveAction PlayState::onLeaveState(Editor* editor, EditorState* newState)
|
|
{
|
|
if (!m_toScroll) {
|
|
if (m_playOnce || Preferences::instance().general.rewindOnStop())
|
|
m_editor->setFrame(m_refFrame);
|
|
|
|
// We don't stop the timer if we are going to the ScrollingState
|
|
// (we keep playing the animation).
|
|
m_playTimer.stop();
|
|
}
|
|
return KeepState;
|
|
}
|
|
|
|
bool PlayState::onMouseDown(Editor* editor, MouseMessage* msg)
|
|
{
|
|
if (editor->hasCapture())
|
|
return true;
|
|
|
|
// When an editor is clicked the current view is changed.
|
|
UIContext* context = UIContext::instance();
|
|
context->setActiveView(editor->getDocumentView());
|
|
|
|
// A click with right-button stops the animation
|
|
if (msg->buttons() == kButtonRight) {
|
|
editor->stop();
|
|
return true;
|
|
}
|
|
|
|
// Set this flag to indicate that we are going to ScrollingState for
|
|
// some time, so we don't change the current frame.
|
|
m_toScroll = true;
|
|
|
|
// Start scroll loop
|
|
EditorStatePtr newState(new ScrollingState());
|
|
editor->setState(newState);
|
|
newState->onMouseDown(editor, msg);
|
|
return true;
|
|
}
|
|
|
|
bool PlayState::onMouseUp(Editor* editor, MouseMessage* msg)
|
|
{
|
|
editor->releaseMouse();
|
|
return true;
|
|
}
|
|
|
|
bool PlayState::onMouseMove(Editor* editor, MouseMessage* msg)
|
|
{
|
|
editor->updateStatusBar();
|
|
return true;
|
|
}
|
|
|
|
bool PlayState::onKeyDown(Editor* editor, KeyMessage* msg)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool PlayState::onKeyUp(Editor* editor, KeyMessage* msg)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void PlayState::onPlaybackTick()
|
|
{
|
|
if (m_nextFrameTime < 0)
|
|
return;
|
|
|
|
m_nextFrameTime -= (base::current_tick() - m_curFrameTick);
|
|
|
|
doc::Sprite* sprite = m_editor->sprite();
|
|
|
|
while (m_nextFrameTime <= 0) {
|
|
doc::frame_t frame = m_editor->frame();
|
|
|
|
if (m_playOnce) {
|
|
bool atEnd = false;
|
|
if (m_tag) {
|
|
switch (m_tag->aniDir()) {
|
|
case AniDir::FORWARD:
|
|
atEnd = (frame == m_tag->toFrame());
|
|
break;
|
|
case AniDir::REVERSE:
|
|
atEnd = (frame == m_tag->fromFrame());
|
|
break;
|
|
case AniDir::PING_PONG:
|
|
atEnd = (!m_pingPongForward &&
|
|
frame == m_tag->fromFrame());
|
|
break;
|
|
}
|
|
}
|
|
else {
|
|
atEnd = (frame == sprite->lastFrame());
|
|
}
|
|
if (atEnd) {
|
|
m_editor->stop();
|
|
break;
|
|
}
|
|
}
|
|
|
|
frame = calculate_next_frame(
|
|
sprite, frame, frame_t(1), m_tag,
|
|
m_pingPongForward);
|
|
|
|
m_editor->setFrame(frame);
|
|
m_nextFrameTime += getNextFrameTime();
|
|
m_editor->invalidate();
|
|
}
|
|
|
|
m_curFrameTick = base::current_tick();
|
|
}
|
|
|
|
// Before executing any command, we stop the animation
|
|
void PlayState::onBeforeCommandExecution(CommandExecutionEvent& ev)
|
|
{
|
|
// This check just in case we stay connected to context signals when
|
|
// the editor is already deleted.
|
|
ASSERT(m_editor);
|
|
ASSERT(m_editor->manager() == ui::Manager::getDefault());
|
|
|
|
// If the command is for other editor, we don't stop the animation.
|
|
if (!m_editor->isActive())
|
|
return;
|
|
|
|
// If we're executing PlayAnimation command, it means that the
|
|
// user wants to stop the animation. We cannot stop the animation
|
|
// here, because if it's stopped, PlayAnimation will re-play it
|
|
// (so it would be impossible to stop the animation using
|
|
// PlayAnimation command/Enter key).
|
|
//
|
|
// There are other commands that just doesn't stop the animation
|
|
// (zoom, scroll, etc.)
|
|
if (ev.command()->id() == CommandId::PlayAnimation ||
|
|
ev.command()->id() == CommandId::Zoom ||
|
|
ev.command()->id() == CommandId::Scroll) {
|
|
return;
|
|
}
|
|
|
|
m_editor->stop();
|
|
}
|
|
|
|
double PlayState::getNextFrameTime()
|
|
{
|
|
return
|
|
m_editor->sprite()->frameDuration(m_editor->frame())
|
|
/ m_editor->getAnimationSpeedMultiplier(); // The "speed multiplier" is a "duration divider"
|
|
}
|
|
|
|
} // namespace app
|