mirror of
https://github.com/aseprite/aseprite.git
synced 2025-01-30 15:32:38 +00:00
Add option to change the first frame number on the Timeline (fix #1300)
This commit is contained in:
parent
4e3a979b97
commit
eacf28b65a
@ -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" />
|
||||||
|
@ -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 when a new frame or layer is added." />
|
<check text="Show timeline automatically" id="autotimeline" tooltip="Show the timeline automatically 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 where it was started." />
|
<check text="Rewind on Stop" id="rewind_on_stop" tooltip="The 'Stop' button should rewind the animation where it was started." />
|
||||||
|
<hbox>
|
||||||
|
<label text="Default First Frame:" />
|
||||||
|
<entry id="first_frame" maxsize="3" />
|
||||||
|
</hbox>
|
||||||
</vbox>
|
</vbox>
|
||||||
|
|
||||||
<!-- Cursors -->
|
<!-- Cursors -->
|
||||||
|
@ -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="&Left" />
|
<separator cell_hspan="2" text="Position:" left="true" horizontal="true" />
|
||||||
<item text="&Right" />
|
<hbox>
|
||||||
<item text="&Bottom" hspan="2" />
|
<buttonset columns="2" id="position">
|
||||||
</buttonset>
|
<item text="&Left" />
|
||||||
|
<item text="&Right" />
|
||||||
|
<item text="&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" />
|
||||||
|
@ -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));
|
||||||
|
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -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();
|
||||||
|
@ -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)
|
||||||
|
@ -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();
|
||||||
|
@ -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()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
|
@ -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;
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user