All Classes Namespaces Functions Variables Enumerations Properties Pages
timeline.cpp
1 /*
2 
3 Pencil2D - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2020 Matthew Chiawen Chang
6 
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License
9 as published by the Free Software Foundation; version 2 of the License.
10 
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU 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 
31 #include "layer.h"
32 #include "editor.h"
33 #include "layermanager.h"
34 #include "timecontrols.h"
35 #include "timelinecells.h"
36 
37 
38 TimeLine::TimeLine(QWidget* parent) : BaseDockWidget(parent)
39 {
40 }
41 
42 void 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  QLabel* layerLabel = new QLabel(tr("Layers:"));
72  layerLabel->setIndent(5);
73 
74  QToolButton* addLayerButton = new QToolButton(this);
75  addLayerButton->setIcon(QIcon(":icons/add.png"));
76  addLayerButton->setToolTip(tr("Add Layer"));
77  addLayerButton->setFixedSize(24, 24);
78 
79  QToolButton* removeLayerButton = new QToolButton(this);
80  removeLayerButton->setIcon(QIcon(":icons/remove.png"));
81  removeLayerButton->setToolTip(tr("Remove Layer"));
82  removeLayerButton->setFixedSize(24, 24);
83 
84  QToolButton* duplicateLayerButton = new QToolButton(this);
85  duplicateLayerButton->setIcon(QIcon(":icons/controls/duplicate.png"));
86  duplicateLayerButton->setToolTip(tr("Duplicate Layer"));
87  duplicateLayerButton->setFixedSize(24, 24);
88 
89  layerButtons->addWidget(layerLabel);
90  layerButtons->addWidget(addLayerButton);
91  layerButtons->addWidget(removeLayerButton);
92  layerButtons->addWidget(duplicateLayerButton);
93  layerButtons->setFixedHeight(30);
94 
95  QHBoxLayout* leftToolBarLayout = new QHBoxLayout();
96  leftToolBarLayout->setMargin(0);
97  leftToolBarLayout->addWidget(layerButtons);
98  leftToolBar->setLayout(leftToolBarLayout);
99 
100  QAction* newBitmapLayerAct = new QAction(QIcon(":icons/layer-bitmap.png"), tr("New Bitmap Layer"), this);
101  QAction* newVectorLayerAct = new QAction(QIcon(":icons/layer-vector.png"), tr("New Vector Layer"), this);
102  QAction* newSoundLayerAct = new QAction(QIcon(":icons/layer-sound.png"), tr("New Sound Layer"), this);
103  QAction* newCameraLayerAct = new QAction(QIcon(":icons/layer-camera.png"), tr("New Camera Layer"), this);
104 
105  QMenu* layerMenu = new QMenu(tr("&Layer", "Timeline add-layer menu"), this);
106  layerMenu->addAction(newBitmapLayerAct);
107  layerMenu->addAction(newVectorLayerAct);
108  layerMenu->addAction(newSoundLayerAct);
109  layerMenu->addAction(newCameraLayerAct);
110  addLayerButton->setMenu(layerMenu);
111  addLayerButton->setPopupMode(QToolButton::InstantPopup);
112 
113  QGridLayout* leftLayout = new QGridLayout();
114  leftLayout->addWidget(leftToolBar, 0, 0);
115  leftLayout->addWidget(mLayerList, 1, 0);
116  leftLayout->setMargin(0);
117  leftLayout->setSpacing(0);
118  leftWidget->setLayout(leftLayout);
119 
120  // --- right widget ---
121  // --------- key buttons ---------
122  QToolBar* timelineButtons = new QToolBar(this);
123  QLabel* keyLabel = new QLabel(tr("Keys:"));
124  keyLabel->setIndent(5);
125 
126  QToolButton* addKeyButton = new QToolButton(this);
127  addKeyButton->setIcon(QIcon(":icons/add.png"));
128  addKeyButton->setToolTip(tr("Add Frame"));
129  addKeyButton->setFixedSize(24, 24);
130 
131  QToolButton* removeKeyButton = new QToolButton(this);
132  removeKeyButton->setIcon(QIcon(":icons/remove.png"));
133  removeKeyButton->setToolTip(tr("Remove Frame"));
134  removeKeyButton->setFixedSize(24, 24);
135 
136  QToolButton* duplicateKeyButton = new QToolButton(this);
137  duplicateKeyButton->setIcon(QIcon(":icons/controls/duplicate.png"));
138  duplicateKeyButton->setToolTip(tr("Duplicate Frame"));
139  duplicateKeyButton->setFixedSize(24, 24);
140 
141  QLabel* zoomLabel = new QLabel(tr("Zoom:"));
142  zoomLabel->setIndent(5);
143 
144  QSlider* zoomSlider = new QSlider(this);
145  zoomSlider->setRange(4, 40);
146  zoomSlider->setFixedWidth(74);
147  zoomSlider->setValue(mTracks->getFrameSize());
148  zoomSlider->setToolTip(tr("Adjust frame width"));
149  zoomSlider->setOrientation(Qt::Horizontal);
150  zoomSlider->setFocusPolicy(Qt::TabFocus);
151 
152  timelineButtons->addWidget(keyLabel);
153  timelineButtons->addWidget(addKeyButton);
154  timelineButtons->addWidget(removeKeyButton);
155  timelineButtons->addWidget(duplicateKeyButton);
156  timelineButtons->addSeparator();
157  timelineButtons->addWidget(zoomLabel);
158  timelineButtons->addWidget(zoomSlider);
159  timelineButtons->setFixedHeight(30);
160 
161  // --------- Time controls ---------
162  mTimeControls = new TimeControls(this);
163  mTimeControls->setEditor(editor());
164  mTimeControls->initUI();
165  updateLength();
166 
167  QHBoxLayout* rightToolBarLayout = new QHBoxLayout();
168  rightToolBarLayout->addWidget(timelineButtons);
169  rightToolBarLayout->setAlignment(Qt::AlignLeft);
170  rightToolBarLayout->addWidget(mTimeControls);
171  rightToolBarLayout->setMargin(0);
172  rightToolBarLayout->setSpacing(0);
173  rightToolBar->setLayout(rightToolBarLayout);
174 
175  QGridLayout* rightLayout = new QGridLayout();
176  rightLayout->addWidget(rightToolBar, 0, 0);
177  rightLayout->addWidget(mTracks, 1, 0);
178  rightLayout->setMargin(0);
179  rightLayout->setSpacing(0);
180  rightWidget->setLayout(rightLayout);
181 
182  // --- Splitter ---
183  QSplitter* splitter = new QSplitter(this);
184  splitter->addWidget(leftWidget);
185  splitter->addWidget(rightWidget);
186  splitter->setSizes(QList<int>() << 100 << 600);
187 
188 
189  QGridLayout* lay = new QGridLayout();
190  lay->addWidget(splitter, 0, 0);
191  lay->addWidget(mVScrollbar, 0, 1);
192  lay->addWidget(mHScrollbar, 1, 0);
193  lay->setMargin(0);
194  lay->setSpacing(0);
195  timeLineContent->setLayout(lay);
196  setWidget(timeLineContent);
197 
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 
205  connect(splitter, &QSplitter::splitterMoved, this, &TimeLine::updateLength);
206 
207  connect(addKeyButton, &QToolButton::clicked, this, &TimeLine::insertKeyClick);
208  connect(removeKeyButton, &QToolButton::clicked, this, &TimeLine::removeKeyClick);
209  connect(duplicateLayerButton, &QToolButton::clicked, this , &TimeLine::duplicateLayerClick);
210  connect(duplicateKeyButton, &QToolButton::clicked, this, &TimeLine::duplicateKeyClick);
211  connect(zoomSlider, &QSlider::valueChanged, mTracks, &TimeLineCells::setFrameSize);
212 
213  connect(mTimeControls, &TimeControls::soundToggled, this, &TimeLine::soundClick);
214  connect(mTimeControls, &TimeControls::fpsChanged, this, &TimeLine::fpsChanged);
215  connect(mTimeControls, &TimeControls::fpsChanged, this, &TimeLine::updateLength);
216  connect(mTimeControls, &TimeControls::playButtonTriggered, this, &TimeLine::playButtonTriggered);
217  connect(editor(), &Editor::scrubbed, mTimeControls, &TimeControls::updateTimecodeLabel);
218  connect(mTimeControls, &TimeControls::fpsChanged, mTimeControls, &TimeControls::setFps);
219  connect(this, &TimeLine::fpsChanged, mTimeControls, &TimeControls::setFps);
220 
221  connect(newBitmapLayerAct, &QAction::triggered, this, &TimeLine::newBitmapLayer);
222  connect(newVectorLayerAct, &QAction::triggered, this, &TimeLine::newVectorLayer);
223  connect(newSoundLayerAct, &QAction::triggered, this, &TimeLine::newSoundLayer);
224  connect(newCameraLayerAct, &QAction::triggered, this, &TimeLine::newCameraLayer);
225  connect(removeLayerButton, &QPushButton::clicked, this, &TimeLine::deleteCurrentLayer);
226 
227  connect(mLayerList, &TimeLineCells::mouseMovedY, mLayerList, &TimeLineCells::setMouseMoveY);
228  connect(mLayerList, &TimeLineCells::mouseMovedY, mTracks, &TimeLineCells::setMouseMoveY);
229  connect(mTracks, &TimeLineCells::lengthChanged, this, &TimeLine::updateLength);
230  connect(mTracks, &TimeLineCells::selectionChanged, this, &TimeLine::selectionChanged);
231  connect(mTracks, &TimeLineCells::insertNewKeyFrame, this, &TimeLine::insertKeyClick);
232 
233  connect(editor(), &Editor::scrubbed, this, &TimeLine::updateFrame);
234  connect(editor(), &Editor::frameModified, this, &TimeLine::updateContent);
235 
236  LayerManager* layer = editor()->layers();
237  connect(layer, &LayerManager::layerCountChanged, this, &TimeLine::updateLayerNumber);
238  mNumLayers = layer->count();
239 
240  scrubbing = false;
241 }
242 
243 void TimeLine::updateUI()
244 {
245  updateContent();
246 }
247 
248 int TimeLine::getLength()
249 {
250  return mTracks->getFrameLength();
251 }
252 
253 void TimeLine::setLength(int frame)
254 {
255  mTracks->setFrameLength(frame);
256  updateLength();
257 }
258 
263 void TimeLine::extendLength(int frame)
264 {
265  int currentLength = mTracks->getFrameLength();
266  if(frame > (currentLength * 0.75))
267  {
268  int newLength = static_cast<int>(std::max(frame, currentLength) * 1.5);
269 
270  if (newLength > 9999)
271  newLength = 9999;
272 
273  mTracks->setFrameLength(newLength);
274  updateLength();
275  }
276 }
277 
278 void TimeLine::resizeEvent(QResizeEvent*)
279 {
280  updateLayerView();
281 }
282 
283 void TimeLine::wheelEvent(QWheelEvent* event)
284 {
285  if (event->modifiers() & Qt::ShiftModifier)
286  {
287  mHScrollbar->event(event);
288  }
289  else
290  {
291  mVScrollbar->event(event);
292  }
293 }
294 
295 void TimeLine::deleteCurrentLayer()
296 {
297  LayerManager* layerMgr = editor()->layers();
298  QString strLayerName = layerMgr->currentLayer()->name();
299 
300  int ret = QMessageBox::warning(this,
301  tr("Delete Layer", "Windows title of Delete current layer pop-up."),
302  tr("Are you sure you want to delete layer: %1? This cannot be undone.").arg(strLayerName),
305  if (ret == QMessageBox::Ok)
306  {
307  Status st = layerMgr->deleteLayer(editor()->currentLayerIndex());
308  if (st == Status::ERROR_NEED_AT_LEAST_ONE_CAMERA_LAYER)
309  {
310  QMessageBox::information(this, "",
311  tr("Please keep at least one camera layer in project"));
312  }
313  }
314 }
315 
316 void TimeLine::updateFrame(int frameNumber)
317 {
318  Q_ASSERT(mTracks);
319 
320 
321  mTracks->updateFrame(mLastUpdatedFrame);
322  mTracks->updateFrame(frameNumber);
323 
324  mLastUpdatedFrame = frameNumber;
325 }
326 
327 void TimeLine::updateLayerView()
328 {
329  int pageDisplay = (mTracks->height() - mTracks->getOffsetY()) / mTracks->getLayerHeight();
330 
331  mVScrollbar->setMinimum(0);
332  mVScrollbar->setMaximum(qMax(0, mNumLayers - pageDisplay));
333  update();
334  updateContent();
335 }
336 
337 void TimeLine::updateLayerNumber(int numberOfLayers)
338 {
339  mNumLayers = numberOfLayers;
340  updateLayerView();
341 }
342 
343 void TimeLine::updateLength()
344 {
345  int frameLength = getLength();
346  mHScrollbar->setMaximum(qMax(0, frameLength - mTracks->width() / mTracks->getFrameSize()));
347  mTimeControls->updateLength(frameLength);
348  updateContent();
349 }
350 
351 void TimeLine::updateContent()
352 {
353  mLayerList->updateContent();
354  mTracks->updateContent();
355  update();
356 }
357 
358 void TimeLine::setLoop(bool loop)
359 {
360  mTimeControls->setLoop(loop);
361 }
362 
363 void TimeLine::setPlaying(bool isPlaying)
364 {
365  Q_UNUSED(isPlaying)
366  mTimeControls->updatePlayState();
367 }
368 
369 void TimeLine::setRangeState(bool range)
370 {
371  mTimeControls->setRangeState(range);
372 }
373 
374 int TimeLine::getRangeLower()
375 {
376  return mTimeControls->getRangeLower();
377 }
378 
379 int TimeLine::getRangeUpper()
380 {
381  return mTimeControls->getRangeUpper();
382 }
383 
384 void TimeLine::onObjectLoaded()
385 {
386  mTimeControls->updateUI();
387  updateLayerNumber(editor()->layers()->count());
388 }
ShiftModifier
void triggered(bool checked)
void setMenu(QMenu *menu)
TabFocus
void setSizes(const QList< int > &list)
void setFixedWidth(int w)
void addWidget(QWidget *widget, int row, int column, Qt::Alignment alignment)
void setRange(int min, int max)
void setFocusPolicy(Qt::FocusPolicy policy)
void setMinimumWidth(int minw)
QAction * addWidget(QWidget *widget)
void setPageStep(int)
void scrubbed(int frameNumber)
This should be emitted after scrubbing.
void setIcon(const QIcon &icon)
QString tr(const char *sourceText, const char *disambiguation, int n)
QMessageBox::StandardButton information(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
QAction * addAction(const QString &text)
void update()
AlignLeft
void setSpacing(int spacing)
void addWidget(QWidget *widget)
void frameModified(int frameNumber)
This should be emitted after modifying the frame content.
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
void setLayout(QLayout *layout)
void setOrientation(Qt::Orientation)
void clicked(bool checked)
void splitterMoved(int pos, int index)
void setMinimum(int)
Qt::KeyboardModifiers modifiers() const const
virtual bool event(QEvent *event) override
void setFixedSize(const QSize &s)
void setValue(int)
void setWidget(QWidget *widget)
bool setAlignment(QWidget *w, Qt::Alignment alignment)
void setWindowFlags(Qt::WindowFlags type)
void setFixedHeight(int h)
void setWindowTitle(const QString &)
QWidget(QWidget *parent, Qt::WindowFlags f)
void setPopupMode(QToolButton::ToolButtonPopupMode mode)
QAction * addSeparator()
void extendLength(int frame)
Extends the timeline frame length if necessary.
Definition: timeline.cpp:263
void valueChanged(int value)
QMessageBox::StandardButton warning(QWidget *parent, const QString &title, const QString &text, QMessageBox::StandardButtons buttons, QMessageBox::StandardButton defaultButton)
Horizontal
void setToolTip(const QString &)
void setIndent(int)
void setMaximum(int)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setMargin(int margin)
WindowStaysOnTopHint
void setSpacing(int spacing)