diff --git a/data/pref.xml b/data/pref.xml
index 2cf5914e9..f8a8c1d32 100644
--- a/data/pref.xml
+++ b/data/pref.xml
@@ -269,6 +269,9 @@
+
diff --git a/data/widgets/options.xml b/data/widgets/options.xml
index 75043bd9e..850fac377 100644
--- a/data/widgets/options.xml
+++ b/data/widgets/options.xml
@@ -82,6 +82,10 @@
+
+
+
+
diff --git a/data/widgets/timeline_conf.xml b/data/widgets/timeline_conf.xml
index df995869e..cd45c9631 100644
--- a/data/widgets/timeline_conf.xml
+++ b/data/widgets/timeline_conf.xml
@@ -2,13 +2,24 @@
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/app/commands/cmd_frame_properties.cpp b/src/app/commands/cmd_frame_properties.cpp
index bf1bdc666..20c087eb2 100644
--- a/src/app/commands/cmd_frame_properties.cpp
+++ b/src/app/commands/cmd_frame_properties.cpp
@@ -66,7 +66,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
}
else {
m_target = SPECIFIC_FRAME;
- m_frame = frame_t(base::convert_to(frame)-1);
+ m_frame = frame_t(base::convert_to(frame));
}
}
@@ -79,10 +79,11 @@ void FramePropertiesCommand::onExecute(Context* context)
{
const ContextReader reader(context);
const Sprite* sprite = reader.sprite();
-
+ auto& docPref = Preferences::instance().document(context->activeDocument());
app::gen::FrameProperties window;
frame_t firstFrame = 0;
frame_t lastFrame = 0;
+ int base = docPref.timeline.firstFrame();
switch (m_target) {
@@ -104,14 +105,14 @@ void FramePropertiesCommand::onExecute(Context* context)
}
case SPECIFIC_FRAME:
- firstFrame = lastFrame = m_frame;
+ firstFrame = lastFrame = m_frame-base;
break;
}
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
- window.frame()->setTextf("%d", (int)firstFrame+1);
+ window.frame()->setTextf("%d", (int)firstFrame+base);
window.frlen()->setTextf("%d", sprite->frameDuration(firstFrame));
diff --git a/src/app/commands/cmd_goto_frame.cpp b/src/app/commands/cmd_goto_frame.cpp
index 9c6028fd9..bd1611392 100644
--- a/src/app/commands/cmd_goto_frame.cpp
+++ b/src/app/commands/cmd_goto_frame.cpp
@@ -1,5 +1,5 @@
// Aseprite
-// Copyright (C) 2001-2015 David Capello
+// Copyright (C) 2001-2016 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@@ -137,22 +137,27 @@ class GotoFrameCommand : public GotoCommand {
public:
GotoFrameCommand() : GotoCommand("GotoFrame",
"Go to Frame")
- , m_frame(0) { }
+ , m_showUI(true) { }
Command* clone() const override { return new GotoFrameCommand(*this); }
protected:
void onLoadParams(const Params& params) override {
std::string frame = params.get("frame");
- if (!frame.empty()) m_frame = strtol(frame.c_str(), NULL, 10);
- else m_frame = 0;
+ if (!frame.empty()) {
+ m_frame = strtol(frame.c_str(), nullptr, 10);
+ m_showUI = false;
+ }
+ else
+ m_showUI = true;
}
frame_t onGetFrame(Editor* editor) override {
- if (m_frame == 0) {
+ auto& docPref = editor->docPref();
+
+ if (m_showUI) {
app::gen::GotoFrame window;
-
- window.frame()->setTextf("%d", editor->frame()+1);
-
+ window.frame()->setTextf(
+ "%d", editor->frame()+docPref.timeline.firstFrame());
window.openWindowInForeground();
if (window.closer() != window.ok())
return editor->frame();
@@ -160,12 +165,11 @@ protected:
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:
- // The frame to go. 0 is "show the UI dialog", another value is the
- // frame (1 is the first name for the user).
+ bool m_showUI;
int m_frame;
};
diff --git a/src/app/commands/cmd_options.cpp b/src/app/commands/cmd_options.cpp
index b8e98dba6..a3ee62212 100644
--- a/src/app/commands/cmd_options.cpp
+++ b/src/app/commands/cmd_options.cpp
@@ -109,6 +109,8 @@ public:
if (m_pref.general.rewindOnStop())
rewindOnStop()->setSelected(true);
+ firstFrame()->setTextf("%d", m_globPref.timeline.firstFrame());
+
if (m_pref.general.expandMenubarOnMouseover())
expandMenubarOnMouseover()->setSelected(true);
@@ -244,6 +246,7 @@ public:
void saveConfig() {
m_pref.general.autoshowTimeline(autotimeline()->isSelected());
m_pref.general.rewindOnStop(rewindOnStop()->isSelected());
+ m_globPref.timeline.firstFrame(firstFrame()->textInt());
m_pref.general.showFullPath(showFullPath()->isSelected());
bool expandOnMouseover = expandMenubarOnMouseover()->isSelected();
diff --git a/src/app/ui/configure_timeline_popup.cpp b/src/app/ui/configure_timeline_popup.cpp
index 7a9a93ced..a2e492444 100644
--- a/src/app/ui/configure_timeline_popup.cpp
+++ b/src/app/ui/configure_timeline_popup.cpp
@@ -47,6 +47,7 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
addChild(m_box);
m_box->position()->ItemChange.connect(base::Bind(&ConfigureTimelinePopup::onChangePosition, this));
+ m_box->firstFrame()->Change.connect(base::Bind(&ConfigureTimelinePopup::onChangeFirstFrame, this));
m_box->merge()->Click.connect(base::Bind(&ConfigureTimelinePopup::onChangeType, this));
m_box->tint()->Click.connect(base::Bind(&ConfigureTimelinePopup::onChangeType, this));
m_box->opacity()->Change.connect(base::Bind(&ConfigureTimelinePopup::onOpacity, this));
@@ -82,6 +83,9 @@ void ConfigureTimelinePopup::updateWidgetsFromCurrentSettings()
}
m_box->position()->setSelectedItem(selItem, false);
+ m_box->firstFrame()->setTextf(
+ "%d", docPref.timeline.firstFrame());
+
switch (docPref.onionskin.type()) {
case app::gen::OnionskinType::MERGE:
m_box->merge()->setSelected(true);
@@ -141,6 +145,12 @@ void ConfigureTimelinePopup::onChangePosition()
Preferences::instance().general.timelinePosition(newTimelinePos);
}
+void ConfigureTimelinePopup::onChangeFirstFrame()
+{
+ docPref().timeline.firstFrame(
+ m_box->firstFrame()->textInt());
+}
+
void ConfigureTimelinePopup::onChangeType()
{
if (m_lockUpdates)
diff --git a/src/app/ui/configure_timeline_popup.h b/src/app/ui/configure_timeline_popup.h
index 4d41ab588..2987422b0 100644
--- a/src/app/ui/configure_timeline_popup.h
+++ b/src/app/ui/configure_timeline_popup.h
@@ -33,6 +33,7 @@ namespace app {
protected:
bool onProcessMessage(ui::Message* msg) override;
void onChangePosition();
+ void onChangeFirstFrame();
void onChangeType();
void onOpacity();
void onOpacityStep();
diff --git a/src/app/ui/editor/standby_state.cpp b/src/app/ui/editor/standby_state.cpp
index 7ca988399..decfea121 100644
--- a/src/app/ui/editor/standby_state.cpp
+++ b/src/app/ui/editor/standby_state.cpp
@@ -487,7 +487,7 @@ bool StandbyState::onUpdateStatusBar(Editor* editor)
if (sprite->totalFrames() > 1) {
sprintf(
buf+std::strlen(buf), " :frame: %d :clock: %d",
- editor->frame()+1,
+ editor->frame()+editor->docPref().timeline.firstFrame(),
sprite->frameDuration(editor->frame()));
}
diff --git a/src/app/ui/status_bar.cpp b/src/app/ui/status_bar.cpp
index 25cc8437f..b11072957 100644
--- a/src/app/ui/status_bar.cpp
+++ b/src/app/ui/status_bar.cpp
@@ -489,13 +489,11 @@ public:
scancode == kKeyEnterPad)) {
Command* cmd = CommandsModule::instance()->getCommandByName(CommandId::GotoFrame);
Params params;
- int frame = textInt();
- if (frame > 0) {
- params.set("frame", text().c_str());
- UIContext::instance()->executeCommand(cmd, params);
- }
+ params.set("frame", text().c_str());
+ UIContext::instance()->executeCommand(cmd, params);
+
// Select the text again
- selectText(0, -1);
+ selectAllText();
releaseFocus();
return true; // Key used.
}
@@ -740,13 +738,15 @@ void StatusBar::onActiveSiteChange(const doc::Site& site)
ASSERT(m_doc == site.document());
}
+ auto& docPref = Preferences::instance().document(
+ static_cast(m_doc));
+
m_docControls->setVisible(true);
- showSnapToGridWarning(
- Preferences::instance().document(
- static_cast(m_doc)).grid.snap());
+ showSnapToGridWarning(docPref.grid.snap());
// Current frame
- m_currentFrame->setTextf("%d", site.frame()+1);
+ m_currentFrame->setTextf(
+ "%d", site.frame()+docPref.timeline.firstFrame());
}
else {
ASSERT(m_doc == nullptr);
diff --git a/src/app/ui/timeline.cpp b/src/app/ui/timeline.cpp
index 52976605a..bb6d3332b 100644
--- a/src/app/ui/timeline.cpp
+++ b/src/app/ui/timeline.cpp
@@ -38,6 +38,7 @@
#include "app/ui/workspace.h"
#include "app/ui_context.h"
#include "app/util/clipboard.h"
+#include "base/bind.h"
#include "base/convert_to.h"
#include "base/memory.h"
#include "base/scoped_value.h"
@@ -206,6 +207,9 @@ void Timeline::updateUsingEditor(Editor* editor)
m_hot.part = PART_NOTHING;
m_clk.part = PART_NOTHING;
+ m_firstFrameConn = Preferences::instance().document(m_document)
+ .timeline.firstFrame.AfterChange.connect(base::Bind(&Timeline::invalidate, this));
+
setFocusStop(true);
regenerateLayers();
setViewScroll(viewScroll());
@@ -214,6 +218,8 @@ void Timeline::updateUsingEditor(Editor* editor)
void Timeline::detachDocument()
{
+ m_firstFrameConn.disconnect();
+
if (m_document) {
m_document->remove_observer(this);
m_document = NULL;
@@ -1357,8 +1363,11 @@ void Timeline::drawHeaderFrame(ui::Graphics* g, frame_t frame)
return;
// Draw the header for the layers.
- char buf[256];
- std::sprintf(buf, "%d", (frame+1)%100); // Draw only the first two digits.
+ char buf[4];
+ std::snprintf(
+ buf, sizeof(buf), "%d",
+ // Draw only the first two digits
+ (docPref().timeline.firstFrame()+frame) % 100);
she::Font* oldFont = g->font();
g->setFont(skinTheme()->getMiniFont());
@@ -2236,9 +2245,10 @@ void Timeline::updateStatusBar(ui::Message* msg)
case PART_HEADER_FRAME:
if (validFrame(m_hot.frame)) {
- sb->setStatusText(0,
+ sb->setStatusText(
+ 0,
":frame: %d :clock: %d",
- (int)m_hot.frame+1,
+ (int)m_hot.frame+docPref().timeline.firstFrame(),
m_sprite->frameDuration(m_hot.frame));
return;
}
diff --git a/src/app/ui/timeline.h b/src/app/ui/timeline.h
index ff4c60ec9..602a3489c 100644
--- a/src/app/ui/timeline.h
+++ b/src/app/ui/timeline.h
@@ -278,6 +278,7 @@ namespace app {
// Configure timeline
ConfigureTimelinePopup* m_confPopup;
obs::scoped_connection m_ctxConn;
+ obs::connection m_firstFrameConn;
// Marching ants stuff to show the range in the clipboard.
// TODO merge this with the marching ants of the sprite editor (ui::Editor)