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  layerButtons->addWidget(layerLabel);
85  layerButtons->addWidget(addLayerButton);
86  layerButtons->addWidget(removeLayerButton);
87  layerButtons->setFixedHeight(30);
88 
89  QHBoxLayout* leftToolBarLayout = new QHBoxLayout();
90  leftToolBarLayout->setMargin(0);
91  leftToolBarLayout->addWidget(layerButtons);
92  leftToolBar->setLayout(leftToolBarLayout);
93 
94  QAction* newBitmapLayerAct = new QAction(QIcon(":icons/layer-bitmap.png"), tr("New Bitmap Layer"), this);
95  QAction* newVectorLayerAct = new QAction(QIcon(":icons/layer-vector.png"), tr("New Vector Layer"), this);
96  QAction* newSoundLayerAct = new QAction(QIcon(":icons/layer-sound.png"), tr("New Sound Layer"), this);
97  QAction* newCameraLayerAct = new QAction(QIcon(":icons/layer-camera.png"), tr("New Camera Layer"), this);
98 
99  QMenu* layerMenu = new QMenu(tr("&Layer", "Timeline add-layer menu"), this);
100  layerMenu->addAction(newBitmapLayerAct);
101  layerMenu->addAction(newVectorLayerAct);
102  layerMenu->addAction(newSoundLayerAct);
103  layerMenu->addAction(newCameraLayerAct);
104  addLayerButton->setMenu(layerMenu);
105  addLayerButton->setPopupMode(QToolButton::InstantPopup);
106 
107  QGridLayout* leftLayout = new QGridLayout();
108  leftLayout->addWidget(leftToolBar, 0, 0);
109  leftLayout->addWidget(mLayerList, 1, 0);
110  leftLayout->setMargin(0);
111  leftLayout->setSpacing(0);
112  leftWidget->setLayout(leftLayout);
113 
114  // --- right widget ---
115  // --------- key buttons ---------
116  QToolBar* timelineButtons = new QToolBar(this);
117  QLabel* keyLabel = new QLabel(tr("Keys:"));
118  keyLabel->setIndent(5);
119 
120  QToolButton* addKeyButton = new QToolButton(this);
121  addKeyButton->setIcon(QIcon(":icons/add.png"));
122  addKeyButton->setToolTip(tr("Add Frame"));
123  addKeyButton->setFixedSize(24, 24);
124 
125  QToolButton* removeKeyButton = new QToolButton(this);
126  removeKeyButton->setIcon(QIcon(":icons/remove.png"));
127  removeKeyButton->setToolTip(tr("Remove Frame"));
128  removeKeyButton->setFixedSize(24, 24);
129 
130  QToolButton* duplicateKeyButton = new QToolButton(this);
131  duplicateKeyButton->setIcon(QIcon(":icons/controls/duplicate.png"));
132  duplicateKeyButton->setToolTip(tr("Duplicate Frame"));
133  duplicateKeyButton->setFixedSize(24, 24);
134 
135  QLabel* zoomLabel = new QLabel(tr("Zoom:"));
136  zoomLabel->setIndent(5);
137 
138  QSlider* zoomSlider = new QSlider(this);
139  zoomSlider->setRange(4, 40);
140  zoomSlider->setFixedWidth(74);
141  zoomSlider->setValue(mTracks->getFrameSize());
142  zoomSlider->setToolTip(tr("Adjust frame width"));
143  zoomSlider->setOrientation(Qt::Horizontal);
144  zoomSlider->setFocusPolicy(Qt::TabFocus);
145 
146  timelineButtons->addWidget(keyLabel);
147  timelineButtons->addWidget(addKeyButton);
148  timelineButtons->addWidget(removeKeyButton);
149  timelineButtons->addWidget(duplicateKeyButton);
150  timelineButtons->addSeparator();
151  timelineButtons->addWidget(zoomLabel);
152  timelineButtons->addWidget(zoomSlider);
153  timelineButtons->addSeparator();
154  timelineButtons->setFixedHeight(30);
155 
156  // --------- Time controls ---------
157  mTimeControls = new TimeControls(this);
158  mTimeControls->setEditor(editor());
159  mTimeControls->initUI();
160  updateLength();
161 
162  QHBoxLayout* rightToolBarLayout = new QHBoxLayout();
163  rightToolBarLayout->addWidget(timelineButtons);
164  rightToolBarLayout->setAlignment(Qt::AlignLeft);
165  rightToolBarLayout->addWidget(mTimeControls);
166  rightToolBarLayout->setMargin(0);
167  rightToolBarLayout->setSpacing(0);
168  rightToolBar->setLayout(rightToolBarLayout);
169 
170  QGridLayout* rightLayout = new QGridLayout();
171  rightLayout->addWidget(rightToolBar, 0, 0);
172  rightLayout->addWidget(mTracks, 1, 0);
173  rightLayout->setMargin(0);
174  rightLayout->setSpacing(0);
175  rightWidget->setLayout(rightLayout);
176 
177  // --- Splitter ---
178  QSplitter* splitter = new QSplitter(this);
179  splitter->addWidget(leftWidget);
180  splitter->addWidget(rightWidget);
181  splitter->setSizes(QList<int>() << 100 << 600);
182 
183 
184  QGridLayout* lay = new QGridLayout();
185  lay->addWidget(splitter, 0, 0);
186  lay->addWidget(mVScrollbar, 0, 1);
187  lay->addWidget(mHScrollbar, 1, 0);
188  lay->setMargin(0);
189  lay->setSpacing(0);
190  timeLineContent->setLayout(lay);
191  setWidget(timeLineContent);
192 
194 
195  connect(mHScrollbar, &QScrollBar::valueChanged, mTracks, &TimeLineCells::hScrollChange);
196  connect(mTracks, &TimeLineCells::offsetChanged, mHScrollbar, &QScrollBar::setValue);
197  connect(mVScrollbar, &QScrollBar::valueChanged, mTracks, &TimeLineCells::vScrollChange);
198  connect(mVScrollbar, &QScrollBar::valueChanged, mLayerList, &TimeLineCells::vScrollChange);
199 
200  connect(splitter, &QSplitter::splitterMoved, this, &TimeLine::updateLength);
201 
202  connect(addKeyButton, &QToolButton::clicked, this, &TimeLine::addKeyClick);
203  connect(removeKeyButton, &QToolButton::clicked, this, &TimeLine::removeKeyClick);
204  connect(duplicateKeyButton, &QToolButton::clicked, this, &TimeLine::duplicateKeyClick);
205  connect(zoomSlider, &QSlider::valueChanged, mTracks, &TimeLineCells::setFrameSize);
206 
207  connect(mTimeControls, &TimeControls::soundToggled, this, &TimeLine::soundClick);
208  connect(mTimeControls, &TimeControls::fpsChanged, this, &TimeLine::fpsChanged);
209  connect(mTimeControls, &TimeControls::fpsChanged, this, &TimeLine::updateLength);
210  connect(mTimeControls, &TimeControls::playButtonTriggered, this, &TimeLine::playButtonTriggered);
211  connect(editor(), &Editor::currentFrameChanged, mTimeControls, &TimeControls::updateTimecodeLabel);
212  connect(mTimeControls, &TimeControls::fpsChanged, mTimeControls, &TimeControls::setFps);
213  connect(this, &TimeLine::fpsChanged, mTimeControls, &TimeControls::setFps);
214 
215  connect(newBitmapLayerAct, &QAction::triggered, this, &TimeLine::newBitmapLayer);
216  connect(newVectorLayerAct, &QAction::triggered, this, &TimeLine::newVectorLayer);
217  connect(newSoundLayerAct, &QAction::triggered, this, &TimeLine::newSoundLayer);
218  connect(newCameraLayerAct, &QAction::triggered, this, &TimeLine::newCameraLayer);
219  connect(removeLayerButton, &QPushButton::clicked, this, &TimeLine::deleteCurrentLayer);
220 
221  connect(mLayerList, &TimeLineCells::mouseMovedY, mLayerList, &TimeLineCells::setMouseMoveY);
222  connect(mLayerList, &TimeLineCells::mouseMovedY, mTracks, &TimeLineCells::setMouseMoveY);
223  connect(mTracks, &TimeLineCells::lengthChanged, this, &TimeLine::updateLength);
224 
225  connect(editor(), &Editor::currentFrameChanged, this, &TimeLine::updateFrame);
226 
227  LayerManager* layer = editor()->layers();
228  connect(layer, &LayerManager::layerCountChanged, this, &TimeLine::updateLayerNumber);
229  mNumLayers = layer->count();
230 
231  scrubbing = false;
232 }
233 
234 void TimeLine::updateUI()
235 {
236  updateContent();
237 }
238 
239 int TimeLine::getLength()
240 {
241  return mTracks->getFrameLength();
242 }
243 
244 void TimeLine::setLength(int frame)
245 {
246  mTracks->setFrameLength(frame);
247  updateLength();
248 }
249 
254 void TimeLine::extendLength(int frame)
255 {
256  int currentLength = mTracks->getFrameLength();
257  if(frame > (currentLength * 0.75))
258  {
259  int newLength = static_cast<int>(std::max(frame, currentLength) * 1.5);
260 
261  if (newLength > 9999)
262  newLength = 9999;
263 
264  mTracks->setFrameLength(newLength);
265  updateLength();
266  }
267 }
268 
269 void TimeLine::resizeEvent(QResizeEvent*)
270 {
271  updateLayerView();
272 }
273 
274 void TimeLine::wheelEvent(QWheelEvent* event)
275 {
276  if (event->modifiers() & Qt::ShiftModifier)
277  {
278  mHScrollbar->event(event);
279  }
280  else
281  {
282  mVScrollbar->event(event);
283  }
284 }
285 
286 void TimeLine::deleteCurrentLayer()
287 {
288  LayerManager* layerMgr = editor()->layers();
289  QString strLayerName = layerMgr->currentLayer()->name();
290 
291  int ret = QMessageBox::warning(this,
292  tr("Delete Layer", "Windows title of Delete current layer pop-up."),
293  tr("Are you sure you want to delete layer: %1? This cannot be undone.").arg(strLayerName),
296  if (ret == QMessageBox::Ok)
297  {
298  Status st = layerMgr->deleteLayer(editor()->currentLayerIndex());
299  if (st == Status::ERROR_NEED_AT_LEAST_ONE_CAMERA_LAYER)
300  {
301  QMessageBox::information(this, "",
302  tr("Please keep at least one camera layer in project"));
303  }
304  }
305 }
306 
307 void TimeLine::updateFrame(int frameNumber)
308 {
309  Q_ASSERT(mTracks);
310 
311 
312  mTracks->updateFrame(mLastUpdatedFrame);
313  mTracks->updateFrame(frameNumber);
314 
315  mLastUpdatedFrame = frameNumber;
316 }
317 
318 void TimeLine::updateLayerView()
319 {
320  int pageDisplay = (mTracks->height() - mTracks->getOffsetY()) / mTracks->getLayerHeight();
321 
322  mVScrollbar->setMinimum(0);
323  mVScrollbar->setMaximum(qMax(0, mNumLayers - pageDisplay));
324  update();
325  updateContent();
326 }
327 
328 void TimeLine::updateLayerNumber(int numberOfLayers)
329 {
330  mNumLayers = numberOfLayers;
331  updateLayerView();
332 }
333 
334 void TimeLine::updateLength()
335 {
336  int frameLength = getLength();
337  mHScrollbar->setMaximum(qMax(0, frameLength - mTracks->width() / mTracks->getFrameSize()));
338  mTimeControls->updateLength(frameLength);
339  updateContent();
340 }
341 
342 void TimeLine::updateContent()
343 {
344  mLayerList->updateContent();
345  mTracks->updateContent();
346  update();
347 }
348 
349 void TimeLine::setLoop(bool loop)
350 {
351  mTimeControls->setLoop(loop);
352 }
353 
354 void TimeLine::setPlaying(bool isPlaying)
355 {
356  Q_UNUSED(isPlaying)
357  mTimeControls->updatePlayState();
358 }
359 
360 void TimeLine::setRangeState(bool range)
361 {
362  mTimeControls->setRangeState(range);
363 }
364 
365 int TimeLine::getRangeLower()
366 {
367  return mTimeControls->getRangeLower();
368 }
369 
370 int TimeLine::getRangeUpper()
371 {
372  return mTimeControls->getRangeUpper();
373 }
374 
375 void TimeLine::onObjectLoaded()
376 {
377  mTimeControls->updateUI();
378  updateLayerNumber(editor()->layers()->count());
379 }
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 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 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:254
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)