diff --git a/src/app/ui/ani_controls.cpp b/src/app/ui/ani_controls.cpp index aac1c5053..81d14e574 100644 --- a/src/app/ui/ani_controls.cpp +++ b/src/app/ui/ani_controls.cpp @@ -105,4 +105,12 @@ void AniControls::onPlayButton() } } +void AniControls::onRightClick(Item* item) +{ + ButtonSet::onRightClick(item); + + if (item == getItem(ACTION_PLAY) && current_editor) + current_editor->showAnimationSpeedMultiplierPopup(); +} + } // namespace app diff --git a/src/app/ui/ani_controls.h b/src/app/ui/ani_controls.h index fdf78074f..cd723fd1a 100644 --- a/src/app/ui/ani_controls.h +++ b/src/app/ui/ani_controls.h @@ -24,6 +24,9 @@ namespace app { void updateUsingEditor(Editor* editor); + protected: + void onRightClick(Item* item) override; + private: void onPlayButton(); }; diff --git a/src/app/ui/button_set.cpp b/src/app/ui/button_set.cpp index b4dcea942..5f38b94a9 100644 --- a/src/app/ui/button_set.cpp +++ b/src/app/ui/button_set.cpp @@ -110,8 +110,10 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg) buttonSet()->setSelectedItem(this); invalidate(); - if (!buttonSet()->m_triggerOnMouseUp) + if (static_cast(msg)->left() && + !buttonSet()->m_triggerOnMouseUp) { buttonSet()->onItemChange(); + } break; case ui::kMouseUpMessage: @@ -119,8 +121,13 @@ bool ButtonSet::Item::onProcessMessage(ui::Message* msg) releaseMouse(); invalidate(); - if (buttonSet()->m_triggerOnMouseUp) - buttonSet()->onItemChange(); + if (static_cast(msg)->left()) { + if (buttonSet()->m_triggerOnMouseUp) + buttonSet()->onItemChange(); + } + else if (static_cast(msg)->right()) { + buttonSet()->onRightClick(this); + } } break; @@ -229,6 +236,11 @@ void ButtonSet::onItemChange() ItemChange(); } +void ButtonSet::onRightClick(Item* item) +{ + RightClick(item); +} + ButtonSet::Item* ButtonSet::findSelectedItem() const { for (Widget* child : getChildren()) { diff --git a/src/app/ui/button_set.h b/src/app/ui/button_set.h index fedc4d787..41c2eaa93 100644 --- a/src/app/ui/button_set.h +++ b/src/app/ui/button_set.h @@ -47,9 +47,11 @@ namespace app { void setTriggerOnMouseUp(bool state); Signal0 ItemChange; + Signal1 RightClick; protected: virtual void onItemChange(); + virtual void onRightClick(Item* item); private: Item* findSelectedItem() const; diff --git a/src/app/ui/editor/editor.cpp b/src/app/ui/editor/editor.cpp index e9d5cea40..fad08ab8c 100644 --- a/src/app/ui/editor/editor.cpp +++ b/src/app/ui/editor/editor.cpp @@ -43,6 +43,7 @@ #include "app/ui_context.h" #include "app/util/boundary.h" #include "base/bind.h" +#include "base/convert_to.h" #include "base/unique_ptr.h" #include "doc/conversion_she.h" #include "doc/doc.h" @@ -164,6 +165,7 @@ Editor::Editor(Document* document, EditorFlags flags) , m_docView(NULL) , m_flags(flags) , m_secondaryButton(false) + , m_aniSpeed(1.0) { // Add the first state into the history. m_statesHistory.push(m_state); @@ -1561,6 +1563,31 @@ bool Editor::isPlaying() const return (dynamic_cast(m_state.get()) != nullptr); } +void Editor::showAnimationSpeedMultiplierPopup() +{ + double options[] = { 0.25, 0.5, 1.0, 1.5, 2.0, 3.0 }; + Menu menu; + + for (double option : options) { + MenuItem* item = new MenuItem("x" + base::convert_to(option)); + item->Click.connect(Bind(&Editor::setAnimationSpeedMultiplier, this, option)); + item->setSelected(m_aniSpeed == option); + menu.addChild(item); + } + + menu.showPopup(ui::get_mouse_position()); +} + +double Editor::getAnimationSpeedMultiplier() const +{ + return m_aniSpeed; +} + +void Editor::setAnimationSpeedMultiplier(double speed) +{ + m_aniSpeed = speed; +} + // static ImageBufferPtr Editor::getRenderImageBuffer() { diff --git a/src/app/ui/editor/editor.h b/src/app/ui/editor/editor.h index 4ca92ed63..21bab9a5a 100644 --- a/src/app/ui/editor/editor.h +++ b/src/app/ui/editor/editor.h @@ -194,6 +194,11 @@ namespace app { void stop(); bool isPlaying() const; + // Shows a popup menu to change the editor animation speed. + void showAnimationSpeedMultiplierPopup(); + double getAnimationSpeedMultiplier() const; + void setAnimationSpeedMultiplier(double speed); + // Returns the buffer used to render editor viewports. // E.g. It can be re-used by PreviewCommand static ImageBufferPtr getRenderImageBuffer(); @@ -311,6 +316,9 @@ namespace app { bool m_secondaryButton; + // Animation speed multiplier. + double m_aniSpeed; + static doc::ImageBufferPtr m_renderBuffer; static AppRender m_renderEngine; }; diff --git a/src/app/ui/editor/play_state.cpp b/src/app/ui/editor/play_state.cpp index cfe0963dd..1b0ae90da 100644 --- a/src/app/ui/editor/play_state.cpp +++ b/src/app/ui/editor/play_state.cpp @@ -50,7 +50,7 @@ void PlayState::onAfterChangeState(Editor* editor) } m_toScroll = false; - m_nextFrameTime = editor->sprite()->frameDuration(editor->frame()); + m_nextFrameTime = getNextFrameTime(); m_curFrameTick = ui::clock(); m_pingPongForward = true; @@ -138,7 +138,7 @@ void PlayState::onPlaybackTick() m_pingPongForward); m_editor->setFrame(frame); - m_nextFrameTime += m_editor->sprite()->frameDuration(frame); + m_nextFrameTime += getNextFrameTime(); } m_curFrameTick = ui::clock(); @@ -169,4 +169,11 @@ void PlayState::onBeforeCommandExecution(Command* command) 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 diff --git a/src/app/ui/editor/play_state.h b/src/app/ui/editor/play_state.h index d6388c280..1d45415aa 100644 --- a/src/app/ui/editor/play_state.h +++ b/src/app/ui/editor/play_state.h @@ -35,13 +35,15 @@ namespace app { // ContextObserver void onBeforeCommandExecution(Command* command); + double getNextFrameTime(); + Editor* m_editor; bool m_toScroll; ui::Timer m_playTimer; // Number of milliseconds to go to the next frame if m_playTimer // is activated. - int m_nextFrameTime; + double m_nextFrameTime; int m_curFrameTick; bool m_pingPongForward; diff --git a/src/app/ui/preview_editor.cpp b/src/app/ui/preview_editor.cpp index 46ee322aa..bba5b2dfc 100644 --- a/src/app/ui/preview_editor.cpp +++ b/src/app/ui/preview_editor.cpp @@ -102,6 +102,8 @@ public: bool isPlaying() const { return m_isPlaying; } + Signal0 Popup; + protected: void onClick(Event& ev) override { @@ -143,6 +145,20 @@ protected: case kSetCursorMessage: ui::set_mouse_cursor(kArrowCursor); return true; + + case kMouseUpMessage: { + MouseMessage* mouseMsg = static_cast(msg); + if (mouseMsg->right()) { + if (hasCapture()) { + releaseMouse(); + Popup(); + + setSelected(false); + return true; + } + } + break; + } } return SkinButton