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 mEditor->playback()->setCheckForSoundsHalfway(true);
268 }
269 else
270 {
271 mEditor->scrubTo(mEditor->layers()->firstKeyFrameIndex());
272 }
273 mEditor->playback()->stopSounds();
274}
275
276void TimeControls::jumpToEndButtonClicked()
277{
278 if (mPlaybackRangeCheckBox->isChecked())
279 {
280 mEditor->scrubTo(mLoopEndSpinBox->value());
281 }
282 else
283 {
284 mEditor->scrubTo(mEditor->layers()->lastKeyFrameIndex());
285 }
286}
287
288void TimeControls::loopButtonClicked(bool bChecked)
289{
290 mEditor->playback()->setLooping(bChecked);
291}
292
293void TimeControls::playbackRangeClicked(bool bChecked)
294{
295 mEditor->playback()->enableRangedPlayback(bChecked);
296}
297
298void TimeControls::loopStartValueChanged(int i)
299{
300 if (i >= mLoopEndSpinBox->value())
301 {
302 mLoopEndSpinBox->setValue(i + 1);
303 }
304 mLoopEndSpinBox->setMinimum(i + 1);
305
306 mEditor->playback()->setRangedStartFrame(i);
307 mTimeline->updateLength();
308}
309
310void TimeControls::loopEndValueChanged(int i)
311{
312 mEditor->playback()->setRangedEndFrame(i);
313 mTimeline->updateLength();
314}
315
316void TimeControls::updateSoundScrubIcon(bool soundScrubEnabled)
317{
318 mEditor->playback()->setSoundScrubActive(soundScrubEnabled);
319 mEditor->preference()->set(SETTING::SOUND_SCRUB_ACTIVE, soundScrubEnabled);
320}
321
322void TimeControls::noTimecodeText()
323{
324 QSettings settings(PENCIL2D, PENCIL2D);
325 settings.setValue(SETTING_TIMECODE_TEXT, NOTEXT);
326 mTimecodeLabelEnum = NOTEXT;
327 mTimecodeLabel->setToolTip(tr(""));
328 updateTimecodeLabel(mEditor->currentFrame());
329}
330
331void TimeControls::onlyFramesText()
332{
333 QSettings settings(PENCIL2D, PENCIL2D);
334 settings.setValue(SETTING_TIMECODE_TEXT, FRAMES);
335 mTimecodeLabelEnum = FRAMES;
336 mTimecodeLabel->setToolTip(tr("Actual frame number"));
337 updateTimecodeLabel(mEditor->currentFrame());
338}
339
340void TimeControls::sffText()
341{
342 QSettings settings(PENCIL2D, PENCIL2D);
343 settings.setValue(SETTING_TIMECODE_TEXT, SFF);
344 mTimecodeLabelEnum = SFF;
345 mTimecodeLabel->setToolTip(tr("Timecode format S:FF"));
346 updateTimecodeLabel(mEditor->currentFrame());
347}
348
349void TimeControls::smpteText()
350{
351 QSettings settings(PENCIL2D, PENCIL2D);
352 settings.setValue(SETTING_TIMECODE_TEXT, SMPTE);
353 mTimecodeLabelEnum = SMPTE;
354 mTimecodeLabel->setToolTip(tr("Timecode format MM:SS:FF"));
355 updateTimecodeLabel(mEditor->currentFrame());
356}
357
358void TimeControls::onFpsEditingFinished()
359{
360 mFpsBox->clearFocus();
361 emit fpsChanged(mFpsBox->value());
362 mFps = mFpsBox->value();
363}
364
365void TimeControls::updateTimecodeLabel(int frame)
366{
367 mTimecodeLabelAction->setVisible(true);
368
369 switch (mTimecodeLabelEnum)
370 {
371 case TimecodeTextLevel::SMPTE:
372 mTimecodeLabel->setText(QString("%1:%2:%3")
373 .arg(frame / (60 * mFps) % 60, 2, 10, QLatin1Char('0'))
374 .arg(frame / mFps % 60, 2, 10, QLatin1Char('0'))
375 .arg(frame % mFps, 2, 10, QLatin1Char('0')));
376 break;
377 case TimecodeTextLevel::SFF:
378 mTimecodeLabel->setText(QString("%1:%2")
379 .arg(frame / mFps)
380 .arg(frame % mFps, 2, 10, QLatin1Char('0')));
381 break;
382 case TimecodeTextLevel::FRAMES:
383 mTimecodeLabel->setText(QString::number(frame).rightJustified(4, '0'));
384 break;
385 case TimecodeTextLevel::NOTEXT:
386 default:
387 mTimecodeLabelAction->setVisible(false);
388 break;
389 }
390
391}
392
393void TimeControls::updateLength(int frameLength)
394{
395 mLoopStartSpinBox->setMaximum(frameLength - 1);
396 mLoopEndSpinBox->setMaximum(frameLength);
397}
Editor
Definition: editor.h:71
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:358
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 Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39