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="color2" type="app::Color" default="app::Color::fromRgb(192, 192, 192)" migrate="Option.CheckedBgColor2" />
</section>
<section id="timeline">
<option id="first_frame" type="int" default="1" />
</section>
<section id="onionskin">
<option id="active" type="bool" default="false" migrate="Onionskin.Enabled" />
<option id="prev_frames" type="int" default="1" migrate="Onionskin.PrevFrames" />

View File

@ -82,6 +82,10 @@
<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="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>
<!-- Cursors -->

View File

@ -2,6 +2,8 @@
<!-- Copyright (C) 2014-2016 by David Capello -->
<gui>
<vbox id="timeline_conf">
<hbox>
<vbox>
<separator cell_hspan="2" text="Position:" left="true" horizontal="true" />
<hbox>
<buttonset columns="2" id="position">
@ -10,6 +12,15 @@
<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>
<separator cell_hspan="2" text="Onion Skin:" left="true" horizontal="true" />
<grid columns="2">

View File

@ -66,7 +66,7 @@ void FramePropertiesCommand::onLoadParams(const Params& params)
}
else {
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 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));

View File

@ -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;
};

View File

@ -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();

View File

@ -47,6 +47,7 @@ ConfigureTimelinePopup::ConfigureTimelinePopup()
addChild(m_box);
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->tint()->Click.connect(base::Bind<void>(&ConfigureTimelinePopup::onChangeType, 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->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)

View File

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

View File

@ -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()));
}

View File

@ -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);
}
// 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<app::Document*>(m_doc));
m_docControls->setVisible(true);
showSnapToGridWarning(
Preferences::instance().document(
static_cast<app::Document*>(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);

View File

@ -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<void>(&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;
}

View File

@ -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)