All Classes Namespaces Functions Variables Enumerations Properties Pages
timelinecells.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 "timelinecells.h"
19 
20 #include <QApplication>
21 #include <QResizeEvent>
22 #include <QInputDialog>
23 #include <QPainter>
24 #include <QSettings>
25 #include <QMenu>
26 
27 #include "camerapropertiesdialog.h"
28 #include "editor.h"
29 #include "keyframe.h"
30 #include "camera.h"
31 #include "cameraeasingtype.h"
32 #include "layermanager.h"
33 #include "viewmanager.h"
34 #include "object.h"
35 #include "playbackmanager.h"
36 #include "preferencemanager.h"
37 #include "timeline.h"
38 #include "toolmanager.h"
39 
40 
41 TimeLineCells::TimeLineCells(TimeLine* parent, Editor* editor, TIMELINE_CELL_TYPE type) : QWidget(parent)
42 {
43  mTimeLine = parent;
44  mEditor = editor;
45  mPrefs = editor->preference();
46  mType = type;
47 
48  mFrameLength = mPrefs->getInt(SETTING::TIMELINE_SIZE);
49  mFontSize = mPrefs->getInt(SETTING::LABEL_FONT_SIZE);
50  mFrameSize = mPrefs->getInt(SETTING::FRAME_SIZE);
51  mbShortScrub = mPrefs->isOn(SETTING::SHORT_SCRUB);
52  mDrawFrameNumber = mPrefs->isOn(SETTING::DRAW_LABEL);
53 
54  setMinimumSize(500, 4 * mLayerHeight);
57  setMouseTracking(true);
58 
59  connect(mPrefs, &PreferenceManager::optionChanged, this, &TimeLineCells::loadSetting);
60 }
61 
62 TimeLineCells::~TimeLineCells()
63 {
64  delete mCache;
65 }
66 
67 void TimeLineCells::loadSetting(SETTING setting)
68 {
69  switch (setting)
70  {
71  case SETTING::TIMELINE_SIZE:
72  mFrameLength = mPrefs->getInt(SETTING::TIMELINE_SIZE);
73  mTimeLine->updateLength();
74  break;
75  case SETTING::LABEL_FONT_SIZE:
76  mFontSize = mPrefs->getInt(SETTING::LABEL_FONT_SIZE);
77  break;
78  case SETTING::FRAME_SIZE:
79  mFrameSize = mPrefs->getInt(SETTING::FRAME_SIZE);
80  mTimeLine->updateLength();
81  break;
82  case SETTING::SHORT_SCRUB:
83  mbShortScrub = mPrefs->isOn(SETTING::SHORT_SCRUB);
84  break;
85  case SETTING::DRAW_LABEL:
86  mDrawFrameNumber = mPrefs->isOn(SETTING::DRAW_LABEL);
87  break;
88  default:
89  break;
90  }
91  updateContent();
92 }
93 
94 void TimeLineCells::setHold(int frame)
95 {
96  LayerCamera* curLayer = static_cast<LayerCamera*>(mEditor->layers()->currentLayer());
97  QList<int> frames = curLayer->getSelectedFramesByPos();
98  if (!frames.empty())
99  {
100  for (int pos:frames)
101  {
102  Camera* cam = curLayer->getCameraAtFrame(pos);
103  Camera* next = curLayer->getCameraAtFrame(curLayer->getNextKeyFramePosition(pos));
104  next->assign(*cam);
105  cam->setEasingType(CameraEasingType::LINEAR);
106  }
107  }
108  else
109  {
110  Camera* cam = curLayer->getCameraAtFrame(frame);
111  Camera* next = curLayer->getCameraAtFrame(curLayer->getNextKeyFramePosition(frame));
112  next->assign(*cam);
113  cam->setEasingType(CameraEasingType::LINEAR);
114  }
115  updateContent();
116 }
117 
118 void TimeLineCells::setCameraEasing(CameraEasingType type, int frame)
119 {
120  LayerCamera* layer = static_cast<LayerCamera*>(mEditor->layers()->currentLayer());
121  QList<int> frames = layer->getSelectedFramesByPos();
122  if (!frames.empty())
123  {
124  for (int pos:frames)
125  {
126  Camera* cam = layer->getCameraAtFrame(pos);
127  cam->setEasingType(type);
128  }
129  }
130  else
131  {
132  Camera* cam = layer->getCameraAtFrame(frame);
133  cam->setEasingType(type);
134  }
135 }
136 
137 int TimeLineCells::getFrameNumber(int x) const
138 {
139  return mFrameOffset + 1 + (x - mOffsetX) / mFrameSize;
140 }
141 
142 int TimeLineCells::getFrameX(int frameNumber) const
143 {
144  return mOffsetX + (frameNumber - mFrameOffset) * mFrameSize;
145 }
146 
147 void TimeLineCells::setFrameSize(int size)
148 {
149  mFrameSize = size;
150  mPrefs->set(SETTING::FRAME_SIZE, mFrameSize);
151  updateContent();
152 }
153 
154 int TimeLineCells::getLayerNumber(int y) const
155 {
156  int layerNumber = mLayerOffset + (y - mOffsetY) / mLayerHeight;
157 
158  int totalLayerCount = mEditor->object()->getLayerCount();
159 
160  // Layers numbers are displayed in descending order
161  // The last row is layer 0
162  if (layerNumber <= totalLayerCount)
163  layerNumber = (totalLayerCount - 1) - layerNumber;
164  else
165  layerNumber = 0;
166 
167  if (y < mOffsetY)
168  {
169  layerNumber = -1;
170  }
171 
172  if (layerNumber >= totalLayerCount)
173  {
174  layerNumber = totalLayerCount;
175  }
176 
177  //If the mouse release event if fired with mouse off the frame of the application
178  // mEditor->object()->getLayerCount() doesn't return the correct value.
179  if (layerNumber < -1)
180  {
181  layerNumber = -1;
182  }
183  return layerNumber;
184 }
185 
186 int TimeLineCells::getInbetweenLayerNumber(int y) const {
187  int layerNumber = getLayerNumber(y);
188  // Round the layer number towards the drag start
189  if(layerNumber != mFromLayer) {
190  if(mMouseMoveY > 0 && y < getLayerY(layerNumber) + mLayerHeight / 2) {
191  layerNumber++;
192  }
193  else if(mMouseMoveY < 0 && y > getLayerY(layerNumber) + mLayerHeight / 2) {
194  layerNumber--;
195  }
196  }
197  return layerNumber;
198 }
199 
200 int TimeLineCells::getLayerY(int layerNumber) const
201 {
202  return mOffsetY + (mEditor->object()->getLayerCount() - 1 - layerNumber - mLayerOffset)*mLayerHeight;
203 }
204 
205 void TimeLineCells::updateFrame(int frameNumber)
206 {
207  int x = getFrameX(frameNumber);
208  update(x - mFrameSize, 0, mFrameSize + 1, height());
209 }
210 
211 void TimeLineCells::updateContent()
212 {
213  drawContent();
214  update();
215 }
216 
217 bool TimeLineCells::didDetachLayer() const {
218  return abs(mMouseMoveY) > mLayerDetachThreshold;
219 }
220 
221 void TimeLineCells::showCameraMenu(QPoint pos)
222 {
223  int frameNumber = getFrameNumber(pos.x());
224  pos = this->mapToGlobal(pos);
225 
226  Layer* curLayer = mEditor->layers()->currentLayer();
227  Q_ASSERT(curLayer);
228  // only show menu if on camera layer
229  if (curLayer->type() != Layer::CAMERA)
230  {
231  return;
232  }
233 
234  int nextFrame = curLayer->getNextKeyFramePosition(frameNumber);
235 
236  const QString interpolateFrom = tr("Interpolate frames from: %1 to %2");
237  const QString clearMovementDesc = tr("Clear interpolation on: %1");
238 
239  if (mEasingMenu == nullptr)
240  {
241  mEasingMenu = new QMenu(this);
242 
243  mInterpolationMenu = mEasingMenu->addMenu(interpolateFrom.arg(frameNumber).arg(nextFrame));
244 
245  QMenu* subSine = mInterpolationMenu->addMenu(tr("Slow"));
246  QMenu* subQuad = mInterpolationMenu->addMenu(tr("Normal"));
247  QMenu* subCubic = mInterpolationMenu->addMenu(tr("Quick"));
248  QMenu* subQuart = mInterpolationMenu->addMenu(tr("Fast"));
249  QMenu* subQuint = mInterpolationMenu->addMenu(tr("Faster"));
250  QMenu* subExpo = mInterpolationMenu->addMenu(tr("Fastest"));
251 
252  mInterpolationMenu->addSeparator();
253  QMenu* subCirc = mInterpolationMenu->addMenu(tr("Circle-based"));
254  QMenu* subOther = mInterpolationMenu->addMenu(tr("Other"));
255 
256  subSine->addAction(tr("Slow Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INSINE, frameNumber); });
257  subSine->addAction(tr("Slow Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTSINE, frameNumber); });
258  subSine->addAction(tr("Slow Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTSINE, frameNumber); });
259  subSine->addAction(tr("Slow Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINSINE, frameNumber); });
260  subQuad->addAction(tr("Normal Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INQUAD, frameNumber); });
261  subQuad->addAction(tr("Normal Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTQUAD, frameNumber); });
262  subQuad->addAction(tr("Normal Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTQUAD, frameNumber); });
263  subQuad->addAction(tr("Normal Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINQUAD, frameNumber); });
264  subCubic->addAction(tr("Quick Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INCUBIC, frameNumber); });
265  subCubic->addAction(tr("Quick Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTCUBIC, frameNumber); });
266  subCubic->addAction(tr("Quick Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTCUBIC, frameNumber); });
267  subCubic->addAction(tr("Quick Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINCUBIC, frameNumber); });
268  subQuart->addAction(tr("Fast Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INQUART, frameNumber); });
269  subQuart->addAction(tr("Fast Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTQUART, frameNumber); });
270  subQuart->addAction(tr("Fast Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTQUART, frameNumber); });
271  subQuart->addAction(tr("Fast Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINQUART, frameNumber); });
272  subQuint->addAction(tr("Faster Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INQUINT, frameNumber); });
273  subQuint->addAction(tr("Faster Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTQUINT, frameNumber); });
274  subQuint->addAction(tr("Faster Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTQUINT, frameNumber); });
275  subQuint->addAction(tr("Faster Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINQUINT, frameNumber); });
276  subExpo->addAction(tr("Fastest Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INEXPO, frameNumber); });
277  subExpo->addAction(tr("Fastest Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTEXPO, frameNumber); });
278  subExpo->addAction(tr("Fastest Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTEXPO, frameNumber); });
279  subExpo->addAction(tr("Fastest Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINEXPO, frameNumber); });
280  subCirc->addAction(tr("Circle-based Ease-in"), [=] { this->setCameraEasing(CameraEasingType::INCIRC, frameNumber); });
281  subCirc->addAction(tr("Circle-based Ease-out"), [=] { this->setCameraEasing(CameraEasingType::OUTCIRC, frameNumber); });
282  subCirc->addAction(tr("Circle-based Ease-in - Ease-out"), [=] { this->setCameraEasing(CameraEasingType::INOUTCIRC, frameNumber); });
283  subCirc->addAction(tr("Circle-based Ease-out - Ease-in"), [=] { this->setCameraEasing(CameraEasingType::OUTINCIRC, frameNumber); });
284  mHoldAction = subOther->addAction(tr("Hold to frame %1").arg(nextFrame), [=] { this->setHold(frameNumber); });
285  subOther->addAction(tr("Linear interpolation"), [=] { this->setCameraEasing(CameraEasingType::LINEAR, frameNumber); });
286  }
287 
288  if (curLayer->getSelectedFramesByPos().empty() && !curLayer->keyExists(frameNumber)) {
289  return;
290  }
291 
292  if (curLayer->getSelectedFramesByPos().size() > 1)
293  {
294  QList<int> frameList = curLayer->getSelectedFramesByPos();
295  QString keyNumbers = "";
296  for (int pos:frameList)
297  {
298  keyNumbers += " " + QString::number(pos) + ",";
299  }
300  // Remove last comma
301  keyNumbers.chop(1);
302  mInterpolationMenu->setTitle(tr("Interpolate frames at: %1").arg(keyNumbers));
303  mHoldAction->setText(clearMovementDesc.arg(keyNumbers));
304  }
305  else if(curLayer->keyExists(frameNumber))
306  {
307  QString keyPosString = QString::number(nextFrame);
308  if (frameNumber == nextFrame) {
309  keyPosString = "-";
310  }
311  mInterpolationMenu->setTitle(interpolateFrom.arg(frameNumber).arg(keyPosString));
312  mHoldAction->setText(clearMovementDesc.arg(nextFrame));
313  }
314 
315  mEasingMenu->popup(pos);
316 }
317 
318 void TimeLineCells::drawContent()
319 {
321 
322  if (mCache == nullptr)
323  {
324  mCache = new QPixmap(size());
325  if (mCache->isNull())
326  {
327  // fail to create cache
328  return;
329  }
330  }
331 
332  QPainter painter(mCache);
333 
334  const Object* object = mEditor->object();
335 
336  Q_ASSERT(object != nullptr);
337 
338  const Layer* currentLayer = mEditor->layers()->currentLayer();
339  if (currentLayer == nullptr) return;
340 
341  // grey background of the view
342  painter.setPen(Qt::NoPen);
343  painter.setBrush(palette.color(QPalette::Base));
344  painter.drawRect(QRect(0, 0, width(), height()));
345 
346  const int widgetWidth = width();
347  const int layerHeight = mLayerHeight;
348 
349  // --- draw layers of the current object
350  for (int i = 0; i < object->getLayerCount(); i++)
351  {
352  if (i == mEditor->layers()->currentLayerIndex())
353  {
354  continue;
355  }
356  const Layer* layeri = object->getLayer(i);
357 
358  if (layeri != nullptr)
359  {
360  const int layerY = getLayerY(i);
361  switch (mType)
362  {
363  case TIMELINE_CELL_TYPE::Tracks:
364  paintTrack(painter, layeri, mOffsetX,
365  layerY, widgetWidth - mOffsetX,
366  layerHeight, false, mFrameSize);
367  break;
368 
369  case TIMELINE_CELL_TYPE::Layers:
370  paintLabel(painter, layeri, 0,
371  layerY, widgetWidth - 1,
372  layerHeight, false, mEditor->layerVisibility());
373  break;
374  }
375  }
376  }
377 
378  if (didDetachLayer())
379  {
380  int layerYMouseMove = getLayerY(mEditor->layers()->currentLayerIndex()) + mMouseMoveY;
381  if (mType == TIMELINE_CELL_TYPE::Tracks)
382  {
383  paintTrack(painter, currentLayer,
384  mOffsetX, layerYMouseMove,
385  widgetWidth - mOffsetX, layerHeight,
386  true, mFrameSize);
387  }
388  else if (mType == TIMELINE_CELL_TYPE::Layers)
389  {
390  paintLabel(painter, currentLayer,
391  0, layerYMouseMove,
392  widgetWidth - 1, layerHeight, true, mEditor->layerVisibility());
393 
394  paintLayerGutter(painter);
395  }
396  }
397  else
398  {
399  if (mType == TIMELINE_CELL_TYPE::Tracks)
400  {
401  paintTrack(painter,
402  currentLayer,
403  mOffsetX,
404  getLayerY(mEditor->layers()->currentLayerIndex()),
405  widgetWidth - mOffsetX,
406  layerHeight,
407  true,
408  mFrameSize);
409  }
410  else if (mType == TIMELINE_CELL_TYPE::Layers)
411  {
412  paintLabel(painter,
413  currentLayer,
414  0,
415  getLayerY(mEditor->layers()->currentLayerIndex()),
416  widgetWidth - 1,
417  layerHeight,
418  true,
419  mEditor->layerVisibility());
420  }
421  }
422 
423  // --- draw track bar background
424  painter.setPen(Qt::NoPen);
425  painter.setBrush(palette.color(QPalette::Base));
426  painter.drawRect(QRect(0, 0, width() - 1, mOffsetY - 1));
427 
428  // --- draw bottom line splitter for track bar
429  painter.setPen(palette.color(QPalette::Mid));
430  painter.drawLine(0, mOffsetY - 2, width() - 1, mOffsetY - 2);
431 
432  if (mType == TIMELINE_CELL_TYPE::Layers)
433  {
434  // --- draw circle
435  painter.setPen(palette.color(QPalette::Text));
436  if (mEditor->layerVisibility() == LayerVisibility::CURRENTONLY)
437  {
438  painter.setBrush(palette.color(QPalette::Base));
439  }
440  else if (mEditor->layerVisibility() == LayerVisibility::RELATED)
441  {
442  QColor color = palette.color(QPalette::Text);
443  color.setAlpha(128);
444  painter.setBrush(color);
445  }
446  else if (mEditor->layerVisibility() == LayerVisibility::ALL)
447  {
448  painter.setBrush(palette.brush(QPalette::Text));
449  }
450  painter.setRenderHint(QPainter::Antialiasing, true);
451  painter.drawEllipse(6, 4, 9, 9);
452  painter.setRenderHint(QPainter::Antialiasing, false);
453  }
454  else if (mType == TIMELINE_CELL_TYPE::Tracks)
455  {
456  paintTicks(painter, palette);
457 
458  const auto object = mEditor->object();
459  for (int i = 0; i < object->getLayerCount(); i++) {
460  paintSelectedFrames(painter, object->getLayer(i), i);
461  }
462  }
463 }
464 
465 void TimeLineCells::paintTicks(QPainter& painter, const QPalette& palette) const
466 {
467  painter.setPen(palette.color(QPalette::Text));
468  painter.setBrush(palette.brush(QPalette::Text));
469  int fps = mEditor->playback()->fps();
470  for (int i = mFrameOffset; i < mFrameOffset + (width() - mOffsetX) / mFrameSize; i++)
471  {
472  // line x pos + some offset
473  const int lineX = getFrameX(i) + 1;
474  if (i + 1 >= mTimeLine->getRangeLower() && i < mTimeLine->getRangeUpper())
475  {
476  painter.setPen(Qt::NoPen);
477  painter.setBrush(palette.color(QPalette::Highlight));
478 
479  painter.drawRect(lineX, 1, mFrameSize + 1, 2);
480 
481  painter.setPen(palette.color(QPalette::Text));
482  painter.setBrush(palette.brush(QPalette::Text));
483  }
484 
485  // Draw large tick at fps mark
486  if (i % fps == 0 || i % fps == fps / 2)
487  {
488  painter.drawLine(lineX, 1, lineX, 5);
489  }
490  else // draw small tick
491  {
492  painter.drawLine(lineX, 1, lineX, 3);
493  }
494  if (i == 0 || i % fps == fps - 1)
495  {
496  int incr = (i < 9) ? 4 : 0; // poor man’s text centering
497  painter.drawText(QPoint(lineX + incr, 15), QString::number(i + 1));
498  }
499  }
500 }
501 
502 void TimeLineCells::paintTrack(QPainter& painter, const Layer* layer,
503  int x, int y, int width, int height,
504  bool selected, int frameSize) const
505 {
506  const QPalette palette = QApplication::palette();
507  QColor col;
508  // Color each track according to the layer type
509  if (layer->type() == Layer::BITMAP) col = QColor(51, 155, 252);
510  if (layer->type() == Layer::VECTOR) col = QColor(70, 205, 123);
511  if (layer->type() == Layer::SOUND) col = QColor(255, 141, 112);
512  if (layer->type() == Layer::CAMERA) col = QColor(253, 202, 92);
513  // Dim invisible layers
514  if (!layer->visible()) col.setAlpha(64);
515 
516  painter.save();
517  painter.setBrush(col);
519  painter.drawRect(x, y - 1, width, height);
520 
521  if (!layer->visible())
522  {
523  painter.restore();
524  return;
525  }
526 
527  // Changes the appearance if selected
528  if (selected)
529  {
530  paintSelection(painter, x, y, width, height);
531  }
532  else
533  {
534  painter.save();
535  QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
536  linearGrad.setColorAt(0, QColor(255,255,255,150));
537  linearGrad.setColorAt(1, QColor(0,0,0,0));
539  painter.setBrush(linearGrad);
540  painter.drawRect(x, y - 1, width, height);
541  painter.restore();
542  }
543 
544  paintFrames(painter, col, layer, y, height, selected, frameSize);
545 
546  painter.restore();
547 }
548 
549 void TimeLineCells::paintFrames(QPainter& painter, QColor trackCol, const Layer* layer, int y, int height, bool selected, int frameSize) const
550 {
551  painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
552 
553  int recTop = y + 1;
554  int standardWidth = frameSize - 2;
555 
556  int recHeight = height - 4;
557  layer->foreachKeyFrame([&](KeyFrame* key)
558  {
559  int framePos = key->pos();
560  int recWidth = standardWidth;
561  int recLeft = getFrameX(framePos) - recWidth;
562 
563  if (key->length() > 1)
564  {
565  // This is especially for sound clips.
566  // Sound clips are the only type of KeyFrame with variable frame length.
567  recWidth = frameSize * key->length();
568  }
569 
570  // Paint the frame border
571  if (selected && framePos == mEditor->currentFrame()) {
572  painter.setPen(Qt::white);
573  } else {
574  painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
575  }
576 
577  // Paint the frame contents
578  if (selected)
579  {
580  if (key->isSelected()) {
581  painter.setBrush(QColor(60, 60, 60));
582  }
583  else
584  {
585  painter.setBrush(QColor(trackCol.red(), trackCol.green(), trackCol.blue(), 150));
586  }
587  }
588 
589  if (!key->isSelected()) {
590  painter.drawRect(recLeft, recTop, recWidth, recHeight);
591  }
592  });
593 
594  if (!mMovingFrames && selected && mLayerPosMoveY != -1 && mLayerPosMoveY == mEditor->currentLayerIndex()) {
595  paintFrameCursorOnCurrentLayer(painter, recTop, standardWidth, recHeight);
596  }
597 }
598 
599 void TimeLineCells::paintFrameCursorOnCurrentLayer(QPainter &painter, int recTop, int recWidth, int recHeight) const
600 {
601  int recLeft = getFrameX(mFramePosMoveX) - recWidth;
602 
603  painter.save();
604  const QPalette palette = QApplication::palette();
605  // Don't fill
606  painter.setBrush(Qt::NoBrush);
607  // paint border
608  QColor penColor = palette.color(QPalette::WindowText);
609  penColor.setAlpha(127);
610  painter.setPen(penColor);
611  painter.drawRect(recLeft, recTop, recWidth, recHeight);
612  painter.restore();
613 }
614 
615 void TimeLineCells::paintSelectedFrames(QPainter& painter, const Layer* layer, const int layerIndex) const
616 {
617  int mouseX = mMouseMoveX;
618  int posUnderCursor = getFrameNumber(mMousePressX);
619  int standardWidth = mFrameSize - 2;
620  int recWidth = standardWidth;
621  int recHeight = mLayerHeight - 4;
622  int recTop = getLayerY(layerIndex) + 1;
623 
624  painter.save();
625  for (int framePos : layer->getSelectedFramesByPos()) {
626 
627  KeyFrame* key = layer->getKeyFrameAt(framePos);
628  if (key->length() > 1)
629  {
630  // This is a special case for sound clip.
631  // Sound clip is the only type of KeyFrame that has variable frame length.
632  recWidth = mFrameSize * key->length();
633  }
634 
635  painter.setBrush(QColor(60, 60, 60));
636  painter.setPen(QPen(QBrush(QColor(40, 40, 40)), 1, Qt::SolidLine, Qt::RoundCap, Qt::RoundJoin));
637 
638  int frameX = getFrameX(framePos);
639  if (mMovingFrames) {
640  int offset = (framePos - posUnderCursor) + mFrameOffset;
641  int newFrameX = getFrameX(getFrameNumber(getFrameX(offset)+mouseX)) - standardWidth;
642  // Paint as frames are hovering
643  painter.drawRect(newFrameX, recTop-4, recWidth, recHeight);
644 
645  } else {
646  int currentFrameX = frameX - standardWidth;
647  painter.drawRect(currentFrameX, recTop, recWidth, recHeight);
648  }
649  }
650  painter.restore();
651 }
652 
653 void TimeLineCells::paintLabel(QPainter& painter, const Layer* layer,
654  int x, int y, int width, int height,
655  bool selected, LayerVisibility layerVisibility) const
656 {
657  const QPalette palette = QApplication::palette();
658 
659  if (selected)
660  {
661  painter.setBrush(palette.color(QPalette::Highlight));
662  }
663  else
664  {
665  painter.setBrush(palette.color(QPalette::Base));
666  }
667  painter.setPen(Qt::NoPen);
668  painter.drawRect(x, y - 1, width, height); // empty rectangle by default
669 
670  if (!layer->visible())
671  {
672  painter.setBrush(palette.color(QPalette::Base));
673  }
674  else
675  {
676  if ((layerVisibility == LayerVisibility::ALL) || selected)
677  {
678  painter.setBrush(palette.color(QPalette::Text));
679  }
680  else if (layerVisibility == LayerVisibility::CURRENTONLY)
681  {
682  painter.setBrush(palette.color(QPalette::Base));
683  }
684  else if (layerVisibility == LayerVisibility::RELATED)
685  {
686  QColor color = palette.color(QPalette::Text);
687  color.setAlpha(128);
688  painter.setBrush(color);
689  }
690  }
691  if (selected)
692  {
693  painter.setPen(palette.color(QPalette::HighlightedText));
694  }
695  else
696  {
697  painter.setPen(palette.color(QPalette::Text));
698  }
699  painter.setRenderHint(QPainter::Antialiasing, true);
700  painter.drawEllipse(x + 6, y + 4, 9, 9);
701  painter.setRenderHint(QPainter::Antialiasing, false);
702 
703  if (layer->type() == Layer::BITMAP) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-bitmap.png"));
704  if (layer->type() == Layer::VECTOR) painter.drawPixmap(QPoint(20, y + 2), QPixmap(":/icons/layer-vector.png"));
705  if (layer->type() == Layer::SOUND) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-sound.png"));
706  if (layer->type() == Layer::CAMERA) painter.drawPixmap(QPoint(21, y + 2), QPixmap(":/icons/layer-camera.png"));
707 
708  if (selected)
709  {
710  painter.setPen(palette.color(QPalette::HighlightedText));
711  }
712  else
713  {
714  painter.setPen(palette.color(QPalette::Text));
715  }
716  painter.drawText(QPoint(45, y + (2 * height) / 3), layer->name());
717 }
718 
719 void TimeLineCells::paintSelection(QPainter& painter, int x, int y, int width, int height) const
720 {
721  QLinearGradient linearGrad(QPointF(0, y), QPointF(0, y + height));
722  linearGrad.setColorAt(0, QColor(0, 0, 0, 255));
723  linearGrad.setColorAt(1, QColor(255, 255, 255, 0));
724  painter.save();
726  painter.setBrush(linearGrad);
727  painter.setPen(Qt::NoPen);
728  painter.drawRect(x, y, width, height - 1);
729  painter.restore();
730 }
731 
732 void TimeLineCells::paintLayerGutter(QPainter& painter) const
733 {
734  painter.setPen(QApplication::palette().color(QPalette::Mid));
735  if (mMouseMoveY > mLayerDetachThreshold)
736  {
737  painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY))+mLayerHeight, width(), 2);
738  }
739  else
740  {
741  painter.drawRect(0, getLayerY(getInbetweenLayerNumber(mEndY)), width(), 2);
742  }
743 }
744 
745 void TimeLineCells::paintOnionSkin(QPainter& painter) const
746 {
747  Layer* layer = mEditor->layers()->currentLayer();
748  if (layer == nullptr) { return; }
749 
750  int frameNumber = mEditor->currentFrame();
751 
752  int prevOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_PREV_FRAMES_NUM);
753  int nextOnionSkinCount = mEditor->preference()->getInt(SETTING::ONION_NEXT_FRAMES_NUM);
754 
755  bool isAbsolute = (mEditor->preference()->getString(SETTING::ONION_TYPE) == "absolute");
756 
757  if (mEditor->preference()->isOn(SETTING::PREV_ONION) && prevOnionSkinCount > 0)
758  {
759  int onionFrameNumber = frameNumber;
760  if (isAbsolute)
761  {
762  onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber+1, true);
763  }
764  onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
765  int onionPosition = 0;
766 
767  while (onionPosition < prevOnionSkinCount && onionFrameNumber > 0)
768  {
769  painter.setBrush(QColor(128, 128, 128, 128));
770  painter.setPen(Qt::NoPen);
771  QRect onionRect;
772  onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
773  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
774  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
775  painter.drawRect(onionRect);
776 
777  onionFrameNumber = layer->getPreviousFrameNumber(onionFrameNumber, isAbsolute);
778  onionPosition++;
779  }
780  }
781 
782  if (mEditor->preference()->isOn(SETTING::NEXT_ONION) && nextOnionSkinCount > 0) {
783 
784  int onionFrameNumber = layer->getNextFrameNumber(frameNumber, isAbsolute);
785  int onionPosition = 0;
786 
787  while (onionPosition < nextOnionSkinCount && onionFrameNumber > 0)
788  {
789  painter.setBrush(QColor(128, 128, 128, 128));
790  painter.setPen(Qt::NoPen);
791  QRect onionRect;
792  onionRect.setTopLeft(QPoint(getFrameX(onionFrameNumber - 1), 0));
793  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), height()));
794  onionRect.setBottomRight(QPoint(getFrameX(onionFrameNumber), 19));
795  painter.drawRect(onionRect);
796 
797  onionFrameNumber = layer->getNextFrameNumber(onionFrameNumber, isAbsolute);
798  onionPosition++;
799  }
800  }
801 }
802 
803 void TimeLineCells::paintEvent(QPaintEvent*)
804 {
805  Object* object = mEditor->object();
806  Layer* layer = mEditor->layers()->currentLayer();
807 
808  Q_ASSUME(object != nullptr && layer != nullptr);
809 
810  const QPalette palette = QApplication::palette();
811  QPainter painter(this);
812 
813  bool isPlaying = mEditor->playback()->isPlaying();
814  if ((!isPlaying && !mTimeLine->scrubbing) || mCache == nullptr)
815  {
816  drawContent();
817  }
818  if (mCache)
819  {
820  painter.drawPixmap(QPoint(0, 0), *mCache);
821  }
822 
823  if (mType == TIMELINE_CELL_TYPE::Tracks)
824  {
825 
826  if (mPrevFrame != mEditor->currentFrame() || mEditor->playback()->isPlaying())
827  {
828  mPrevFrame = mEditor->currentFrame();
829  trackScrubber();
830  }
831 
832  if (!isPlaying) {
833  paintOnionSkin(painter);
834  }
835 
836  // --- draw the position of the current frame
837  if (mEditor->currentFrame() > mFrameOffset)
838  {
839  QColor scrubColor = palette.color(QPalette::Highlight);
840  scrubColor.setAlpha(160);
841  painter.setBrush(scrubColor);
842  painter.setPen(Qt::NoPen);
843 
844  int currentFrameStartX = getFrameX(mEditor->currentFrame() - 1) + 1;
845  int currentFrameEndX = getFrameX(mEditor->currentFrame());
846  QRect scrubRect;
847  scrubRect.setTopLeft(QPoint(currentFrameStartX, 0));
848  scrubRect.setBottomRight(QPoint(currentFrameEndX, height()));
849  if (mbShortScrub)
850  {
851  scrubRect.setBottomRight(QPoint(currentFrameEndX, 19));
852  }
853  painter.save();
854 
855  bool mouseUnderScrubber = mEditor->currentFrame() == mFramePosMoveX;
856  if (mouseUnderScrubber) {
857  QRect smallScrub = QRect(QPoint(currentFrameStartX, 0), QPoint(currentFrameEndX,19));
858  QPen pen = scrubColor;
859  pen.setWidth(2);
860  painter.setPen(pen);
861  painter.drawRect(smallScrub);
862  painter.setBrush(Qt::NoBrush);
863  }
864  painter.drawRect(scrubRect);
865  painter.restore();
866 
867  painter.setPen(palette.color(QPalette::HighlightedText));
868  int incr = (mEditor->currentFrame() < 10) ? 4 : 0;
869  painter.drawText(QPoint(currentFrameStartX + incr, 15),
870  QString::number(mEditor->currentFrame()));
871  }
872  }
873 }
874 
875 void TimeLineCells::resizeEvent(QResizeEvent* event)
876 {
877  clearCache();
878  updateContent();
879  event->accept();
880  emit lengthChanged(getFrameLength());
881 }
882 
883 bool TimeLineCells::event(QEvent* event)
884 {
885  if (event->type() == QEvent::Leave) {
886  onDidLeaveWidget();
887  }
888 
889  return QWidget::event(event);
890 }
891 
892 void TimeLineCells::mousePressEvent(QMouseEvent* event)
893 {
894  int frameNumber = getFrameNumber(event->pos().x());
895  int layerNumber = getLayerNumber(event->pos().y());
896  mCurrentLayerNumber = layerNumber;
897 
898  mMousePressX = event->pos().x();
899  mFromLayer = mToLayer = layerNumber;
900 
901  mStartY = event->pos().y();
902  mStartLayerNumber = layerNumber;
903  mEndY = event->pos().y();
904 
905  mStartFrameNumber = frameNumber;
906  mLastFrameNumber = mStartFrameNumber;
907 
908  mCanMoveFrame = false;
909  mMovingFrames = false;
910 
911  mCanBoxSelect = false;
912  mBoxSelecting = false;
913 
914  mClickSelecting = false;
915 
916  primaryButton = event->button();
917 
918  bool switchLayer = mEditor->tools()->currentTool()->switchingLayer();
919  if (!switchLayer) { return; }
920 
921  switch (mType)
922  {
923  case TIMELINE_CELL_TYPE::Layers:
924  if (layerNumber != -1 && layerNumber < mEditor->object()->getLayerCount())
925  {
926  if (event->pos().x() < 15)
927  {
928  mEditor->switchVisibilityOfLayer(layerNumber);
929  }
930  else if (mEditor->currentLayerIndex() != layerNumber)
931  {
932  mEditor->layers()->setCurrentLayer(layerNumber);
933  mEditor->layers()->currentLayer()->deselectAll();
934  }
935  }
936  if (layerNumber == -1)
937  {
938  if (event->pos().x() < 15)
939  {
940  if (event->button() == Qt::LeftButton) {
941  mEditor->increaseLayerVisibilityIndex();
942  } else if (event->button() == Qt::RightButton) {
943  mEditor->decreaseLayerVisibilityIndex();
944  }
945  }
946  }
947  break;
948  case TIMELINE_CELL_TYPE::Tracks:
949  if (event->button() == Qt::MidButton)
950  {
951  mLastFrameNumber = getFrameNumber(event->pos().x());
952  }
953  else
954  {
955  if (frameNumber == mEditor->currentFrame() && mStartY < 20)
956  {
957  if (mEditor->playback()->isPlaying())
958  {
959  mEditor->playback()->stop();
960  }
961  mTimeLine->scrubbing = true;
962  }
963  else
964  {
965  if ((layerNumber != -1) && layerNumber < mEditor->object()->getLayerCount())
966  {
967  int previousLayerNumber = mEditor->layers()->currentLayerIndex();
968 
969  if (previousLayerNumber != layerNumber)
970  {
971  Layer *previousLayer = mEditor->object()->getLayer(previousLayerNumber);
972  previousLayer->deselectAll();
973  mEditor->selectedFramesChanged();
974  mEditor->layers()->setCurrentLayer(layerNumber);
975  }
976 
977  Layer *currentLayer = mEditor->object()->getLayer(layerNumber);
978 
979  // Check if we are using the alt key
980  if (event->modifiers() == Qt::AltModifier)
981  {
982  // If it is the case, we select everything that is after the selected frame
983  mClickSelecting = true;
984  mCanMoveFrame = true;
985 
986  currentLayer->selectAllFramesAfter(frameNumber);
987  mEditor->selectedFramesChanged();
988  }
989  // Check if we are clicking on a non selected frame
990  else if (!currentLayer->isFrameSelected(frameNumber))
991  {
992  // If it is the case, we select it
993  mCanBoxSelect = true;
994  mClickSelecting = true;
995  if (event->button() == Qt::RightButton)
996  {
997  showCameraMenu(event->pos());
998  }
999 
1000  if (event->modifiers() == Qt::ControlModifier)
1001  {
1002  // Add/remove from already selected
1003  currentLayer->toggleFrameSelected(frameNumber, true);
1004  mEditor->selectedFramesChanged();
1005  }
1006  else if (event->modifiers() == Qt::ShiftModifier)
1007  {
1008  // Select a range from the last selected
1009  currentLayer->extendSelectionTo(frameNumber);
1010  mEditor->selectedFramesChanged();
1011  }
1012  else
1013  {
1014  currentLayer->toggleFrameSelected(frameNumber, false);
1015  mEditor->selectedFramesChanged();
1016  }
1017  }
1018  else
1019  {
1020  // If selected they can also be interpolated
1021  if (event->button() == Qt::RightButton)
1022  {
1023  showCameraMenu(event->pos());
1024  }
1025  // We clicked on a selected frame, we can move it
1026  mCanMoveFrame = true;
1027  }
1028 
1029  if (currentLayer->hasAnySelectedFrames()) {
1030  emit selectionChanged();
1031  }
1032 
1033  mTimeLine->updateContent();
1034  }
1035  else
1036  {
1037  if (frameNumber > 0)
1038  {
1039  if (mEditor->playback()->isPlaying())
1040  {
1041  mEditor->playback()->stop();
1042  }
1043  if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != frameNumber)
1044  {
1045  mEditor->playback()->playScrub(frameNumber);
1046  mLastScrubFrame = frameNumber;
1047  }
1048 
1049  mEditor->scrubTo(frameNumber);
1050 
1051  mTimeLine->scrubbing = true;
1052  qDebug("Scrub to %d frame", frameNumber);
1053  }
1054  }
1055  }
1056  }
1057  break;
1058  }
1059 }
1060 
1061 void TimeLineCells::mouseMoveEvent(QMouseEvent* event)
1062 {
1063  mMouseMoveX = event->pos().x();
1064  mFramePosMoveX = getFrameNumber(mMouseMoveX);
1065  mLayerPosMoveY = getLayerNumber(event->pos().y());
1066 
1067  if (mType == TIMELINE_CELL_TYPE::Layers)
1068  {
1069  if (event->buttons() & Qt::LeftButton ) {
1070  mEndY = event->pos().y();
1071  emit mouseMovedY(mEndY - mStartY);
1072  }
1073  }
1074  else if (mType == TIMELINE_CELL_TYPE::Tracks)
1075  {
1076  if (primaryButton == Qt::MidButton)
1077  {
1078  mFrameOffset = qMin(qMax(0, mFrameLength - width() / getFrameSize()), qMax(0, mFrameOffset + mLastFrameNumber - mFramePosMoveX));
1079  update();
1080  emit offsetChanged(mFrameOffset);
1081  }
1082  else
1083  {
1084  if (mTimeLine->scrubbing)
1085  {
1086  if (mEditor->playback()->getSoundScrubActive() && mLastScrubFrame != mFramePosMoveX)
1087  {
1088  mEditor->playback()->playScrub(mFramePosMoveX);
1089  mLastScrubFrame = mFramePosMoveX;
1090  }
1091  mEditor->scrubTo(mFramePosMoveX);
1092  }
1093  else
1094  {
1095  if (event->buttons() & Qt::LeftButton) {
1096  if (mStartLayerNumber != -1 && mStartLayerNumber < mEditor->object()->getLayerCount())
1097  {
1098  Layer *currentLayer = mEditor->object()->getLayer(mStartLayerNumber);
1099 
1100  // Check if the frame we clicked was selected
1101  if (mCanMoveFrame) {
1102 
1103  // If it is the case, we move the selected frames in the layer
1104  mMovingFrames = true;
1105  }
1106  else if (mCanBoxSelect)
1107  {
1108  // Otherwise, we do a box select
1109  mBoxSelecting = true;
1110 
1111  currentLayer->deselectAll();
1112  currentLayer->setFrameSelected(mStartFrameNumber, true);
1113  currentLayer->extendSelectionTo(mFramePosMoveX);
1114  }
1115  mLastFrameNumber = mFramePosMoveX;
1116  }
1117  }
1118  }
1119  }
1120  }
1121  mTimeLine->update();
1122 }
1123 
1124 void TimeLineCells::mouseReleaseEvent(QMouseEvent* event)
1125 {
1126  if (event->button() != primaryButton) return;
1127 
1128  primaryButton = Qt::NoButton;
1129  mEndY = mStartY;
1130  mTimeLine->scrubbing = false;
1131 
1132  int frameNumber = getFrameNumber(event->pos().x());
1133  if (frameNumber < 1) frameNumber = 1;
1134  int layerNumber = getLayerNumber(event->pos().y());
1135 
1136  if (mCurrentLayerNumber != -1 && mType == TIMELINE_CELL_TYPE::Tracks && primaryButton != Qt::MidButton)
1137  {
1138  // We should affect the current layer based on what's selected, not where the mouse currently is.
1139  Layer* currentLayer = mEditor->layers()->getLayer(mCurrentLayerNumber);
1140 
1141  Q_ASSERT(currentLayer);
1142  if (mMovingFrames)
1143  {
1144  int posUnderCursor = getFrameNumber(mMousePressX);
1145  int offset = frameNumber - posUnderCursor;
1146 
1147  currentLayer->moveSelectedFrames(offset);
1148 
1149  mEditor->layers()->notifyAnimationLengthChanged();
1150  mEditor->framesModified();
1151  }
1152  else if (!mTimeLine->scrubbing && !mMovingFrames && !mClickSelecting && !mBoxSelecting)
1153  {
1154  // De-selecting if we didn't move, scrub nor select anything
1155  bool multipleSelection = (event->modifiers() == Qt::ControlModifier);
1156 
1157  // Add/remove from already selected
1158  currentLayer->toggleFrameSelected(frameNumber, multipleSelection);
1159  mEditor->selectedFramesChanged();
1160  }
1161  }
1162  if (mType == TIMELINE_CELL_TYPE::Layers && layerNumber != mStartLayerNumber && mStartLayerNumber != -1 && layerNumber != -1)
1163  {
1164  mToLayer = getInbetweenLayerNumber(event->pos().y());
1165  if (mToLayer != mFromLayer && mToLayer > -1 && mToLayer < mEditor->layers()->count())
1166  {
1167  // Bubble the from layer up or down to the to layer
1168  if (mToLayer < mFromLayer) // bubble up
1169  {
1170  for (int i = mFromLayer - 1; i >= mToLayer; i--)
1171  mEditor->swapLayers(i, i + 1);
1172  }
1173  else // bubble down
1174  {
1175  for (int i = mFromLayer + 1; i <= mToLayer; i++)
1176  mEditor->swapLayers(i, i - 1);
1177  }
1178  }
1179  }
1180 
1181  emit mouseMovedY(0);
1182  mTimeLine->updateContent();
1183 
1184  mMovingFrames = false;
1185 }
1186 
1187 void TimeLineCells::mouseDoubleClickEvent(QMouseEvent* event)
1188 {
1189  int frameNumber = getFrameNumber(event->pos().x());
1190  int layerNumber = getLayerNumber(event->pos().y());
1191 
1192  // -- short scrub --
1193  if (event->pos().y() < 20 && (mType != TIMELINE_CELL_TYPE::Layers || event->pos().x() >= 15))
1194  {
1195  mPrefs->set(SETTING::SHORT_SCRUB, !mbShortScrub);
1196  }
1197 
1198  // -- layer --
1199  Layer* layer = mEditor->object()->getLayer(layerNumber);
1200  if (layer && event->buttons() & Qt::LeftButton)
1201  {
1202  if (mType == TIMELINE_CELL_TYPE::Tracks && (layerNumber != -1) && (frameNumber > 0) && layerNumber < mEditor->object()->getLayerCount())
1203  {
1204  mEditor->scrubTo(frameNumber);
1205  emit insertNewKeyFrame();
1206 
1207  // The release event will toggle the frame on again, so we make sure it gets
1208  // deselected now instead.
1209  layer->setFrameSelected(frameNumber, true);
1210  }
1211  else if (mType == TIMELINE_CELL_TYPE::Layers && event->pos().x() >= 15)
1212  {
1213  editLayerProperties(layer);
1214  }
1215  }
1217 }
1218 
1219 void TimeLineCells::editLayerProperties(Layer *layer) const
1220 {
1221  if (layer->type() != Layer::CAMERA)
1222  {
1223  editLayerName(layer);
1224  return;
1225  }
1226 
1227  auto cameraLayer = dynamic_cast<LayerCamera*>(layer);
1228  Q_ASSERT(cameraLayer);
1229  editLayerProperties(cameraLayer);
1230 }
1231 
1232 void TimeLineCells::editLayerProperties(LayerCamera* cameraLayer) const
1233 {
1234  QRegExp regex("([\\xFFEF-\\xFFFF])+");
1235 
1236  CameraPropertiesDialog dialog(cameraLayer->name(), cameraLayer->getViewRect().width(), cameraLayer->getViewRect().height());
1237  if (dialog.exec() != QDialog::Accepted)
1238  {
1239  return;
1240  }
1241  QString name = dialog.getName().replace(regex, "");
1242 
1243  if (!name.isEmpty())
1244  {
1245  mEditor->layers()->renameLayer(cameraLayer, name);
1246  }
1247  QSettings settings(PENCIL2D, PENCIL2D);
1248  settings.setValue(SETTING_FIELD_W, dialog.getWidth());
1249  settings.setValue(SETTING_FIELD_H, dialog.getHeight());
1250  cameraLayer->setViewRect(QRect(-dialog.getWidth() / 2, -dialog.getHeight() / 2, dialog.getWidth(), dialog.getHeight()));
1251  mEditor->view()->forceUpdateViewTransform();
1252 }
1253 
1254 void TimeLineCells::editLayerName(Layer* layer) const
1255 {
1256  QRegExp regex("([\\xFFEF-\\xFFFF])+");
1257 
1258  bool ok;
1259  QString name = QInputDialog::getText(nullptr, tr("Layer Properties"),
1260  tr("Layer name:"), QLineEdit::Normal,
1261  layer->name(), &ok);
1262  name.replace(regex, "");
1263  if (!ok || name.isEmpty())
1264  {
1265  return;
1266  }
1267 
1268  mEditor->layers()->renameLayer(layer, name);
1269 }
1270 
1271 void TimeLineCells::hScrollChange(int x)
1272 {
1273  mFrameOffset = x;
1274  update();
1275 }
1276 
1277 void TimeLineCells::vScrollChange(int x)
1278 {
1279  mLayerOffset = x;
1280  update();
1281 }
1282 
1283 void TimeLineCells::setMouseMoveY(int x)
1284 {
1285  mMouseMoveY = x;
1286  if (x == 0)
1287  {
1288  update();
1289  }
1290 }
1291 
1292 void TimeLineCells::trackScrubber()
1293 {
1294  if (mEditor->currentFrame() <= mFrameOffset)
1295  {
1296  // Move the timeline back if the scrubber is offscreen to the left
1297  mFrameOffset = mEditor->currentFrame() - 1;
1298  emit offsetChanged(mFrameOffset);
1299  mTimeLine->updateContent();
1300  }
1301  else if (width() < (mEditor->currentFrame() - mFrameOffset + 1) * mFrameSize)
1302  {
1303  // Move timeline forward if the scrubber is offscreen to the right
1304  if (mEditor->playback()->isPlaying())
1305  mFrameOffset = mFrameOffset + ((mEditor->currentFrame() - mFrameOffset) / 2);
1306  else
1307  mFrameOffset = mEditor->currentFrame() - width() / mFrameSize;
1308  emit offsetChanged(mFrameOffset);
1309  mTimeLine->updateContent();
1310  }
1311 }
1312 
1313 void TimeLineCells::onDidLeaveWidget()
1314 {
1315  // Reset last known frame pos to avoid wrong UI states when leaving the widget
1316  mFramePosMoveX = 0;
1317  update();
1318 }
void setText(const QString &text)
AltModifier
QEvent::Type type() const const
const QPalette & palette() const const
void setBottomRight(const QPoint &position)
void setCompositionMode(QPainter::CompositionMode mode)
void setRenderHint(QPainter::RenderHint hint, bool on)
const QColor & color(QPalette::ColorGroup group, QPalette::ColorRole role) const const
QPoint mapToGlobal(const QPoint &pos) const const
void save()
LeftButton
void setAttribute(Qt::WidgetAttribute attribute, bool on)
void setAlpha(int alpha)
int height() const const
Qt::MouseButtons buttons() const const
void drawLine(const QLineF &line)
void chop(int n)
Definition: camera.h:24
QString tr(const char *sourceText, const char *disambiguation, int n)
RoundCap
QAction * addAction(const QString &text)
void update()
int x() const const
int y() const const
int size() const const
int width() const const
QSize size() const const
void setMinimumSize(const QSize &)
void drawRect(const QRectF &rectangle)
QString number(int n, int base)
bool empty() const const
void popup(const QPoint &p, QAction *atAction)
const QBrush & brush(QPalette::ColorGroup group, QPalette::ColorRole role) const const
int x() const const
int red() const const
void setPen(const QColor &color)
void drawEllipse(const QRectF &rectangle)
QPalette palette()
Qt::MouseButton button() const const
void drawPixmap(const QRectF &target, const QPixmap &pixmap, const QRectF &source)
bool isEmpty() const const
Definition: layer.h:38
QPoint pos() const const
void setBrush(const QBrush &brush)
void drawText(const QPointF &position, const QString &text)
void setTitle(const QString &title)
QAction * addSeparator()
void setSizePolicy(QSizePolicy)
QString getText(QWidget *parent, const QString &title, const QString &label, QLineEdit::EchoMode mode, const QString &text, bool *ok, Qt::WindowFlags flags, Qt::InputMethodHints inputMethodHints)
Qt::KeyboardModifiers modifiers() const const
int green() const const
CompositionMode_Overlay
bool isNull() const const
RoundJoin
void restore()
virtual void mouseDoubleClickEvent(QMouseEvent *event)
WA_OpaquePaintEvent
void setTopLeft(const QPoint &position)
QString & replace(int position, int n, QChar after)
int blue() const const
int width() const const
void setWidth(int width)
QAction * addMenu(QMenu *menu)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void setMouseTracking(bool enable)
Definition: object.h:41
QPoint pos() const const
Definition: editor.h:55
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
QObject * parent() const const
virtual bool event(QEvent *event) override
int height() const const
void notifyAnimationLengthChanged()
This should be emitted whenever the animation length frames, eg.
void framesModified()
This should be emitted after modifying multiple frames.