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="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" />
|
||||
|
@ -82,6 +82,10 @@
|
||||
<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="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>
|
||||
|
||||
<!-- Cursors -->
|
||||
|
@ -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="&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">
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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)
|
||||
|
@ -33,6 +33,7 @@ namespace app {
|
||||
protected:
|
||||
bool onProcessMessage(ui::Message* msg) override;
|
||||
void onChangePosition();
|
||||
void onChangeFirstFrame();
|
||||
void onChangeType();
|
||||
void onOpacity();
|
||||
void onOpacityStep();
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user