Pencil2D  ff90c0872e88be3bf81c548cd60f01983012ec49
Pencil2D is an animation software for both bitmap and vector graphics. It is free, multi-platform, and open source.
 All Classes Functions
timeline.cpp
1 /*
2 
3 Pencil - Traditional Animation Software
4 Copyright (C) 2005-2007 Patrick Corrieri & Pascal Naidon
5 Copyright (C) 2012-2017 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 <QImage>
21 #include <QPoint>
22 #include <QWidget>
23 #include <QScrollBar>
24 #include <QHBoxLayout>
25 #include <QMenu>
26 #include <QAction>
27 #include <QSplitter>
28 #include <QMessageBox>
29 #include <QLabel>
30 #include <QWheelEvent>
31 
32 #include "layer.h"
33 #include "editor.h"
34 #include "layermanager.h"
35 #include "timecontrols.h"
36 #include "timelinecells.h"
37 
38 
39 TimeLine::TimeLine( QWidget* parent ) : BaseDockWidget( parent )
40 {
41 }
42 
43 void TimeLine::initUI()
44 {
45  Q_ASSERT( editor() != nullptr );
46 
47  setWindowTitle( tr( "Timeline" ) );
48 
49  QWidget* timeLineContent = new QWidget( this );
50 
51  mLayerList = new TimeLineCells( this, editor(), TIMELINE_CELL_TYPE::Layers );
52  mTracks = new TimeLineCells( this, editor(), TIMELINE_CELL_TYPE::Tracks );
53 
54  mHScrollbar = new QScrollBar( Qt::Horizontal );
55  mVScrollbar = new QScrollBar( Qt::Vertical );
56  mVScrollbar->setMinimum( 0 );
57  mVScrollbar->setMaximum( 1 );
58  mVScrollbar->setPageStep( 1 );
59 
60  QWidget* leftWidget = new QWidget();
61  leftWidget->setMinimumWidth( 120 );
62  QWidget* rightWidget = new QWidget();
63 
64  QWidget* leftToolBar = new QWidget();
65  leftToolBar->setFixedHeight( 30 );
66  QWidget* rightToolBar = new QWidget();
67  rightToolBar->setFixedHeight( 30 );
68 
69  // --- left widget ---
70  // --------- layer buttons ---------
71  QToolBar* layerButtons = new QToolBar( this );
72  QLabel* layerLabel = new QLabel( tr( "Layers:" ) );
73  layerLabel->setIndent( 5 );
74  layerLabel->setFont( QFont( "Helvetica", 10 ) );
75 
76  QToolButton* addLayerButton = new QToolButton( this );
77  addLayerButton->setIcon( QIcon( ":icons/add.png" ) );
78  addLayerButton->setToolTip( tr("Add Layer") );
79  addLayerButton->setFixedSize( 24, 24 );
80 
81  QToolButton* removeLayerButton = new QToolButton( this );
82  removeLayerButton->setIcon( QIcon( ":icons/remove.png" ) );
83  removeLayerButton->setToolTip( tr("Remove Layer") );
84  removeLayerButton->setFixedSize( 24, 24 );
85 
86  layerButtons->addWidget( layerLabel );
87  layerButtons->addWidget( addLayerButton );
88  layerButtons->addWidget( removeLayerButton );
89  layerButtons->setFixedHeight(30);
90 
91  QHBoxLayout* leftToolBarLayout = new QHBoxLayout();
92  leftToolBarLayout->setMargin( 0 );
93  leftToolBarLayout->addWidget( layerButtons );
94  leftToolBar->setLayout( leftToolBarLayout );
95 
96  QAction* newBitmapLayerAct = new QAction( QIcon( ":icons/layer-bitmap.png" ), tr( "New Bitmap Layer" ), this );
97  QAction* newVectorLayerAct = new QAction( QIcon( ":icons/layer-vector.png" ), tr( "New Vector Layer" ), this );
98  QAction* newSoundLayerAct = new QAction( QIcon( ":icons/layer-sound.png" ), tr( "New Sound Layer" ), this );
99  QAction* newCameraLayerAct = new QAction( QIcon( ":icons/layer-camera.png" ), tr( "New Camera Layer" ), this );
100 
101  QMenu* layerMenu = new QMenu( tr( "&Layer" ), this );
102  layerMenu->addAction( newBitmapLayerAct );
103  layerMenu->addAction( newVectorLayerAct );
104  layerMenu->addAction( newSoundLayerAct );
105  layerMenu->addAction( newCameraLayerAct );
106  addLayerButton->setMenu( layerMenu );
107  addLayerButton->setPopupMode( QToolButton::InstantPopup );
108 
109  QGridLayout* leftLayout = new QGridLayout();
110  leftLayout->addWidget( leftToolBar, 0, 0 );
111  leftLayout->addWidget( mLayerList, 1, 0 );
112  leftLayout->setMargin( 0 );
113  leftLayout->setSpacing( 0 );
114  leftWidget->setLayout( leftLayout );
115 
116  // --- right widget ---
117  // --------- key buttons ---------
118  QToolBar* timelineButtons = new QToolBar( this );
119  QLabel* keyLabel = new QLabel( tr( "Keys:" ) );
120  keyLabel->setFont( QFont( "Helvetica", 10 ) );
121  keyLabel->setIndent( 5 );
122 
123  QToolButton* addKeyButton = new QToolButton( this );
124  addKeyButton->setIcon( QIcon( ":icons/add.png" ) );
125  addKeyButton->setToolTip( tr("Add Frame") );
126  addKeyButton->setFixedSize( 24, 24 );
127 
128  QToolButton* removeKeyButton = new QToolButton( this );
129  removeKeyButton->setIcon( QIcon( ":icons/remove.png" ) );
130  removeKeyButton->setToolTip( tr("Remove Frame") );
131  removeKeyButton->setFixedSize( 24, 24 );
132 
133  QToolButton* duplicateKeyButton = new QToolButton( this );
134  duplicateKeyButton->setIcon( QIcon( ":icons/controls/duplicate.png" ) );
135  duplicateKeyButton->setToolTip( tr("Duplicate Frame") );
136  duplicateKeyButton->setFixedSize( 24, 24 );
137 
138  QLabel* onionLabel = new QLabel( tr( "Onion skin:" ) );
139  onionLabel->setFont( QFont( "Helvetica", 10 ) );
140 
141  QToolButton* onionTypeButton = new QToolButton( this );
142  onionTypeButton->setIcon( QIcon( ":icons/onion_type.png" ) );
143  onionTypeButton->setToolTip( tr("Toggle match keyframes") );
144  onionTypeButton->setFixedSize( 24, 24 );
145 
146  timelineButtons->addWidget( keyLabel );
147  timelineButtons->addWidget( addKeyButton );
148  timelineButtons->addWidget( removeKeyButton );
149  timelineButtons->addWidget( duplicateKeyButton );
150  timelineButtons->addSeparator();
151  timelineButtons->addWidget( onionLabel );
152  timelineButtons->addWidget( onionTypeButton );
153  timelineButtons->addSeparator();
154  timelineButtons->setFixedHeight(30);
155 
156  // --------- Time controls ---------
157  mTimeControls = new TimeControls( this );
158  mTimeControls->setCore( editor() );
159  mTimeControls->initUI();
160  mTimeControls->setSizePolicy(QSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding));
161  updateLength();
162 
163  QHBoxLayout* rightToolBarLayout = new QHBoxLayout();
164  rightToolBarLayout->addWidget( timelineButtons );
165  rightToolBarLayout->setAlignment(Qt::AlignLeft);
166  rightToolBarLayout->addWidget( mTimeControls );
167  rightToolBarLayout->setMargin( 0 );
168  rightToolBarLayout->setSpacing( 0 );
169  rightToolBar->setLayout( rightToolBarLayout );
170 
171  QGridLayout* rightLayout = new QGridLayout();
172  rightLayout->addWidget( rightToolBar, 0, 0 );
173  rightLayout->addWidget( mTracks, 1, 0 );
174  rightLayout->setMargin( 0 );
175  rightLayout->setSpacing( 0 );
176  rightWidget->setLayout( rightLayout );
177 
178  // --- Splitter ---
179  QSplitter* splitter = new QSplitter( this );
180  splitter->addWidget( leftWidget );
181  splitter->addWidget( rightWidget );
182  splitter->setSizes( QList<int>() << 100 << 600 );
183 
184 
185  QGridLayout* lay = new QGridLayout();
186  lay->addWidget( splitter, 0, 0 );
187  lay->addWidget( mVScrollbar, 0, 1 );
188  lay->addWidget( mHScrollbar, 1, 0 );
189  lay->setMargin( 0 );
190  lay->setSpacing( 0 );
191  timeLineContent->setLayout( lay );
192  setWidget( timeLineContent );
193 
194  setWindowFlags( Qt::WindowStaysOnTopHint );
195 
196  connect( mHScrollbar, &QScrollBar::valueChanged, mTracks, &TimeLineCells::hScrollChange );
197  connect (mTracks, &TimeLineCells::offsetChanged, mHScrollbar, &QScrollBar::setValue);
198  connect( mVScrollbar, &QScrollBar::valueChanged, mTracks, &TimeLineCells::vScrollChange );
199  connect( mVScrollbar, &QScrollBar::valueChanged, mLayerList, &TimeLineCells::vScrollChange );
200 
201  connect( splitter, &QSplitter::splitterMoved, this, &TimeLine::updateLength );
202 
203  connect( addKeyButton, &QToolButton::clicked, this, &TimeLine::addKeyClick );
204  connect( removeKeyButton, &QToolButton::clicked, this, &TimeLine::removeKeyClick );
205  connect( duplicateKeyButton, &QToolButton::clicked, this, &TimeLine::duplicateKeyClick );
206  connect( onionTypeButton, &QToolButton::clicked, this, &TimeLine::toogleAbsoluteOnionClick );
207 
208  connect( mTimeControls, &TimeControls::loopStartClick, this, &TimeLine::loopStartClick );
209  connect( mTimeControls, &TimeControls::loopEndClick, this, &TimeLine::loopEndClick );
210  connect( mTimeControls, &TimeControls::loopStartClick, this, &TimeLine::updateLength );
211  connect( mTimeControls, &TimeControls::loopEndClick, this, &TimeLine::updateLength );
212  connect( mTimeControls, &TimeControls::rangeStateChange, this, &TimeLine::updateLength );
213 
214  connect( mTimeControls, &TimeControls::soundClick, this, &TimeLine::soundClick );
215  connect( mTimeControls, &TimeControls::fpsClick, this, &TimeLine::fpsClick );
216  connect( mTimeControls, &TimeControls::fpsClick, this, &TimeLine::updateLength );
217 
218  connect( newBitmapLayerAct, &QAction::triggered, this, &TimeLine::newBitmapLayer );
219  connect( newVectorLayerAct, &QAction::triggered, this, &TimeLine::newVectorLayer );
220  connect( newSoundLayerAct, &QAction::triggered, this, &TimeLine::newSoundLayer );
221  connect( newCameraLayerAct, &QAction::triggered, this, &TimeLine::newCameraLayer );
222  connect( removeLayerButton, &QPushButton::clicked, this, &TimeLine::deleteCurrentLayer );
223 
224  connect( mLayerList, &TimeLineCells::mouseMovedY, mLayerList, &TimeLineCells::setMouseMoveY );
225  connect( mLayerList, &TimeLineCells::mouseMovedY, mTracks, &TimeLineCells::setMouseMoveY );
226  connect (mTracks, &TimeLineCells::lengthChanged, this, &TimeLine::updateLength );
227 
228  connect( editor(), &Editor::currentFrameChanged, this, &TimeLine::updateFrame );
229 
230  LayerManager* layer = editor()->layers();
231  connect( layer, &LayerManager::layerCountChanged, this, &TimeLine::updateLayerNumber );
232 
233  scrubbing = false;
234 }
235 
236 void TimeLine::updateUI()
237 {
238  mTracks->update();
239  mLayerList->update();
240 }
241 
242 int TimeLine::getFrameLength()
243 {
244  return mTracks->getFrameLength();
245 }
246 
247 void TimeLine::resizeEvent(QResizeEvent*)
248 {
249  updateLayerView();
250 }
251 
252 void TimeLine::wheelEvent(QWheelEvent* event)
253 {
254  if( event->modifiers() & Qt::ShiftModifier )
255  {
256  mHScrollbar->event(event);
257  }
258  else
259  {
260  mVScrollbar->event(event);
261  /*QPoint numPixels = event->pixelDelta();
262  QPoint numDegrees = event->angleDelta() / 8;
263  int isForward =0;
264  if ( !numPixels.isNull() )
265  {
266  if ( numPixels.ry() > 0 )
267  isForward =1;
268  else if ( numPixels.ry() < 0 )
269  isForward =-1;
270  }
271  else if (!numDegrees.isNull())
272  {
273  if ( numDegrees.ry() > 0 )
274  isForward =1;
275  else if ( numDegrees.ry() < 0 )
276  isForward =-1;
277  }
278 
279  if ( isForward > 0 )
280  {
281  mVScrollbar->triggerAction( QAbstractSlider::SliderSingleStepAdd );
282  }
283  else if ( isForward < 0 )
284  {
285  mVScrollbar->triggerAction( QAbstractSlider::SliderSingleStepSub );
286  }
287  else
288  {
289  //Do nothing we've had a wheel event where we are neither going forward or backward
290  //which should never happen?
291  }
292 
293  event->accept();*/
294  }
295 }
296 
297 void TimeLine::deleteCurrentLayer()
298 {
299  QString strLayerName = editor()->layers()->currentLayer()->name();
300 
301  int ret = QMessageBox::warning( this,
302  tr( "Warning" ),
303  tr( "Are you sure you want to delete layer: " ) + strLayerName + " ?",
304  QMessageBox::Ok | QMessageBox::Cancel,
305  QMessageBox::Ok );
306  if ( ret == QMessageBox::Ok )
307  {
308  editor()->layers()->deleteCurrentLayer();
309  }
310 }
311 
312 void TimeLine::updateFrame( int frameNumber )
313 {
314  Q_ASSERT ( mTracks );
315 
316  mTracks->updateFrame( mLastUpdatedFrame );
317  mTracks->updateFrame( frameNumber );
318 
319  mLastUpdatedFrame = frameNumber;
320 }
321 
322 void TimeLine::updateLayerView()
323 {
324  int pageDisplay = ( mTracks->height() - mTracks->getOffsetY() ) / mTracks->getLayerHeight();
325 
326  mVScrollbar->setMinimum( 0 );
327  mVScrollbar->setMaximum( qMax(0, qMax( 0, mNumLayers - pageDisplay ) ) );
328  update();
329  updateContent();
330 }
331 
332 void TimeLine::updateLayerNumber(int numberOfLayers)
333 {
334  mNumLayers = numberOfLayers;
335  updateLayerView();
336 }
337 
338 void TimeLine::updateLength()
339 {
340  int frameLength = getFrameLength();
341  mHScrollbar->setMaximum( qMax( 0, frameLength - mTracks->width() / mTracks->getFrameSize() ) );
342  mTimeControls->updateLength(frameLength);
343  update();
344  updateContent();
345 }
346 
347 void TimeLine::updateContent()
348 {
349  mLayerList->updateContent();
350  mTracks->updateContent();
351  update();
352 }
353 
354 void TimeLine::setLoop( bool loop )
355 {
356  mTimeControls->toggleLoop(loop);
357 }
358 
359 void TimeLine::setPlaying( bool isPlaying )
360 {
361  Q_UNUSED(isPlaying);
362  mTimeControls->updatePlayState();
363 }
364 
365 void TimeLine::setRangeState( bool range )
366 {
367  mTimeControls->toggleLoopControl(range);
368 }
369 
370 int TimeLine::getRangeLower()
371 {
372  return mTimeControls->getRangeLower();
373 }
374 
375 int TimeLine::getRangeUpper()
376 {
377  return mTimeControls->getRangeUpper();
378 }
379 
380 void TimeLine::onObjectLoaded()
381 {
382  mTimeControls->updateUI();
383 }