Pencil2D Animation
Download Community News Docs Contribute
  • Overview
  • Articles
  • Code
  •  
  • Class List
  • Class Index
  • Class Hierarchy
  • Class Members
  • File List
Loading...
Searching...
No Matches
  • app
  • src
timecontrols.cpp
1/*
2
3Pencil2D - Traditional Animation Software
4Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5Copyright (C) 2012-2020 Matthew Chiawen Chang
6
7This program is free software; you can redistribute it and/or
8modify it under the terms of the GNU General Public License
9as published by the Free Software Foundation; version 2 of the License.
10
11This program is distributed in the hope that it will be useful,
12but WITHOUT ANY WARRANTY; without even the implied warranty of
13MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14GNU General Public License for more details.
15
16*/
17
18#include "timecontrols.h"
19
20#include <QLabel>
21#include <QSettings>
22#include <QMenu>
23#include <QDebug>
24#include "editor.h"
25#include "playbackmanager.h"
26#include "layermanager.h"
27#include "pencildef.h"
28#include "util.h"
29#include "preferencemanager.h"
30#include "timeline.h"
31#include "pencildef.h"
32
33TimeControls::TimeControls(TimeLine* parent) : QToolBar(parent)
34{
35 mTimeline = parent;
36}
37
38void TimeControls::initUI()
39{
40 QSettings settings(PENCIL2D, PENCIL2D);
41
42 mFpsBox = new QSpinBox(this);
43 mFpsBox->setFixedHeight(24);
44 mFpsBox->setValue(settings.value("Fps").toInt());
45 mFpsBox->setMinimum(1);
46 mFpsBox->setMaximum(90);
47 mFpsBox->setSuffix(tr(" fps"));
48 mFpsBox->setToolTip(tr("Frames per second"));
49 mFpsBox->setFocusPolicy(Qt::WheelFocus);
50
51 mFps = mFpsBox->value();
52 mTimecodeSelect = new QToolButton(this);
53
54 QMenu* timeSelectMenu = new QMenu(tr("Display timecode", "Timeline menu for choose a timecode"), this);
55 mTimecodeSelect->setIcon(QIcon(":/icons/themes/playful/misc/more-options.svg"));
56
57 timeSelectMenu->addAction(mNoTimecodeAction = new QAction(tr("No text"), this));
58 timeSelectMenu->addAction(mOnlyFramesAction = new QAction(tr("Frames"), this));
59 timeSelectMenu->addAction(mSmpteAction = new QAction(tr("SMPTE Timecode"), this));
60 timeSelectMenu->addAction(mSffAction = new QAction(tr("SFF Timecode"), this));
61 mTimecodeSelect->setMenu(timeSelectMenu);
62 mTimecodeSelect->setPopupMode(QToolButton::InstantPopup);
63 mTimecodeSelect->setStyleSheet("::menu-indicator{ image: none; }");
64 mTimecodeLabelEnum = mEditor->preference()->getInt(SETTING::TIMECODE_TEXT);
65 mTimecodeLabel = new QLabel(this);
66 mTimecodeLabel->setContentsMargins(2, 0, 0, 0);
67 mTimecodeLabel->setText("");
68
69 switch (mTimecodeLabelEnum)
70 {
71 case NOTEXT:
72 mTimecodeLabel->setToolTip("");
73 break;
74 case FRAMES:
75 mTimecodeLabel->setToolTip(tr("Actual frame number"));
76 break;
77 case SMPTE:
78 mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF"));
79 break;
80 case SFF:
81 mTimecodeLabel->setToolTip(tr("Timecode format S:FF"));
82 break;
83 default:
84 mTimecodeLabel->setToolTip("");
85 }
86
87 mLoopStartSpinBox = new QSpinBox(this);
88 mLoopStartSpinBox->setFixedHeight(24);
89 mLoopStartSpinBox->setValue(settings.value("loopStart").toInt());
90 mLoopStartSpinBox->setMinimum(1);
91 mLoopStartSpinBox->setEnabled(false);
92 mLoopStartSpinBox->setToolTip(tr("Start of playback loop"));
93 mLoopStartSpinBox->setFocusPolicy(Qt::WheelFocus);
94
95 mLoopEndSpinBox = new QSpinBox(this);
96 mLoopEndSpinBox->setFixedHeight(24);
97 mLoopEndSpinBox->setValue(settings.value("loopEnd").toInt());
98 mLoopEndSpinBox->setMinimum(2);
99 mLoopEndSpinBox->setEnabled(false);
100 mLoopEndSpinBox->setToolTip(tr("End of playback loop"));
101 mLoopEndSpinBox->setFocusPolicy(Qt::WheelFocus);
102
103 mPlaybackRangeCheckBox = new QCheckBox(tr("Range"));
104 mPlaybackRangeCheckBox->setFixedHeight(24);
105 mPlaybackRangeCheckBox->setToolTip(tr("Playback range"));
106
107 mPlayButton = new QPushButton(this);
108 mPlayButton->setIconSize(QSize(22,22));
109 mLoopButton = new QPushButton(this);
110 mLoopButton->setIconSize(QSize(22,22));
111 mSoundButton = new QPushButton(this);
112 mSoundButton->setIconSize(QSize(22,22));
113 mSoundScrubButton = new QPushButton(this);
114 mSoundScrubButton->setIconSize(QSize(22,22));
115 mJumpToEndButton = new QPushButton(this);
116 mJumpToEndButton->setIconSize(QSize(22,22));
117 mJumpToStartButton = new QPushButton(this);
118 mJumpToStartButton->setIconSize(QSize(22,22));
119
120 mLoopIcon = QIcon(":icons/themes/playful/controls/control-loop.svg");
121 mSoundIcon = QIcon(":icons/themes/playful/controls/control-sound-enable.svg");
122 mSoundScrubIcon = QIcon(":icons/themes/playful/controls/control-sound-scrub.svg");
123 mJumpToEndIcon = QIcon(":icons/themes/playful/controls/control-play-end.svg");
124 mJumpToStartIcon = QIcon(":icons/themes/playful/controls/control-play-start.svg");
125 mStartIcon = QIcon(":icons/themes/playful/controls/control-play.svg");
126 mStopIcon = QIcon(":icons/themes/playful/controls/control-stop.svg");
127 mPlayButton->setIcon(mStartIcon);
128 mLoopButton->setIcon(mLoopIcon);
129 mSoundButton->setIcon(mSoundIcon);
130 mSoundScrubButton->setIcon(mSoundScrubIcon);
131 mJumpToEndButton->setIcon(mJumpToEndIcon);
132 mJumpToStartButton->setIcon(mJumpToStartIcon);
133
134 mPlayButton->setToolTip(tr("Play"));
135 mLoopButton->setToolTip(tr("Loop"));
136 mSoundButton->setToolTip(tr("Sound on/off"));
137 mSoundScrubButton->setToolTip(tr("Sound scrub on/off"));
138 mJumpToEndButton->setToolTip(tr("Jump to the End", "Tooltip of the jump to end button"));
139 mJumpToStartButton->setToolTip(tr("Jump to the Start", "Tooltip of the jump to start button"));
140
141 mLoopButton->setCheckable(true);
142 mSoundButton->setCheckable(true);
143 mSoundButton->setChecked(true);
144 mSoundScrubButton->setCheckable(true);
145 mSoundScrubButton->setChecked(mEditor->preference()->isOn(SETTING::SOUND_SCRUB_ACTIVE));
146
147
148 addWidget(mJumpToStartButton);
149 addWidget(mPlayButton);
150 addWidget(mJumpToEndButton);
151 addWidget(mLoopButton);
152 addWidget(mFpsBox);
153 addWidget(mPlaybackRangeCheckBox);
154 addWidget(mLoopStartSpinBox);
155 addWidget(mLoopEndSpinBox);
156 addWidget(mSoundButton);
157 addWidget(mSoundScrubButton);
158 addWidget(mTimecodeSelect);
159 mTimecodeLabelAction = addWidget(mTimecodeLabel);
160
161 makeConnections();
162
163 updateUI();
164}
165
166void TimeControls::updateUI()
167{
168 PlaybackManager* playback = mEditor->playback();
169
170 mPlaybackRangeCheckBox->setChecked(playback->isRangedPlaybackOn()); // don't block this signal since it enables start/end range spinboxes.
171
172 QSignalBlocker b1(mLoopStartSpinBox);
173 mLoopStartSpinBox->setValue(playback->markInFrame());
174
175 QSignalBlocker b2(mLoopEndSpinBox);
176 mLoopEndSpinBox->setValue(playback->markOutFrame());
177
178 QSignalBlocker b3(mFpsBox);
179 mFpsBox->setValue(playback->fps());
180
181 QSignalBlocker b4(mLoopButton);
182 mLoopButton->setChecked(playback->isLooping());
183}
184
185void TimeControls::setEditor(Editor* editor)
186{
187 Q_ASSERT(editor != nullptr);
188 mEditor = editor;
189}
190
191void TimeControls::setFps(int value)
192{
193 QSignalBlocker blocker(mFpsBox);
194 mFpsBox->setValue(value);
195 mFps = value;
196 updateTimecodeLabel(mEditor->currentFrame());
197}
198
199void TimeControls::setLoop(bool checked)
200{
201 mLoopButton->setChecked(checked);
202}
203
204void TimeControls::setRangeState(bool checked)
205{
206 mPlaybackRangeCheckBox->setChecked(checked);
207 mTimeline->updateLength();
208}
209
210void TimeControls::makeConnections()
211{
212 connect(mPlayButton, &QPushButton::clicked, this, &TimeControls::playButtonClicked);
213 connect(mJumpToEndButton, &QPushButton::clicked, this, &TimeControls::jumpToEndButtonClicked);
214 connect(mJumpToStartButton, &QPushButton::clicked, this, &TimeControls::jumpToStartButtonClicked);
215 connect(mLoopButton, &QPushButton::clicked, this, &TimeControls::loopButtonClicked);
216 connect(mPlaybackRangeCheckBox, &QCheckBox::clicked, this, &TimeControls::playbackRangeClicked);
217
218 auto spinBoxValueChanged = static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged);
219 connect(mLoopStartSpinBox, spinBoxValueChanged, this, &TimeControls::loopStartValueChanged);
220 clearFocusOnFinished(mLoopStartSpinBox);
221 connect(mLoopEndSpinBox, spinBoxValueChanged, this, &TimeControls::loopEndValueChanged);
222 clearFocusOnFinished(mLoopEndSpinBox);
223
224 connect(mPlaybackRangeCheckBox, &QCheckBox::toggled, mLoopStartSpinBox, &QSpinBox::setEnabled);
225 connect(mPlaybackRangeCheckBox, &QCheckBox::toggled, mLoopEndSpinBox, &QSpinBox::setEnabled);
226
227 connect(mSoundButton, &QPushButton::clicked, this, &TimeControls::soundToggled);
228
229 connect(mSoundScrubButton, &QPushButton::clicked, this, &TimeControls::soundScrubToggled);
230 connect(mSoundScrubButton, &QPushButton::clicked, this, &TimeControls::updateSoundScrubIcon);
231
232 connect(mFpsBox, spinBoxValueChanged, this, &TimeControls::fpsChanged);
233 connect(mFpsBox, &QSpinBox::editingFinished, this, &TimeControls::onFpsEditingFinished);
234
235 connect(mFpsBox, spinBoxValueChanged, this, &TimeControls::setFps);
236 connect(mEditor, &Editor::fpsChanged, this, &TimeControls::setFps);
237 connect(mNoTimecodeAction, &QAction::triggered, this, &TimeControls::noTimecodeText);
238 connect(mOnlyFramesAction, &QAction::triggered, this, &TimeControls::onlyFramesText);
239 connect(mSmpteAction, &QAction::triggered, this, &TimeControls::smpteText);
240 connect(mSffAction, &QAction::triggered, this, &TimeControls::sffText);
241}
242
243void TimeControls::playButtonClicked()
244{
245 emit playButtonTriggered();
246}
247
248void TimeControls::updatePlayState()
249{
250 if (mEditor->playback()->isPlaying())
251 {
252 mPlayButton->setIcon(mStopIcon);
253 mPlayButton->setToolTip(tr("Stop"));
254 }
255 else
256 {
257 mPlayButton->setIcon(mStartIcon);
258 mPlayButton->setToolTip(tr("Play"));
259 }
260}
261
262void TimeControls::jumpToStartButtonClicked()
263{
264 if (mPlaybackRangeCheckBox->isChecked())
265 {
266 mEditor->scrubTo(mLoopStartSpinBox->value());
267 }
268 else
269 {
270 mEditor->scrubTo(mEditor->layers()->firstKeyFrameIndex());
271 }
272 mEditor->playback()->setCheckForSoundsHalfway(true);
273}
274
275void TimeControls::jumpToEndButtonClicked()
276{
277 if (mPlaybackRangeCheckBox->isChecked())
278 {
279 mEditor->scrubTo(mLoopEndSpinBox->value());
280 }
281 else
282 {
283 mEditor->scrubTo(mEditor->layers()->animationLength(true));
284 }
285}
286
287void TimeControls::loopButtonClicked(bool bChecked)
288{
289 mEditor->playback()->setLooping(bChecked);
290}
291
292void TimeControls::playbackRangeClicked(bool bChecked)
293{
294 mEditor->playback()->enableRangedPlayback(bChecked);
295}
296
297void TimeControls::loopStartValueChanged(int i)
298{
299 if (i >= mLoopEndSpinBox->value())
300 {
301 mLoopEndSpinBox->setValue(i + 1);
302 }
303 mLoopEndSpinBox->setMinimum(i + 1);
304
305 mEditor->playback()->setRangedStartFrame(i);
306 mTimeline->updateLength();
307}
308
309void TimeControls::loopEndValueChanged(int i)
310{
311 mEditor->playback()->setRangedEndFrame(i);
312 mTimeline->updateLength();
313}
314
315void TimeControls::updateSoundScrubIcon(bool soundScrubEnabled)
316{
317 mEditor->playback()->setSoundScrubActive(soundScrubEnabled);
318 mEditor->preference()->set(SETTING::SOUND_SCRUB_ACTIVE, soundScrubEnabled);
319}
320
321void TimeControls::noTimecodeText()
322{
323 QSettings settings(PENCIL2D, PENCIL2D);
324 settings.setValue(SETTING_TIMECODE_TEXT, NOTEXT);
325 mTimecodeLabelEnum = NOTEXT;
326 mTimecodeLabel->setToolTip("");
327 updateTimecodeLabel(mEditor->currentFrame());
328}
329
330void TimeControls::onlyFramesText()
331{
332 QSettings settings(PENCIL2D, PENCIL2D);
333 settings.setValue(SETTING_TIMECODE_TEXT, FRAMES);
334 mTimecodeLabelEnum = FRAMES;
335 mTimecodeLabel->setToolTip(tr("Actual frame number"));
336 updateTimecodeLabel(mEditor->currentFrame());
337}
338
339void TimeControls::sffText()
340{
341 QSettings settings(PENCIL2D, PENCIL2D);
342 settings.setValue(SETTING_TIMECODE_TEXT, SFF);
343 mTimecodeLabelEnum = SFF;
344 mTimecodeLabel->setToolTip(tr("Timecode format S:FF"));
345 updateTimecodeLabel(mEditor->currentFrame());
346}
347
348void TimeControls::smpteText()
349{
350 QSettings settings(PENCIL2D, PENCIL2D);
351 settings.setValue(SETTING_TIMECODE_TEXT, SMPTE);
352 mTimecodeLabelEnum = SMPTE;
353 mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF"));
354 updateTimecodeLabel(mEditor->currentFrame());
355}
356
357void TimeControls::onFpsEditingFinished()
358{
359 mFpsBox->clearFocus();
360 emit fpsChanged(mFpsBox->value());
361 mFps = mFpsBox->value();
362}
363
364void TimeControls::updateTimecodeLabel(int frame)
365{
366 mTimecodeLabelAction->setVisible(true);
367
368 switch (mTimecodeLabelEnum)
369 {
370 case TimecodeTextLevel::SMPTE:
371 mTimecodeLabel->setText(QString("%1:%2:%3")
372 .arg(frame / (60 * mFps) % 60, 2, 10, QLatin1Char('0'))
373 .arg(frame / mFps % 60, 2, 10, QLatin1Char('0'))
374 .arg(frame % mFps, 2, 10, QLatin1Char('0')));
375 break;
376 case TimecodeTextLevel::SFF:
377 mTimecodeLabel->setText(QString("%1:%2")
378 .arg(frame / mFps)
379 .arg(frame % mFps, 2, 10, QLatin1Char('0')));
380 break;
381 case TimecodeTextLevel::FRAMES:
382 mTimecodeLabel->setText(QString::number(frame).rightJustified(4, '0'));
383 break;
384 case TimecodeTextLevel::NOTEXT:
385 default:
386 mTimecodeLabelAction->setVisible(false);
387 break;
388 }
389
390}
391
392void TimeControls::updateLength(int frameLength)
393{
394 mLoopStartSpinBox->setMaximum(frameLength - 1);
395 mLoopEndSpinBox->setMaximum(frameLength);
396}
Editor
Definition: editor.h:71
LayerManager::animationLength
int animationLength(bool includeSounds=true)
Get the length of current project.
Definition: layermanager.cpp:352
PlaybackManager
Definition: playbackmanager.h:30
TimeControls::onFpsEditingFinished
void onFpsEditingFinished()
Work-around in case the FPS spin-box "valueChanged" signal doesn't work.
Definition: timecontrols.cpp:357
TimeLine
Definition: timeline.h:32
QAbstractButton::setCheckable
void setCheckable(bool)
QAbstractButton::setChecked
void setChecked(bool)
QAbstractButton::clicked
void clicked(bool checked)
QAbstractButton::setIcon
void setIcon(const QIcon &icon)
QAbstractButton::setIconSize
void setIconSize(const QSize &size)
QAbstractButton::toggled
void toggled(bool checked)
QAbstractSpinBox::editingFinished
void editingFinished()
QAction
QAction::triggered
void triggered(bool checked)
QAction::setVisible
void setVisible(bool)
QCheckBox
QIcon
QLabel
QLabel::setText
void setText(const QString &)
QLatin1Char
QMenu
QMenu::addAction
QAction * addAction(const QString &text)
QObject::connect
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject::tr
QString tr(const char *sourceText, const char *disambiguation, int n)
QPushButton
QSettings
QSignalBlocker
QSize
QSpinBox
QSpinBox::setMaximum
void setMaximum(int max)
QSpinBox::setMinimum
void setMinimum(int min)
QSpinBox::setSuffix
void setSuffix(const QString &suffix)
QSpinBox::setValue
void setValue(int val)
QSpinBox::valueChanged
void valueChanged(int i)
QString
QString::number
QString number(int n, int base)
Qt::WheelFocus
WheelFocus
QToolBar
QToolBar::addWidget
QAction * addWidget(QWidget *widget)
QToolButton
QToolButton::InstantPopup
InstantPopup
QToolButton::setPopupMode
void setPopupMode(QToolButton::ToolButtonPopupMode mode)
QToolButton::setMenu
void setMenu(QMenu *menu)
QWidget::clearFocus
void clearFocus()
QWidget::setEnabled
void setEnabled(bool)
QWidget::setFocusPolicy
void setFocusPolicy(Qt::FocusPolicy policy)
QWidget::setContentsMargins
void setContentsMargins(int left, int top, int right, int bottom)
QWidget::setFixedHeight
void setFixedHeight(int h)
QWidget::setStyleSheet
void setStyleSheet(const QString &styleSheet)
QWidget::setToolTip
void setToolTip(const QString &)
Generated on Fri Nov 13 2026 12:01:03 for Pencil2D by doxygen 1.9.6 based on revision 3f1e1d4b3be7f7c4a38e44a6b96b0e8e74438e32