Add option to change the first frame number on the Timeline (fix #1300)

This commit is contained in:
David Capello 2016-11-22 18:05:56 -03:00
parent 4e3a979b97
commit eacf28b65a
12 changed files with 85 additions and 37 deletions

View File

@ -269,6 +269,9 @@
<option id="color1" type="app::Color" default="app::Color::fromRgb(128, 128, 128)" migrate="Option.CheckedBgColor1" /> <option id="color1" type="app::Color" default="app::Color::fromRgb(128, 128, 128)" migrate="Option.CheckedBgColor1" />
<option id="color2" type="app::Color" default="app::Color::fromRgb(192, 192, 192)" migrate="Option.CheckedBgColor2" /> <option id="color2" type="app::Color" default="app::Color::fromRgb(192, 192, 192)" migrate="Option.CheckedBgColor2" />
</section> </section>
<section id="timeline">
<option id="first_frame" type="int" default="1" />
</section>
<section id="onionskin"> <section id="onionskin">
<option id="active" type="bool" default="false" migrate="Onionskin.Enabled" /> <option id="active" type="bool" default="false" migrate="Onionskin.Enabled" />
<option id="prev_frames" type="int" default="1" migrate="Onionskin.PrevFrames" /> <option id="prev_frames" type="int" default="1" migrate="Onionskin.PrevFrames" />

View File

@ -82,6 +82,10 @@
<separator text="Timeline" horizontal="true" /> <separator text="Timeline" horizontal="true" />
<check text="Show timeline automatically" id="autotimeline" tooltip="Show the timeline automatically&#10;when a new frame or layer is added." /> <check text="Show timeline automatically" id="autotimeline" tooltip="Show the timeline automatically&#10;when a new frame or layer is added." />
<check text="Rewind on Stop" id="rewind_on_stop" tooltip="The 'Stop' button should rewind the animation&#10;where it was started." /> <check text="Rewind on Stop" id="rewind_on_stop" tooltip="The 'Stop' button should rewind the animation&#10;where it was started." />
<hbox>
<label text="Default First Frame:" />
<entry id="first_frame" maxsize="3" />
</hbox>
</vbox> </vbox>
<!-- Cursors --> <!-- Cursors -->

View File

@ -2,13 +2,24 @@
<!-- Copyright (C) 2014-2016 by David Capello --> <!-- Copyright (C) 2014-2016 by David Capello -->
<gui> <gui>
<vbox id="timeline_conf"> <vbox id="timeline_conf">
<separator cell_hspan="2" text="Position:" left="true" horizontal="true" />
<hbox> <hbox>
<buttonset columns="2" id="position"> <vbox>
<item text="&amp;Left" /> <separator cell_hspan="2" text="Position:" left="true" horizontal="true" />
<item text="&amp;Right" /> <hbox>
<item text="&amp;Bottom" hspan="2" /> <buttonset columns="2" id="position">
</buttonset> <item text="&amp;Left" />
<item text="&amp;Right" />
<item text="&amp;Bottom" hspan="2" />
</buttonset>
</hbox>
</vbox>
<vbox>
<separator cell_hspan="2" text="Frame Header:" left="true" horizontal="true" />
<hbox>
<label text="First Frame:" />
<entry id="first_frame" maxsize="3" />
</hbox>
</vbox>
</hbox> </hbox>
<separator cell_hspan="2" text="Onion Skin:" left="true" horizontal="true" /> <separator cell_hspan="2" text="Onion Skin:" left="true" horizontal="true" />

View File

@ -66,7 +66,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
} }
else { else {
m_target = SPECIFIC_FRAME; m_target = SPECIFIC_FRAME;
m_frame = frame_t(base::convert_to<int>(frame)-1); m_frame = frame_t(base::convert_to<int>(frame));
} }
} }
@ -79,10 +79,11 @@ void FramePropertiesCommand::onExecute(Context* context)
{ {
const ContextReader reader(context); const ContextReader reader(context);
const Sprite* sprite = reader.sprite(); const Sprite* sprite = reader.sprite();
auto& docPref = Preferences::instance().document(context->activeDocument());
app::gen::FrameProperties window; app::gen::FrameProperties window;
frame_t firstFrame = 0; frame_t firstFrame = 0;
frame_t lastFrame = 0; frame_t lastFrame = 0;
int base = docPref.timeline.firstFrame();
switch (m_target) { switch (m_target) {
@ -104,14 +105,14 @@ void FramePropertiesCommand::onExecute(Context* context)
} }
case SPECIFIC_FRAME: case SPECIFIC_FRAME:
firstFrame = lastFrame = m_frame; firstFrame = lastFrame = m_frame-base;
break; break;
} }
if (firstFrame != lastFrame) if (firstFrame != lastFrame)
window.frame()->setTextf("[%d...%d]", (int)firstFrame+1, (int)lastFrame+1); window.frame()->setTextf("[%d...%d]", (int)firstFrame+base, (int)lastFrame+base);
else else
window.frame()->setTextf("%d", (int)firstFrame+1); window.frame()->setTextf("%d", (int)firstFrame+base);
window.frlen()->setTextf("%d", sprite->frameDuration(firstFrame)); window.frlen()->setTextf("%d", sprite->frameDuration(firstFrame));

View File

@ -1,5 +1,5 @@
// Aseprite // Aseprite
// Copyright (C) 2001-2015 David Capello // Copyright (C) 2001-2016 David Capello
// //
// This program is distributed under the terms of // This program is distributed under the terms of
// the End-User License Agreement for Aseprite. // the End-User License Agreement for Aseprite.
@ -137,22 +137,27 @@ class GotoFrameCommand : public GotoCommand {
public: public:
GotoFrameCommand() : GotoCommand("GotoFrame", GotoFrameCommand() : GotoCommand("GotoFrame",
"Go to Frame") "Go to Frame")
, m_frame(0) { } , m_showUI(true) { }
Command* clone() const override { return new GotoFrameCommand(*this); } Command* clone() const override { return new GotoFrameCommand(*this); }
protected: protected:
void onLoadParams(const Params& params) override { void onLoadParams(const Params& params) override {
std::string frame = params.get("frame"); std::string frame = params.get("frame");
if (!frame.empty()) m_frame = strtol(frame.c_str(), NULL, 10); if (!frame.empty()) {
else m_frame = 0; m_frame = strtol(frame.c_str(), nullptr, 10);
m_showUI = false;
}
else
m_showUI = true;
} }
frame_t onGetFrame(Editor* editor) override { frame_t onGetFrame(Editor* editor) override {
if (m_frame == 0) { auto& docPref = editor->docPref();
if (m_showUI) {
app::gen::GotoFrame window; app::gen::GotoFrame window;
window.frame()->setTextf(
window.frame()->setTextf("%d", editor->frame()+1); "%d", editor->frame()+docPref.timeline.firstFrame());
window.openWindowInForeground(); window.openWindowInForeground();
if (window.closer() != window.ok()) if (window.closer() != window.ok())
return editor->frame(); return editor->frame();
@ -160,12 +165,11 @@ protected:
m_frame = window.frame()->textInt(); m_frame = window.frame()->textInt();
} }
return MID(0, m_frame-1, editor->sprite()->lastFrame()); return MID(0, m_frame-docPref.timeline.firstFrame(), editor->sprite()->lastFrame());
} }
private: private:
// The frame to go. 0 is "show the UI dialog", another value is the bool m_showUI;
// frame (1 is the first name for the user).
int m_frame; int m_frame;
}; };

View File

@ -109,6 +109,8 @@ public:
if (m_pref.general.rewindOnStop()) if (m_pref.general.rewindOnStop())
rewindOnStop()->setSelected(true); rewindOnStop()->setSelected(true);
firstFrame()->setTextf("%d", m_globPref.timeline.firstFrame());
if (m_pref.general.expandMenubarOnMouseover()) if (m_pref.general.expandMenubarOnMouseover())
expandMenubarOnMouseover()->setSelected(true); expandMenubarOnMouseover()->setSelected(true);
@ -244,6 +246,7 @@ public:
void saveConfig() { void saveConfig() {
m_pref.general.autoshowTimeline(autotimeline()->isSelected()); m_pref.general.autoshowTimeline(autotimeline()->isSelected());
m_pref.general.rewindOnStop(rewindOnStop()->isSelected()); m_pref.general.rewindOnStop(rewindOnStop()->isSelected());
m_globPref.timeline.firstFrame(firstFrame()->textInt());
m_pref.general.showFullPath(showFullPath()->isSelected()); m_pref.general.showFullPath(showFullPath()->isSelected());
bool expandOnMouseover = expandMenubarOnMouseover()->isSelected(); bool expandOnMouseover = expandMenubarOnMouseover()->isSelected();

View File

@ -47,6 +47,7 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
addChild(m_box); addChild(m_box);
m_box->position()->ItemChange.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangePosition, this)); m_box->position()->ItemChange.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangePosition, this));
m_box->firstFrame()->Change.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeFirstFrame, this));
m_box->merge()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, this)); m_box->merge()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, this));
m_box->tint()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, this)); m_box->tint()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, this));
m_box->opacity()->Change.connect(base::Bind<void>(&ConfigureTimelinePopup::onOpacity, this)); m_box->opacity()->Change.connect(base::Bind<void>(&ConfigureTimelinePopup::onOpacity, this));
@ -82,6 +83,9 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
} }
m_box->position()->setSelectedItem(selItem, false); m_box->position()->setSelectedItem(selItem, false);
m_box->firstFrame()->setTextf(
"%d", docPref.timeline.firstFrame());
switch (docPref.onionskin.type()) { switch (docPref.onionskin.type()) {
case app::gen::OnionskinType::MERGE: case app::gen::OnionskinType::MERGE:
m_box->merge()->setSelected(true); m_box->merge()->setSelected(true);
@ -141,6 +145,12 @@ void ConfigureTimelinePopup::onChangePosition()
Preferences::instance().general.timelinePosition(newTimelinePos); Preferences::instance().general.timelinePosition(newTimelinePos);
} }
void ConfigureTimelinePopup::onChangeFirstFrame()
{
docPref().timeline.firstFrame(
m_box->firstFrame()->textInt());
}
void ConfigureTimelinePopup::onChangeType() void ConfigureTimelinePopup::onChangeType()
{ {
if (m_lockUpdates) if (m_lockUpdates)

View File

@ -33,6 +33,7 @@ namespace app {
protected: protected:
bool onProcessMessage(ui::Message* msg) override; bool onProcessMessage(ui::Message* msg) override;
void onChangePosition(); void onChangePosition();
void onChangeFirstFrame();
void onChangeType(); void onChangeType();
void onOpacity(); void onOpacity();
void onOpacityStep(); void onOpacityStep();

View File

@ -487,7 +487,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
if (sprite->totalFrames() > 1) { if (sprite->totalFrames() > 1) {
sprintf( sprintf(
buf+std::strlen(buf), " :frame: %d :clock: %d", buf+std::strlen(buf), " :frame: %d :clock: %d",
editor->frame()+1, editor->frame()+editor->docPref().timeline.firstFrame(),
sprite->frameDuration(editor->frame())); sprite->frameDuration(editor->frame()));
} }

View File

@ -489,13 +489,11 @@ public:
scancode == kKeyEnterPad)) { scancode == kKeyEnterPad)) {
Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoFrame); Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoFrame);
Params params; Params params;
int frame = textInt(); params.set("frame", text().c_str());
if (frame > 0) { UIContext::instance()->executeCommand(cmd, params);
params.set("frame", text().c_str());
UIContext::instance()->executeCommand(cmd, params);
}
// Select the text again // Select the text again
selectText(0, -1); selectAllText();
releaseFocus(); releaseFocus();
return true; // Key used. return true; // Key used.
} }
@ -740,13 +738,15 @@ void StatusBar::onActiveSiteChange(const doc::Site& site)
ASSERT(m_doc == site.document()); ASSERT(m_doc == site.document());
} }
auto& docPref = Preferences::instance().document(
static_cast<app::Document*>(m_doc));
m_docControls->setVisible(true); m_docControls->setVisible(true);
showSnapToGridWarning( showSnapToGridWarning(docPref.grid.snap());
Preferences::instance().document(
static_cast<app::Document*>(m_doc)).grid.snap());
// Current frame // Current frame
m_currentFrame->setTextf("%d", site.frame()+1); m_currentFrame->setTextf(
"%d", site.frame()+docPref.timeline.firstFrame());
} }
else { else {
ASSERT(m_doc == nullptr); ASSERT(m_doc == nullptr);

View File

@ -38,6 +38,7 @@
#include "app/ui/workspace.h" #include "app/ui/workspace.h"
#include "app/ui_context.h" #include "app/ui_context.h"
#include "app/util/clipboard.h" #include "app/util/clipboard.h"
#include "base/bind.h"
#include "base/convert_to.h" #include "base/convert_to.h"
#include "base/memory.h" #include "base/memory.h"
#include "base/scoped_value.h" #include "base/scoped_value.h"
@ -206,6 +207,9 @@ void Timeline::updateUsingEditor(Editor* editor)
m_hot.part = PART_NOTHING; m_hot.part = PART_NOTHING;
m_clk.part = PART_NOTHING; m_clk.part = PART_NOTHING;
m_firstFrameConn = Preferences::instance().document(m_document)
.timeline.firstFrame.AfterChange.connect(base::Bind<void>(&Timeline::invalidate, this));
setFocusStop(true); setFocusStop(true);
regenerateLayers(); regenerateLayers();
setViewScroll(viewScroll()); setViewScroll(viewScroll());
@ -214,6 +218,8 @@ void Timeline::updateUsingEditor(Editor* editor)
void Timeline::detachDocument() void Timeline::detachDocument()
{ {
m_firstFrameConn.disconnect();
if (m_document) { if (m_document) {
m_document->remove_observer(this); m_document->remove_observer(this);
m_document = NULL; m_document = NULL;
@ -1357,8 +1363,11 @@ void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame)
return; return;
// Draw the header for the layers. // Draw the header for the layers.
char buf[256]; char buf[4];
std::sprintf(buf, "%d", (frame+1)%100); // Draw only the first two digits. std::snprintf(
buf, sizeof(buf), "%d",
// Draw only the first two digits
(docPref().timeline.firstFrame()+frame) % 100);
she::Font* oldFont = g->font(); she::Font* oldFont = g->font();
g->setFont(skinTheme()->getMiniFont()); g->setFont(skinTheme()->getMiniFont());
@ -2236,9 +2245,10 @@ void Timeline::updateStatusBar(ui::Message* msg)
case PART_HEADER_FRAME: case PART_HEADER_FRAME:
if (validFrame(m_hot.frame)) { if (validFrame(m_hot.frame)) {
sb->setStatusText(0, sb->setStatusText(
0,
":frame: %d :clock: %d", ":frame: %d :clock: %d",
(int)m_hot.frame+1, (int)m_hot.frame+docPref().timeline.firstFrame(),
m_sprite->frameDuration(m_hot.frame)); m_sprite->frameDuration(m_hot.frame));
return; return;
} }

View File

@ -278,6 +278,7 @@ namespace app {
// Configure timeline // Configure timeline
ConfigureTimelinePopup* m_confPopup; ConfigureTimelinePopup* m_confPopup;
obs::scoped_connection m_ctxConn; obs::scoped_connection m_ctxConn;
obs::connection m_firstFrameConn;
// Marching ants stuff to show the range in the clipboard. // Marching ants stuff to show the range in the clipboard.
// TODO merge this with the marching ants of the sprite editor (ui::Editor) // TODO merge this with the marching ants of the sprite editor (ui::Editor)