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
timeline.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 "timeline.h"
19
20#include <QWidget>
21#include <QScrollBar>
22#include <QHBoxLayout>
23#include <QMenu>
24#include <QAction>
25#include <QSplitter>
26#include <QMessageBox>
27#include <QLabel>
28#include <QWheelEvent>
29#include <QSlider>
30#include <QTimer>
31
32#include "editor.h"
33#include "layermanager.h"
34#include "timecontrols.h"
35#include "timelinecells.h"
36
37
38TimeLine::TimeLine(QWidget* parent) : BaseDockWidget(parent)
39{
40}
41
42void TimeLine::initUI()
43{
44 Q_ASSERT(editor() != nullptr);
45
46 setWindowTitle(tr("Timeline", "Subpanel title"));
47
48 QWidget* timeLineContent = new QWidget(this);
49
50 mLayerList = new TimeLineCells(this, editor(), TIMELINE_CELL_TYPE::Layers);
51 mTracks = new TimeLineCells(this, editor(), TIMELINE_CELL_TYPE::Tracks);
52
53 mHScrollbar = new QScrollBar(Qt::Horizontal);
54 mVScrollbar = new QScrollBar(Qt::Vertical);
55 mVScrollbar->setMinimum(0);
56 mVScrollbar->setMaximum(1);
57 mVScrollbar->setPageStep(1);
58
59 QWidget* leftWidget = new QWidget();
60 leftWidget->setMinimumWidth(120);
61 QWidget* rightWidget = new QWidget();
62
63 QWidget* leftToolBar = new QWidget();
64 leftToolBar->setFixedHeight(30);
65 QWidget* rightToolBar = new QWidget();
66 rightToolBar->setFixedHeight(30);
67
68 // --- left widget ---
69 // --------- layer buttons ---------
70 QToolBar* layerButtons = new QToolBar(this);
71 layerButtons->setIconSize(QSize(22,22));
72 QLabel* layerLabel = new QLabel(tr("Layers:"));
73 layerLabel->setIndent(5);
74
75 QToolButton* addLayerButton = new QToolButton(this);
76 addLayerButton->setIcon(QIcon(":icons/themes/playful/timeline/layer-add.svg"));
77 addLayerButton->setToolTip(tr("Add Layer"));
78
79 mLayerDeleteButton = new QToolButton(this);
80 mLayerDeleteButton->setIcon(QIcon(":icons/themes/playful/timeline/layer-remove.svg"));
81 mLayerDeleteButton->setToolTip(tr("Delete Layer"));
82
83 QToolButton* duplicateLayerButton = new QToolButton(this);
84 duplicateLayerButton->setIcon(QIcon(":icons/themes/playful/timeline/layer-duplicate.svg"));
85 duplicateLayerButton->setToolTip(tr("Duplicate Layer"));
86
87 layerButtons->addWidget(layerLabel);
88 layerButtons->addWidget(addLayerButton);
89 layerButtons->addWidget(mLayerDeleteButton);
90 layerButtons->addWidget(duplicateLayerButton);
91 layerButtons->setFixedHeight(30);
92
93 QHBoxLayout* leftToolBarLayout = new QHBoxLayout();
94 leftToolBarLayout->setContentsMargins(0, 0, 0, 0);
95 leftToolBarLayout->addWidget(layerButtons);
96 leftToolBar->setLayout(leftToolBarLayout);
97
98 QAction* newBitmapLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-bitmap.svg"), tr("New Bitmap Layer"), this);
99 QAction* newVectorLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-vector.svg"), tr("New Vector Layer"), this);
100 QAction* newSoundLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-sound.svg"), tr("New Sound Layer"), this);
101 QAction* newCameraLayerAct = new QAction(QIcon(":icons/themes/playful/timeline/cell-camera.svg"), tr("New Camera Layer"), this);
102
103 QMenu* layerMenu = new QMenu(tr("Layer", "Timeline add-layer menu"), this);
104 layerMenu->addAction(newBitmapLayerAct);
105 layerMenu->addAction(newVectorLayerAct);
106 layerMenu->addAction(newSoundLayerAct);
107 layerMenu->addAction(newCameraLayerAct);
108 addLayerButton->setMenu(layerMenu);
109 addLayerButton->setPopupMode(QToolButton::InstantPopup);
110
111 QGridLayout* leftLayout = new QGridLayout();
112 leftLayout->addWidget(leftToolBar, 0, 0);
113 leftLayout->addWidget(mLayerList, 1, 0);
114 leftLayout->setContentsMargins(0, 0, 0, 0);
115 leftLayout->setSpacing(0);
116 leftWidget->setLayout(leftLayout);
117
118 // --- right widget ---
119 // --------- key buttons ---------
120 QToolBar* timelineButtons = new QToolBar(this);
121 timelineButtons->setIconSize(QSize(22,22));
122 QLabel* keyLabel = new QLabel(tr("Keys:"));
123 keyLabel->setIndent(5);
124
125 QToolButton* addKeyButton = new QToolButton(this);
126 addKeyButton->setIcon(QIcon(":icons/themes/playful/timeline/frame-add.svg"));
127 addKeyButton->setToolTip(tr("Add Frame"));
128
129 QToolButton* removeKeyButton = new QToolButton(this);
130 removeKeyButton->setIcon(QIcon(":icons/themes/playful/timeline/frame-remove.svg"));
131 removeKeyButton->setToolTip(tr("Remove Frame"));
132
133 QToolButton* duplicateKeyButton = new QToolButton(this);
134 duplicateKeyButton->setIcon(QIcon(":icons/themes/playful/timeline/frame-duplicate.svg"));
135 duplicateKeyButton->setToolTip(tr("Duplicate Frame"));
136
137 QLabel* zoomLabel = new QLabel(tr("Zoom:"));
138 zoomLabel->setIndent(5);
139
140 QSlider* zoomSlider = new QSlider(this);
141 zoomSlider->setRange(4, 40);
142 zoomSlider->setFixedWidth(74);
143 zoomSlider->setValue(mTracks->getFrameSize());
144 zoomSlider->setToolTip(tr("Adjust frame width"));
145 zoomSlider->setOrientation(Qt::Horizontal);
146 zoomSlider->setFocusPolicy(Qt::TabFocus);
147
148 timelineButtons->addWidget(keyLabel);
149 timelineButtons->addWidget(addKeyButton);
150 timelineButtons->addWidget(removeKeyButton);
151 timelineButtons->addWidget(duplicateKeyButton);
152 timelineButtons->addSeparator();
153 timelineButtons->addWidget(zoomLabel);
154 timelineButtons->addWidget(zoomSlider);
155 timelineButtons->setFixedHeight(30);
156
157 // --------- Time controls ---------
158 mTimeControls = new TimeControls(this);
159 mTimeControls->setIconSize(QSize(22,22));
160 mTimeControls->setEditor(editor());
161 mTimeControls->initUI();
162 updateLength();
163
164 QHBoxLayout* rightToolBarLayout = new QHBoxLayout();
165 rightToolBarLayout->addWidget(timelineButtons);
166 rightToolBarLayout->setAlignment(Qt::AlignLeft);
167 rightToolBarLayout->addWidget(mTimeControls);
168 rightToolBarLayout->setContentsMargins(0, 0, 0, 0);
169 rightToolBarLayout->setSpacing(0);
170 rightToolBar->setLayout(rightToolBarLayout);
171
172 QGridLayout* rightLayout = new QGridLayout();
173 rightLayout->addWidget(rightToolBar, 0, 0);
174 rightLayout->addWidget(mTracks, 1, 0);
175 rightLayout->setContentsMargins(0, 0, 0, 0);
176 rightLayout->setSpacing(0);
177 rightWidget->setLayout(rightLayout);
178
179 // --- Splitter ---
180 QSplitter* splitter = new QSplitter(this);
181 splitter->addWidget(leftWidget);
182 splitter->addWidget(rightWidget);
183 splitter->setSizes(QList<int>() << 100 << 600);
184
185
186 QGridLayout* lay = new QGridLayout();
187 lay->addWidget(splitter, 0, 0);
188 lay->addWidget(mVScrollbar, 0, 1);
189 lay->addWidget(mHScrollbar, 1, 0);
190 lay->setContentsMargins(0, 0, 0, 0);
191 lay->setSpacing(0);
192 timeLineContent->setLayout(lay);
193 setWidget(timeLineContent);
194
195 mScrollingStoppedTimer = new QTimer();
196 mScrollingStoppedTimer->setSingleShot(true);
197
198 setWindowFlags(Qt::WindowStaysOnTopHint);
199
200 connect(mHScrollbar, &QScrollBar::valueChanged, mTracks, &TimeLineCells::hScrollChange);
201 connect(mTracks, &TimeLineCells::offsetChanged, mHScrollbar, &QScrollBar::setValue);
202 connect(mVScrollbar, &QScrollBar::valueChanged, mTracks, &TimeLineCells::vScrollChange);
203 connect(mVScrollbar, &QScrollBar::valueChanged, mLayerList, &TimeLineCells::vScrollChange);
204 connect(mVScrollbar, &QScrollBar::valueChanged, this, &TimeLine::onScrollbarValueChanged);
205 connect(mScrollingStoppedTimer, &QTimer::timeout, mLayerList, &TimeLineCells::onScrollingVerticallyStopped);
206
207 connect(splitter, &QSplitter::splitterMoved, this, &TimeLine::updateLength);
208
209 connect(addKeyButton, &QToolButton::clicked, this, &TimeLine::insertKeyClick);
210 connect(removeKeyButton, &QToolButton::clicked, this, &TimeLine::removeKeyClick);
211 connect(duplicateLayerButton, &QToolButton::clicked, this , &TimeLine::duplicateLayerClick);
212 connect(duplicateKeyButton, &QToolButton::clicked, this, &TimeLine::duplicateKeyClick);
213 connect(zoomSlider, &QSlider::valueChanged, mTracks, &TimeLineCells::setFrameSize);
214
215 connect(mTimeControls, &TimeControls::soundToggled, this, &TimeLine::soundClick);
216 connect(mTimeControls, &TimeControls::fpsChanged, this, &TimeLine::fpsChanged);
217 connect(mTimeControls, &TimeControls::fpsChanged, this, &TimeLine::updateLength);
218 connect(mTimeControls, &TimeControls::playButtonTriggered, this, &TimeLine::playButtonTriggered);
219 connect(editor(), &Editor::scrubbed, mTimeControls, &TimeControls::updateTimecodeLabel);
220 connect(mTimeControls, &TimeControls::fpsChanged, mTimeControls, &TimeControls::setFps);
221 connect(this, &TimeLine::fpsChanged, mTimeControls, &TimeControls::setFps);
222
223 connect(newBitmapLayerAct, &QAction::triggered, this, &TimeLine::newBitmapLayer);
224 connect(newVectorLayerAct, &QAction::triggered, this, &TimeLine::newVectorLayer);
225 connect(newSoundLayerAct, &QAction::triggered, this, &TimeLine::newSoundLayer);
226 connect(newCameraLayerAct, &QAction::triggered, this, &TimeLine::newCameraLayer);
227 connect(mLayerDeleteButton, &QPushButton::clicked, this, &TimeLine::deleteCurrentLayerClick);
228
229 connect(mLayerList, &TimeLineCells::mouseMovedY, mLayerList, &TimeLineCells::setMouseMoveY);
230 connect(mLayerList, &TimeLineCells::mouseMovedY, mTracks, &TimeLineCells::setMouseMoveY);
231 connect(mTracks, &TimeLineCells::lengthChanged, this, &TimeLine::updateLength);
232 connect(mTracks, &TimeLineCells::selectionChanged, this, &TimeLine::selectionChanged);
233 connect(mTracks, &TimeLineCells::insertNewKeyFrame, this, &TimeLine::insertKeyClick);
234
235 connect(editor(), &Editor::scrubbed, this, &TimeLine::updateFrame);
236 connect(editor(), &Editor::frameModified, this, &TimeLine::updateContent);
237 connect(editor(), &Editor::framesModified, this, &TimeLine::updateContent);
238
239 LayerManager* layer = editor()->layers();
240 connect(layer, &LayerManager::layerCountChanged, this, &TimeLine::updateLayerNumber);
241 connect(layer, &LayerManager::currentLayerChanged, this, &TimeLine::onCurrentLayerChanged);
242 mNumLayers = layer->count();
243
244 scrubbing = false;
245}
246
247void TimeLine::updateUI()
248{
249 updateContent();
250}
251
252void TimeLine::updateUICached()
253{
254 mLayerList->update();
255 mTracks->update();
256}
257
262void TimeLine::extendLength(int frame)
263{
264 int currentLength = mTracks->getFrameLength();
265 if(frame > (currentLength * 0.75))
266 {
267 int newLength = static_cast<int>(std::max(frame, currentLength) * 1.5);
268
269 if (newLength > 9999)
270 newLength = 9999;
271
272 mTracks->setFrameLength(newLength);
273 updateLength();
274 }
275}
276
277void TimeLine::resizeEvent(QResizeEvent*)
278{
279 updateLayerView();
280}
281
282void TimeLine::wheelEvent(QWheelEvent* event)
283{
284 if (event->modifiers() & Qt::ShiftModifier)
285 {
286 mHScrollbar->event(event);
287 }
288 else
289 {
290 mVScrollbar->event(event);
291 }
292}
293
294void TimeLine::onScrollbarValueChanged()
295{
296 // After the scrollbar has been updated, prepare to trigger stopped event
297 mScrollingStoppedTimer->start(150);
298}
299
300void TimeLine::updateFrame(int frameNumber)
301{
302 Q_ASSERT(mTracks);
303
304
305 mTracks->updateFrame(mLastUpdatedFrame);
306 mTracks->updateFrame(frameNumber);
307
308 mLastUpdatedFrame = frameNumber;
309}
310
311void TimeLine::updateLayerView()
312{
313 int pageDisplay = (mTracks->height() - mTracks->getOffsetY()) / mTracks->getLayerHeight();
314
315 mVScrollbar->setMinimum(0);
316 mVScrollbar->setMaximum(qMax(0, mNumLayers - pageDisplay));
317 updateContent();
318}
319
320void TimeLine::updateLayerNumber(int numberOfLayers)
321{
322 mNumLayers = numberOfLayers;
323 updateLayerView();
324}
325
326void TimeLine::updateLength()
327{
328 int frameLength = mTracks->getFrameLength();
329 mHScrollbar->setMaximum(qMax(0, frameLength - mTracks->width() / mTracks->getFrameSize()));
330 mTimeControls->updateLength(frameLength);
331 updateContent();
332}
333
334void TimeLine::updateContent()
335{
336 mLayerList->updateContent();
337 mTracks->updateContent();
338 update();
339}
340
341void TimeLine::setLoop(bool loop)
342{
343 mTimeControls->setLoop(loop);
344}
345
346void TimeLine::setPlaying(bool isPlaying)
347{
348 Q_UNUSED(isPlaying)
349 mTimeControls->updatePlayState();
350}
351
352void TimeLine::setRangeState(bool range)
353{
354 mTimeControls->setRangeState(range);
355}
356
357int TimeLine::getRangeLower()
358{
359 return mTimeControls->getRangeLower();
360}
361
362int TimeLine::getRangeUpper()
363{
364 return mTimeControls->getRangeUpper();
365}
366
367void TimeLine::onObjectLoaded()
368{
369 mTimeControls->updateUI();
370 updateLayerNumber(editor()->layers()->count());
371}
372
373void TimeLine::onCurrentLayerChanged()
374{
375 updateVerticalScrollbarPosition();
376 mLayerDeleteButton->setEnabled(editor()->layers()->canDeleteLayer(editor()->currentLayerIndex()));
377}
378
379void TimeLine::updateVerticalScrollbarPosition()
380{
381 // invert index so 0 is at the top
382 int idx = mNumLayers - editor()->currentLayerIndex() - 1;
383 // number of visible layers
384 int height = mNumLayers - mVScrollbar->maximum();
385 // scroll bar position/offset
386 int pos = mVScrollbar->value();
387
388 if (idx < pos) // above visible area
389 {
390 mVScrollbar->setValue(idx);
391 }
392 else if (idx >= pos + height) // below visible area
393 {
394 mVScrollbar->setValue(idx - height + 1);
395 }
396}
BaseDockWidget
Definition: basedockwidget.h:27
Editor::framesModified
void framesModified()
This should be emitted after modifying multiple frames.
Editor::frameModified
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
Editor::scrubbed
void scrubbed(int frameNumber)
This should be emitted after scrubbing.
LayerManager
Definition: layermanager.h:31
TimeControls
Definition: timecontrols.h:33
TimeLineCells
Definition: timelinecells.h:44
TimeLine::extendLength
void extendLength(int frame)
Extends the timeline frame length if necessary.
Definition: timeline.cpp:262
QAbstractButton::clicked
void clicked(bool checked)
QAbstractButton::setIcon
void setIcon(const QIcon &icon)
QAbstractSlider::setMaximum
void setMaximum(int)
QAbstractSlider::setMinimum
void setMinimum(int)
QAbstractSlider::setOrientation
void setOrientation(Qt::Orientation)
QAbstractSlider::setPageStep
void setPageStep(int)
QAbstractSlider::setRange
void setRange(int min, int max)
QAbstractSlider::setValue
void setValue(int)
QAbstractSlider::valueChanged
void valueChanged(int value)
QAction
QAction::triggered
void triggered(bool checked)
QBoxLayout::addWidget
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
QBoxLayout::setSpacing
void setSpacing(int spacing)
QDockWidget::event
virtual bool event(QEvent *event) override
QDockWidget::setWidget
void setWidget(QWidget *widget)
QGridLayout
QGridLayout::addWidget
void addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
QGridLayout::setSpacing
void setSpacing(int spacing)
QHBoxLayout
QIcon
QLabel
QLabel::setIndent
void setIndent(int)
QLayout::setAlignment
bool setAlignment(QWidget *w, Qt::Alignment alignment)
QLayout::setContentsMargins
void setContentsMargins(int left, int top, int right, int bottom)
QList
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)
QResizeEvent
QScrollBar
QScrollBar::event
virtual bool event(QEvent *event) override
QSize
QSlider
QSplitter
QSplitter::addWidget
void addWidget(QWidget *widget)
QSplitter::setSizes
void setSizes(const QList< int > &list)
QSplitter::splitterMoved
void splitterMoved(int pos, int index)
Qt::AlignLeft
AlignLeft
Qt::TabFocus
TabFocus
Qt::ShiftModifier
ShiftModifier
Qt::Horizontal
Horizontal
Qt::WindowStaysOnTopHint
WindowStaysOnTopHint
QTimer
QTimer::setSingleShot
void setSingleShot(bool singleShot)
QTimer::start
void start(int msec)
QTimer::timeout
void timeout()
QToolBar
QToolBar::addSeparator
QAction * addSeparator()
QToolBar::addWidget
QAction * addWidget(QWidget *widget)
QToolBar::setIconSize
void setIconSize(const QSize &iconSize)
QToolButton
QToolButton::InstantPopup
InstantPopup
QToolButton::setPopupMode
void setPopupMode(QToolButton::ToolButtonPopupMode mode)
QToolButton::setMenu
void setMenu(QMenu *menu)
QWheelEvent
QWidget
QWidget::setEnabled
void setEnabled(bool)
QWidget::setFocusPolicy
void setFocusPolicy(Qt::FocusPolicy policy)
QWidget::height
height
QWidget::setMinimumWidth
void setMinimumWidth(int minw)
QWidget::pos
pos
QWidget::setFixedHeight
void setFixedHeight(int h)
QWidget::setFixedWidth
void setFixedWidth(int w)
QWidget::setLayout
void setLayout(QLayout *layout)
QWidget::setToolTip
void setToolTip(const QString &)
QWidget::update
void update()
QWidget::width
width
QWidget::setWindowFlags
void setWindowFlags(Qt::WindowFlags type)
QWidget::setWindowTitle
void setWindowTitle(const QString &)
Generated on Thu May 8 2025 04:47:53 for Pencil2D by doxygen 1.9.6 based on revision 4513250b1d5b1a3676ec0e67b06b7a885ceaae39